Harden admin API routes and shell usage
- Require admin auth on interface-local-ip, network-interfaces, next-listen-port and public-ip (interface-local-ip and public-ip allowed unauthenticated command injection as root) - Validate interface names and call ip/curl via execFileSync - Write host/client WireGuard configs with fs instead of shell heredocs, validating IPs, AllowedIPs, endpoint and listen port - Replace rm -rf/cat/mkdir/chmod/test shell calls with fs APIs - Refuse private media requested through the public media route Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -5,6 +5,8 @@ import type {
|
||||
} from "@/db/types/db";
|
||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||
import { execSync } from "node:child_process";
|
||||
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import validateIpv4 from "@/src/utils/validate-ipv4";
|
||||
import type { APIResponseObject } from "@moduletrace/bunext/types";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { TableType, User } from "@/src/types";
|
||||
@@ -125,6 +127,24 @@ export default async function setupWireguardHost({
|
||||
};
|
||||
}
|
||||
|
||||
if (!validateIpv4({ ip: HOST_WG_IP })) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Invalid Host Private IP address: ${HOST_WG_IP}`,
|
||||
};
|
||||
}
|
||||
|
||||
const LISTEN_PORT = Number(
|
||||
host?.listen_port || AppData["DefaultWGListenPort"],
|
||||
);
|
||||
|
||||
if (!Number.isInteger(LISTEN_PORT)) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Invalid Host listen port: ${host?.listen_port}`,
|
||||
};
|
||||
}
|
||||
|
||||
const is_ip_available = is_update_after_client_setup
|
||||
? { success: true }
|
||||
: host?.wg_ip_address
|
||||
@@ -163,13 +183,12 @@ export default async function setupWireguardHost({
|
||||
}
|
||||
|
||||
try {
|
||||
const HOST_PUBLIC_KEY = execSync(`cat ${HOST_PUBLIC_KEY_FILE}`, {
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const HOST_PUBLIC_KEY = readFileSync(HOST_PUBLIC_KEY_FILE, "utf-8").trim();
|
||||
|
||||
const HOST_PRIVATE_KEY = execSync(`cat ${HOST_PRIVATE_KEY_FILE}`, {
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const HOST_PRIVATE_KEY = readFileSync(
|
||||
HOST_PRIVATE_KEY_FILE,
|
||||
"utf-8",
|
||||
).trim();
|
||||
|
||||
if (!is_update_after_client_setup) {
|
||||
const update_host = await BunSQLite.insert<
|
||||
@@ -189,12 +208,6 @@ export default async function setupWireguardHost({
|
||||
});
|
||||
}
|
||||
|
||||
let sh = ``;
|
||||
|
||||
sh += `set -e\n`;
|
||||
|
||||
sh += `cd ${HOST_CONFIG_DIR}\n`;
|
||||
|
||||
const HOST_SUBNET = `${HOST_WG_IP.split(".").slice(0, 3).join(".")}.0/24`;
|
||||
|
||||
const iptables_scripts = buildHostIptablesScripts({
|
||||
@@ -224,53 +237,53 @@ export default async function setupWireguardHost({
|
||||
};
|
||||
}
|
||||
|
||||
execSync(`mkdir -p ${HOST_IPTABLES_DIR}`, { encoding: "utf-8" });
|
||||
mkdirSync(HOST_IPTABLES_DIR, { recursive: true });
|
||||
await Bun.write(POST_UP_PATH, iptables_scripts.post_up);
|
||||
await Bun.write(POST_DOWN_PATH, iptables_scripts.post_down);
|
||||
execSync(`chmod +x ${POST_UP_PATH} ${POST_DOWN_PATH}`, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
chmodSync(POST_UP_PATH, 0o755);
|
||||
chmodSync(POST_DOWN_PATH, 0o755);
|
||||
|
||||
sh += `cat > ${INTERFACE_NAME}.conf << EOF\n`;
|
||||
sh += `[Interface]\n`;
|
||||
sh += `Address = ${HOST_WG_IP}/24\n`;
|
||||
sh += `ListenPort = ${host?.listen_port || AppData["DefaultWGListenPort"]}\n`;
|
||||
sh += `PrivateKey = ${HOST_PRIVATE_KEY}\n`;
|
||||
sh += `PostUp = ${POST_UP_PATH}\n`;
|
||||
sh += `PostDown = ${POST_DOWN_PATH}\n`;
|
||||
sh += `\n`;
|
||||
let HOST_CONFIG = ``;
|
||||
|
||||
HOST_CONFIG += `[Interface]\n`;
|
||||
HOST_CONFIG += `Address = ${HOST_WG_IP}/24\n`;
|
||||
HOST_CONFIG += `ListenPort = ${LISTEN_PORT}\n`;
|
||||
HOST_CONFIG += `PrivateKey = ${HOST_PRIVATE_KEY}\n`;
|
||||
HOST_CONFIG += `PostUp = ${POST_UP_PATH}\n`;
|
||||
HOST_CONFIG += `PostDown = ${POST_DOWN_PATH}\n`;
|
||||
HOST_CONFIG += `\n`;
|
||||
|
||||
if (clients[0]) {
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const client = clients[i];
|
||||
if (!client?.id) continue;
|
||||
|
||||
if (!validateIpv4({ ip: client.wg_ip_address })) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Invalid Private IP address for client ${client.id}: ${client.wg_ip_address}`,
|
||||
};
|
||||
}
|
||||
|
||||
const { CLIENT_PUBLIC_KEY_FILE } = grabClientDirnames({
|
||||
client,
|
||||
host_id: HOST_ID,
|
||||
});
|
||||
|
||||
const CLIENT_PUBLIC_KEY = execSync(
|
||||
`cat ${CLIENT_PUBLIC_KEY_FILE}`,
|
||||
{
|
||||
encoding: "utf-8",
|
||||
},
|
||||
const CLIENT_PUBLIC_KEY = readFileSync(
|
||||
CLIENT_PUBLIC_KEY_FILE,
|
||||
"utf-8",
|
||||
).trim();
|
||||
|
||||
sh += `[Peer]\n`;
|
||||
sh += `PublicKey = ${CLIENT_PUBLIC_KEY}\n`;
|
||||
sh += `AllowedIPs = ${client.wg_ip_address}/32\n`;
|
||||
sh += `\n`;
|
||||
HOST_CONFIG += `[Peer]\n`;
|
||||
HOST_CONFIG += `PublicKey = ${CLIENT_PUBLIC_KEY}\n`;
|
||||
HOST_CONFIG += `AllowedIPs = ${client.wg_ip_address}/32\n`;
|
||||
HOST_CONFIG += `\n`;
|
||||
}
|
||||
}
|
||||
|
||||
sh += `EOF\n`;
|
||||
|
||||
sh += `chmod 600 ${INTERFACE_NAME}.conf\n`;
|
||||
|
||||
sh += `\n`;
|
||||
|
||||
const exec = execSync(sh, { encoding: "utf-8" });
|
||||
writeFileSync(HOST_CONFIG_FILE, HOST_CONFIG, { mode: 0o600 });
|
||||
chmodSync(HOST_CONFIG_FILE, 0o600);
|
||||
|
||||
if (!is_update_after_client_setup) {
|
||||
execSync(
|
||||
@@ -293,7 +306,7 @@ export default async function setupWireguardHost({
|
||||
|
||||
const firewall_res = await allowSubnetFirewallRules({
|
||||
subnet: HOST_SUBNET,
|
||||
listen_port: host?.listen_port || AppData["DefaultWGListenPort"],
|
||||
listen_port: LISTEN_PORT,
|
||||
});
|
||||
|
||||
if (!firewall_res.success) {
|
||||
@@ -304,7 +317,7 @@ export default async function setupWireguardHost({
|
||||
|
||||
return {
|
||||
success: true,
|
||||
msg: [exec.trim(), manage_res.msg, firewall_res.msg].join("\n\n"),
|
||||
msg: [manage_res.msg, firewall_res.msg].join("\n\n"),
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user