acme: answer HTTP-01 from the server itself, on port 80
HTTP-01 always arrives on port 80 - the CA chooses the port, not the operator - so it never collides with an admin UI on 443. The conflict only exists for TLS-ALPN-01, which is the challenge type that does use 443. Given that, the server keeps a permanent listener on 80 that answers challenges from a webroot and redirects everything else to the admin UI. Same arrangement as the webroot plugins for Apache and nginx, and better than letting the ACME client bind 80 per renewal: nothing binds and unbinds, so a renewal cannot fail because the port was briefly busy, and the client needs only write access to a directory instead of the privilege to bind a low port. Port 80 also gets a use it would want anyway. The ACME client stays an external program. lego is also a Go library, but importing it would put a large dependency tree into a server that deliberately has none, and the CLI does the same job from a timer. Tokens are validated by *shape* before any filesystem call, so traversal never reaches the disk - a stronger guarantee than sanitising a path and trusting the sanitiser. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6afcb131ef
commit
5d7f59a66a
@@ -95,6 +95,13 @@ type Config struct {
|
||||
// is made rather than stumbled into.
|
||||
AdminInsecure bool // ECHOLOT_ADMIN_INSECURE / --admin-insecure
|
||||
|
||||
// Port-80 listener that answers ACME HTTP-01 challenges and redirects everything else to
|
||||
// the admin UI. Empty disables it. HTTP-01 always arrives on port 80 — the CA picks the
|
||||
// port — so this never collides with the admin UI on 443.
|
||||
ACMEHTTPListen string // ECHOLOT_ACME_HTTP_LISTEN / --acme-http-listen
|
||||
// Directory an ACME client writes challenge tokens into. Defaults to <state-dir>/acme.
|
||||
ACMEWebroot string // ECHOLOT_ACME_WEBROOT / --acme-webroot
|
||||
|
||||
// Mode
|
||||
Docker bool // --docker (or autodetected; env ECHOLOT_DOCKER=1 forces)
|
||||
}
|
||||
@@ -166,6 +173,8 @@ func Load(args []string) (*Config, *Actions, error) {
|
||||
fs.StringVar(&c.AdminTLSCert, "admin-tls-cert", envOr("ADMIN_TLS_CERT", ""), "TLS certificate for the admin listener")
|
||||
fs.StringVar(&c.AdminTLSKey, "admin-tls-key", envOr("ADMIN_TLS_KEY", ""), "TLS key for the admin listener")
|
||||
fs.BoolVar(&c.AdminInsecure, "admin-insecure", envOr("ADMIN_INSECURE", "") == "1", "allow the admin UI in plaintext off loopback (you are on your own)")
|
||||
fs.StringVar(&c.ACMEHTTPListen, "acme-http-listen", envOr("ACME_HTTP_LISTEN", ""), "port-80 listener for ACME HTTP-01 challenges and http->https redirects")
|
||||
fs.StringVar(&c.ACMEWebroot, "acme-webroot", envOr("ACME_WEBROOT", ""), "directory an ACME client writes challenges into (default <state-dir>/acme)")
|
||||
fs.StringVar(&c.PublicControlURL, "public-url", envOr("PUBLIC_URL", ""), "public control-plane URL for enrollment links, e.g. https://probe.example.net:8443")
|
||||
fs.StringVar(&c.MinAppVersion, "min-app-version", envOr("MIN_APP_VERSION", "0.2.0"), "oldest app version this server will serve (SemVer, inclusive)")
|
||||
fs.StringVar(&c.MaxAppVersion, "max-app-version", envOr("MAX_APP_VERSION", "1.0.0"), "first app version this server will refuse (SemVer, exclusive); empty = unbounded")
|
||||
|
||||
Reference in New Issue
Block a user