feat(extensions): add Enable HTTPS extension (Let's Encrypt / certbot DNS-01)#806
Open
lexfrei wants to merge 10 commits into
Open
feat(extensions): add Enable HTTPS extension (Let's Encrypt / certbot DNS-01)#806lexfrei wants to merge 10 commits into
lexfrei wants to merge 10 commits into
Conversation
Introduce the side-effect-free core of an optional HTTPS feature: helpers that rewrite a KIAUH-generated Mainsail/Fluidd nginx site into an HTTP->HTTPS redirect server block plus a TLS server block carrying the original body. The body is taken from the on-disk config rather than a duplicate template, so it always reflects what KIAUH actually generated. Keeping this logic pure (no subprocess, sudo or certbot) makes the config transformation fully unit-testable without a running nginx or a Debian host. Covered by structural tests plus a golden-output snapshot. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
get_nginx_listen_port took the last whitespace token of a listen line, so "listen 443 ssl http2;" parsed to "http2" and the port silently dropped to None. That excluded such ports from read_ports_from_nginx_configs and any port-conflict check. Take the address token right after the listen keyword and keep the part after the last colon, so both "listen 443 ssl http2;" and "listen [::]:80;" resolve to the numeric port. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
get_string_input reads via input(), which echoes to the terminal and leaves the value in scrollback - unsafe for credentials such as DNS provider API tokens. Add get_secret_input, which reads through getpass so the secret is never displayed. The value is returned verbatim (only blank input is rejected) so a secret is never silently altered. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Describe each supported certbot DNS plugin as pure data (apt package, certbot flags, credentials-file fields) and assemble the certbot command and the credentials-file body generically from it, so adding a provider is one dict entry. Ship Cloudflare, DigitalOcean and Linode. The credentials and propagation flags are optional, leaving room for a future env/IAM provider with no credentials file. The assembled command references only the credentials file path, never the secret, and uses long-form flags throughout. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Thin, mockable wrappers for the privileged side of HTTPS issuance: install certbot and the DNS plugin, write the provider credentials to a root-owned 0600 file, run certbot certonly, and install a renewal deploy hook that reloads nginx. The provider secret reaches the credentials file only through tee's stdin (stdout discarded) and never appears as a command argument or a log line. Certbot output is shown live and never captured, so a secret cannot leak through its stderr. The deploy hook is idempotent and reloads nginx only when a certificate is actually renewed. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Wrap the privileged nginx steps needed to apply an HTTPS rewrite safely: back up the current site, write the new one, validate with nginx -t, and reload or restore the backup. This lets the orchestration switch a site to HTTPS without ever leaving nginx holding a configuration it cannot load - critical when nginx is the only way to reach the printer. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Wire the HTTPS feature into the Extensions menu. Enabling gates on a Debian-based host, selects the Mainsail/Fluidd site and a DNS provider, prompts for the domain, email and credentials (the token via the no-echo input), obtains a certificate, installs the renewal hook and applies the HTTPS rewrite transactionally - reverting nginx if the new config fails nginx -t. Disabling regenerates the original HTTP site from KIAUH's own template and keeps the certificate and credentials, printing the manual cleanup commands instead of deleting them. A same-origin reverse proxy does not need Moonraker cors_domains changes, so that is surfaced as a conditional note rather than applied automatically. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
An HTTPS site carries a dedicated 443 TLS block alongside an :80 redirect. Running "Reconfigure Listen Port" on it would regenerate a plain-HTTP config, stripping the TLS directives and orphaning the certificate. Detect a TLS block and refuse, pointing the user at the HTTPS extension to disable it first. Also pin that the listen-port parser resolves such a dual-block site to 443 - the value the HTTPS port-conflict guard depends on. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Make enabling then disabling HTTPS preserve a non-default port: the redirect block now listens on the port the site is currently served on (read from the live config), and disabling reads the port back from that redirect block rather than from KIAUH settings, so a custom port survives the round-trip. Harden the two issuance failure paths: a certbot/plugin apt install failure is caught and reported instead of crashing the menu, and a failed certbot run removes the credentials file it wrote moments earlier so a token is not left behind for an operation that aborted. Also document that the rewrite only carries the first server block. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
…ields A non-secret DNS credential field with no default was read through the alphanumeric-only input path, which would reject any value containing '-', '_', '.' or '/' - common in zone ids, account names and API versions. Read all non-secret fields with special characters allowed and the field default offered, so a future provider that adds such a field works without a silent "invalid choice" rejection. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an optional "Enable HTTPS" extension. It obtains a Let's Encrypt certificate via certbot's DNS-01 challenge and reconfigures the Mainsail/Fluidd nginx site into an HTTP→HTTPS redirect plus a TLS server block on port 443.
DNS-01 is used instead of HTTP-01 so it works on a printer that is only reachable on the LAN: HTTP-01 needs the CA to reach
http://<fqdn>/.well-known/...from the public internet, which a private-IP host cannot satisfy. DNS-01 only needs an API token for a domain whose DNS is hosted by a supported provider. Supported here: Cloudflare, DigitalOcean and Linode; the provider model is a small dataclass, so adding more is a single entry.Why
KIAUH installs Mainsail/Fluidd behind nginx on port 80, HTTP only, with no built-in TLS option. Every LAN-only user currently hand-rolls a certbot DNS-01 issuance plus an nginx rewrite by hand. This turns it into a guided, reversible action.
Behaviour
nginx -tand restore the previous config if it fails, so nginx is never left unable to load./root/.secrets/<provider>.ini(chmod 600 in a 700 dir), passed to the file via stdin, never as a command argument, never logged, never in KIAUH config. The prompt does not echo.Also included
get_nginx_listen_port: it took the last token of alistenline, solisten 443 ssl http2;parsed tohttp2and the port dropped toNone. It now reads the address token afterlisten, so ssl / IPv6 /host:portforms resolve to the numeric port. Independent of the feature; happy to split it out.get_secret_input, viagetpass) for credentials.Notes
maintained_byis set todw-0by analogy with the other bundled extensions — glad to switch it to my username if you'd prefer I maintain it.cors_domainsis left unchanged: nginx is a same-origin reverse proxy, so the normal UI flow is not subject to CORS. The success dialog mentions the manual step only for the cross-origin case.Testing
86 new tests: pure transform with a golden snapshot, provider command/credentials assembly, secret handling, transactional apply + rollback, port round-trip, distro gate, listen-port parser edge cases, and the port-reconfigure guard. Full suite passes; ruff and mypy clean on the new modules.