Skip to content

Security and reliability fixes - #85

Open
LDLDL wants to merge 30 commits into
mainfrom
security_fix
Open

LDLDL wants to merge 30 commits into
mainfrom
security_fix

Conversation

@LDLDL

@LDLDL LDLDL commented Oct 10, 2026

Copy link
Copy Markdown
Member

Supersedes #82, #83 and #84. This PR fixes the issues they found, plus others from a
full audit of the server, client, Caddy plugin and tools. Each commit is small
and stands on its own, so the PR can be reviewed commit by commit.

Config breaking changes are listed in docs/breaking-changes-v0.8.0.md.

Security

  • HTTP API
    • Compare the token in constant time.
    • Set read and idle timeouts (header 10s, read 30s, idle 120s).
    • Cap request bodies at 64KiB and answer malformed requests with 400.
  • Server warnings: warn when the token is empty or the API is served over
    plain HTTP.
  • authMethod: defaults to token. Unknown values are rejected on the server,
    the client and the Caddy plugin.
  • mTLS bundles: load errors are returned instead of calling os.Exit. The
    bundles are loaded once at startup, and Caddy refuses a config with a bad
    bundle.
  • Client
    • Limit HTTP cert responses to 1MiB.
    • Warn on --test (TLS verification off) and when a token is sent to an
      http:// URL.
  • ACME account key: an existing key is no longer overwritten. It is written
    only after the account registers, and only replaced with --force
    (certdx_tools google-account -f).
  • SDS: requests without error detail no longer crash the server, and a
    stream panic is recovered. On the client, a response without a TLS
    certificate ends the stream with an error instead of crashing.

Correctness

  • Domains: canonicalized in one place (domain.Canonical), covering case,
    order, duplicates and a trailing dot. Empty domain sets are rejected by the
    server, the client and the Caddy plugin. cache.json entries from older
    versions are canonicalized when loaded.
  • HTTP API never answers 200 with an empty cert. It waits up to 30s while
    the cert is being issued, then answers 503.
  • Client: drops updates that are not a valid cert/key pair.
  • Server renewal
    • ValidBefore stays in the future and within the issued cert's lifetime.
    • Without a valid cert, the renewer retries on a 30s→5m backoff.
    • It never sleeps past ValidBefore.
  • Config validation
    • Server: positive durations, Google lifetime ≤ 90 days, host[:port]
      nameservers, required S3 fields.
    • Client: certificates that would overwrite each other are rejected (same
      domain set, same name in gRPC mode, same file path).
  • DNS-01: disableCompletePropagationRequirement keeps its behavior, but now
    logs a prominent warning, plus another when conservativeDnsCheck
    overrides it.

Reliability

  • Atomic writes: cache.json is written atomically. Renewed certs are saved
    synchronously, and the cert store uses a mutex instead of a writer goroutine.
  • File update action: fsyncs files and their directory. The reload command
    is bounded by a 5 minute timeout.
  • HTTP client failover: each server is retried retryCount times (30s→90s
    backoff) before switching from main to standby. If both fail, the client
    waits 1h and starts again from main. It always goes back to main after a
    success, because main and standby may issue from different CAs.
  • S3 HTTP-01: challenge requests time out after 30s.
  • Logging: the logger and the debug flag can be swapped at runtime without
    races.
  • Caddy plugin
    • Each cert is parsed once instead of on every TLS handshake.
    • Native JSON configs get the same defaults as the Caddyfile.
    • The full client config is validated in Provision.

Refactors

  • SDS server: each stream is served from one event loop. Only the loop sends,
    and offers are deduplicated by entry sequence.
  • DNS-01: options are built in one place (dns01Options).

Testing

  • go vet and go test -race ./pkg/... pass, along with the exec/caddytls
    unit tests.
  • The e2e suite passes: cd test/e2e && go test -tags e2e ./..., about 375s
    (was about 257s). Most of the extra time comes from the 5s minimum
    renew-check interval and the longer HTTP retry waits.
  • New unit tests cover domain canonicalization, empty cert handling, SDS stream
    handling, renew scheduling, config validation, atomic writes, HTTP failover
    and the Caddy JSON defaults.

LDLDL added 30 commits October 9, 2026 18:15
@LDLDL
LDLDL requested review from ExerciseBook and sosyz October 10, 2026 09:01
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant