mirror of
https://github.com/wireadmin/wireadmin
synced 2025-03-09 13:20:39 +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 {
|
||||
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) {
|
||||
await Shell.exec(`ip link delete dev ${inet}`, true);
|
||||
await execaCommand(`ip link delete dev ${inet}`);
|
||||
}
|
||||
|
||||
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> {
|
||||
return await Shell.exec(`ip link show | grep ${inet}`, true).then((o) => o.trim() !== '');
|
||||
public static async interfaceExists(inet: string): Promise<boolean> {
|
||||
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 output = await Shell.exec(
|
||||
const { stdout: output } = await execaCommand(
|
||||
`netstat -tulpn | grep LISTEN | awk '{print $4}' | awk -F ':' '{print $NF}'`,
|
||||
true,
|
||||
);
|
||||
for (const line of output.split('\n')) {
|
||||
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 type { Peer, WgKey, WgServer } from '$lib/typings';
|
||||
import Network from '$lib/network';
|
||||
import Shell from '$lib/shell';
|
||||
import { WG_PATH, WG_SEVER_PATH } from '$lib/constants';
|
||||
import { dynaJoin, isJson } from '$lib/utils';
|
||||
import { getPeerConf } from '$lib/wireguard/utils';
|
||||
@ -51,8 +50,8 @@ export class WGServer {
|
||||
async stop(): Promise<boolean> {
|
||||
const server = await this.get();
|
||||
|
||||
if (await Network.checkInterfaceExists(`wg${server.confId}`)) {
|
||||
await Shell.exec(`wg-quick down wg${server.confId}`, true);
|
||||
if (await Network.interfaceExists(`wg${server.confId}`)) {
|
||||
await execaCommand(`wg-quick down wg${server.confId}`);
|
||||
}
|
||||
|
||||
await this.update({ status: 'down' });
|
||||
@ -67,11 +66,14 @@ export class WGServer {
|
||||
await this.writeConfigFile(server);
|
||||
}
|
||||
|
||||
if (await Network.checkInterfaceExists(`wg${server.confId}`)) {
|
||||
await Shell.exec(`wg-quick down wg${server.confId}`, true);
|
||||
const isAlreadyUp = await this.isUp();
|
||||
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' });
|
||||
return true;
|
||||
@ -132,7 +134,7 @@ export class WGServer {
|
||||
await this.update({ confHash: getConfigHash(wg.confId) });
|
||||
}
|
||||
|
||||
async hasInterface(): Promise<boolean> {
|
||||
async isUp(): Promise<boolean> {
|
||||
const server = await this.get();
|
||||
try {
|
||||
const res = await execaCommand(`wg show wg${server.confId}`);
|
||||
@ -144,7 +146,7 @@ export class WGServer {
|
||||
|
||||
async getUsage(): Promise<WgUsage> {
|
||||
const server = await this.get();
|
||||
const hasInterface = await this.hasInterface();
|
||||
const hasInterface = await this.isUp();
|
||||
|
||||
const usages: WgUsage = {
|
||||
total: { rx: 0, tx: 0 },
|
||||
@ -371,14 +373,6 @@ function resolveConfigPath(confId: number): string {
|
||||
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> {
|
||||
const confPath = resolveConfigPath(configId);
|
||||
const conf = fs.readFileSync(confPath, 'utf-8');
|
||||
@ -462,7 +456,10 @@ export async function readWgConf(configId: number): Promise<WgServer> {
|
||||
reachedPeers = true;
|
||||
}
|
||||
}
|
||||
server.status = (await wgCheckout(configId)) ? 'up' : 'down';
|
||||
|
||||
const hasInterface = await Network.interfaceExists(`wg${configId}`);
|
||||
server.status = hasInterface ? 'up' : 'down';
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
@ -505,9 +502,9 @@ function wgPeersStr(configId: number): string[] {
|
||||
}
|
||||
|
||||
export async function generateWgKey(): Promise<WgKey> {
|
||||
const privateKey = await Shell.exec('wg genkey');
|
||||
const publicKey = await Shell.exec(`echo ${privateKey} | wg pubkey`);
|
||||
const preSharedKey = await Shell.exec('wg genkey');
|
||||
const { stdout: privateKey } = await execaCommand('wg genkey');
|
||||
const { stdout: publicKey } = await execaCommand(`echo ${privateKey} | wg pubkey`);
|
||||
const { stdout: preSharedKey } = await execaCommand('wg genkey');
|
||||
return { privateKey, publicKey, preSharedKey };
|
||||
}
|
||||
|
||||
@ -579,10 +576,10 @@ export async function generateWgServer(config: GenerateWgServerParams): Promise<
|
||||
await wg.update({ confHash: getConfigHash(confId) });
|
||||
|
||||
// to ensure interface does not exists
|
||||
await Shell.exec(`wg-quick down wg${confId}`, true);
|
||||
await wg.stop();
|
||||
|
||||
// restart WireGuard
|
||||
await Shell.exec(`wg-quick up wg${confId}`);
|
||||
await wg.start();
|
||||
|
||||
// return server id
|
||||
return uuid;
|
||||
@ -595,7 +592,7 @@ export async function isIPReserved(ip: string): Promise<boolean> {
|
||||
|
||||
export async function isPortReserved(port: number): Promise<boolean> {
|
||||
const inUsePorts = [
|
||||
await Network.getInUsePorts(),
|
||||
await Network.inUsePorts(),
|
||||
(await getServers()).map((s) => Number(s.listen)),
|
||||
].flat();
|
||||
return inUsePorts.includes(port);
|
||||
@ -676,7 +673,7 @@ export async function findServer(
|
||||
|
||||
export async function makeWgIptables(s: WgServer): Promise<{ up: string; down: string }> {
|
||||
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 wg_inet = `wg${s.confId}`;
|
||||
|
@ -15,7 +15,7 @@ export const load: PageServerLoad = async ({ params }) => {
|
||||
const server = await wg.get();
|
||||
|
||||
if (server.status === 'up') {
|
||||
const hasInterface = await wg.hasInterface();
|
||||
const hasInterface = await wg.isUp();
|
||||
if (!hasInterface) {
|
||||
await wg.start();
|
||||
}
|
||||
@ -143,7 +143,10 @@ export const actions: Actions = {
|
||||
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
logger.error('Exception: ChangeState:', e);
|
||||
logger.error({
|
||||
message: 'Exception: ChangeState',
|
||||
exception: e,
|
||||
});
|
||||
throw error(500, 'Unhandled Exception');
|
||||
}
|
||||
},
|
||||
|
@ -7,7 +7,7 @@ export const GET: RequestHandler = async () => {
|
||||
for (const { id } of await getServers()) {
|
||||
const wg = new WGServer(id);
|
||||
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 (server.status === 'up' && !hasInterface) {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import Shell from '$lib/shell';
|
||||
import 'dotenv/config';
|
||||
import logger from '$lib/logger';
|
||||
import { execaCommand } from 'execa';
|
||||
import 'dotenv/config';
|
||||
|
||||
export const GET: RequestHandler = async () => {
|
||||
let { WG_HOST } = process.env;
|
||||
|
||||
// if the host is not set, then we are using the server's public IP
|
||||
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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user