From 68f481095a218eea85cebf21de0c34b08200be9f Mon Sep 17 00:00:00 2001 From: Vlad Doloman Date: Sat, 15 Aug 2026 05:40:24 +0300 Subject: [PATCH] Document CERT_DAYS and --days --- CLAUDE.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index f579058..abc507c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,6 +13,7 @@ pip install -r requirements.txt # just: cryptography>=41 python3 openvpncertupdate.py # interactive TUI python3 openvpncertupdate.py --create CN --email user@example.com python3 openvpncertupdate.py --reissue CN [--email user@example.com] +python3 openvpncertupdate.py --create CN --email user@example.com --days 90 python3 openvpncertupdate.py --revoke CN python3 openvpncertupdate.py --gen-crl python3 openvpncertupdate.py --list @@ -33,6 +34,7 @@ Edit the `SETTINGS` block at the top of `openvpncertupdate.py` before first run. | `--list-all` | List all CNs with email; read-only, no CA passphrase needed | | | Both grow a trailing `CERT` column marking `MISSING` CNs — see `CertInfo.has_cert_file`. The column is omitted entirely when every CN has its `.crt` | | `--email EMAIL` | Recipient address | +| `--days N` | Certificate lifetime in days for `--create`/`--reissue`; overrides `CERT_DAYS` for that run | | `--send-email` | Force email delivery | | `--no-send-email` | Skip email; print URL to stdout | | `--show-eml` | Print base64-encoded `.eml` to stdout (implies `--no-send-email` unless `--send-email` also given) | @@ -54,7 +56,7 @@ python3 -m pytest tests/test_pki.py::test_sorted_ascending -v # single test | SETTINGS OVERRIDE | `ConfigError`, `load_settings_overrides()`, `_OVERRIDABLE_SETTINGS` | | PKI | `CertInfo`, `_load_current_certs()`, `load_expiring_certs()`, `load_all_certs()`, `_parse_index_line()`, `get_email()` | | PASSWORD | `generate_password()` | -| EASYRSA | `EasyRSAError`, `_easyrsa_diagnostics()`, `issued_cert_path()`, `has_issued_cert()`, `revoke_issued()`, `build_client_full()`, `gen_crl()`, `copy_crl()`, `is_ca_key_encrypted()`, `resolve_ca_passphrase()` | +| EASYRSA | `EasyRSAError`, `_easyrsa_diagnostics()`, `issued_cert_path()`, `has_issued_cert()`, `revoke_issued()`, `build_client_full()`, `gen_crl()`, `copy_crl()`, `is_ca_key_encrypted()`, `resolve_ca_passphrase()`, `resolve_cert_days()` | | CONFIG | `build_ovpn()` → `vpn-configs/__/CONFIG_NAME` | | CRYPTGEON | `CryptgeonError`, `create_note()` | | MAILER | `build_mime_message()`, `send_email()` | @@ -90,6 +92,7 @@ python3 -m pytest tests/test_pki.py::test_sorted_ascending -v # single test - EasyRSA called with `--batch`; `--passin=pass:` omitted when the resolved passphrase is empty - EasyRSA error text arrives on **stdout**, not stderr: its `print()` is `printf '%s\n'`, and both `die()` and `user_error()` route through it. stderr only carries output from the tools EasyRSA shells out to (openssl), and even that is silenced under `-S/--silent-ssl` (not passed here). `_easyrsa_diagnostics()` therefore merges both streams — building an error from stderr alone reports failures as blank - CA passphrase resolution (`resolve_ca_passphrase()`, run once in `main()` right after arg parsing, before dispatch): `CA_PASSPHRASE=""` → auto-detect via `is_ca_key_encrypted()` (checks `/private/ca.key` PEM header for `ENCRYPTED`) and prompt only if encrypted; `"!empty"` → never check/prompt, passphrase is `""`; `"!ask"` → always prompt, skip detection; any other value → used literally. `--list`/`--list-all` skip this resolution entirely since they only read `index.txt` and never touch the CA +- Certificate lifetime (`resolve_cert_days()`, run once in `main()` right after the config overrides are applied — before the `--list` early return, since it validates config and never prompts): `CERT_DAYS = "default"` or `""` → omit `--days` entirely and let EasyRSA's own `EASYRSA_CERT_EXPIRE` (from `vars`) decide; a positive number → passed as `--days=N`, normalised so `"090"` becomes `"90"`. `0`, negatives and non-numeric values raise `ConfigError` — a fast-fail mirror of EasyRSA's own gate (`Cannot use --days=0 for command build-client-full`), not a substitute for it. `--days N` overrides `CERT_DAYS` for one CLI run; the TUI's Days field overrides it per certificate. The flag is added in `build_client_full()` only, never in `_base_cmd()` — `gen-crl` reads `--days` as CRL validity - Cryptgeon: matches the `occulto` browser client — `key=os.urandom(32)` used directly (no derivation) for AES-256-GCM; `contents` = `base64(b"AES-GCM") + "--" + base64(nonce) + "--" + base64(ciphertext)`; `meta` = JSON string `{"type": "text"}`; URL = `/note/#` - `copy_crl()` does `chmod 644` after copy, then runs `RESTORECON_BINARY` (default `restorecon`) on the copied file — best-effort like `is_ca_key_encrypted()`: a missing/misconfigured binary is swallowed, not fatal. Set `RESTORECON_BINARY=""` to disable on non-SELinux systems - Password: pos 1=uppercase, pos 2=lowercase (no j), pos 3-27=alphanumeric, pos 28=lowercase (no j); `oO01lIQ5S2Z8B` banned everywhere