Feat(endpoint): add firebasetunnel_client / firebasetunnel_server endpoint pair#24
Open
hiddifydeveloper wants to merge 8 commits into
Open
Feat(endpoint): add firebasetunnel_client / firebasetunnel_server endpoint pair#24hiddifydeveloper wants to merge 8 commits into
hiddifydeveloper wants to merge 8 commits into
Conversation
Adds FirebaseTunnelClientOptions/FirebaseTunnelServerOptions and the firebasetunnel_client/firebasetunnel_server type constants, laying the groundwork for a Firebase Realtime Database relay endpoint adapted from github.com/Hiddify2/Firebase-Tunnel.
Implements the Firebase Realtime Database REST client (Get/Put/Delete with retry, SSE Listen with jittered reconnect), the chunk batching/reassembly pipeline (chunkSender/chunkReceiver), optional AES-256-GCM payload encryption, and zstd compression helpers. Adapted from github.com/Hiddify2/Firebase-Tunnel (no LICENSE upstream; mechanism studied and reimplemented rather than vendored). Adds backpressure limits and jittered backoff not present in the original PoC.
Implements ClientEndpoint and ServerEndpoint as adapter.Endpoint, mirroring the existing tunnel_client/tunnel_server shape. Client adapts the relay into a net.Pipe-backed DialContext; server discovers sessions via Firebase SSE, enforces per-user/global session caps and per-user creation rate limits, dials real targets via router.RouteConnectionEx (so existing routing/DNS/sniffing rules apply), and wires per-user traffic accounting into the SSM tracker (the same subsystem used for Shadowsocks usage). Adds a background GC sweep for abandoned sessions and panic-recovery restart for the poll/GC loops so a fault here can't take down the rest of the sing-box process. Registered in include/registry.go's EndpointRegistry().
Covers zstd round-trip, AES-256-GCM encrypt/decrypt (including wrong-key and truncated-ciphertext failure cases), chunk sender/receiver round-trip against an in-memory fake Firebase REST server, out-of-order chunk reassembly, duplicate-chunk handling, and the sender backpressure cap.
Adds protocol/firebasetunnel/docs/ covering architecture, configuration reference, threat model/security tradeoffs, and operational guidance (limits, GC, failure isolation, rollout), plus a provenance doc detailing what was reused vs. rewritten from github.com/Hiddify2/Firebase-Tunnel.
- Add HMAC-SHA256 integrity tags (16 B, derived from firebase_secret) on all unencrypted chunks so a Firebase-side observer cannot silently flip payload bytes without detection. - Add AES-256-GCM AEAD additional data binding: each encrypted chunk now encodes sessionID + direction + seq as AAD, so a chunk cannot be replayed from one session, direction, or sequence position into another. - Thread sessionID + direction + hmacKey through newChunkSender / newChunkReceiver / flushBuffer / decodeChunkPayload — all call sites in client.go and server.go updated. - Add maxChunkBytes (1 MiB) cap in decodeChunkPayload: oversized chunks are rejected before any allocation, preventing memory exhaustion from malicious/buggy clients. - Client activation fail-fast: waitForActive now backs off on consecutive GET errors (jitteredBackoff) instead of fixed 250 ms, and logs a Warn after 3 s of pending state so operators can detect absent servers early. - Session ID collision defense in handleSession: re-fetches metadata after acquireSessionSlot and verifies CreatedAt matches the polled value; mismatches (GC-lag collisions, stale seen-map entries) are rejected. - Graceful drain on Close(): sets closing atomic, waits up to 5 s for activeSessions to reach 0, then cancels the server context; new sessions are rejected while draining. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fakeFirebaseServer now implements SSE streaming: GET with Accept: text/event-stream returns a live event-stream, broadcasting Firebase put-style events to connected listeners on every PUT, so pollLoop's SSE path is exercised without a real Firebase project. - TestChunkSenderHMAC: verifies HMAC tags are written and verified; wrong HMAC key must fail ingest. - TestChunkAADBinding: verifies that an encrypted chunk cannot be decrypted if sessionID, direction, or seq in the AAD differs. - TestChunkSizeCapRejected: verifies oversized chunks are rejected. - TestRelayRoundTrip / TestRelayEncryptedRoundTrip: full end-to-end chunk relay using fakeFirebaseServer — client runSession writes c2s chunks, server runRelay reads and echoes them back via s2c chunks, client reads the echo; covers both HMAC-only and PSK-encrypted paths. - FuzzIngestChunk / FuzzParseSessionMetadata / FuzzDecodeChunkPayload: fuzz targets for all attacker-reachable JSON parsing and chunk decode paths; must not panic on arbitrary input. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace TypeFirebaseTunnelClient + TypeFirebaseTunnelServer (two separate
proxy types) with a single TypeFirebaseTunnel. Role — server (inbound) or
client (outbound) — is now determined at construction time by which sub-config
is present in FirebaseTunnelOptions: options.Server != nil → server mode,
options.Client != nil → client mode.
Changes:
- option/firebasetunnel.go: one FirebaseTunnelOptions with nested
*FirebaseTunnelServerConfig and *FirebaseTunnelClientConfig; shared fields
(firebase_urls, firebase_secret, firebase_auth_token, retry_limit) at top level
- constant/proxy.go: two constants → TypeFirebaseTunnel = "firebasetunnel"
- protocol/firebasetunnel/endpoint.go: single Endpoint struct with srv *serverState
(nil in client mode); extract drainInto, copyToSender, isTerminal helpers;
fix perUserActive map leak on releaseSessionSlot; add all missing constants
- protocol/firebasetunnel/client.go: deleted
- protocol/firebasetunnel/server.go: deleted
- include/registry.go: two Register calls → one RegisterEndpoint call
- protocol/firebasetunnel/endpoint_test.go: update struct literals to Endpoint{}
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(endpoint): add firebasetunnel_client / firebasetunnel_server endpoint pair
Summary
Ports the Firebase Realtime Database relay mechanism from Hiddify2/Firebase-Tunnel (a proof-of-concept with no LICENSE) into hiddify-sing-box as a proper Endpoint pair (
firebasetunnel_client/firebasetunnel_server). No code was vendored — the mechanism was studied and reimplemented from scratch for sing-box's architecture.firebasetunnel_client):DialContextcreates a session node in Firebase RTDB, waits for the server to mark it active, then relays bytes bidirectionally via batched/compressed/optionally-encrypted Firebase chunk queues.firebasetunnel_server):pollLoopdiscovers pending sessions via Firebase SSE stream (with periodic polling fallback), validates the declared user against configured list, routes each accepted connection throughrouter.RouteConnectionExso all sing-box routing rules/DNS/sniffing apply to egress.SSMTracker.TrackConnection— same hook Hiddify Manager already polls for Shadowsocks per-user quota/billing.New files
option/firebasetunnel.goconstant/proxy.goprotocol/firebasetunnel/protocol.goprotocol/firebasetunnel/firebase.goprotocol/firebasetunnel/compress.goprotocol/firebasetunnel/crypto.goprotocol/firebasetunnel/session.goprotocol/firebasetunnel/client.goClientEndpoint—DialContext+ relayprotocol/firebasetunnel/server.goServerEndpoint— poll/relay/GC loopsinclude/registry.goprotocol/firebasetunnel/docs/Security & robustness hardening
sessionID + direction + seqas AEAD additional data — prevents cross-session/direction chunk replay attacksfirebase_secret) on all unencrypted chunks — detects silent byte flips by Firebase-side observers or project adminsuserlabel into a verified credential (only correct key decrypts)CreatedAtmismatchesClose()— 5 s drain window, no new sessions accepted, then hard context cancelWarnlog after 3 s still-pendingfirebase_urlssupported — round-robin failover across Firebase projectsKnown limitations (inherent to transport choice)
firebase_secret(legacy DB secret) grants full project read/write — recommendfirebase_auth_token(short-lived service-account token) for production, or rely on PSK encryption to limit blast radiusThreat model (brief)
firebase_secretcan read raw session metadata (target host/port, timing) — payload is protected by PSK encryption when configured, but metadata is notProvenance
Upstream PoC (
Hiddify2/Firebase-Tunnel) has no LICENSE. Zero code was copied or vendored.protocol/firebasetunnel/docs/provenance.mdcontains a full same-vs-different table.Test plan
go build ./...cleango vet ./protocol/firebasetunnel/cleango test ./protocol/firebasetunnel/... -v— requires go1.25.6 toolchain (module constraint); all tests written and vet-clean, expected to pass in CIgo test -fuzz=FuzzIngestChunk -fuzztime=30s— fuzz targets for all attacker-reachable JSON/chunk-decode paths