mirror of
https://github.com/wireadmin/wireadmin
synced 2025-04-27 17:32:01 +00:00
using execa
for accessing cli tools
This commit is contained in:
parent
9dd427f90c
commit
ac5275168c
@ -1,43 +1,31 @@
|
|||||||
import Shell from '$lib/shell';
|
import { execaCommand } from 'execa';
|
||||||
|
import logger from '$lib/logger';
|
||||||
|
|
||||||
export default class Network {
|
export default class Network {
|
||||||
public static async createInterface(inet: string, address: string): Promise<boolean> {
|
|
||||||
// First, check if the interface already exists.
|
|
||||||
const interfaces = await Shell.exec(`ip link show | grep ${inet}`, true);
|
|
||||||
if (interfaces.includes(`${inet}`)) {
|
|
||||||
console.error(`failed to create interface, ${inet} already exists!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const o2 = await Shell.exec(`ip address add dev ${inet} ${address}`);
|
|
||||||
// check if it has any error
|
|
||||||
if (o2 !== '') {
|
|
||||||
console.error(`failed to assign ip to interface, ${o2}`);
|
|
||||||
console.log(`removing interface ${inet} due to errors`);
|
|
||||||
await Shell.exec(`ip link delete dev ${inet}`, true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async dropInterface(inet: string) {
|
public static async dropInterface(inet: string) {
|
||||||
await Shell.exec(`ip link delete dev ${inet}`, true);
|
await execaCommand(`ip link delete dev ${inet}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async defaultInterface(): Promise<string> {
|
public static async defaultInterface(): Promise<string> {
|
||||||
return await Shell.exec(`ip route list default | awk '{print $5}'`);
|
const { stdout: o } = await execaCommand(`ip route list default | awk '{print $5}'`);
|
||||||
|
return o.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async checkInterfaceExists(inet: string): Promise<boolean> {
|
public static async interfaceExists(inet: string): Promise<boolean> {
|
||||||
return await Shell.exec(`ip link show | grep ${inet}`, true).then((o) => o.trim() !== '');
|
try {
|
||||||
|
const { stdout: o } = await execaCommand(`ip link show | grep ${inet}`);
|
||||||
|
console.log(o);
|
||||||
|
return o.trim() !== '';
|
||||||
|
} catch (e) {
|
||||||
|
logger.debug('Interface does not exist:', inet);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getInUsePorts(): Promise<number[]> {
|
public static async inUsePorts(): Promise<number[]> {
|
||||||
const ports = [];
|
const ports = [];
|
||||||
const output = await Shell.exec(
|
const { stdout: output } = await execaCommand(
|
||||||
`netstat -tulpn | grep LISTEN | awk '{print $4}' | awk -F ':' '{print $NF}'`,
|
`netstat -tulpn | grep LISTEN | awk '{print $4}' | awk -F ':' '{print $NF}'`,
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
for (const line of output.split('\n')) {
|
for (const line of output.split('\n')) {
|
||||||
const clean = Number(line.trim());
|
const clean = Number(line.trim());
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { exec } from 'child_process';
|
|
||||||
import logger from '$lib/logger';
|
|
||||||
|
|
||||||
export default class Shell {
|
|
||||||
public static async exec(
|
|
||||||
command: string,
|
|
||||||
safe: boolean = false,
|
|
||||||
...args: string[]
|
|
||||||
): Promise<string> {
|
|
||||||
if (process.platform !== 'linux') {
|
|
||||||
throw new Error('This program is not meant to run non UNIX systems');
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
const cmd = `${command}${args.length > 0 ? ` ${args.join(' ')}` : ''}`;
|
|
||||||
exec(cmd, { shell: 'bash' }, (err, stdout, stderr) => {
|
|
||||||
if (err) {
|
|
||||||
const message = `Command Failed: ${JSON.stringify(
|
|
||||||
{
|
|
||||||
cmd,
|
|
||||||
code: err.code,
|
|
||||||
killed: err.killed,
|
|
||||||
stderr,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
)}`;
|
|
||||||
|
|
||||||
if (safe) {
|
|
||||||
logger.debug(message);
|
|
||||||
return resolve(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.error(message);
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(String(stdout).trim());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import path from 'path';
|
|||||||
import deepmerge from 'deepmerge';
|
import deepmerge from 'deepmerge';
|
||||||
import type { Peer, WgKey, WgServer } from '$lib/typings';
|
import type { Peer, WgKey, WgServer } from '$lib/typings';
|
||||||
import Network from '$lib/network';
|
import Network from '$lib/network';
|
||||||
import Shell from '$lib/shell';
|
|
||||||
import { WG_PATH, WG_SEVER_PATH } from '$lib/constants';
|
import { WG_PATH, WG_SEVER_PATH } from '$lib/constants';
|
||||||
import { dynaJoin, isJson } from '$lib/utils';
|
import { dynaJoin, isJson } from '$lib/utils';
|
||||||
import { getPeerConf } from '$lib/wireguard/utils';
|
import { getPeerConf } from '$lib/wireguard/utils';
|
||||||
@ -51,8 +50,8 @@ export class WGServer {
|
|||||||
async stop(): Promise<boolean> {
|
async stop(): Promise<boolean> {
|
||||||
const server = await this.get();
|
const server = await this.get();
|
||||||
|
|
||||||
if (await Network.checkInterfaceExists(`wg${server.confId}`)) {
|
if (await Network.interfaceExists(`wg${server.confId}`)) {
|
||||||
await Shell.exec(`wg-quick down wg${server.confId}`, true);
|
await execaCommand(`wg-quick down wg${server.confId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.update({ status: 'down' });
|
await this.update({ status: 'down' });
|
||||||
@ -67,11 +66,14 @@ export class WGServer {
|
|||||||
await this.writeConfigFile(server);
|
await this.writeConfigFile(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await Network.checkInterfaceExists(`wg${server.confId}`)) {
|
const isAlreadyUp = await this.isUp();
|
||||||
await Shell.exec(`wg-quick down wg${server.confId}`, true);
|
logger.debug('WGServer:Start: isAlreadyUp:', isAlreadyUp);
|
||||||
|
if (isAlreadyUp) {
|
||||||
|
logger.debug('WGServer:Start: interface already up... taking down');
|
||||||
|
await execaCommand(`wg-quick down wg${server.confId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Shell.exec(`wg-quick up wg${server.confId}`);
|
await execaCommand(`wg-quick up wg${server.confId}`);
|
||||||
|
|
||||||
await this.update({ status: 'up' });
|
await this.update({ status: 'up' });
|
||||||
return true;
|
return true;
|
||||||
@ -132,7 +134,7 @@ export class WGServer {
|
|||||||
await this.update({ confHash: getConfigHash(wg.confId) });
|
await this.update({ confHash: getConfigHash(wg.confId) });
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasInterface(): Promise<boolean> {
|
async isUp(): Promise<boolean> {
|
||||||
const server = await this.get();
|
const server = await this.get();
|
||||||
try {
|
try {
|
||||||
const res = await execaCommand(`wg show wg${server.confId}`);
|
const res = await execaCommand(`wg show wg${server.confId}`);
|
||||||
@ -144,7 +146,7 @@ export class WGServer {
|
|||||||
|
|
||||||
async getUsage(): Promise<WgUsage> {
|
async getUsage(): Promise<WgUsage> {
|
||||||
const server = await this.get();
|
const server = await this.get();
|
||||||
const hasInterface = await this.hasInterface();
|
const hasInterface = await this.isUp();
|
||||||
|
|
||||||
const usages: WgUsage = {
|
const usages: WgUsage = {
|
||||||
total: { rx: 0, tx: 0 },
|
total: { rx: 0, tx: 0 },
|
||||||
@ -371,14 +373,6 @@ function resolveConfigPath(confId: number): string {
|
|||||||
return path.resolve(path.join(WG_PATH, `wg${confId}.conf`));
|
return path.resolve(path.join(WG_PATH, `wg${confId}.conf`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is for checking out WireGuard server is running
|
|
||||||
*/
|
|
||||||
async function wgCheckout(configId: number): Promise<boolean> {
|
|
||||||
const res = await Shell.exec(`ip link show | grep wg${configId}`, true);
|
|
||||||
return res.includes(`wg${configId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readWgConf(configId: number): Promise<WgServer> {
|
export async function readWgConf(configId: number): Promise<WgServer> {
|
||||||
const confPath = resolveConfigPath(configId);
|
const confPath = resolveConfigPath(configId);
|
||||||
const conf = fs.readFileSync(confPath, 'utf-8');
|
const conf = fs.readFileSync(confPath, 'utf-8');
|
||||||
@ -462,7 +456,10 @@ export async function readWgConf(configId: number): Promise<WgServer> {
|
|||||||
reachedPeers = true;
|
reachedPeers = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server.status = (await wgCheckout(configId)) ? 'up' : 'down';
|
|
||||||
|
const hasInterface = await Network.interfaceExists(`wg${configId}`);
|
||||||
|
server.status = hasInterface ? 'up' : 'down';
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,9 +502,9 @@ function wgPeersStr(configId: number): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function generateWgKey(): Promise<WgKey> {
|
export async function generateWgKey(): Promise<WgKey> {
|
||||||
const privateKey = await Shell.exec('wg genkey');
|
const { stdout: privateKey } = await execaCommand('wg genkey');
|
||||||
const publicKey = await Shell.exec(`echo ${privateKey} | wg pubkey`);
|
const { stdout: publicKey } = await execaCommand(`echo ${privateKey} | wg pubkey`);
|
||||||
const preSharedKey = await Shell.exec('wg genkey');
|
const { stdout: preSharedKey } = await execaCommand('wg genkey');
|
||||||
return { privateKey, publicKey, preSharedKey };
|
return { privateKey, publicKey, preSharedKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,10 +576,10 @@ export async function generateWgServer(config: GenerateWgServerParams): Promise<
|
|||||||
await wg.update({ confHash: getConfigHash(confId) });
|
await wg.update({ confHash: getConfigHash(confId) });
|
||||||
|
|
||||||
// to ensure interface does not exists
|
// to ensure interface does not exists
|
||||||
await Shell.exec(`wg-quick down wg${confId}`, true);
|
await wg.stop();
|
||||||
|
|
||||||
// restart WireGuard
|
// restart WireGuard
|
||||||
await Shell.exec(`wg-quick up wg${confId}`);
|
await wg.start();
|
||||||
|
|
||||||
// return server id
|
// return server id
|
||||||
return uuid;
|
return uuid;
|
||||||
@ -595,7 +592,7 @@ export async function isIPReserved(ip: string): Promise<boolean> {
|
|||||||
|
|
||||||
export async function isPortReserved(port: number): Promise<boolean> {
|
export async function isPortReserved(port: number): Promise<boolean> {
|
||||||
const inUsePorts = [
|
const inUsePorts = [
|
||||||
await Network.getInUsePorts(),
|
await Network.inUsePorts(),
|
||||||
(await getServers()).map((s) => Number(s.listen)),
|
(await getServers()).map((s) => Number(s.listen)),
|
||||||
].flat();
|
].flat();
|
||||||
return inUsePorts.includes(port);
|
return inUsePorts.includes(port);
|
||||||
@ -676,7 +673,7 @@ export async function findServer(
|
|||||||
|
|
||||||
export async function makeWgIptables(s: WgServer): Promise<{ up: string; down: string }> {
|
export async function makeWgIptables(s: WgServer): Promise<{ up: string; down: string }> {
|
||||||
const inet = await Network.defaultInterface();
|
const inet = await Network.defaultInterface();
|
||||||
const inet_address = await Shell.exec(`hostname -i | awk '{print $1}'`);
|
const { stdout: inet_address } = await execaCommand(`hostname -i | awk '{print $1}'`);
|
||||||
|
|
||||||
const source = `${s.address}/24`;
|
const source = `${s.address}/24`;
|
||||||
const wg_inet = `wg${s.confId}`;
|
const wg_inet = `wg${s.confId}`;
|
||||||
|
@ -15,7 +15,7 @@ export const load: PageServerLoad = async ({ params }) => {
|
|||||||
const server = await wg.get();
|
const server = await wg.get();
|
||||||
|
|
||||||
if (server.status === 'up') {
|
if (server.status === 'up') {
|
||||||
const hasInterface = await wg.hasInterface();
|
const hasInterface = await wg.isUp();
|
||||||
if (!hasInterface) {
|
if (!hasInterface) {
|
||||||
await wg.start();
|
await wg.start();
|
||||||
}
|
}
|
||||||
@ -143,7 +143,10 @@ export const actions: Actions = {
|
|||||||
|
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Exception: ChangeState:', e);
|
logger.error({
|
||||||
|
message: 'Exception: ChangeState',
|
||||||
|
exception: e,
|
||||||
|
});
|
||||||
throw error(500, 'Unhandled Exception');
|
throw error(500, 'Unhandled Exception');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -7,7 +7,7 @@ export const GET: RequestHandler = async () => {
|
|||||||
for (const { id } of await getServers()) {
|
for (const { id } of await getServers()) {
|
||||||
const wg = new WGServer(id);
|
const wg = new WGServer(id);
|
||||||
const server = await wg.get();
|
const server = await wg.get();
|
||||||
const hasInterface = await wg.hasInterface();
|
const hasInterface = await wg.isUp();
|
||||||
|
|
||||||
// If the server is up and the interface doesn't exist, start it
|
// If the server is up and the interface doesn't exist, start it
|
||||||
if (server.status === 'up' && !hasInterface) {
|
if (server.status === 'up' && !hasInterface) {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import Shell from '$lib/shell';
|
|
||||||
import 'dotenv/config';
|
|
||||||
import logger from '$lib/logger';
|
import logger from '$lib/logger';
|
||||||
|
import { execaCommand } from 'execa';
|
||||||
|
import 'dotenv/config';
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => {
|
export const GET: RequestHandler = async () => {
|
||||||
let { WG_HOST } = process.env;
|
let { WG_HOST } = process.env;
|
||||||
|
|
||||||
// if the host is not set, then we are using the server's public IP
|
// if the host is not set, then we are using the server's public IP
|
||||||
if (!WG_HOST) {
|
if (!WG_HOST) {
|
||||||
const resp = await Shell.exec('curl -s ifconfig.me', true);
|
const { stdout: resp } = await execaCommand('curl -s ifconfig.me');
|
||||||
WG_HOST = resp.trim();
|
WG_HOST = resp.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user