Files
juergen 50497780c7
ci/woodpecker/push/build Pipeline was successful
fix ipps certificate lookup
2026-10-19 20:39:02 +02:00

3.9 KiB

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 <host name>.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 <name>.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 <host name>.crt and <Avahi FQDN>.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 <name>.crt/<name>.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.