# barox SecPen

Webanwendung zur **kontrollierten Sicherheitsprüfung von Netzwerk-Switches** mit
Nmap und Tenable Nessus.

Die Anwendung wurde für einen konkreten Zweck gebaut: Netzwerkscans können bei
Industrieswitches zu CPU-Spitzen, Paketverlust, Ausfall des Webinterfaces oder
sogar zu Neustarts führen. SecPen macht dieses Verhalten **reproduzierbar,
messbar und nachweisbar** — und bricht ab, bevor ein Gerät ausfällt.

> **Einsatzgrenze.** Ausschliesslich für eigene Geräte, Laborgeräte und
> ausdrücklich autorisierte Kundensysteme. Ohne hinterlegte Scanberechtigung und
> ohne Eintrag in der Allowlist startet kein Scan.

---

## Inhalt

- [Was die Anwendung tut](#was-die-anwendung-tut)
- [Architektur](#architektur)
- [Sicherheitsarchitektur](#sicherheitsarchitektur)
- [Installation](#installation)
- [Konfiguration](#konfiguration)
- [Betrieb](#betrieb)
- [Scanprofile](#scanprofile)
- [Tests](#tests)
- [Weiterführende Dokumentation](#weiterführende-dokumentation)

---

## Was die Anwendung tut

| Bereich | Funktion |
|---|---|
| Erkennung | ICMP, TCP-/UDP-Portscan, Dienst- und Versionserkennung |
| Schwachstellen | Nessus-Scan über vorhandene, freigegebene Policy; Import der Findings |
| Belastung | stufenweiser Belastungstest (Baseline → niedrig → mittel → hoch → Nachbeobachtung) |
| Überwachung | ICMP-Latenz und -Verlust, HTTPS-Erreichbarkeit, SNMP (CPU, RAM, Temperatur, Uptime, STP, Interfaces) |
| Schutz | automatischer Abbruch bei Grenzwertverletzung, Not-Aus, Wartungsfenster, Freigaben |
| Auswertung | Zeitachse, Vergleich mehrerer Scans, Berichte als HTML, PDF, CSV und JSON |
| Nachweis | revisionssicheres Audit-Log mit Hash-Kette |

Typische Fragestellungen, die sich damit beantworten lassen:

- Ab welcher Scanrate steigt die CPU des Switches auf 100 %?
- Verliert das Gerät während eines Scans Pakete oder startet es neu?
- Verhält sich Firmware 1.2.3 anders als 1.3.0?
- Ist das Verhalten ein Konfigurationsproblem, eine Belastungsgrenze oder ein
  Firmwarefehler?

---

## Architektur

```
┌─────────────────────────────────────────────────────────────────────────┐
│ Apache2 + PHP-FPM (Pool "barox-secpen")                                 │
│   DocumentRoot: /var/www/html/barox-secpen/public                       │
│   Laravel 12 · PHP 8.3 · Blade (SSR) · kontrolliertes Polling           │
│   Rolle: NUR Web und API. Startet NIEMALS einen Scanprozess.            │
└───────────────┬─────────────────────────────────────────────────────────┘
                │ Queue (database oder Redis)
                ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ systemd: barox-secpen-worker.service      (User barox, CAP_NET_RAW)     │
│   RunScanJob → ScanRunner → NmapArgumentBuilder → ProcessNmapAdapter    │
│                           → NessusScanManager  → Nessus-Adapter         │
│                                                                          │
│ systemd: barox-secpen-health.service      (unabhängiger Dienst)         │
│   HealthMonitor (ICMP · HTTPS · SNMP) → AbortEvaluator → ScanAborter    │
│                                                                          │
│ systemd: barox-secpen-scheduler.timer                                   │
│   Reaper (verwaiste Prozesse) · Aufbewahrung · Audit-Prüfung            │
└───────────────┬─────────────────────────────────────────────────────────┘
                ▼
   MariaDB/MySQL  ·  /var/lib/barox-secpen  (XML, .nessus, PDF — ausserhalb
                                             des DocumentRoot, mit SHA-256)
```

**Warum drei getrennte Dienste?** Der Health-Monitor läuft bewusst nicht im
Scan-Worker: Bleibt ein Scanprozess hängen, wird trotzdem weiter gemessen und
der automatische Abbruch greift.

### Datenmodell (Auszug)

```
customers ──< projects ──< scan_jobs ──< scan_targets ──< nmap_results ──< nmap_ports
    │            │             │  │                              │
    │            │             │  ├──< scan_stages               └──< findings
    │            │             │  ├──< scan_approvals
    │            │             │  ├──< scan_events        (Zeitachse)
    │            │             │  ├──< scan_health_metrics (Zeitreihe)
    │            │             │  ├──< health_incidents
    │            │             │  └──< nessus_scans ──< findings ──< finding_comments
    │            │
    ├──< sites ──┴──< switches ──> switch_profiles (OID-Mapping)
    │                    └──> switch_credentials  (verschlüsselt)
    │
    ├──< network_allowlists      users ──< role_user >── roles ──< permission_role >── permissions
    └──< network_blocklists      audit_logs (append-only, Hash-Kette) · settings · reports
```

---

## Sicherheitsarchitektur

Die verbindlichen Regeln aus der Anforderung sind an folgenden Stellen im Code
umgesetzt — jede mit einem eigenen Test:

| Regel | Umsetzung | Test |
|---|---|---|
| Keine frei eingebbaren Shell-Befehle | `NmapArgumentBuilder` erzeugt ein `array`, Symfony Process ruft `execve()` ohne Shell | `NmapArgumentBuilderTest` |
| Keine frei eingebbaren Nmap-Optionen | Whitelist in `config/scanner.php`; unbekannte Schlüssel werden ignoriert, Endkontrolle prüft jedes Argument | dito |
| Keine Scans ausserhalb der Allowlist | `TargetGuard` — im Wizard, beim Start **und** unmittelbar vor Prozessstart (TOCTOU) | `TargetGuardTest` |
| Keine öffentlichen Ziele | `IpClassifier::isPublic()`, `TARGETS_ALLOW_PUBLIC=false` | dito |
| Keine DoS-/Brute-Force-Tests | NSE-Whitelist, `PolicyGuard` prüft Nessus-Policies vor jedem Start | `NessusAndReportTest` |
| Keine Konfigurationsänderungen am Ziel | Es existiert kein schreibender Codepfad zum Gerät (nur ICMP, HTTP GET, SNMP GET) | — |
| Aggressive Scans nur mit Freigabe | `ScanOrchestrator::requiresApproval()`, Vier-Augen-Prinzip in `ScanJobPolicy::approve()` | `ScanLifecycleTest` |
| Jeder Scan auditierbar | `AuditLogger` mit `prev_hash`/`hash`-Kette, Model verweigert `update`/`delete` | `SecurityGuaranteesTest` |
| Sofortiger Abbruch möglich | `cancel_requested`-Flag, vom Worker in jeder Schleife geprüft; Not-Aus stoppt alles | dito |
| Kritische Werte brechen ab | `AbortEvaluator` (Sofort- und Dauerkriterien) | `ScanLifecycleTest` |
| Keine Klartext-Secrets | `encrypted`-Casts, `LogRedactor` in Logs und Audit, `toArray()` entfernt Secrets | `SecurityGuaranteesTest` |

Weitere Maßnahmen: strenge CSP mit Nonce, HSTS, `SameSite`-Cookies,
verschlüsselte Sessions, Rate Limiting und Kontosperre, XXE-sicheres Parsen von
Nmap-XML und `.nessus`, Path-Traversal-Prüfung aller Dateipfade, Uploads
ausserhalb des Webroots mit Zufallsnamen und MIME-Prüfung, `disable_functions`
für Prozessstarts im Webprozess.

---

## Installation

### Voraussetzungen

- Ubuntu 22.04 / 24.04 mit Apache2
- PHP **8.3+** mit `pdo_mysql`, `dom`, `simplexml`, `mbstring`, `curl`,
  `openssl`, `fileinfo` (empfohlen zusätzlich `snmp`, `redis`)
- MariaDB 10.6+ oder MySQL 8
- Composer 2
- Nmap (`apt install nmap`), `iputils-ping`, optional `snmp`
- Nessus lokal oder erreichbar (optional)

### Ablauf

```bash
git clone <repository> /opt/barox-secpen-src
cd /opt/barox-secpen-src
sudo bash deploy/install.sh
```

Das Skript prüft Voraussetzungen, legt Benutzer `barox`, Verzeichnisse und
Berechtigungen an, installiert die Abhängigkeiten, erzeugt die `.env` samt
Application Key, führt Migrationen und Seeder aus und legt die systemd-Units
ab. **Bestehende Apache-, PHP- und MySQL-Konfigurationen werden nicht
überschrieben** — abweichende Dateien werden gemeldet, nicht ersetzt.

Danach:

```bash
sudo a2enmod rewrite headers ssl
sudo a2ensite barox-secpen && sudo systemctl reload apache2
sudo systemctl enable --now barox-secpen-worker barox-secpen-health
sudo systemctl enable --now barox-secpen-scheduler.timer
```

Rohsocket-Rechte für Nmap: siehe [`deploy/nmap-capabilities.md`](deploy/nmap-capabilities.md).
`www-data` erhält dabei **keine** Sonderrechte.

### Erste Anmeldung

Der Seeder legt `jeneeben.jesujeevagan@barox.ch` als Administrator an. Ist
`AUTH_BREAKGLASS_PASSWORD` leer, wird ein Zufallspasswort erzeugt und **einmalig
in der Konsolenausgabe angezeigt**. Im Regelbetrieb erfolgt die Anmeldung über
Microsoft Entra ID (`AUTH_OIDC_ENABLED=true`).

---

## Konfiguration

Alle installationsabhängigen Werte stehen in der `.env` — siehe
[`.env.example`](.env.example). Die wichtigsten Gruppen:

| Bereich | Schlüssel |
|---|---|
| Pfade | `BAROX_DATA_PATH`, `BAROX_SCANDATA_PATH`, `BAROX_REPORT_PATH` |
| Zielgrenzen | `TARGETS_ALLOW_PUBLIC`, `TARGETS_HARD_BLOCKLIST`, `TARGETS_MAX_PREFIX_V4`, `TARGETS_MAX_PER_JOB` |
| Nebenläufigkeit | `NMAP_MAX_PARALLEL_JOBS`, `NMAP_MAX_RUNTIME_SECONDS` |
| Grenzwerte | `HEALTH_CPU_THRESHOLD`, `HEALTH_PACKETLOSS_THRESHOLD`, `HEALTH_LATENCY_THRESHOLD_MS`, … |
| Entra ID | `AUTH_OIDC_TENANT_ID`, `AUTH_OIDC_CLIENT_ID`, `AUTH_OIDC_CLIENT_SECRET`, `AUTH_OIDC_ROLE_MAP` |
| Nessus | `NESSUS_PRODUCT`, `NESSUS_BASE_URL`, `NESSUS_ACCESS_KEY`, `NESSUS_SECRET_KEY`, `NESSUS_POLICY_ID` |
| Not-Aus | `SCAN_EMERGENCY_STOP` (per `.env` gesetzt, in der Oberfläche nicht aufhebbar) |
| Adapter | `SCAN_NMAP_ADAPTER`, `SCAN_SNMP_ADAPTER`, … (`real` oder `mock`) |

**Mock-Betrieb.** Mit `SCAN_*_ADAPTER=mock` läuft die gesamte Anwendung ohne
jeden Netzwerkverkehr — geeignet für Schulung, Vorführung und Tests.

---

## Betrieb

```bash
# Zustand prüfen
sudo -u barox php artisan barox:preflight

# Not-Aus
sudo -u barox php artisan barox:emergency-stop on --reason="Kundenanfrage"
sudo -u barox php artisan barox:emergency-stop off

# Verwaiste Prozesse und hängende Aufträge aufräumen
sudo -u barox php artisan barox:reap-scans --dry-run

# Audit-Kette prüfen
sudo -u barox php artisan barox:audit-verify

# Dienste
systemctl status barox-secpen-worker barox-secpen-health
journalctl -u barox-secpen-worker -f
```

---

## Scanprofile

| # | Profil | Intensität | Freigabe | Fenster |
|---|---|---|---|---|
| 1 | Erreichbarkeit | sehr schonend | – | – |
| 2 | Service Discovery | schonend | – | – |
| 3 | Vollständiger TCP-Portscan | erhöht | ja | ja |
| 4 | UDP Management Scan | moderat | ja | – |
| 5 | Nessus Basic Network Scan | moderat | ja | ja |
| 6 | Kontrollierter Belastungstest | maximal | ja (je Stufe) | ja |
| 7 | Nicht-destruktive Validierung | schonend | – | – |

Die Profile sind Systemprofile: Portlisten und Grenzwerte lassen sich
administrativ anpassen, Scantechnik und Timing nicht. Beim Speichern wird jedes
Profil testweise in eine Argumentliste übersetzt — ergibt sich dabei eine
unzulässige Option, wird nicht gespeichert.

---

## Tests

```bash
php artisan test
php artisan test --filter=NmapArgumentBuilderTest   # Command Injection
php artisan test --filter=TargetGuardTest           # Allow-/Blocklist
php artisan test --filter=SecurityGuaranteesTest    # Audit, Secrets, Rechte
```

Alle Tests laufen ausschliesslich gegen Mock-Adapter (`phpunit.xml` erzwingt
das). **Es entsteht kein Netzwerkverkehr gegen reale Geräte.**

---

## Weiterführende Dokumentation

- [Administratorhandbuch](docs/administratorhandbuch.md)
- [Benutzerhandbuch](docs/benutzerhandbuch.md)
- [API-Dokumentation](docs/api.md)
- [Troubleshooting](docs/troubleshooting.md)
- [Sicherung und Wiederherstellung](docs/backup-restore.md)
- [Nmap-Berechtigungen](deploy/nmap-capabilities.md)

---

© barox Kommunikation AG — interne Nutzung.
