1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 04:38:53 +03:00
This commit is contained in:
Anton Afanasyeu
2026-06-08 20:53:37 +02:00
parent b981d00f66
commit 1ddb51312c
13 changed files with 302 additions and 105 deletions

View File

@@ -47,15 +47,20 @@ return [
],
// On-demand remote access (WireGuard v1) — see docs/REMOTE_ACCESS_IMPL.md
'remote_access' => [
// Public UDP endpoint shown to devices (FE DNAT → BE wg0)
'wg_endpoint' => 'ra.apps.f0xx.org:51820',
// Server WG public key (wg show wg0 public-key on BE)
'wg_server_public_key' => '',
// Public UDP endpoint (FE DNAT → BE wg0 ListenPort)
'wg_endpoint' => 'ra.apps.f0xx.org:45340',
// wg show wg0 public-key on BE — REQUIRED for production connect
'wg_server_public_key' => 'EV2nnwHH7xe3jA5J46Rjtl8ao+ybZinibKg2Hy83GBw=',
'wg_interface' => 'wg0',
// Run `wg set wg0 peer …` when device receives connect (requires wireguard-tools on BE)
// Client / server addresses (must match wg0 Address subnet on BE)
'wg_server_allowed_ips' => '172.200.2.1/32',
'wg_client_ip_prefix' => '172.200.2.',
'wg_client_ip_min' => 10,
'wg_client_ip_max' => 250,
// Prefix for `wg set` when PHP-FPM runs as nobody (see examples/wireguard/alpine-be-sudoers-wg)
'wg_set_prefix' => 'sudo -n ',
'provision_peers' => true,
// Reject connect if wg genkey/pubkey unavailable (set true on production BE)
'require_wg_tools' => false,
'require_wg_tools' => true,
'session_ttl_s' => 3600,
'min_poll_interval_s' => 45,
// Reverse SSH (alpha): outbound -R from device; no VpnService on phone

View File

@@ -9,8 +9,8 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
source "$ROOT/scripts/ra_lib.sh"
WG_HOST="${WG_HOST:-ra.apps.f0xx.org}"
WG_PORT="${WG_PORT:-51820}"
BE_WG_IP="${BE_WG_IP:-10.66.66.1}"
WG_PORT="${WG_PORT:-45340}"
BE_WG_IP="${BE_WG_IP:-172.200.2.1}"
DEVICE_ID="${RA_DEVICE_ID:-ra-udp-$(hostname -s | tr ' ' '-')}"
CONF="/tmp/ra-udp-${DEVICE_ID}.conf"
APPLY="${RA_UDP_APPLY:-0}"

View File

@@ -336,7 +336,7 @@ final class RemoteAccessRepository {
&& (bool) cfg('remote_access.require_wg_tools', false)) {
return self::raEnvelope('deny', ['reason' => 'wg_tools_required']);
}
$endpoint = (string) ($session['endpoint'] ?? cfg('remote_access.wg_endpoint', 'ra.apps.f0xx.org:51820'));
$endpoint = (string) ($session['endpoint'] ?? WireGuardAddressPool::defaultEndpoint());
$expiresAt = (int) ($session['expires_at'] ?? (time() + 3600));
self::markSessionActive($sessionId);
@@ -435,7 +435,7 @@ final class RemoteAccessRepository {
'client_public' => $pub,
'client_address' => $addr,
'server_public' => $serverPub,
'allowed_ips' => $allowed !== '' ? $allowed : '10.66.66.1/32',
'allowed_ips' => $allowed !== '' ? $allowed : WireGuardAddressPool::serverAllowedIps(),
];
}
$pair = self::generateWireGuardKeyPair();
@@ -444,13 +444,13 @@ final class RemoteAccessRepository {
if ($pub === '' || str_starts_with($pub, 'dev-placeholder-pub-')) {
$pub = WireGuardPeerProvisioner::publicKeyFromPrivate($priv);
}
$clientIp = self::allocateClientAddress($sessionId);
$clientIp = WireGuardAddressPool::allocateClientAddress($sessionId);
$serverPub = (string) cfg('remote_access.wg_server_public_key', '');
if ($serverPub === '') {
$serverPair = self::generateWireGuardKeyPair();
$serverPub = $serverPair['public'];
}
$allowed = '10.66.66.1/32';
$allowed = WireGuardAddressPool::serverAllowedIps();
Database::pdo()->prepare(
'UPDATE remote_access_sessions SET wg_client_private_key = ?, wg_client_public_key = ?, wg_client_address = ?, wg_server_public_key = ?, wg_peer_allowed_ips = ? WHERE session_id = ?'
)->execute([$priv, $pub, $clientIp, $serverPub, $allowed, $sessionId]);
@@ -468,11 +468,6 @@ final class RemoteAccessRepository {
];
}
private static function allocateClientAddress(string $sessionId): string {
$n = abs(crc32($sessionId)) % 200 + 2;
return '10.66.66.' . $n . '/32';
}
/** @return array{private:string,public:string} */
public static function generateWireGuardKeyPair(): array {
$priv = trim((string) @shell_exec('wg genkey 2>/dev/null') ?: '');
@@ -552,7 +547,7 @@ final class RemoteAccessRepository {
$port = (int) cfg('remote_access.rssh.bastion_port', 443);
$endpoint = $port === 443 ? $host : $host . ':' . $port;
} else {
$endpoint = (string) cfg('remote_access.wg_endpoint', 'ra.apps.f0xx.org:51820');
$endpoint = WireGuardAddressPool::defaultEndpoint();
}
$expiresAt = time() + (int) cfg('remote_access.session_ttl_s', 3600);
$now = self::nowSql();

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/** WG client IPs + server AllowedIPs from config (matches live wg0 on BE). */
final class WireGuardAddressPool {
private function __construct() {
}
public static function defaultEndpoint(): string {
return (string) cfg('remote_access.wg_endpoint', 'ra.apps.f0xx.org:45340');
}
public static function serverAllowedIps(): string {
return (string) cfg('remote_access.wg_server_allowed_ips', '172.200.2.1/32');
}
public static function allocateClientAddress(string $sessionId): string {
$prefix = (string) cfg('remote_access.wg_client_ip_prefix', '172.200.2.');
$min = max(2, (int) cfg('remote_access.wg_client_ip_min', 10));
$max = max($min, (int) cfg('remote_access.wg_client_ip_max', 250));
$span = $max - $min + 1;
$host = $min + (abs(crc32($sessionId)) % $span);
return $prefix . $host . '/32';
}
}

View File

@@ -33,15 +33,13 @@ final class WireGuardPeerProvisioner {
$iface = self::interfaceName();
$allowed = trim($clientAddressCidr);
if ($allowed === '') {
$allowed = '10.66.66.2/32';
$allowed = WireGuardAddressPool::allocateClientAddress('default');
}
$cmd = 'wg set ' . escapeshellarg($iface)
$code = self::runWg(
'set ' . escapeshellarg($iface)
. ' peer ' . escapeshellarg($pub)
. ' allowed-ips ' . escapeshellarg($allowed);
exec($cmd, $out, $code);
if ($code !== 0) {
error_log('WireGuardPeerProvisioner add failed: ' . implode(' ', $out));
}
. ' allowed-ips ' . escapeshellarg($allowed)
);
return $code === 0;
}
@@ -54,9 +52,20 @@ final class WireGuardPeerProvisioner {
return;
}
$iface = self::interfaceName();
$cmd = 'wg set ' . escapeshellarg($iface)
self::runWg(
'set ' . escapeshellarg($iface)
. ' peer ' . escapeshellarg($pub)
. ' remove';
exec($cmd);
. ' remove'
);
}
private static function runWg(string $args): int {
$prefix = rtrim((string) cfg('remote_access.wg_set_prefix', ''), " \t");
$cmd = ($prefix !== '' ? $prefix . ' ' : '') . 'wg ' . $args;
exec($cmd . ' 2>&1', $out, $code);
if ($code !== 0) {
error_log('WireGuardPeerProvisioner: ' . $cmd . ' failed: ' . implode(' ', $out));
}
return $code;
}
}

View File

@@ -65,6 +65,7 @@ require_once __DIR__ . '/TicketRepository.php';
require_once __DIR__ . '/GraphRepository.php';
require_once __DIR__ . '/RemoteAccessRepository.php';
require_once __DIR__ . '/WireGuardPeerProvisioner.php';
require_once __DIR__ . '/WireGuardAddressPool.php';
require_once __DIR__ . '/RsshSessionProvisioner.php';
require_once __DIR__ . '/AnalyticsHead.php';