# Proposal ## Why The previous change (`advertise-valid-ipps-certificate`) made the container *generate* a certificate with the right subject alternative names, but clients still get a certificate they do not trust. From the workstation, `curl https://podp.local:631/` fails with `SSL certificate problem: unable to get local issuer certificate`, and the certificate the server presents has `CN=host.containers.internal` — not the certificate the entrypoint installed. The reasoning in the previous change ("CUPS 2.4 keeps serving `ServerCertificate`, so also write `.crt`") was wrong. Reading the CUPS 2.4.2 sources: - `ServerCertificate`/`ServerKey` are no longer supported. In `cupsd.conf` they log "Please move … to the cups-files.conf file"; in `cups-files.conf` they log "is no longer supported" and are ignored (`scheduler/conf.c`). The certificate directory is `ServerKeychain` (default `ssl` → `/etc/cups/ssl`). - On a server TLS handshake, `_httpTLSStart()` (`cups/tls-openssl.c`) ignores the requested (SNI) name. It takes the **local socket address** the client connected to, reverse-resolves it, and loads `.crt` from the keychain directory. A name that is not alphanumeric, `-` or `.` is mangled to `_` (`http_make_path()`), and a numeric IPv6 result is used verbatim because CUPS only rejects numeric names that start with a digit. So with host networking the client's connection to the LAN address `192.168.100.20` makes cupsd look for `/etc/cups/ssl/host.containers.internal.crt` (the name Podman writes into `/etc/hosts` for the host address). That file did not exist — only `.crt` and `.crt` were written — so cupsd minted its own, which does not cover `podp.local`. IPv6 connections get a certificate named after the IPv6 literal for the same reason. The previous change's verification only checked `localhost`, where cupsd falls back to the default common name and happened to find the copy. ## What Changes - Derive the certificate copies from what cupsd actually looks up: write `.crt`/`.key` for every DNS name in the SAN list **and** for every local address (the address literal, mangled the way `http_make_path()` does, plus whatever name the address reverse-resolves to). This covers the Podman `host.containers.internal` name for the LAN address and the IPv6 literals. - Keep the SAN list and the regeneration logic unchanged: the certificate content was already correct and reissuing it is not desirable. - Correct the comments, the README and the spec so they no longer claim `ServerCertificate` is what makes cupsd serve our certificate. The directive is kept only for CUPS versions older than 2.4. Non-goals: generating a publicly trusted certificate or a private CA, adding the local addresses to the certificate's IP SANs (that needs `CERT_EXTRA_NAMES` and is out of scope), and changing the plain `_ipp` service. ## Capabilities ### New Capabilities ### Modified Capabilities - `printserver/cups`: the requirement that the secure AirPrint certificate matches the advertised name gains a scenario for serving that certificate for the address a client connected to, not only for the name it asked for. ## Impact - `entrypoint.sh`: `install_default_cert_copy` now enumerates the local addresses and cupsd's filename mangling; `ensure_cupsd_cert_directives` comment corrected. - `README.md`: certificate section explains how CUPS selects the certificate. - `openspec/specs/printserver/cups/spec.md`: one scenario added to the certificate requirement. - Clients: fully validating clients (macOS/iOS AirPrint, `curl`) complete the secure connection once they trust the certificate; plain IPP is unaffected. - Existing deployments: an existing `/etc/cups` volume is fixed on the next start; the certificate itself is not reissued, so clients that already trust it keep trusting it.