231 lines
6.6 KiB
Markdown
231 lines
6.6 KiB
Markdown
|
|
# Zabbix SSL Checker
|
||
|
|
|
||
|
|
Centrale SSL/TLS- en HTTPS-monitoring voor homelab- en kleine infra-omgevingen met Zabbix 7.x.
|
||
|
|
|
||
|
|
## Architectuur
|
||
|
|
|
||
|
|
Deze checker gebruikt één centrale Zabbix host, bijvoorbeeld **Zabbix SSL Checker**. Op die host draaien external scripts vanuit `/usr/lib/zabbix/externalscripts/`.
|
||
|
|
|
||
|
|
De Zabbix-template gebruikt Low-Level Discovery om targets uit `/etc/zabbix/ssl_targets.json` te ontdekken. Per ontdekt target draait één master item:
|
||
|
|
|
||
|
|
```text
|
||
|
|
ssl_check.py["--config","{$SSL_CONFIG}","--host","{#SSL_HOST}","--port","{#SSL_PORT}"]
|
||
|
|
```
|
||
|
|
|
||
|
|
Dat master item geeft één JSON-object terug met TLS-, certificaat- en HTTP-informatie. Alle andere items zijn dependent items met JSONPath preprocessing.
|
||
|
|
|
||
|
|
Er is dus géén Zabbix agent-interface nodig op je losse homelab-hosts. Dat voorkomt rode **ZBX agent availability** op systemen waar je helemaal geen agent wilt of kunt installeren. Zabbix bewaakt alleen de centrale checker-host; de echte HTTPS-targets worden agentloos vanaf die plek gecontroleerd.
|
||
|
|
|
||
|
|
## Bestanden
|
||
|
|
|
||
|
|
```text
|
||
|
|
zabbix-ssl-checker/
|
||
|
|
README.md
|
||
|
|
config/
|
||
|
|
ssl_targets.json.example
|
||
|
|
scripts/
|
||
|
|
ssl_discovery.py
|
||
|
|
ssl_check.py
|
||
|
|
zabbix/
|
||
|
|
template_ssl_checker_relaxed_zabbix_7.yaml
|
||
|
|
MANUAL_TEMPLATE_STEPS.md
|
||
|
|
tests/
|
||
|
|
test_config_validation.py
|
||
|
|
test_ssl_check_basic.py
|
||
|
|
```
|
||
|
|
|
||
|
|
## Requirements
|
||
|
|
|
||
|
|
- Python 3.11+
|
||
|
|
- Zabbix server of proxy met external scripts enabled
|
||
|
|
- Geen verplichte Python dependencies buiten de standaardbibliotheek
|
||
|
|
|
||
|
|
Optioneel:
|
||
|
|
|
||
|
|
- `cryptography` voor betere parsing van SAN, issuer, subject, public key type, key size en signature algorithm.
|
||
|
|
|
||
|
|
Zonder `cryptography` blijft de checker werken. Velden die niet betrouwbaar te bepalen zijn, worden `null` en er komt een melding in `warnings`.
|
||
|
|
|
||
|
|
## Installatie
|
||
|
|
|
||
|
|
Voer dit uit op de Zabbix server, proxy of checker-host:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo install -o zabbix -g zabbix -m 0755 scripts/ssl_discovery.py /usr/lib/zabbix/externalscripts/
|
||
|
|
sudo install -o zabbix -g zabbix -m 0755 scripts/ssl_check.py /usr/lib/zabbix/externalscripts/
|
||
|
|
sudo install -o zabbix -g zabbix -m 0640 config/ssl_targets.json.example /etc/zabbix/ssl_targets.json
|
||
|
|
```
|
||
|
|
|
||
|
|
Controleer dat de Zabbix user de config kan lezen:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo chown zabbix:zabbix /etc/zabbix/ssl_targets.json
|
||
|
|
sudo chmod 0640 /etc/zabbix/ssl_targets.json
|
||
|
|
```
|
||
|
|
|
||
|
|
Pas daarna `/etc/zabbix/ssl_targets.json` aan voor je eigen targets.
|
||
|
|
|
||
|
|
## Configuratie
|
||
|
|
|
||
|
|
Voorbeeld:
|
||
|
|
|
||
|
|
```json
|
||
|
|
[
|
||
|
|
{
|
||
|
|
"name": "Blockje Home",
|
||
|
|
"host": "home.blockje.nl",
|
||
|
|
"port": 443,
|
||
|
|
"owner": "blockje",
|
||
|
|
"profile": "relaxed",
|
||
|
|
"expected_issuer_contains": "Let's Encrypt",
|
||
|
|
"expected_hostname": "home.blockje.nl",
|
||
|
|
"http_check": true,
|
||
|
|
"expected_http_status": [200, 301, 302, 401, 403],
|
||
|
|
"timeout": 10
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
Verplichte velden:
|
||
|
|
|
||
|
|
- `name`
|
||
|
|
- `host`
|
||
|
|
- `port`
|
||
|
|
- `owner`
|
||
|
|
- `profile`
|
||
|
|
|
||
|
|
Toegestane profiles:
|
||
|
|
|
||
|
|
- `relaxed`
|
||
|
|
- `serious`
|
||
|
|
- `internal`
|
||
|
|
- `external`
|
||
|
|
|
||
|
|
Als `expected_hostname` ontbreekt, gebruikt de checker automatisch `host`.
|
||
|
|
|
||
|
|
Dubbele targets op dezelfde `host:port` worden één keer meegenomen in discovery.
|
||
|
|
|
||
|
|
## Testcommando's
|
||
|
|
|
||
|
|
Als gebruiker `zabbix`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo -u zabbix /usr/lib/zabbix/externalscripts/ssl_discovery.py --config /etc/zabbix/ssl_targets.json
|
||
|
|
sudo -u zabbix /usr/lib/zabbix/externalscripts/ssl_check.py --config /etc/zabbix/ssl_targets.json --host home.blockje.nl --port 443
|
||
|
|
```
|
||
|
|
|
||
|
|
Zonder config kan ook:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
./scripts/ssl_check.py --host home.blockje.nl --port 443 --expected-hostname home.blockje.nl --http-check
|
||
|
|
```
|
||
|
|
|
||
|
|
De scripts schrijven meetdata naar stdout en fouten/debugmeldingen naar stderr. `ssl_check.py` geeft bij TLS- of bereikbaarheidsproblemen nog steeds JSON terug, zodat dependent items stabiel blijven.
|
||
|
|
|
||
|
|
## Zabbix import
|
||
|
|
|
||
|
|
1. Ga naar **Data collection -> Templates -> Import**.
|
||
|
|
2. Importeer `zabbix/template_ssl_checker_relaxed_zabbix_7.yaml`.
|
||
|
|
3. Maak een nieuwe host:
|
||
|
|
- Host name: `Zabbix SSL Checker`
|
||
|
|
- Interfaces: geen externe homelab-agent interfaces nodig
|
||
|
|
- Link template: `Template SSL Checker Relaxed`
|
||
|
|
- Macro `{$SSL_CONFIG}`: `/etc/zabbix/ssl_targets.json`
|
||
|
|
4. Laat discovery draaien of klik op **Execute now** bij de discovery rule.
|
||
|
|
|
||
|
|
Als import faalt door YAML-verschillen tussen Zabbix 7.x minor releases, gebruik dan `zabbix/MANUAL_TEMPLATE_STEPS.md`.
|
||
|
|
|
||
|
|
## Wat wordt gecontroleerd
|
||
|
|
|
||
|
|
TLS/certificaat:
|
||
|
|
|
||
|
|
- bereikbaarheid
|
||
|
|
- hostname match
|
||
|
|
- chain validity via de default trust store
|
||
|
|
- self-signed detectie waar mogelijk
|
||
|
|
- geldigheid nu, not before, not after en days left
|
||
|
|
- issuer CN/org, subject CN, SAN names
|
||
|
|
- SHA256 fingerprint
|
||
|
|
- negotiated TLS version
|
||
|
|
- TLS 1.0, 1.1, 1.2 en 1.3 support waar Python/OpenSSL dat toestaat
|
||
|
|
|
||
|
|
HTTP:
|
||
|
|
|
||
|
|
- HTTPS request met maximaal 5 redirects
|
||
|
|
- response time
|
||
|
|
- final URL
|
||
|
|
- statuscode en verwachte status
|
||
|
|
- Server header
|
||
|
|
- HSTS
|
||
|
|
- X-Content-Type-Options
|
||
|
|
- X-Frame-Options
|
||
|
|
- Content-Security-Policy
|
||
|
|
- Referrer-Policy
|
||
|
|
- simpele security header score van 0 tot 5
|
||
|
|
|
||
|
|
## Debugtips
|
||
|
|
|
||
|
|
Unsupported items:
|
||
|
|
|
||
|
|
- Controleer of de scripts executable zijn.
|
||
|
|
- Controleer of ze in de Zabbix `ExternalScripts` directory staan.
|
||
|
|
- Run exact dezelfde key als `zabbix` user.
|
||
|
|
- Kijk in `zabbix_server.log` of `zabbix_proxy.log`.
|
||
|
|
|
||
|
|
Script permissies:
|
||
|
|
|
||
|
|
- Scripts: `0755`, owner `zabbix:zabbix`
|
||
|
|
- Config: `0640`, owner `zabbix:zabbix`
|
||
|
|
|
||
|
|
Python dependencies:
|
||
|
|
|
||
|
|
- De basis gebruikt alleen standaardbibliotheken.
|
||
|
|
- Installeer optioneel `cryptography` als je rijkere key- en signaturevelden wilt.
|
||
|
|
|
||
|
|
DNS/firewall:
|
||
|
|
|
||
|
|
- Test DNS vanaf de checker-host, niet vanaf je laptop.
|
||
|
|
- Test of de checker-host TCP 443 naar het target mag openen.
|
||
|
|
|
||
|
|
CA trust store:
|
||
|
|
|
||
|
|
- Chain-validatie gebruikt de default trust store van Python/OpenSSL.
|
||
|
|
- Voor interne CA's moet de CA op de checker-host trusted zijn.
|
||
|
|
|
||
|
|
SNI:
|
||
|
|
|
||
|
|
- De checker gebruikt SNI met `expected_hostname`.
|
||
|
|
- Bij shared hosting moet `expected_hostname` overeenkomen met de certificaatnaam die je verwacht.
|
||
|
|
|
||
|
|
## Security
|
||
|
|
|
||
|
|
- Het configbestand bevat geen secrets.
|
||
|
|
- Maak het toch niet world-writable.
|
||
|
|
- Hostnamen, poorten en profiles worden gevalideerd.
|
||
|
|
- Er wordt geen `shell=True` gebruikt.
|
||
|
|
- Er wordt geen ruwe input in shellcommando's gestopt.
|
||
|
|
- De checker gebruikt geen `openssl` subprocess; TLS loopt via Python `ssl` en `socket`.
|
||
|
|
- Timeouts worden op netwerkverbindingen toegepast zodat checks niet blijven hangen.
|
||
|
|
|
||
|
|
## Tuning
|
||
|
|
|
||
|
|
Standaard in de template:
|
||
|
|
|
||
|
|
- Discovery interval: `1h`
|
||
|
|
- Check interval: `15m`
|
||
|
|
- History raw JSON: `7d`
|
||
|
|
- History dependent items: `30d`
|
||
|
|
- Trends numerieke dependent items: `365d`
|
||
|
|
|
||
|
|
De profiles `relaxed`, `serious`, `internal` en `external` zijn nu vooral metadata en tags. Je kunt later per profile extra triggers of strengere policies toevoegen.
|
||
|
|
|
||
|
|
## Tests
|
||
|
|
|
||
|
|
Lokaal:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python -m pytest
|
||
|
|
```
|
||
|
|
|
||
|
|
De tests controleren configuratievalidatie, discovery output en JSON-output bij een onbereikbaar target.
|