diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a406c617..bd4907da 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -536,6 +536,11 @@ sudo apt-get install -y build-essential libsqlite3-dev pkg-config libssl-dev git 153. [x] **PS1 `RandomNumberGenerator::Fill` crash (Issue #38)**: `[System.Security.Cryptography.RandomNumberGenerator]::Fill()` is a .NET 6+ static method unavailable in Windows PowerShell 5.1 (.NET Framework 4.x). Changed to `RNGCryptoServiceProvider.GetBytes()` instance method which works on both .NET Framework 4.x and .NET 6+. Fixes API key generation failure → 0 devices in panel on fresh Windows install. 154. [x] **Rust→Go upgrade detection (Issues #66, #38)**: `Do-Update` (PS1) and `do_update()` (bash) now detect `SERVER_TYPE=rust` (legacy hbbs/hbbr) and warn user that Rust→Go is a major architecture change requiring fresh installation. In auto mode, redirects to `Do-Install`/`do_install` automatically. In interactive mode, prompts user to confirm fresh install (recommended) or continue with partial update. Prevents broken upgrade path from v1.5.0 (Rust) to v2.3.0+ (Go). +#### Go Server — ForceRelay UUID Fix & Docker GHCR (Phase 27) ✅ COMPLETED 2026-03-19 +155. [x] **ForceRelay TCP UUID mismatch (Issue #66)**: `handlePunchHoleRequestTCP` ForceRelay path returned `RelayResponse{uuid=SERVER_UUID}` directly to TCP initiator. Some RustDesk client versions ignore the UUID from `RelayResponse` received in response to `PunchHoleRequest`, generate their own UUID, and connect to relay with it — while the target connects with the server's UUID. Relay pairing always failed (different UUIDs). **Fix**: ForceRelay TCP now returns `PunchHoleResponse{nat_type=SYMMETRIC}` instead of `RelayResponse`. Client sees SYMMETRIC NAT → sends `RequestRelay{uuid=CLIENT_UUID}` on same TCP connection → `handleRequestRelayTCP` forwards CLIENT_UUID to target → both sides use same UUID → relay pairing succeeds. +156. [x] **Relay diagnostic logging**: Added `log.Printf` with UUID and relay server in `handleRequestRelayTCP` and `handleRequestRelay` (UDP) return paths for better relay pairing diagnostics. +157. [x] **Docker GHCR "denied" error (Issue #67)**: Pre-built images on `ghcr.io/unitronix/betterdesk-*:latest` not available — workflow never triggered or packages are private. Added troubleshooting section to `DOCKER_QUICKSTART.md` (3 solutions: build locally, trigger workflow, authenticate). Added fallback comment to `docker-compose.quick.yml`. Added package visibility reminder to CI workflow summary step. + --- ## 🔄 System Statusu v3.0 @@ -805,4 +810,4 @@ All code changes MUST include a security review as part of the implementation pr --- -*Ostatnia aktualizacja: 2026-03-19 (ALL-IN-ONE Scripts — PS1 Compatibility & Upgrade Detection — Phase 26) przez GitHub Copilot* +*Ostatnia aktualizacja: 2026-03-19 (Go Server — ForceRelay UUID Fix & Docker GHCR — Phase 27) przez GitHub Copilot* diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 5983caee..c8d17319 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -209,3 +209,8 @@ jobs: echo "curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/main/docker-compose.quick.yml -o docker-compose.yml" >> $GITHUB_STEP_SUMMARY echo "docker compose up -d" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ Package Visibility" >> $GITHUB_STEP_SUMMARY + echo "If packages are newly created, they default to **private**." >> $GITHUB_STEP_SUMMARY + echo "To allow unauthenticated pulls, change visibility to **Public**:" >> $GITHUB_STEP_SUMMARY + echo "GitHub repo → Packages → (each package) → Package settings → Change visibility → Public" >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index c3f6cc19..3dbd1280 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,6 @@ package-lock.json hbbs-patch/ hbbs-patch-v2/ tasks/todo.md +docs/SECURITY_AUDIT_FULL_2026-03-19.md +.github/copilot-instructions.md +.github/copilot-instructions.md diff --git a/DOCKER_QUICKSTART.md b/DOCKER_QUICKSTART.md index 34737f5c..5afdb002 100644 --- a/DOCKER_QUICKSTART.md +++ b/DOCKER_QUICKSTART.md @@ -115,6 +115,33 @@ curl http://localhost:21114/api/health ## ❓ Troubleshooting +### "denied" or "pull access denied" when starting + +This means the pre-built images are not yet published to GitHub Container Registry. + +**Solution A — Build locally (recommended):** +```bash +# Use the full docker-compose.yml which builds images from source +git clone https://github.com/UNITRONIX/Rustdesk-FreeConsole.git +cd Rustdesk-FreeConsole +docker compose -f docker-compose.yml up -d --build +``` + +**Solution B — Wait for images to be published:** + +The repository maintainer needs to trigger the Docker publish workflow: +1. Go to: GitHub repo → Actions → "Build & Publish Docker Images" +2. Click "Run workflow" → Branch: main → Click "Run workflow" +3. Wait ~10 minutes for images to build +4. Once images are published, retry `docker compose up -d` + +**Solution C — Authenticate (if repo is private):** +```bash +# Create a GitHub Personal Access Token with 'read:packages' scope +docker login ghcr.io -u YOUR_GITHUB_USERNAME -p YOUR_GITHUB_TOKEN +docker compose up -d +``` + ### "Cannot connect to devices" 1. Check firewall allows ports 21116-21117 diff --git a/betterdesk-server/signal/handler.go b/betterdesk-server/signal/handler.go index 3452d650..e18de4dd 100644 --- a/betterdesk-server/signal/handler.go +++ b/betterdesk-server/signal/handler.go @@ -574,13 +574,23 @@ func (s *Server) handlePunchHoleRequestTCP(msg *pb.PunchHoleRequest, raddr *net. log.Printf("[signal] PunchHole (TCP): target %s found (addr=%s, status=%s), relay=%s", targetID, target.UDPAddr, target.StatusTier, relayServer) - // ForceRelay or AlwaysUseRelay: send RelayResponse (NOT PunchHoleResponse) - // with a generated UUID, matching the UDP path's sendRelayResponse behavior. - // RelayResponse contains the uuid field required by hbbr for session pairing. + // ForceRelay or AlwaysUseRelay: return PunchHoleResponse with SYMMETRIC NAT + // type instead of RelayResponse. This tells the client that direct P2P is + // impossible and it should fall back to relay via RequestRelay. + // + // The client will then send RequestRelay (with its own UUID) on this same + // TCP connection. handleRequestRelayTCP will forward it to the target and + // return RelayResponse to the initiator. Both sides connect to relay with + // the SAME client-generated UUID, ensuring relay pairing succeeds. + // + // Previously, returning RelayResponse directly with a server-generated UUID + // caused UUID mismatch: some RustDesk client versions ignore the UUID from + // a RelayResponse received in response to PunchHoleRequest (they expect + // PunchHoleResponse), generate their own UUID, and connect to relay with it + // — while the target connects with the server's UUID. This broke relay + // pairing every time (Issue #66). if msg.ForceRelay || s.cfg.AlwaysUseRelay { - log.Printf("[signal] PunchHole (TCP): force relay for %s", targetID) - - relayUUID := uuid.New().String() + log.Printf("[signal] PunchHole (TCP): force relay for %s (returning SYMMETRIC to let client drive relay UUID)", targetID) var signedPk []byte if len(target.PK) > 0 { @@ -592,32 +602,18 @@ func (s *Server) handlePunchHoleRequestTCP(msg *pb.PunchHoleRequest, raddr *net. } } - // Forward RequestRelay to target so it connects to hbbr with the same UUID. - reqRelay := &pb.RendezvousMessage{ - Union: &pb.RendezvousMessage_RequestRelay{ - RequestRelay: &pb.RequestRelay{ - Id: msg.Id, - Uuid: relayUUID, - SocketAddr: crypto.EncodeAddr(raddr), - RelayServer: relayServer, - Secure: false, - ConnType: msg.ConnType, - }, - }, - } + var targetAddr []byte if target.UDPAddr != nil { - // Store the UUID so we can recover it if target responds with empty UUID. - s.storePendingUUID(targetID, relayUUID) - s.sendUDP(reqRelay, target.UDPAddr) - log.Printf("[signal] PunchHole (TCP): forwarded RequestRelay to target %s (uuid=%s)", targetID, relayUUID[:8]) + targetAddr = crypto.EncodeAddr(target.UDPAddr) } return &pb.RendezvousMessage{ - Union: &pb.RendezvousMessage_RelayResponse{ - RelayResponse: &pb.RelayResponse{ - Uuid: relayUUID, + Union: &pb.RendezvousMessage_PunchHoleResponse{ + PunchHoleResponse: &pb.PunchHoleResponse{ + SocketAddr: targetAddr, + Pk: signedPk, RelayServer: relayServer, - Union: &pb.RelayResponse_Pk{Pk: signedPk}, + Union: &pb.PunchHoleResponse_NatType{NatType: pb.NatType_SYMMETRIC}, }, }, } @@ -892,6 +888,7 @@ func (s *Server) handleRequestRelay(msg *pb.RequestRelay, raddr *net.UDPAddr) { } // Confirm to initiator with SIGNED public key + log.Printf("[signal] RequestRelay (UDP): returning RelayResponse to initiator %s (uuid=%s, relay=%s)", raddr, relayUUID[:8], relayServer) resp := &pb.RendezvousMessage{ Union: &pb.RendezvousMessage_RelayResponse{ RelayResponse: &pb.RelayResponse{ @@ -997,6 +994,7 @@ func (s *Server) handleRequestRelayTCP(msg *pb.RequestRelay, raddr *net.UDPAddr) } // Immediate RelayResponse to TCP initiator — matching the UDP handler's behavior. + log.Printf("[signal] RequestRelay (TCP): returning RelayResponse to initiator %s (uuid=%s, relay=%s)", raddr, relayUUID[:8], relayServer) return &pb.RendezvousMessage{ Union: &pb.RendezvousMessage_RelayResponse{ RelayResponse: &pb.RelayResponse{ diff --git a/docker-compose.quick.yml b/docker-compose.quick.yml index b3552328..2d6cb183 100644 --- a/docker-compose.quick.yml +++ b/docker-compose.quick.yml @@ -10,6 +10,11 @@ # Web Console: http://localhost:5000 # Default credentials shown on first startup in logs: # docker compose logs console | grep -i password +# +# TROUBLESHOOTING: If you get "denied" or "pull access denied" error, +# images may not be published yet. Build from source instead: +# git clone https://github.com/UNITRONIX/Rustdesk-FreeConsole.git +# cd Rustdesk-FreeConsole && docker compose -f docker-compose.yml up -d --build # ============================================================================= services: