# Slack Alerting — Webhook Integration

**Stand:** 2026-04-21

Drei Quellen posten nach Slack auf den gleichen Webhook `ALERT_WEBHOOK_SLACK`:

1. **`dns-maintenance.sh send_alert()`** — Drain/Restore, Peer-Monitoring, Self-Check, Fixed-IP-Server (siehe unten)
2. **`verify-and-alert.sh`** — rotierender 2h-Cluster-Health-Check (21 Verify-Tests) via systemd-Timer
3. **`alert-service-failure.sh`** — Failsafe für `verify-deployment.service` via systemd `OnFailure=`

## Uebersicht

Die `send_alert()` Funktion in dns-maintenance.sh sendet Alerts an einen Slack-Channel via Incoming Webhook. Jeder Alert enthaelt:
- **Severity** (Emoji + Level)
- **Nachricht** (detaillierte Beschreibung)
- **Betroffener Server** (Name + IP)
- **Melder** (welcher Server hat das erkannt)
- **SSH-Link** (Cloudflare Tunnel, direkt klickbar)

## Cloudflare SSH Tunnel URLs

| Server | SSH URL |
|--------|---------|
| Cert-Server-1-NBG (.2) | https://cert-server-1-nbg-ssh.db-app.dev/ |
| Cert-Server-0-NBG (.3) | https://cert-server-0-nbg-ssh.db-app.dev/ |
| Cert-Server-0-FSN (.4) | https://cert-server-0-fsn-ssh.db-app.dev/ |
| Cert-Server-1-FSN (.5) | https://cert-server-1-fsn-ssh.db-app.dev/ |
| Cert-Server-HEL (.6) | https://cert-server-hel-ssh.db-app.dev/ |

## Alle Alert-Events

### Drain / Restore (geplante Wartung)

| Event | Severity | Nachricht |
|-------|----------|-----------|
| Drain gestartet | info | `DRAIN gestartet — Server wird aus DNS entfernt (900s Timeout, 300s Quiet)` |
| Drain erfolgreich | info | `DRAIN abgeschlossen nach Xs (Ys silent). 172 Records entfernt.` |
| Drain Timeout | critical | `DRAIN TIMEOUT nach 900s! Traffic kommt moeglicherweise noch an.` |
| Reboot eingeleitet | info | `REBOOT wird ausgefuehrt — Drain erfolgreich, Server startet in 3s neu` |
| Drain-and-Reboot fehlgeschlagen | critical | `DRAIN-AND-REBOOT FEHLGESCHLAGEN — Server wurde NICHT rebootet.` |
| Restore gestartet | info | `RESTORE gestartet — DNS-Records werden wiederhergestellt` |
| Restore erfolgreich | info | `RESTORE abgeschlossen: 172 Records erstellt. Server ist wieder im DNS.` |
| Restore mit Fehlern | critical | `RESTORE mit X FEHLERN abgeschlossen! Manueller Check noetig!` |
| Restore: Apache tot | critical | `RESTORE ABBRUCH: Apache2 laeuft nicht!` |
| Restore: DNS-Propagation | info | `RESTORE abgeschlossen: X Records erstellt. Y DNS-Verifikationen ausstehend.` |

### Peer-Monitoring (automatisch, alle 30s)

| Event | Severity | Nachricht |
|-------|----------|-----------|
| Peer unreachable, kein Quorum | warning | `Peer X nicht erreichbar, aber Quorum nicht erreicht (2/3)` |
| Quorum erreicht | critical | `QUORUM ERREICHT: X von Y Servern als DOWN bestaetigt — Emergency-Drain wird eingeleitet` |
| Dry-Run: Drain uebersprungen | warning | `DRY-RUN: Peer-Drain uebersprungen. Waere sonst gedrained worden!` |
| Emergency-Drain erfolgreich | critical | `EMERGENCY DRAIN abgeschlossen: X wurde aus DNS entfernt.` |
| Emergency-Drain fehlgeschlagen | critical | `EMERGENCY DRAIN FEHLGESCHLAGEN! Cloudflare API-Fehler. Manueller Eingriff noetig!` |
| Peer recovered | info | `Peer X ist wieder erreichbar nach Emergency-Drain (Self-Restore erwartet)` |

### Self-Check (Apache-Gesundheit)

| Event | Severity | Nachricht |
|-------|----------|-----------|
| Apache restartet (Unit lief) | warning | `Self-check: Apache restarted (unit active but port 443 unresponsive)` |
| Apache gestartet (Unit tot) | critical | `Self-check: Apache was dead, started` |
| Apache bleibt tot | critical | `Self-check: Apache still not responding after restart` |

### Fixed-IP Server (.109/.110)

| Event | Severity | Nachricht |
|-------|----------|-----------|
| 3 Min unerreichbar | critical | `Fixed-IP Server bund/bundeswehr seit 3 Min nicht erreichbar! Manueller Eingriff noetig.` |

### Verify-and-Alert (Cluster-Health Rotationsschedule)

`verify-and-alert.sh` ruft `verify-deployment.sh --quiet` auf und postet das Ergebnis. Rotiert zu unterschiedlichen Uhrzeiten, damit jeder Server sich einmal gegen den Cluster testet und kein SPOF existiert.

| Server | OnCalendar |
|---|---|
| Cert-Server-1-NBG (.2) | `Mon..Fri 08:05` |
| Cert-Server-0-NBG (.3) | `Mon..Fri 10:05` |
| Cert-Server-0-FSN (.4) | `Mon..Fri 12:05` |
| Cert-Server-1-FSN (.5) | `Mon..Fri 14:05` |
| Cert-Server-HEL (.6)   | `Mon..Fri 16:05` |

Severity:
- `:information_source: [INFO]` — alle Checks OK
- `:warning: [WARN]` — Warnings aber keine Fails
- `:rotating_light: [CRITICAL]` — mindestens ein FAIL

**`TimeoutStartSec=300`** im Service-Drop-In `timeout.conf`. Default wäre 120 s, HEL braucht aufgrund der Helsinki↔NBG/FSN-Latenz (RTT ~22 ms × 28 Checks × 5 Server = ~150 s bei P95, im Worst-Case >120 s) regelmäßig etwas mehr — deshalb cluster-einheitlich auf 300 s gehoben. Post-Mortem Silent-Failure 2026-04-20 16:19 auf .6 (Default-Timeout gerissen, damals OnFailure noch nicht installiert) motivierte die Erhöhung.

### Service-Failure-Failsafe (OnFailure)

Falls `verify-deployment.service` aus irgendeinem Grund fehlschlägt (Timeout, exit ≠ 0, OOM-Kill, Crash), feuert systemd automatisch das Template `verify-deployment-alert@<unit>.service`, das `alert-service-failure.sh` ausführt und einen Not-Alert postet mit:
- Hostname + IPv4
- Unit-Name
- Reason (`Result` + `ActiveState` + `SubState` + `ExecMainStatus`)
- SSH-Tunnel-Link zum Server
- Letzte 20 Journal-Zeilen der fehlgeschlagenen Invocation

**Installation-Dateien** (auf allen 5 Servern):
- `/etc/systemd/system/verify-deployment-alert@.service` (Template)
- `/etc/systemd/system/verify-deployment.service.d/onfailure.conf` (`OnFailure=verify-deployment-alert@%n.service`)
- `/etc/systemd/system/verify-deployment.service.d/timeout.conf` (`TimeoutStartSec=300`)
- `/root/projects/cert-pki/scripts/alert-service-failure.sh` (repo-managed)

**Restrisiko:** Bei komplettem Server-Ausfall (Netzwerk/Strom) kann auch OnFailure nichts posten → externer Dead-Man's-Switch (healthchecks.io o.ä.) ist separater Task.

**Testen** (transienter Unit, löst OnFailure-Kette komplett aus):
```bash
systemd-run --unit=test-failsafe \
  --property='OnFailure=verify-deployment-alert@test-failsafe.service' \
  /bin/false
# → Slack-Alert kommt mit result=exit-code state=failed/failed exit=1
systemctl reset-failed test-failsafe.service 'verify-deployment-alert@test-failsafe.service'
```

## Nachrichtenformat (Slack)

```
🔴 *[CRITICAL]* QUORUM ERREICHT: Cert-Server-0-FSN (49.12.76.141) von 3 Servern als DOWN bestaetigt
Server: *Cert-Server-0-FSN* | Gemeldet von: *Cert-Server-1-NBG* (188.245.157.241)
SSH: https://cert-server-0-fsn-ssh.db-app.dev/ | Melder-SSH: https://cert-server-1-nbg-ssh.db-app.dev/
```

## Konfiguration

**Variable:** `ALERT_WEBHOOK_SLACK` in `/etc/environment` auf allen 5 Servern.

```
ALERT_WEBHOOK_SLACK="https://hooks.slack.com/services/T.../B.../..."
```

**Slack App:** `db-cert-mtls-cluster-alert` im Workspace BLUEITS.

### Slack App verwalten

- **Dashboard:** https://api.slack.com/apps (einloggen mit BLUEITS-Account)
- **Webhook URL aendern:** App → Incoming Webhooks → neuen Webhook hinzufuegen / alten entfernen

## Testen

```bash
# Manueller Test (direkt):
curl -s -X POST "$ALERT_WEBHOOK_SLACK" \
  -H "Content-Type: application/json" \
  -d '{"text":"🟢 *[TEST]* Webhook-Test von '$(hostname)'"}'

# Via dns-maintenance:
export $(grep ALERT_WEBHOOK_SLACK /etc/environment | xargs)
export $(grep CF_TOKEN /etc/environment | xargs)
dns-maintenance peer-check
```

## Troubleshooting

| Problem | Ursache | Fix |
|---------|---------|-----|
| Keine Slack-Nachricht | `ALERT_WEBHOOK_SLACK` nicht in Environment | `grep ALERT /etc/environment` |
| `no_service` Antwort | Webhook URL ungueltig | Neue URL in Slack App generieren |
| `channel_not_found` | Channel geloescht | App neu installieren |
| Alert bei Peer-Check fehlt | EnvironmentFile fehlt | `EnvironmentFile=/etc/environment` in dns-peer-check.service |
