diff --git a/electron/main/index.ts b/electron/main/index.ts index 55023dc..0789650 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -5,9 +5,10 @@ import * as electronRemote from '@electron/remote/main'; import {is, optimizer} from '@electron-toolkit/utils'; import windowStateKeeper from 'electron-window-state'; import {autoUpdater} from 'electron-updater'; +import sudo from 'sudo-prompt'; import './appinfo'; import * as ipc from './ipc'; -import {PID_FILE_OPENVPN, PID_FILE_WIREGUARD} from './globals'; +import {DATA_PATH, PID_FILE_OPENVPN, PID_FILE_WIREGUARD} from './globals'; import settings from './settings'; import {getCurrentMonitor} from './vpn/monitor'; import {Status} from './vpn/status'; @@ -208,10 +209,26 @@ export async function stopVPN(isSwitchingServer = false): Promise { if (!isSwitchingServer) { getCurrentMonitor()?.setStatus(Status.DISCONNECTING); } - await Promise.all([ - stopVPNFromPidFile(PID_FILE_WIREGUARD), - stopVPNFromPidFile(PID_FILE_OPENVPN) - ]); + if (process.platform === 'linux') { + sudo.exec(`wg-quick down "${DATA_PATH}/dicyvpn.conf"`, { + name: 'DicyVPN' + }, (error, stdout, stderr) => { + if (error) { + console.error(`[WireGuard] error: ${error.message}`); + return; + } + if (stderr) { + console.error(`[WireGuard stderr] ${stderr}`); + return; + } + console.log(`[WireGuard stdout] ${stdout}`); + }); + } else { + await Promise.all([ + stopVPNFromPidFile(PID_FILE_WIREGUARD), + stopVPNFromPidFile(PID_FILE_OPENVPN) + ]); + } updateTray(false); } diff --git a/electron/main/vpn/linux/wireguard.ts b/electron/main/vpn/linux/wireguard.ts new file mode 100644 index 0000000..bcf440c --- /dev/null +++ b/electron/main/vpn/linux/wireguard.ts @@ -0,0 +1,60 @@ +import sudo from 'sudo-prompt'; +import {WireGuard} from '../wireguard'; +import {DATA_PATH} from '../../globals'; + +export class WireGuardLinux extends WireGuard { + constructor( + private ip: string, + private port: number, + private privateKey: string, + private publicKey: string, + private internalIp: string, + private ips: string[], + private isIpsAllowlist: boolean, + private dns: string[] + ) { + super(); + } + + async start(): Promise { + this.writeConfig('dicyvpn.conf'); + sudo.exec(`wg-quick up "${DATA_PATH}/dicyvpn.conf"`, { + name: 'DicyVPN' + }, (error, stdout, stderr) => { + if (error) { + console.error(`[WireGuard] error: ${error.message}`); + return; + } + if (stderr) { + console.error(`[WireGuard stderr] ${stderr}`); + return; + } + console.log(`[WireGuard stdout] ${stdout}`); + }); + } + + protected getConfig(): string { + let allowedIPs = 'AllowedIPs = '; + if (this.ips.length > 0) { // enable split tunneling for IPs + if (this.isIpsAllowlist) { // allowlist + allowedIPs += this.ips.join(', '); + } + } else { + allowedIPs += '0.0.0.0/0'; + } + // route all IPv6 traffic, prevents leaks through IPv6 + allowedIPs += ', ::/0'; + + return ` + [Interface] + PrivateKey = ${this.privateKey} + Address = ${this.internalIp}/32 + DNS = ${this.dns.join(', ')} + + [Peer] + PublicKey = ${this.publicKey} + Endpoint = ${this.ip}:${this.port} + PersistentKeepalive = 15 + ${allowedIPs}`; + } +} diff --git a/electron/main/vpn/vpn.ts b/electron/main/vpn/vpn.ts index 708afc6..3c6d198 100644 --- a/electron/main/vpn/vpn.ts +++ b/electron/main/vpn/vpn.ts @@ -8,6 +8,7 @@ import {Status} from './status'; import {createApi} from '../../../common/api'; import {INVALID_REFRESH_TOKEN} from '../../../common/channels'; import {WireGuardWindows} from './windows/wireguard'; +import {WireGuardLinux} from "./linux/wireguard"; export interface VPN { start(): Promise; @@ -112,6 +113,11 @@ function getNewWireGuardInstance(serverIp: string, port: number, privateKey: str serverIp, port, privateKey, publicKey, internalIp, ips, isIpsAllowlist, apps, isAppsAllowlist, dns ); + case 'linux': + return new WireGuardLinux( + serverIp, port, privateKey, publicKey, internalIp, + ips, isIpsAllowlist, dns + ); } throw new Error('Unsupported platform'); } diff --git a/electron/main/vpn/windows/wireguard.ts b/electron/main/vpn/windows/wireguard.ts index 5931091..ab8b866 100644 --- a/electron/main/vpn/windows/wireguard.ts +++ b/electron/main/vpn/windows/wireguard.ts @@ -21,7 +21,7 @@ export class WireGuardWindows extends WireGuard { } async start(): Promise { - this.writeConfig(); + this.writeConfig('wireguard.conf'); const exe = getWireGuardClientPath(); const args = ['run', '-config', `${DATA_PATH}/wireguard.conf`, '-log-level', 'info']; const child = spawn(exe, args); diff --git a/electron/main/vpn/wireguard.ts b/electron/main/vpn/wireguard.ts index 3b5461e..10e0267 100644 --- a/electron/main/vpn/wireguard.ts +++ b/electron/main/vpn/wireguard.ts @@ -6,9 +6,9 @@ export abstract class WireGuard implements VPN { abstract start(): Promise; - protected writeConfig() { + protected writeConfig(filename: string) { const conf = this.getConfig(); - fs.writeFileSync(`${DATA_PATH}/wireguard.conf`, conf); + fs.writeFileSync(`${DATA_PATH}/${filename}`, conf); } protected abstract getConfig(): string; diff --git a/package-lock.json b/package-lock.json index 365f91b..4d221e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "i18n-iso-countries": "^7.6.0", "ping": "^0.4.2", "pinia": "^2.1.6", + "sudo-prompt": "^9.2.1", "svg-pan-zoom-container": "^0.6.1", "tail": "^2.2.6", "vue": "^3.2.45", @@ -6038,6 +6039,11 @@ "node": "*" } }, + "node_modules/sudo-prompt": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", diff --git a/package.json b/package.json index 8be5643..423c6cd 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "i18n-iso-countries": "^7.6.0", "ping": "^0.4.2", "pinia": "^2.1.6", + "sudo-prompt": "^9.2.1", "svg-pan-zoom-container": "^0.6.1", "tail": "^2.2.6", "vue": "^3.2.45", diff --git a/src/App.vue b/src/App.vue index 44228e7..032fe46 100644 --- a/src/App.vue +++ b/src/App.vue @@ -60,9 +60,11 @@ export default { this.$router.push('/'); }); - this.currentServer.$patch({ - status: await window.preload.isRunning() ? Status.CONNECTED : Status.NOT_RUNNING - }); + if (window.electron.process.platform !== 'linux') { + this.currentServer.$patch({ + status: await window.preload.isRunning() ? Status.CONNECTED : Status.NOT_RUNNING + }); + } }, beforeUnmount() { window.preload.removeListener('status-change', this.onStatusChange);