homelab-codex-ws/scripts/npm/README.md
oskar 3e42218868 feat(npm): create-cert — zamawianie certow Let's Encrypt przez API
Brakowalo certu dla narty27.kapala.org (host 14 na npm@VPS) — 443 zwracalo
tlsv1 unrecognized name. Proby POST /api/nginx/certificates z meta
{letsencrypt_email, letsencrypt_agree} konczyly sie 400 "data/meta must NOT
have additional properties".

Zweryfikowane w GET /api/schema oraz w /app/internal/certificate.js kontenera
(NPM 2.14.0): meta ma additionalProperties:false i nie zna pol email/agree.
Certbot dostaje --agree-tos na sztywno, a -m <email> z konta uzytkownika NPM.
Poprawny payload to {provider, domain_names, meta:{dns_challenge:false}}.

- create-cert --domain (powtarzalne), HTTP-01, dry-run domyslnie + --apply
- POST /nginx/certificates jest synchroniczne (reload + certbot + reload),
  wiec ma wlasny timeout 120 s zamiast globalnych 15 s; po nim polling
  GET /nginx/certificates/<id> az do expires_on, z czytelnym bledem po czasie
- gdy POST padnie na timeoucie klienta, cert jest odszukiwany po domenach
  (certbot moze wciaz pracowac po stronie serwera)
- set-cert: przyjmuje takze --host-id/--cert-id obok pozycyjnych i umie
  ustawic --ssl-forced przy tej samej zmianie
- README: schemat payloadu, skad bierze sie email, przyklad dla narty27

Cert #38 wystawiony (expires 2026-11-01), podpiety pod host 14 z ssl_forced;
https://narty27.kapala.org/viz.html -> 200, http -> 301.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 16:07:21 +02:00

5.7 KiB

npm_api.py — sterowanie Nginx Proxy Manager przez REST API

CLI do zarzadzania dwoma instancjami NPM (PIHA, VPS) bez klikania w web UI. Uzywa tego samego REST API co panel admina (/api/tokens, /api/nginx/*).

Dlaczego stdlib (urllib), nie requests

Skrypt uzywa wylacznie biblioteki standardowej Pythona (3.11+) — zero zaleznosci do zainstalowania. To narzedzie CLI uruchamiane doraznie z roznych miejsc (SATURN, laptop operatora), nie serwis z wlasnym obrazem Docker — pip install przed kazdym uzyciem byloby tarciem bez korzysci. Zapotrzebowanie na API jest proste (JWT bearer, JSON), wiec urllib.request w zupelnosci wystarcza.

Konfiguracja

cp scripts/npm/env.example scripts/npm/.env
# wypelnij NPM_PIHA_USER/PASS i NPM_VPS_USER/PASS realnymi danymi

.env jest gitignored (wzorzec *.env w .gitignore) — nigdy nie trafia do repo.

Adresy domyslne (nadpisywalne w .env przez NPM_PIHA_URL/NPM_VPS_URL):

Instancja URL Uwaga
piha http://192.168.31.5:81 LAN
vps http://100.95.58.48:81 Tailscale mesh, NIE publiczny IP 135.181.153.108:81

Uzycie

python3 scripts/npm/npm_api.py --npm piha|vps <komenda> [opcje]

Komendy

  • token — loguje sie i pokazuje status + czas wygasniecia tokenu (test poswiadczen).
  • list-hosts — tabela proxy hostow (id, domeny, forward, cert, ssl_forced, enabled).
  • list-certs — tabela certyfikatow (id, provider, nice_name, domeny, expires_on).
  • set-cert HOST_ID CERT_ID [--apply] — podpina certyfikat pod host. Dry-run domyslnie, realna zmiana tylko z --apply. NPM przeladowuje nginx sam po PUT. Rownowaznie: set-cert --host-id 14 --cert-id 38. Opcjonalne --ssl-forced/--no-ssl-forced ustawia przy okazji wymuszanie HTTPS.
  • create-cert --domain ... [--timeout N] [--apply] — zamawia certyfikat Let's Encrypt (challenge HTTP-01). Dry-run domyslnie, realne zamowienie tylko z --apply.
  • create-host --domain ... --forward-host ... --forward-port ... [opcje] [--apply] — tworzy nowy proxy host. Dry-run domyslnie, realna zmiana tylko z --apply.

create-cert — schemat payloadu (NPM 2.14.0)

Wbrew starszym poradnikom meta nie przyjmuje letsencrypt_email ani letsencrypt_agree — schemat ma additionalProperties: false, wiec takie pola daja 400 data/meta must NOT have additional properties. Zweryfikowane w GET /api/schema oraz w zrodle kontenera (/app/internal/certificate.js). Faktyczny payload to:

{"provider": "letsencrypt", "domain_names": ["example.com"], "meta": {"dns_challenge": false}}

Certbot dostaje --agree-tos na sztywno, a -m <email> bierze z konta uzytkownika NPM (GET /api/users -> email). Jesli konto nie ma emaila, NPM zwraca A valid email address must be set on your user account to use Let's Encrypt.

POST /nginx/certificates jest synchroniczne — w jednym requescie leci reload nginx, certbot i drugi reload — dlatego ta komenda ma timeout --timeout (domyslnie 120 s) zamiast globalnych 15 s. Po odpowiedzi skrypt i tak dopytuje GET /nginx/certificates/<id> az expires_on bedzie ustawione; jesli POST padnie na timeoucie klienta, cert jest odszukiwany po domenach (certbot moze wciaz dzialac po stronie serwera). Przy porazce challenge'u NPM kasuje wiersz certu i zwraca blad z wyjsciem certbota — wtedy szukaj szczegolow w docker logs npm na docelowym node.

create-host opcje: --domain (powtarzalne), --forward-host, --forward-port, --forward-scheme http|https (domyslnie http), --cert-id (domyslnie 0 = brak), --ssl-forced/--no-ssl-forced (domyslnie: true jesli podano --cert-id), --http2-support/--no-http2-support, --block-exploits/--no-block-exploits (domyslnie true), --websocket/--no-websocket (domyslnie false), --access-list-id, --advanced-config.

Przyklady

Faza 2 okit.pl — przepiecie certow proxy hostow na wildcard

Zamiast bulk-SQL + recznego sed na .conf (patrz docs/sessions/2026-07-07-okit-wildcard.md), API robi reload samo:

# 1. znajdz ID wildcard certu
python3 scripts/npm/npm_api.py --npm piha list-certs

# 2. znajdz ID hosta ktory ma dostac nowy cert
python3 scripts/npm/npm_api.py --npm piha list-hosts

# 3. podejrzyj zmiane (dry-run)
python3 scripts/npm/npm_api.py --npm piha set-cert 11 5

# 4. wykonaj
python3 scripts/npm/npm_api.py --npm piha set-cert 11 5 --apply

Deploy Gokapi — vhost share.okit.pl na npm@VPS

# dry-run
python3 scripts/npm/npm_api.py --npm vps create-host \
  --domain share.okit.pl \
  --forward-host 127.0.0.1 --forward-port 8080 \
  --cert-id 5 --ssl-forced --http2-support --block-exploits

# apply
python3 scripts/npm/npm_api.py --npm vps create-host \
  --domain share.okit.pl \
  --forward-host 127.0.0.1 --forward-port 8080 \
  --cert-id 5 --ssl-forced --http2-support --block-exploits \
  --apply

Cert dla narty27.kapala.org na npm@VPS

# 1. dry-run
python3 scripts/npm/npm_api.py --npm vps create-cert --domain narty27.kapala.org

# 2. zamow cert (HTTP-01; domena musi wskazywac A-rekordem na publiczny IP VPS)
python3 scripts/npm/npm_api.py --npm vps create-cert --domain narty27.kapala.org --apply

# 3. podepnij pod host 14 + wymus HTTPS
python3 scripts/npm/npm_api.py --npm vps set-cert --host-id 14 --cert-id <ID> --ssl-forced --apply

Obsluga bledow

Bledy autentykacji, brakujace hosty/certy i bledy API zwracane sa z czytelnym komunikatem na stderr (kod wyjscia 1) — np.:

Error: PUT /nginx/proxy-hosts/11 -> HTTP 404: Proxy host not found

Referencje API

Oficjalny swagger jest niekompletny; zweryfikowano bezposrednio ze zrodla (GET /api/schema na dzialacej instancji NPM 2.14, OpenAPI 3.1) oraz github.com/DenAV/nginx-proxy-manager-ansible.