5.9 KiB
Remote access — implementation notes (v1 WireGuard)
See the full proposal: 20260602_REVERSE_SSH_proposals_summary.md.
Table of contents
- App (developer settings)
- BE control plane
- Production deploy (no new nginx HTTP paths)
- WireGuard data plane (UDP, not nginx)
- WireGuard third-party (Android)
- Tests
Documentation index: README.md
App (developer settings)
- Dropdown: Disabled | WireGuard | RSSH (greyed — not selectable)
- Pref key:
dev_remote_access_mode(AppPreferences) - Disabled: notifies BE (
status:disable), tears down VPN, stopsRemoteAccessService - WireGuard: foreground service; jittered poll 1–7 min →
heartbeat.phpwithtype: ra - Heartbeat URL derived from crash upload URL (
…/api/heartbeat.php) - Session credentials persisted until
expires_at; expiry tears down tunnel locally - Optional full userspace WG via
third-party/wireguard-android(:tunnel); otherwiseRemoteAccessVpnServiceTUN fallback
BE control plane
Uses the existing crashes vhost — no new nginx location blocks for HTTP.
| Piece | Path |
|---|---|
| Migration | sql/migrations/007_remote_access.sql |
| Repository | src/RemoteAccessRepository.php |
| WG peers | src/WireGuardPeerProvisioner.php (wg set wg0 peer …) |
| Device API | public/api/heartbeat.php (type: ra) |
| Admin API | public/api/remote_access.php |
| UI | ?view=remote_access (nav + hub card) |
| RBAC | remote_access_view, remote_access_operate, remote_access_admin |
| RBAC admin UI | ?view=rbac — global roles (root), company roles, privilege sets |
| Cron | scripts/purge_remote_access.php (hourly recommended) |
RBAC rules (enforced in API)
| Permission | Scope |
|---|---|
remote_access_view |
List devices/sessions/events in allowed companies |
remote_access_operate |
Open session; close own sessions |
remote_access_admin |
Whitelist devices; close any session in company |
| Global admin / root | All companies; root edits global roles via ?view=rbac |
Privilege sets (remote_access_user, remote_access_admin) on company_memberships.permissions_json grant extra actions to member rows — see crash console README § RBAC.
Flow
- Device polls with
{type:ra, status:enable, capabilities:[wireguard], tunnel_mode:wireguard}. - BE upserts
remote_access_devices; returnswaituntil device is whitelisted and operator opens session. - Next eligible poll returns
action:connect+ ephemeral WG credentials; BE runswg setwhenprovision_peers=true. - Disable from app →
status:disable→ closes sessions, removes peers.
URLs (production)
- Dashboard:
https://apps.f0xx.org/app/androidcast_project/crashes/?view=remote_access - Heartbeat:
…/crashes/api/heartbeat.php - Admin API:
…/crashes/api/remote_access.php
Production deploy (no new nginx HTTP paths)
After syncing PHP/JS to the BE tree under
…/androidcast_project/android_cast/examples/crash_reporter/backend/:
1. Database
Migration 007 (already applied on prod per deploy notes). Tables auto-create on SQLite dev; MariaDB should use the migration file once as root.
2. config.php — required keys
'remote_access' => [
'wg_endpoint' => 'ra.apps.f0xx.org:51820', // public UDP hostname:port (FE DNAT → BE)
'wg_server_public_key' => '…', // wg show wg0 public-key on BE — REQUIRED
'wg_interface' => 'wg0',
'provision_peers' => true, // wg set on connect/close
'require_wg_tools' => true, // reject connect if wg genkey missing
'session_ttl_s' => 3600,
'min_poll_interval_s' => 45, // poll rate limit per device
],
Critical: wg_server_public_key must match the live wg0 interface. Empty value makes connect credentials unusable on real devices.
3. BE packages and permissions
apk add wireguard-tools wireguard-tools-wg
# wg0 configured separately (Address 10.66.66.1/24, ListenPort 51820, etc.)
# PHP-FPM user must run wg when provision_peers=true (same as CLI test):
sudo -u www-data wg show wg0
4. Cron (session cleanup)
0 * * * * cd /var/www/.../backend && php scripts/purge_remote_access.php --hours=24
5. Verify
cd examples/crash_reporter/backend
chmod +x scripts/verify_remote_access_prod.sh scripts/test_remote_access_api.sh
./scripts/verify_remote_access_prod.sh
BASE=https://apps.f0xx.org/app/androidcast_project/crashes ./scripts/test_remote_access_api.sh
6. Browser
Hard-refresh after deploying public/assets/js/remote_access.js.
WireGuard data plane (UDP, not nginx)
HTTP control plane stays on FE→BE :80. WireGuard traffic is UDP — configure at the FE firewall/DNAT, not in nginx:
Internet UDP :51820 → FE DNAT → BE wg0 :51820
Hostname ra.apps.f0xx.org (or similar) should resolve to the FE public IP. Devices use wg_endpoint from config/connect payload.
WireGuard third-party (Android)
bash scripts/init-wireguard-submodule.sh
./gradlew :app:assembleDebug # includes :tunnel when submodule present
Without the submodule, WireGuardTunnelBridge uses RemoteAccessVpnService (TUN only — sufficient for lab; prefer :tunnel for production tunnels).
Tests
./gradlew :app:testDebugUnitTest --tests 'com.foxx.androidcast.remoteaccess.*'
bash examples/crash_reporter/backend/scripts/test_rbac_api.sh
bash examples/crash_reporter/backend/scripts/test_remote_access_api.sh
BASE=https://apps.f0xx.org/app/androidcast_project/crashes bash examples/crash_reporter/backend/scripts/test_remote_access_api.sh