MongoDB RS0 — Heartbeat DROP Block (iptables przed UFW)

Symptom

  • bms-3 logs: Heartbeat failed after max retries, NetworkInterfaceExceededTimeLimit, target=bms-2:27017
  • ufw status pokazuje ALLOW dla bms-3 — UFW wygląda poprawnie
  • nc / curl telnet://bms-2:27017 z bms-3 = timeout (TCP SYN nie odpowiada)
  • bms-3 replication lag rośnie → po ~10h SECONDARY STARTUP2/stale

Przyczyna

Manualna reguła iptables INPUT DROP dodana przed regułami UFW:

Chain INPUT (policy DROP):
num 1   66553 packets   DROP  tcp  src=51.68.155.224  dpt=27017
num 2   ...             ufw-before-logging-input ...

Reguła nie jest widoczna przez ufw status. Nie jest w /etc/iptables/rules.v4 — zniknie po reboot. Może być dodana przez skrypt security isolation lub ręcznie.

Diagnoza (5 minut)

# 1. Sprawdź runtime iptables (NIE ufw status) — szukaj DROP przed ufw-before
ssh root@145.239.133.104 "iptables -L INPUT -n -v --line-numbers | head -10"
ssh root@54.36.123.110  "iptables -L INPUT -n -v --line-numbers | head -10"
 
# Sygnał: wysoka liczba packets przy DROP src=51.68.155.224
 
# 2. Potwierdź TCP fail z rescue/bms-3
curl -v telnet://145.239.133.104:27017  # timeout = DROP aktywny

Naprawa

# Na bms-2 (PRIMARY):
ssh root@145.239.133.104 "iptables -D INPUT -s 51.68.155.224 -p tcp --dport 27017 -j DROP"
 
# Na bms-4 (ARBITER):
ssh root@54.36.123.110 "iptables -D INPUT -s 51.68.155.224 -p tcp --dport 27017 -j DROP"
 
# Weryfikacja — rule 1 powinna być ufw-before-logging-input, nie DROP:
ssh root@145.239.133.104 "iptables -L INPUT -n -v --line-numbers | head -5"

Po usunięciu DROP: bms-3 powinno samo się połączyć i rozpocząć sync w ciągu ~30s.

Recovery RS0 po długim lag

Jeśli bms-3 jest w STARTUP2 lub oplog lag > 1h:

// Step 1: Sprawdź lag i czy bms-3 synchronizuje
rs.status().members.forEach(m => print(m.name, m.stateStr, m.syncSourceHost||'none'))
 
// Step 2: Jeśli lag < 126 dni (oplog window bms-2) — poczekaj na oplog sync
// Jeśli bms-3 ma priority:0, votes:0 — po sync przywróć:
var cfg = rs.conf();
cfg.members[2].priority = 1;
cfg.members[2].votes = 1;
rs.reconfigForPSASet(2, cfg);  // wymagane dla PSA topology
 
// Step 3: Przywróć write concern (jeśli był zmieniony na {w:1}):
db.adminCommand({setDefaultRWConcern:1, defaultWriteConcern:{w:'majority', wtimeout:5000}})

Escalation

Jeśli po usunięciu DROP bms-3 nie łączy się w ciągu 2 minut:

  • Sprawdź czy mongod jest aktywny: systemctl status mongod
  • Sprawdź keyFile: md5sum /etc/mongodb-keyfile — powinno być identyczne na bms-2 i bms-3
  • Sprawdź logi: tail -50 /var/log/mongodb/mongod.log | grep -v Heartbeat

Prevention

  1. Monitoring runtime iptables — ✅ ZAIMPLEMENTOWANE (#2471). scripts/mongodb-rs0-drop-detector.sh uruchamiany co 1 min przez p24-rs0-drop-detector.timer na bms-2 i bms-4. Skanuje surowy iptables-save (nie ufw status) w poszukiwaniu reguł DROP/REJECT dla IP członków rs0 i wysyła alert Discord (+ GH issue) w ciągu ~1 min; self-resolves po usunięciu reguły. Deploy: docs/mongodb-rs0-drop-detector-operations.md.
  2. Cross-server TCP health check — Prometheus probe: bms-3 → bms-2:27017, bms-3 → bms-4:27017
  3. Nigdy nie używać iptables -I INPUT 1 -j DROP dla IP RS members bez dokumentowania i TTL

Incident reference

  • 2026-07-01: #2392 — DROP na bms-2 (66553 pkt) i bms-4 (66655 pkt), przyczyna nieznana
  • 2026-07-01: #2471 — detektor iptables DROP (ten playbook + scripts/mongodb-rs0-drop-detector.sh)
  • Powiązane: #2376 (w4 auth hang), playbook mongodb-write-concern-auth-hang.md

Audit Log — Log to infra_operations

After this operation completes, log it to the infra_operations audit table.

Python (Linux server — bms-4, vps-i1, vps-h1, or similar):

import sys
sys.path.insert(0, '/opt/p24-infra')
from scripts.lib.log_op import log_op
 
log_op(
    actor="claude",  # "radieu" for manual human ops, "claude" for agent
    op_type="config_change",
    resource="mongodb-rs0",
    result="success",  # "success" | "failed" | "skipped"
    detail="MongoDB rs0 heartbeat block resolved — network/firewall rule adjusted",
    env="bms-2",
    gh_issue=2730,
)

PowerShell (Windows dev machine):

$env:SUPABASE_URL = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_URL=").ToString().Split("=",2)[1].Trim()
$env:SUPABASE_SERVICE_KEY = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_SERVICE_KEY=").ToString().Split("=",2)[1].Trim()
python -c "
import os, sys
sys.path.insert(0, 'C:/code_2026/p24-infra')
from scripts.lib.log_op import log_op
log_op('claude', 'config_change', 'mongodb-rs0', 'success', 'MongoDB rs0 heartbeat block resolved — network/firewall rule adjusted', 'bms-2')
"
$env:SUPABASE_URL = ''; $env:SUPABASE_SERVICE_KEY = ''