mirror of
git://f0xx.org/android_cast
synced 2026-07-29 02:59:00 +03:00
sync
This commit is contained in:
@@ -85,10 +85,13 @@ public final class RemoteAccessVpnService extends VpnService {
|
||||
String[] parts = address.split("/");
|
||||
b.addAddress(InetAddress.getByName(parts[0]),
|
||||
parts.length > 1 ? Integer.parseInt(parts[1]) : 32);
|
||||
} else {
|
||||
b.addAddress("10.66.66.2", 32);
|
||||
}
|
||||
b.addRoute("10.66.66.0", 24);
|
||||
for (String route : parseAllowedRoutes(wgConfig)) {
|
||||
String[] rp = route.split("/");
|
||||
if (rp.length >= 2) {
|
||||
b.addRoute(rp[0], Integer.parseInt(rp[1]));
|
||||
}
|
||||
}
|
||||
b.setMtu(1280);
|
||||
tun = b.establish();
|
||||
Log.i(TAG, "VPN interface established");
|
||||
@@ -105,6 +108,25 @@ public final class RemoteAccessVpnService extends VpnService {
|
||||
return m.find() ? m.group(1).trim() : null;
|
||||
}
|
||||
|
||||
/** Routes from AllowedIPs= lines (split tunnel — typically server wg /32). */
|
||||
private static String[] parseAllowedRoutes(String wgConfig) {
|
||||
if (wgConfig == null) {
|
||||
return new String[0];
|
||||
}
|
||||
Matcher m = Pattern.compile("(?m)^AllowedIPs\\s*=\\s*(.+)$").matcher(wgConfig);
|
||||
if (!m.find()) {
|
||||
return new String[0];
|
||||
}
|
||||
String[] parts = m.group(1).trim().split("\\s*,\\s*");
|
||||
java.util.ArrayList<String> out = new java.util.ArrayList<>();
|
||||
for (String p : parts) {
|
||||
if (!p.isEmpty()) {
|
||||
out.add(p.trim());
|
||||
}
|
||||
}
|
||||
return out.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void tearDown() {
|
||||
if (tun != null) {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Open tasks — dependency graph
|
||||
|
||||
_Last synced: 2026-06-08. Renumbered per [20260608_ALPHA_PRIORITIES.md](20260608_ALPHA_PRIORITIES.md)._
|
||||
_Last synced: 2026-06-04. **2.1–2.3 postponed** (developer DNS path deferred). Renumbered per [20260608_ALPHA_PRIORITIES.md](20260608_ALPHA_PRIORITIES.md)._
|
||||
|
||||
**Documentation index:** [README.md](README.md)
|
||||
|
||||
@@ -9,25 +9,53 @@ _Last synced: 2026-06-08. Renumbered per [20260608_ALPHA_PRIORITIES.md](20260608
|
||||
## Table of contents
|
||||
|
||||
<!-- toc -->
|
||||
- [Priority tiers](#priority-tiers)
|
||||
- [Removed from graph (done)](#removed-from-graph-done)
|
||||
- [Postponed (off graph)](#postponed-off-graph)
|
||||
- [Priority tiers (open only)](#priority-tiers-open-only)
|
||||
- [Dependency graph](#dependency-graph)
|
||||
- [Task index](#task-index)
|
||||
- [Task index (open)](#task-index-open)
|
||||
- [Critical path](#critical-path)
|
||||
<!-- /toc -->
|
||||
|
||||
---
|
||||
|
||||
## Priority tiers
|
||||
## Removed from graph (done)
|
||||
|
||||
| ID | Note |
|
||||
|----|------|
|
||||
| **0.1** | Stream dump — `StreamDumpWriter` + dev setting |
|
||||
| **3.x** | Register + verify + TOTP + migration `008` (BE + views) |
|
||||
| **4.x** | RSSH alpha — BE bastion, Android mode, JSch tunnel |
|
||||
| **4.wg** | WG lab — BE `wg0`, FE DNAT via `xen_firewall.sh`, prod API smoke OK |
|
||||
| **RBAC** (core) | Admin panel + lockout clear; **5.5 validate** still open |
|
||||
|
||||
---
|
||||
|
||||
## Postponed (off graph)
|
||||
|
||||
| ID | Owner | Note |
|
||||
|----|-------|------|
|
||||
| **2.1** | Developer | DNS MX/SPF — deferred |
|
||||
| **2.2** | Developer | Forward addresses — deferred |
|
||||
| **2.3** | Developer | Inbound verify — deferred |
|
||||
| **2.4** | Developer | Gmail filters — optional; with 2.x |
|
||||
| **2.5** | Agent | SMTP outbound — frozen until 2.1 resumes |
|
||||
| **5.4** (mail) | Agent | G2–G6 **email** legs — frozen until 2.5; UI/TOTP can be tested without mail |
|
||||
|
||||
Checklist when resumed: [tmp/20260608_help_request.md](../tmp/20260608_help_request.md)
|
||||
|
||||
---
|
||||
|
||||
## Priority tiers (open only)
|
||||
|
||||
| Tier | IDs | Alpha blocker |
|
||||
|------|-----|---------------|
|
||||
| P0 debug | **0.1** stream dump | No (feeds 6.5/6.6) |
|
||||
| P1 | **1** graphs | No |
|
||||
| P2 | **2.1–2.4** dev, **2.5** + **3.x** agent, RBAC | **Yes** (auth) |
|
||||
| P3 | **4.x** RSSH | **Yes** |
|
||||
| P4 | **5.x** backend validation | **Yes** |
|
||||
| P5 | **6.x** app LAN | LAN alpha |
|
||||
| P9 | **9.x** landing | No |
|
||||
| **WIP / QA** | Browser JS, reachability | **Yes** (blocks BE sign-off) |
|
||||
| **P1** | **1.1 / 1.2** graphs | No |
|
||||
| **P4** | **5.1–5.3, 5.5** validation + OTA/URLs | **Yes** (auth mail path **deferred**) |
|
||||
| **P5** | **6.1–6.6** app LAN | LAN alpha |
|
||||
| **P9** | **9.x** landing | No |
|
||||
| **Infra** | Router WG DNAT persist | No (lab only) |
|
||||
|
||||
---
|
||||
|
||||
@@ -35,87 +63,90 @@ _Last synced: 2026-06-08. Renumbered per [20260608_ALPHA_PRIORITIES.md](20260608
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
S01["0.1 stream dump<br/>agent · P0"]
|
||||
G1["1.x graphs<br/>agent · P1"]
|
||||
subgraph WIP["WIP — QA / deploy"]
|
||||
BR["Browser checks<br/>JS SyntaxError · views"]
|
||||
REACH["Services reachability<br/>OTA/crash URLs"]
|
||||
end
|
||||
|
||||
D21["2.1 DNS<br/>DEV"]
|
||||
D22["2.2 forward<br/>DEV"]
|
||||
D23["2.3 inbound test<br/>DEV"]
|
||||
A25["2.5 SMTP<br/>agent"]
|
||||
A3["3.x 2FA<br/>agent"]
|
||||
RBAC["RBAC<br/>agent"]
|
||||
subgraph P1["P1 graphs"]
|
||||
G11["1.1 pending fixes"]
|
||||
G12["1.2 pending FRs"]
|
||||
end
|
||||
|
||||
R4["4.x RSSH<br/>agent · P3"]
|
||||
subgraph P4["P4 backend alpha"]
|
||||
B51["5.1 OTA channel<br/>50/50"]
|
||||
B52["5.2 device URLs<br/>50/50"]
|
||||
B53["5.3 RSSH validate"]
|
||||
B55["5.5 RBAC validate"]
|
||||
end
|
||||
|
||||
B51["5.1 OTA<br/>50/50"]
|
||||
B52["5.2 URLs<br/>50/50"]
|
||||
B53["5.3 RSSH validate"]
|
||||
B54["5.4 auth validate"]
|
||||
B55["5.5 RBAC validate"]
|
||||
subgraph P5["P5 app LAN — dev"]
|
||||
A61["6.1 Opus/Speex"]
|
||||
A62["6.2 LAN soak"]
|
||||
A63["6.3 lifecycle"]
|
||||
A64["6.4 rotation"]
|
||||
A65["6.5 audio lag"]
|
||||
A66["6.6 stream analysis<br/>uses 0.1 dumps"]
|
||||
end
|
||||
|
||||
A61["6.1 Opus"]
|
||||
A62["6.2 LAN soak"]
|
||||
A63["6.3 lifecycle"]
|
||||
A64["6.4 rotation"]
|
||||
A65["6.5 audio lag"]
|
||||
subgraph P9["P9 landing"]
|
||||
L9["9.x hub / globe"]
|
||||
end
|
||||
|
||||
L9["9.x landing<br/>LOW"]
|
||||
subgraph INFRA["Infra — non-alpha"]
|
||||
RDNAT["Router WG DNAT<br/>f0xx.org persist"]
|
||||
end
|
||||
|
||||
FULL["FULL_ALPHA"]
|
||||
FULL["FULL_ALPHA<br/>mail path deferred"]
|
||||
|
||||
S01 -.-> A65 & A64
|
||||
D21 --> D22 --> D23
|
||||
D21 --> A25 --> A3 --> RBAC
|
||||
A3 --> B54 --> FULL
|
||||
RBAC --> B55
|
||||
BR --> G11 & G12
|
||||
BR --> B55
|
||||
REACH --> B51 & B52
|
||||
|
||||
R4 --> B53 --> FULL
|
||||
B51 & B52 --> FULL
|
||||
B53 --> FULL
|
||||
B55 --> FULL
|
||||
B51 --> FULL
|
||||
B52 --> FULL
|
||||
A62 --> FULL
|
||||
A63 <--> A64
|
||||
|
||||
G1 -.-> L9
|
||||
L9 -.-> RBAC
|
||||
A63 <--> A64
|
||||
A65 --> A66
|
||||
G11 -.-> L9
|
||||
|
||||
style FULL fill:#1e3a5f,color:#fff
|
||||
style D21 fill:#b45309,color:#fff
|
||||
style D22 fill:#b45309,color:#fff
|
||||
style D23 fill:#b45309,color:#fff
|
||||
style R4 fill:#7c2d12,color:#fff
|
||||
style A3 fill:#7c2d12,color:#fff
|
||||
style BR fill:#991b1b,color:#fff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task index
|
||||
## Task index (open)
|
||||
|
||||
| ID | Owner | Depends on |
|
||||
|----|-------|------------|
|
||||
| **0.1** | Agent | — |
|
||||
| **1 / 1.1 / 1.2** | Agent | BE deploy soft |
|
||||
| **2.1–2.4** | **Developer** | DNS UI access |
|
||||
| **2.5** | Agent | 2.1 |
|
||||
| **3.x** | Agent | 2.5 |
|
||||
| **RBAC** | Agent | 3.x, stable pages |
|
||||
| **4.x** | Agent | BE deploy |
|
||||
| **5.1–5.2** | 50/50 | deploy |
|
||||
| **5.3** | Agent | 4.x |
|
||||
| **5.4–5.5** | Agent | 3.x, RBAC |
|
||||
| **6.1–6.6** | Developer (~99%) | 0.1 ↔ 6.6 |
|
||||
| **9.x** | Agent (low) | — |
|
||||
| ID | Owner | Depends on | Status |
|
||||
|----|-------|------------|--------|
|
||||
| **Browser** | Agent | BE deploy | WIP — `SyntaxError` on crashes/graphs/tickets |
|
||||
| **Reachability** | 50/50 | deploy | WIP — OTA path, crash URL host |
|
||||
| **1.1** | Agent | browser OK | Open |
|
||||
| **1.2** | Agent | 1.1 soft | Open |
|
||||
| **5.1** | 50/50 | reachability | Open |
|
||||
| **5.2** | 50/50 | reachability | Open |
|
||||
| **5.3** | Agent | 4.x ✓ | Validate E2E RSSH |
|
||||
| **5.5** | Agent | RBAC ✓, browser | Validate admin flows |
|
||||
| **6.1–6.6** | Developer (~99%) | 0.1 ✓ for 6.6 | Open |
|
||||
| **9.x** | Agent (low) | bandwidth | Open |
|
||||
| **Router DNAT** | Agent | — | Open — `router_wireguard_dnat.sh` |
|
||||
|
||||
---
|
||||
|
||||
## Critical path
|
||||
|
||||
```text
|
||||
DEV: 2.1 → 2.2 → 2.3
|
||||
AGENT: 2.5 → 3.x → 5.4 ──┐
|
||||
AGENT: 4.x → 5.3 ─────────┼──► FULL_ALPHA
|
||||
SHARED: 5.1 OTA, 5.2 URLs ─┤
|
||||
DEV: 6.2 LAN soak ────────┘
|
||||
BLOCKER: Browser JS fix + deploy ──► 1.x, 5.5 UI validation
|
||||
|
||||
Parallel: 0.1, 1.x, 6.1/6.3–6.5, 9.x
|
||||
AGENT: 5.3 RSSH validate (4.x done) ──┐
|
||||
SHARED: 5.1 OTA, 5.2 URLs ─────────────┼──► FULL_ALPHA (LAN + RSSH + RBAC)
|
||||
DEV: 6.2 LAN soak ─────────────────┘
|
||||
|
||||
Deferred (not on graph): 2.1 → 2.2 → 2.3 → 2.5 → 5.4 mail E2E
|
||||
|
||||
Parallel: 6.1/6.3–6.6, 9.x, router WG DNAT, Android APK rebuild (WG route fix)
|
||||
```
|
||||
|
||||
**Developer blocked checklist:** [tmp/20260608_help_request.md](../tmp/20260608_help_request.md)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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();
|
||||
|
||||
25
examples/crash_reporter/backend/src/WireGuardAddressPool.php
Normal file
25
examples/crash_reporter/backend/src/WireGuardAddressPool.php
Normal 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';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@ PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o
|
||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;iptables -D FORWARD -o %i -j ACCEPT; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;ip6tables -D FORWARD -o %i -j ACCEPT
|
||||
|
||||
[Peer]
|
||||
# obtained from client device via wireguard connection setup process
|
||||
PublicKey = EV2nnwHH7xe3jA5J46Rjtl8ao+ybZinibKg2Hy83GBw=
|
||||
AllowedIPs = 172.200.2.2/16, fddd::1/128
|
||||
# Static lab peer only — remote-access clients are added dynamically via wg set (PHP).
|
||||
# PublicKey = <client-device-pubkey>
|
||||
# AllowedIPs = 172.200.2.2/32
|
||||
|
||||
43
examples/wireguard/FE_wireguard_dnat.sh
Executable file
43
examples/wireguard/FE_wireguard_dnat.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
# FE (f0xx-monstro): DNAT public UDP → BE wg0. Matches existing rules to 10.7.16.128 (8088, etc.).
|
||||
# Invoked from xen_firewall.sh start/stop; also: wireguard_fe_dnat.sh [start|stop]
|
||||
set -euo pipefail
|
||||
|
||||
BE_IP="${BE_IP:-10.7.16.128}"
|
||||
WG_PORT="${WG_PORT:-45340}"
|
||||
|
||||
function start() {
|
||||
if iptables -t nat -C PREROUTING -p udp --dport "$WG_PORT" -j DNAT --to-destination "${BE_IP}:${WG_PORT}" 2>/dev/null; then
|
||||
echo "DNAT rule already present for udp/${WG_PORT}"
|
||||
else
|
||||
iptables -t nat -A PREROUTING -p udp --dport "$WG_PORT" -j DNAT --to-destination "${BE_IP}:${WG_PORT}"
|
||||
echo "Added PREROUTING DNAT udp/${WG_PORT} -> ${BE_IP}:${WG_PORT}"
|
||||
fi
|
||||
|
||||
if iptables -C FORWARD -p udp -d "${BE_IP}" --dport "$WG_PORT" -j ACCEPT 2>/dev/null; then
|
||||
echo "FORWARD rule already present"
|
||||
else
|
||||
iptables -A FORWARD -p udp -d "${BE_IP}" --dport "$WG_PORT" -j ACCEPT
|
||||
echo "Added FORWARD udp -> ${BE_IP}:${WG_PORT}"
|
||||
fi
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if iptables -t nat -C PREROUTING -p udp --dport "$WG_PORT" -j DNAT --to-destination "${BE_IP}:${WG_PORT}" 2>/dev/null; then
|
||||
iptables -t nat -D PREROUTING -p udp --dport "$WG_PORT" -j DNAT --to-destination "${BE_IP}:${WG_PORT}"
|
||||
echo "Removed PREROUTING DNAT udp/${WG_PORT}"
|
||||
else
|
||||
echo "DNAT rule not present for udp/${WG_PORT}"
|
||||
fi
|
||||
|
||||
if iptables -C FORWARD -p udp -d "${BE_IP}" --dport "$WG_PORT" -j ACCEPT 2>/dev/null; then
|
||||
iptables -D FORWARD -p udp -d "${BE_IP}" --dport "$WG_PORT" -j ACCEPT
|
||||
echo "Removed FORWARD udp -> ${BE_IP}:${WG_PORT}"
|
||||
else
|
||||
echo "FORWARD rule not present"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd=$1
|
||||
cmd=${cmd:-start}
|
||||
[ "$cmd" = "start" ] && start || stop
|
||||
6
examples/wireguard/alpine-be-sudoers-wg
Normal file
6
examples/wireguard/alpine-be-sudoers-wg
Normal file
@@ -0,0 +1,6 @@
|
||||
# Install on Alpine BE: /etc/sudoers.d/androidcast-wg (chmod 440)
|
||||
# PHP-FPM pool user is nginx (see /etc/php81/php-fpm.d/www.conf).
|
||||
Defaults:nobody !requiretty
|
||||
Defaults:nginx !requiretty
|
||||
nobody ALL=(root) NOPASSWD: /usr/bin/wg set wg0 peer *
|
||||
nginx ALL=(root) NOPASSWD: /usr/bin/wg set wg0 peer *
|
||||
47
examples/wireguard/install_be_remote_access.sh
Executable file
47
examples/wireguard/install_be_remote_access.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Run on alpine-be as root after syncing backend tree.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="${1:-/var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend}"
|
||||
CFG="$ROOT/config/config.php"
|
||||
SUDOERS_DST="/etc/sudoers.d/androidcast-wg"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
if [[ ! -f "$CFG" ]]; then
|
||||
echo "Missing $CFG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
php81 -r '
|
||||
$path = $argv[1];
|
||||
$c = require $path;
|
||||
if (!is_array($c)) { fwrite(STDERR, "config.php must return array\n"); exit(1); }
|
||||
if (isset($c["remote_access"]) && is_array($c["remote_access"]) && ($c["remote_access"]["wg_server_public_key"] ?? "") !== "") {
|
||||
echo "remote_access already configured\n";
|
||||
exit(0);
|
||||
}
|
||||
$c["remote_access"] = array_merge($c["remote_access"] ?? [], [
|
||||
"wg_endpoint" => "ra.apps.f0xx.org:45340",
|
||||
"wg_server_public_key" => "EV2nnwHH7xe3jA5J46Rjtl8ao+ybZinibKg2Hy83GBw=",
|
||||
"wg_interface" => "wg0",
|
||||
"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,
|
||||
"wg_set_prefix" => "sudo ",
|
||||
"provision_peers" => true,
|
||||
"require_wg_tools" => true,
|
||||
"session_ttl_s" => 3600,
|
||||
"min_poll_interval_s" => 45,
|
||||
]);
|
||||
$export = var_export($c, true);
|
||||
file_put_contents($path, "<?php\nreturn " . $export . ";\n");
|
||||
echo "Wrote remote_access into config.php\n";
|
||||
' "$CFG"
|
||||
|
||||
install -m 440 "$SCRIPT_DIR/alpine-be-sudoers-wg" "$SUDOERS_DST"
|
||||
visudo -cf "$SUDOERS_DST"
|
||||
echo "Installed $SUDOERS_DST"
|
||||
|
||||
rc-service php-fpm81 restart 2>/dev/null || rc-service php-fpm restart 2>/dev/null || true
|
||||
echo "Restarted PHP-FPM"
|
||||
13
examples/wireguard/router_wireguard_dnat.sh
Normal file
13
examples/wireguard/router_wireguard_dnat.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# Raspberry Pi router (f0xx.org): WAN UDP 45340 → FE (10.7.0.10). Persist via your firewall save.
|
||||
set -euo pipefail
|
||||
FE_IP="${FE_IP:-10.7.0.10}"
|
||||
WG_PORT="${WG_PORT:-45340}"
|
||||
WAN_IF="${WAN_IF:-eth0}"
|
||||
|
||||
if iptables -t nat -C PREROUTING -i "$WAN_IF" -p udp --dport "$WG_PORT" -j DNAT --to-destination "${FE_IP}:${WG_PORT}" 2>/dev/null; then
|
||||
echo "Router DNAT already present"
|
||||
else
|
||||
iptables -t nat -A PREROUTING -i "$WAN_IF" -p udp --dport "$WG_PORT" -j DNAT --to-destination "${FE_IP}:${WG_PORT}"
|
||||
echo "Added router PREROUTING udp/${WG_PORT} -> ${FE_IP}:${WG_PORT}"
|
||||
fi
|
||||
Reference in New Issue
Block a user