From bfa2c003f6421a6df859a658b80c41c7587567a9 Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Sun, 12 Jul 2026 19:44:33 +0200 Subject: [PATCH] Remote access: open iodine sessions and return connect credentials. Wire opt_in_mode iodine through openSession and buildConnectResponse for mobile tunnel setup. Co-authored-by: Cursor --- src/RemoteAccessRepository.php | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/RemoteAccessRepository.php b/src/RemoteAccessRepository.php index 5f68bb7..f8fd309 100644 --- a/src/RemoteAccessRepository.php +++ b/src/RemoteAccessRepository.php @@ -386,9 +386,40 @@ final class RemoteAccessRepository { if ($tunnel === 'ssh_reverse') { return self::buildRsshConnectResponse($session, $deviceId, $clientIp); } + if ($tunnel === 'iodine') { + return self::buildIodineConnectResponse($session, $deviceId, $clientIp); + } return self::buildWireGuardConnectResponse($session, $deviceId, $clientIp); } + /** @param array $session */ + private static function buildIodineConnectResponse(array $session, string $deviceId, string $clientIp): array { + $sessionId = (string) ($session['session_id'] ?? ''); + $expiresAt = (int) ($session['expires_at'] ?? (time() + 3600)); + $serverHost = (string) cfg('remote_access.iodine.server_host', 'apps.f0xx.org'); + $udpPort = (int) cfg('remote_access.iodine.udp_port', 5350); + $tunnelDomain = (string) cfg('remote_access.iodine.tunnel_domain', 't0.f0xx.org'); + $password = (string) cfg('remote_access.iodine.password', 'zxc123'); + $endpoint = $serverHost . ':' . $udpPort; + + self::markSessionActive($sessionId); + self::logEvent($deviceId, $sessionId, null, 'tunnel_connect', 'iodine', null, $clientIp); + + return self::raEnvelope('connect', [ + 'session_id' => $sessionId, + 'endpoint' => $endpoint, + 'tunnel' => 'iodine', + 'credentials' => [ + 'mode' => 'iodine_dns', + 'server_host' => $serverHost, + 'udp_port' => $udpPort, + 'tunnel_domain' => $tunnelDomain, + 'password' => $password, + ], + 'expires_at' => $expiresAt, + ]); + } + /** @param array $session */ private static function buildWireGuardConnectResponse(array $session, string $deviceId, string $clientIp): array { $sessionId = (string) ($session['session_id'] ?? ''); @@ -624,11 +655,19 @@ final class RemoteAccessRepository { self::closeActiveSessionsForDevice($deviceId, 'superseded'); $sessionId = self::uuid4(); $companyId = $companyId ?? (int) ($device['company_id'] ?? Rbac::defaultCompanyId()); - $tunnel = $optIn === self::OPT_RSSH ? 'ssh_reverse' : 'wireguard'; + $tunnel = match ($optIn) { + self::OPT_RSSH => 'ssh_reverse', + self::OPT_IODINE => 'iodine', + default => 'wireguard', + }; if ($tunnel === 'ssh_reverse') { $host = (string) cfg('remote_access.rssh.bastion_host', 'ra.apps.f0xx.org'); $port = (int) cfg('remote_access.rssh.bastion_port', 443); $endpoint = $port === 443 ? $host : $host . ':' . $port; + } elseif ($tunnel === 'iodine') { + $host = (string) cfg('remote_access.iodine.server_host', 'apps.f0xx.org'); + $port = (int) cfg('remote_access.iodine.udp_port', 5350); + $endpoint = $host . ':' . $port; } else { $endpoint = WireGuardAddressPool::defaultEndpoint(); }