mirror of
https://github.com/wireadmin/wireadmin
synced 2025-02-26 05:48:44 +00:00
fixes various bugs for Tor service
This commit is contained in:
parent
0d9128cfab
commit
b3b9beddf3
@ -69,7 +69,6 @@ These options can be configured by setting environment variables using `-e KEY="
|
|||||||
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------|----------|
|
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------|----------|
|
||||||
| `WG_HOST` | The public IP address of the WireGuard server. | |
|
| `WG_HOST` | The public IP address of the WireGuard server. | |
|
||||||
| `UI_PASSWORD` | The password for the admin UI. | |
|
| `UI_PASSWORD` | The password for the admin UI. | |
|
||||||
| `ORIGIN` | In case you want to access the web-admin remotely, you must set this to the host you are using, for example, `http://<hostname>:3000`. | ✔️ |
|
|
||||||
| `TOR_USE_BRIDGES` | Set this to `1` and then mount the bridges file at `/etc/torrc.d/bridges.conf`. | ✔️ |
|
| `TOR_USE_BRIDGES` | Set this to `1` and then mount the bridges file at `/etc/torrc.d/bridges.conf`. | ✔️ |
|
||||||
| `TOR_*` | The `Torrc` proxy configuration. (e.g. `SocksPort` as `TOR_SOCKSPORT="9050"`) | ✔️ |
|
| `TOR_*` | The `Torrc` proxy configuration. (e.g. `SocksPort` as `TOR_SOCKSPORT="9050"`) | ✔️ |
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
##### Auto-Generated by the WireAdmin. Do not edit. #####
|
##### Auto-Generated by the WireAdmin. Do not edit. #####
|
||||||
VirtualAddrNetwork 10.192.0.0/10
|
VirtualAddrNetwork 10.192.0.0/10
|
||||||
DNSPort 53530
|
DNSPort {{INET_ADDRESS}}:53530
|
||||||
TransPort 59040
|
TransPort {{INET_ADDRESS}}:59040
|
||||||
ClientTransportPlugin obfs4 exec /usr/local/bin/obfs4proxy managed
|
ClientTransportPlugin obfs4 exec /usr/local/bin/obfs4proxy managed
|
||||||
|
@ -9,8 +9,8 @@ services:
|
|||||||
# You can use `openssl rand -base64 8` to generate a secure password
|
# You can use `openssl rand -base64 8` to generate a secure password
|
||||||
- UI_PASSWORD=super-secret-password
|
- UI_PASSWORD=super-secret-password
|
||||||
|
|
||||||
image: wireadmin
|
image: litehex/wireadmin
|
||||||
container_name: litehex/wireadmin
|
container_name: wireadmin
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- persist-data:/data
|
- persist-data:/data
|
||||||
|
@ -4,6 +4,12 @@ set -e
|
|||||||
TOR_CONFIG="/etc/tor/torrc"
|
TOR_CONFIG="/etc/tor/torrc"
|
||||||
ENV_FILE="/app/.env"
|
ENV_FILE="/app/.env"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
local level=$1
|
||||||
|
local message=$2
|
||||||
|
echo -e "\n[$(date +"%Y-%m-%d %H:%M:%S")] [$level] $message"
|
||||||
|
}
|
||||||
|
|
||||||
to_camel_case() {
|
to_camel_case() {
|
||||||
echo "${1}" | awk -F_ '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2));}1' OFS=""
|
echo "${1}" | awk -F_ '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2));}1' OFS=""
|
||||||
}
|
}
|
||||||
@ -12,6 +18,8 @@ generate_tor_config() {
|
|||||||
# IP address of the container
|
# IP address of the container
|
||||||
local inet_address="$(hostname -i | awk '{print $1}')"
|
local inet_address="$(hostname -i | awk '{print $1}')"
|
||||||
|
|
||||||
|
log "notice" "Using the following IP address for the Tor network: $inet_address"
|
||||||
|
|
||||||
sed -i "s/{{INET_ADDRESS}}/$inet_address/g" "${TOR_CONFIG}"
|
sed -i "s/{{INET_ADDRESS}}/$inet_address/g" "${TOR_CONFIG}"
|
||||||
|
|
||||||
# any other environment variables that start with TOR_ are added to the torrc
|
# any other environment variables that start with TOR_ are added to the torrc
|
||||||
@ -42,6 +50,8 @@ generate_tor_config() {
|
|||||||
sed -i '/^[^ ]* $/d' "${TOR_CONFIG}"
|
sed -i '/^[^ ]* $/d' "${TOR_CONFIG}"
|
||||||
# Remove double empty lines
|
# Remove double empty lines
|
||||||
sed -i '/^$/N;/^\n$/D' "${TOR_CONFIG}"
|
sed -i '/^$/N;/^\n$/D' "${TOR_CONFIG}"
|
||||||
|
|
||||||
|
log "notice" "Tor configuration file has been generated"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo " "
|
echo " "
|
||||||
@ -73,12 +83,12 @@ HASHED_PASSWORD=$(printf "%s" "${UI_PASSWORD}" | od -A n -t x1 | tr -d ' \n')
|
|||||||
EOF
|
EOF
|
||||||
unset UI_PASSWORD
|
unset UI_PASSWORD
|
||||||
else
|
else
|
||||||
echo "[error] no password set for the UI"
|
log "error" "no password set for the UI"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$WG_HOST" ]; then
|
if [ -z "$WG_HOST" ]; then
|
||||||
echo "[error] the WG_HOST environment variable is not set"
|
log "error" "the WG_HOST environment variable is not set"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { execa } from 'execa';
|
import { execa } from 'execa';
|
||||||
import logger from '$lib/logger';
|
|
||||||
import { ip } from 'node-netkit';
|
import { ip } from 'node-netkit';
|
||||||
|
|
||||||
export default class Network {
|
export default class Network {
|
||||||
@ -18,7 +17,6 @@ export default class Network {
|
|||||||
const { stdout: o } = await execa(`ip link show | grep ${inet}`, { shell: true });
|
const { stdout: o } = await execa(`ip link show | grep ${inet}`, { shell: true });
|
||||||
return o.trim() !== '';
|
return o.trim() !== '';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Interface does not exist:', inet);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import { sha256 } from '$lib/hash';
|
|||||||
import { fsAccess } from '$lib/fs-extra';
|
import { fsAccess } from '$lib/fs-extra';
|
||||||
import { client } from '$lib/storage';
|
import { client } from '$lib/storage';
|
||||||
import { execa } from 'execa';
|
import { execa } from 'execa';
|
||||||
|
import { ip } from 'node-netkit';
|
||||||
|
|
||||||
export class WGServer {
|
export class WGServer {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
@ -32,12 +33,16 @@ export class WGServer {
|
|||||||
static exists(id: string): boolean {
|
static exists(id: string): boolean {
|
||||||
const serverIds = getServers().map((s) => s.id);
|
const serverIds = getServers().map((s) => s.id);
|
||||||
|
|
||||||
logger.debug({
|
const exists = serverIds.includes(id);
|
||||||
message: `WGServer:Exists: checking for server with id: ${id}`,
|
|
||||||
servers: serverIds,
|
|
||||||
});
|
|
||||||
|
|
||||||
return serverIds.includes(id);
|
if (!exists) {
|
||||||
|
logger.debug({
|
||||||
|
message: `WGServer: Exists: server by id of ${id} does not exists`,
|
||||||
|
servers: serverIds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(): Promise<WgServer> {
|
async get(): Promise<WgServer> {
|
||||||
@ -62,11 +67,16 @@ export class WGServer {
|
|||||||
async stop(): Promise<boolean> {
|
async stop(): Promise<boolean> {
|
||||||
const server = await this.get();
|
const server = await this.get();
|
||||||
|
|
||||||
if (await Network.interfaceExists(`wg${server.confId}`)) {
|
const iface = `wg${server.confId}`;
|
||||||
await execa(`wg-quick down wg${server.confId}`, { shell: true });
|
|
||||||
|
if (await Network.interfaceExists(iface)) {
|
||||||
|
await execa(`wg-quick down ${iface}`, { shell: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.update({ status: 'down' });
|
await this.update({ status: 'down' });
|
||||||
|
|
||||||
|
// TODO: Drop any iptables rules related the interface
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +89,6 @@ export class WGServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isAlreadyUp = await this.isUp();
|
const isAlreadyUp = await this.isUp();
|
||||||
logger.debug('WGServer:Start: isAlreadyUp:', isAlreadyUp);
|
|
||||||
if (isAlreadyUp) {
|
if (isAlreadyUp) {
|
||||||
logger.debug('WGServer:Start: interface already up... taking down');
|
logger.debug('WGServer:Start: interface already up... taking down');
|
||||||
await execa(`wg-quick down wg${server.confId}`, { shell: true });
|
await execa(`wg-quick down wg${server.confId}`, { shell: true });
|
||||||
@ -121,9 +130,6 @@ export class WGServer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('WGServer:Update: updating server at index:', index);
|
|
||||||
console.log(client.lrange(WG_SEVER_PATH, 0, -1));
|
|
||||||
|
|
||||||
client.lset(
|
client.lset(
|
||||||
WG_SEVER_PATH,
|
WG_SEVER_PATH,
|
||||||
index,
|
index,
|
||||||
@ -703,15 +709,23 @@ 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 source = `${s.address}/24`;
|
const source = `${s.address}/24`;
|
||||||
|
|
||||||
|
const route = await ip.route.defaultRoute();
|
||||||
const wg_inet = `wg${s.confId}`;
|
const wg_inet = `wg${s.confId}`;
|
||||||
|
|
||||||
|
if (!route) {
|
||||||
|
throw new Error('No default route found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { stdout: inet_address } = await execa(`hostname -i | awk '{print $1}'`, { shell: true });
|
||||||
|
|
||||||
if (s.tor) {
|
if (s.tor) {
|
||||||
const up = dynaJoin([
|
const up = dynaJoin([
|
||||||
`iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT`,
|
`iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT`,
|
||||||
`iptables -A INPUT -i ${wg_inet} -s ${source} -m state --state NEW -j ACCEPT`,
|
`iptables -A INPUT -i ${wg_inet} -s ${source} -m state --state NEW -j ACCEPT`,
|
||||||
`iptables -t nat -A PREROUTING -i ${wg_inet} -p udp -s ${source} --dport 53 -j DNAT --to-destination 127.0.0.1:53530`,
|
`iptables -t nat -A PREROUTING -i ${wg_inet} -p udp -s ${source} --dport 53 -j DNAT --to-destination ${inet_address}:53530`,
|
||||||
`iptables -t nat -A PREROUTING -i ${wg_inet} -p tcp -s ${source} -j DNAT --to-destination 127.0.0.1:59040`,
|
`iptables -t nat -A PREROUTING -i ${wg_inet} -p tcp -s ${source} -j DNAT --to-destination ${inet_address}:59040`,
|
||||||
`iptables -t nat -A PREROUTING -i ${wg_inet} -p udp -s ${source} -j DNAT --to-destination 127.0.0.1:59040`,
|
`iptables -t nat -A PREROUTING -i ${wg_inet} -p udp -s ${source} -j DNAT --to-destination ${inet_address}:59040`,
|
||||||
`iptables -t nat -A OUTPUT -o lo -j RETURN`,
|
`iptables -t nat -A OUTPUT -o lo -j RETURN`,
|
||||||
`iptables -A OUTPUT -m conntrack --ctstate INVALID -j DROP`,
|
`iptables -A OUTPUT -m conntrack --ctstate INVALID -j DROP`,
|
||||||
`iptables -A OUTPUT -m state --state INVALID -j DROP`,
|
`iptables -A OUTPUT -m state --state INVALID -j DROP`,
|
||||||
@ -720,9 +734,8 @@ export async function makeWgIptables(s: WgServer): Promise<{ up: string; down: s
|
|||||||
return { up, down: up.replace(/-A/g, '-D') };
|
return { up, down: up.replace(/-A/g, '-D') };
|
||||||
}
|
}
|
||||||
|
|
||||||
const inet = await Network.defaultInterface();
|
|
||||||
const up = dynaJoin([
|
const up = dynaJoin([
|
||||||
`iptables -t nat -A POSTROUTING -s ${source} -o ${inet} -j MASQUERADE`,
|
`iptables -t nat -A POSTROUTING -s ${source} -o ${route.dev} -j MASQUERADE`,
|
||||||
`iptables -A INPUT -p udp -m udp --dport ${s.listen} -j ACCEPT`,
|
`iptables -A INPUT -p udp -m udp --dport ${s.listen} -j ACCEPT`,
|
||||||
`iptables -A INPUT -p tcp -m tcp --dport ${s.listen} -j ACCEPT`,
|
`iptables -A INPUT -p tcp -m tcp --dport ${s.listen} -j ACCEPT`,
|
||||||
`iptables -A FORWARD -i ${wg_inet} -j ACCEPT`,
|
`iptables -A FORWARD -i ${wg_inet} -j ACCEPT`,
|
||||||
|
@ -2,8 +2,12 @@ import { json, type RequestHandler } from '@sveltejs/kit';
|
|||||||
import { execa } from 'execa';
|
import { execa } from 'execa';
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ params }) => {
|
export const GET: RequestHandler = async ({ params }) => {
|
||||||
const { stdout } = await execa('screen', ['-ls'], { shell: true });
|
try {
|
||||||
const isRunning = stdout.includes(params.serviceName!);
|
const { stdout } = await execa('screen', ['-ls'], { shell: true });
|
||||||
|
const isRunning = stdout.includes(params.serviceName!);
|
||||||
|
|
||||||
return json({ healthy: isRunning });
|
return json({ healthy: isRunning });
|
||||||
|
} catch (e) {
|
||||||
|
return json({ healthy: false });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -8,24 +8,18 @@ const services: Record<string, Service> = {
|
|||||||
tor: {
|
tor: {
|
||||||
name: 'Tor',
|
name: 'Tor',
|
||||||
command: {
|
command: {
|
||||||
restart: 'screen -L -Logfile /var/vlogs/tor -dmS "tor" tor -f /etc/tor/torrc',
|
start: 'screen -L -Logfile /var/vlogs/tor -dmS "tor" tor -f /etc/tor/torrc',
|
||||||
|
stop: 'pkill tor',
|
||||||
logs: 'logs tor',
|
logs: 'logs tor',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
redis: {
|
|
||||||
name: 'Redis',
|
|
||||||
command: {
|
|
||||||
restart:
|
|
||||||
'screen -L -Logfile /var/vlogs/redis -dmS "redis" bash -c "redis-server --port 6479 --daemonize no --dir /data --appendonly yes"',
|
|
||||||
logs: 'logs redis',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Service {
|
interface Service {
|
||||||
name: string;
|
name: string;
|
||||||
command: {
|
command: {
|
||||||
restart: string;
|
start: string;
|
||||||
|
stop: string;
|
||||||
logs: string;
|
logs: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -41,6 +35,25 @@ export const load: PageServerLoad = async ({ params }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function restartService(serviceName: string) {
|
||||||
|
// Stop
|
||||||
|
const { exitCode: stopExitCode } = execa(services[serviceName].command.stop, { shell: true });
|
||||||
|
|
||||||
|
// Start
|
||||||
|
const { exitCode: startExitCode } = execa(services[serviceName].command.start, { shell: true });
|
||||||
|
|
||||||
|
logger.info({
|
||||||
|
message: `Restarted ${serviceName} service`,
|
||||||
|
stopExitCode,
|
||||||
|
startExitCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stopExitCode,
|
||||||
|
startExitCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
clearLogs: async ({ request, params }) => {
|
clearLogs: async ({ request, params }) => {
|
||||||
const { serviceName } = params;
|
const { serviceName } = params;
|
||||||
@ -68,8 +81,8 @@ export const actions: Actions = {
|
|||||||
const { serviceName } = params;
|
const { serviceName } = params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await execa(services[serviceName!].command.restart, { shell: true });
|
const { stopExitCode, startExitCode } = restartService(serviceName!);
|
||||||
return {};
|
return { stopExitCode, startExitCode };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
throw error(500, 'Unhandled Exception');
|
throw error(500, 'Unhandled Exception');
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
<CardTitle>Logs</CardTitle>
|
<CardTitle>Logs</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent class="relative">
|
<CardContent class="relative">
|
||||||
<textarea class="w-full h-64 p-2 bg-gray-100" readonly bind:value={logs} />
|
<textarea class="w-full h-80 p-2 bg-gray-100" readonly bind:value={logs} />
|
||||||
{#if !logs}
|
{#if !logs}
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<i class="text-4xl animate-spin fas fa-circle-notch"></i>
|
<i class="text-4xl animate-spin fas fa-circle-notch"></i>
|
||||||
|
Loading…
Reference in New Issue
Block a user