盲中繼濫用防護
AeroNyx 節點在轉送加密中繼流量前套用的限制:前一跳速率限制與隔離、重新啟動後仍有效的路由重放抑制、有邊界的重試與 CPU 准入,且只使用經簽署的中繼資料與彙總計數器。
盲中繼濫用防護是 AeroNyx 節點在為其他節點轉送加密中繼流量之前所套用的一組限制。它只使用經簽署的路由中繼資料與本機計數器,來圍堵具濫用行為或不穩定的對等節點。它絕不剖析密文、絕不保存路由歷史,也絕不揭露誰與誰交談。
範圍
| 控制項 | 狀態 |
|---|---|
| 不透明酬載轉送 | 已實作 |
| 經簽署的新鮮度檢查與路由重放抑制 | 已實作。當節點的聊天中繼儲存區啟用時,重放狀態在重新啟動後仍可保留 |
| 前一跳速率限制與隔離 | 已實作 |
| 全程序範圍的請求與加密運算准入限制 | 已實作 |
| 節點健康狀態與 Nodeboard 中的彙總執行期證據 | 已實作 |
| 檢查明文或酬載 | 依設計不執行 |
| 逐使用者、逐路由或社交圖譜分析 | 依設計不執行 |
盲節點規則
中繼可以驗證路由中繼資料、套用有邊界的政策、轉送不透明信封並回傳經簽署的回執。它絕不可檢查酬載,也絕不可揭露足以重建路由、使用者身分或關係的中繼資料。具體而言,中繼不會記錄或揭露:
- 訊息、封包、媒體或記憶的明文
- DNS 查詢、目的地、網域、URL 或瀏覽紀錄
- 路由 ID、完整路徑、端點 URL 或用戶端 IP 位址
- 完整公鑰、接收者身分、訊息 ID 或社交圖譜關係
- 私鑰、票券機密、錢包層級流量或解密素材
准入流程
每個請求在可以使用轉送容量之前,都必須通過以下檢查。步驟 1 到 6 依序執行。在步驟 7 中,以本節點為目標的請求會交由本節點自身的終端或洋蔥處理,而以其他節點為目標的請求則會在解析出下一跳後進行保留。
1. request gate: process-wide admission window and in-flight limit (64 requests)
2. crypto admission: a free slot for signature and onion work, or Backpressure
3. authenticate the signed previous hop and envelope
4. previous-hop rate limit and quarantine check
5. envelope metadata: size, age and future-skew limits
6. self-loop check
7. route loop, TTL and next-hop descriptor checks, plus the route replay reservation
8. forward the opaque ciphertext, or peel the terminal layer and deliver
每一項決策都只使用經簽署的路由中繼資料與本機彙總狀態,絕不使用解密後的內容。
執行期限制
以下是節點原始碼中目前的預設值。它們是營運數值,而非永久的協定承諾。
| 常數 | 預設值 |
|---|---|
BLIND_RELAY_ROUTE_REPLAY_CAPACITY | 8192 條有效路由 |
BLIND_RELAY_ROUTE_REPLAY_TTL_SECS | 600 秒 |
PREVIOUS_HOP_RATE_LIMIT 每 PREVIOUS_HOP_RATE_WINDOW_SECS | 每 60 秒 120 個請求 |
PREVIOUS_HOP_FAILURE_THRESHOLD 每 PREVIOUS_HOP_FAILURE_WINDOW_SECS | 每 300 秒 12 次計分失敗 |
PREVIOUS_HOP_QUARANTINE_SECS | 300 秒 |
MAX_PREVIOUS_HOP_BUCKETS | 4096 個桶 |
BLIND_RELAY_MAX_ENVELOPE_AGE_SECS | 600 秒 |
BLIND_RELAY_MAX_FUTURE_SKEW_SECS | 120 秒 |
BLIND_RELAY_DELIVERY_RECEIPT_MAX_AGE_SECS | 120 秒 |
DEFAULT_MAX_ATTEMPTS(轉送重試) | 3 次嘗試 |
MAX_IN_FLIGHT_BLIND_RELAY_REQUESTS | 64 個請求 |
前一跳速率限制與隔離
速率限制以前一跳的簽署身分為鍵。若同一前一跳在 60 秒內發出超過 120 個請求,就會對該跳點啟動五分鐘的本機隔離。另有一項獨立的失敗計分,會在五分鐘內累積 12 次計分失敗後啟動相同的隔離。只有以下原因會計入分數:
invalid_previous_hop | invalid_signature | self_loop | route_loop | ttl_exhausted
傳輸逾時、遺失的確認、重複重試與重放衝突都不會計入分數,因此健康的對等節點不會因網路問題而遭到隔離。桶儲存區有容量上限,閒置的桶會被逐出,因此此防護機制不會累積成長期的通訊圖譜。
路由重放與重試
每個中繼請求都帶有一個路由 ID。當節點的聊天中繼儲存區啟用時([memchain.chat_relay] enabled = true),路由保留與已完成的回應會保存在其 SQLite 資料庫中,因此重放保護在重新啟動後仍然有效。否則,節點會使用具有相同容量與時間窗的記憶體內登錄表。若全部 8192 個槽位都被有效路由佔用,新路由會以 replay_capacity 遭到拒絕,直到槽位到期為止。
重複的路由 ID 會有以下四種結果之一:
| 情況 | 回應 | 對前一跳的影響 |
|---|---|---|
| 相同請求;第一次嘗試已完成 | 重放已儲存的回應,包括任何由終端簽署的回執。不會進行第二次遞送或轉送(duplicate_route) | 計為成功 |
| 相同請求;第一次嘗試仍在執行中 | HTTP 503 route_in_flight。傳送者可以重試 | 無 |
| 同一路由 ID 下的不同請求 | HTTP 400 replay_conflict | 記錄於對等節點健康狀態中。不計入隔離分數 |
| 相同請求,但已儲存的回應超過 120 秒 | HTTP 409 replay_response_expired。該路由在其 600 秒時間窗的剩餘期間內仍保持保留,因此無法再次執行 | 無 |
最後一種結果適用於持久儲存區。經簽署的回執只有作為新鮮證據時才有效,因此節點在 120 秒後不會再重放回執。
當轉送至下一跳時因逾時、連線錯誤或請求錯誤而失敗,節點最多會重試至總共三次嘗試。每次重試前的短暫延遲,是從路由 ID、下一跳與嘗試次數衍生而來。下游節點回報為明確拒絕的失敗不會重試。
加密運算准入(營運者容量說明)
簽章檢查、洋蔥剝除與回執簽署都受 CPU 限制,因此每個節點程序都會加以限制:
- 總槽位:主機回報之 CPU 執行緒數的一半,無條件進位,最少 4 個、最多 8 個,因此即使只有一兩個執行緒的主機也有 4 個槽位。
- 傳入請求:比總數少一個槽位,讓傳出路由建立與回執完成始終能夠推進。因此在最小的主機上,傳入請求也有 3 個槽位。
- 所有槽位都忙碌時:請求會立即以 HTTP 429 與
backpressure原因遭到拒絕。它不會在佇列中等候。
因此,在小型主機上,中繼會在流量突增時提早卸除負載。backpressure_dropped 計數器持續上升,表示節點需要更多 CPU 或更少的中繼流量。
彙總執行期計數器
節點會在心跳中,於以下路徑回報概略的累計計數器與新鮮度時間戳記:
system_stats.discovery_status.peer_store.runtime.blind_relay
system_stats.discovery_status.peer_store.blind_relay_quality
system_stats.discovery_status.peer_store.peer_health_summary
| 計數器群組 | 欄位 |
|---|---|
| 接收與處置 | received、terminal、forwarded、rejected |
| 驗證與保護 | invalid_signature、envelope_too_large、ttl_exhausted、no_route、invalid_endpoint、loop_detected、replay_dropped、timestamp_rejected、rate_limited、quarantined、quarantine_started |
| 傳輸與重試 | backpressure_dropped、forward_failed、retry_attempted、retry_succeeded、retry_exhausted |
| 合成探測 | probe_attempted、probe_succeeded、probe_failed、two_hop_probe_attempted、two_hop_probe_succeeded、two_hop_probe_failed |
| 真實遞送與新鮮度 | verified_client_onion_deliveries、last_verified_client_onion_delivery_at、last_accepted_at、last_event_at |
這些計數器描述的是節點的運作情況。它們不是訊息日誌或計費資料,也無法證明任何特定對話曾經發生。
證據品質
品質摘要將四類證據分開處理:已接受的不透明中繼工作、合成可達性探測、合成兩跳探測,以及針對用戶端發起之遞送的終端簽署回執。real_relay_ready 需要一份新鮮且經驗證的用戶端遞送回執。合成證據絕不會被回報為使用者流量。
status | 意義 |
|---|---|
idle | 尚無任何中繼或探測活動 |
observing | 已有部分證據,但尚未確立就緒狀態 |
stale | 先前的成功證據已不再新鮮 |
ready | 有新鮮的已接受工作或符合資格的證明,且沒有進行中的傳輸問題 |
protecting | 濫用防護已觸發,而中繼仍持續運作 |
degraded | 轉送或探測失敗需要調查 |
attention | 背壓或重試耗盡需要營運者立即處理 |
proof_scope 標示所見過的最強證據:client_message_delivery、relay_acceptance、message_delivery、control_plane、single_hop_control_plane、attempted 或 none。
不含流量關係的對等節點健康狀態
peer_health_summary 讓營運者能找出故障或遭隔離的對等節點,而不會看到誰與誰通訊。
包含:縮短的節點 ID 前綴、概略的健康與描述元狀態、gossip 與路由成功的新鮮度、彙總的路由成功與失敗次數、隔離狀態、剩餘時間,以及概略的原因分組。
絕不包含:完整的節點公鑰、路由 ID、端點清單、加密資料塊或酬載雜湊值、訊息 ID、接收者身分、用戶端 IP 位址、目的地、DNS 資料或社交圖譜關係。
原始碼對照
| 層級 | 節點儲存庫中的路徑 | 角色 |
|---|---|---|
| 中繼協調 | crates/aeronyx-server/src/api/chat_peer/blind_relay.rs | 請求閘門、加密運算准入、驗證、迴圈與 TTL 檢查、重放結果、轉送與終端處理 |
| HTTP 介面與限制 | crates/aeronyx-server/src/api/chat_peer.rs | 路由、主體與進行中請求限制、錯誤代碼與原因分組 |
| 濫用政策 | crates/aeronyx-server/src/api/chat_peer_abuse_guard.rs | 前一跳速率限制、失敗計分與隔離 |
| 重放登錄表 | crates/aeronyx-server/src/api/chat_peer_replay.rs | 記憶體內登錄表與已儲存回應的格式 |
| 持久重放儲存區 | crates/aeronyx-server/src/services/chat_relay_blind_route_store.rs | SQLite 路由保留與回應 |
| 重試政策 | crates/aeronyx-server/src/api/chat_peer_retry.rs | 可重試與明確失敗的區分,以及重試時機 |
| 計數器與品質 | crates/aeronyx-server/src/services/peer_store.rs 與 services/peer_store/status.rs | 彙總計數器、就緒狀態與證明分類 |
| 健康狀態回報 | crates/aeronyx-server/src/api/vpn_health.rs 與 management/reporter.rs | 本機健康狀態 JSON 與心跳酬載 |
節點軟體以 AGPL-3.0 授權開源於 github.com/AeroNyxNetwork/AeroNyx。路由如何建立,請參閱 節點探索與加密多跳中繼。
<!-- faq:start -->常見問題
中繼節點能讀取或記錄它所轉送的流量嗎?
不能。中繼檢查經簽署的路由中繼資料,並轉送不透明的信封。它只保留彙總計數器,絕不保留酬載、路由歷史或接收者身分。
當節點重試同一條路由時會發生什麼事?
若第一次嘗試已完成,節點會重放其已儲存的回應,不會進行第二次遞送。若第一次嘗試仍在執行中,節點會回傳可重試的 route_in_flight。同一路由 ID 下的不同請求會以 replay_conflict 遭到拒絕。
重放保護在節點重新啟動後仍然有效嗎?
是的,前提是節點的聊天中繼儲存區已啟用。此時路由保留與已完成的回應會保存在 SQLite 中。若沒有該儲存區,節點會使用在重新啟動時清空的記憶體內登錄表。
對等節點為什麼會遭到隔離?
對等節點若在 60 秒內傳送超過 120 個請求,或在五分鐘內累積 12 次計分驗證失敗,就會被隔離五分鐘。逾時、遺失的確認與重複重試不會計為失敗。
為什麼我的節點在負載下會回傳 backpressure?
因為它所有的中繼加密運算槽位都在忙碌中。節點約以其 CPU 執行緒數的一半(最少 4 個、最多 8 個)用於簽章與洋蔥處理,並會立即拒絕多出的請求,而不是將其排入佇列。
<!-- faq:end -->