This commit is contained in:
1 parent
548ae918ea
commit
50497780c7
7 files changed
+314
-15
No files matched your search
+63
-13
@@ -90,7 +90,8 @@ fi
|
||||
# A mismatch makes the secure add fail, and macOS then silently falls back to
|
||||
# its "Generic PostScript Printer" driver (no duplex, no colour, no trays).
|
||||
# Install our own certificate with SANs covering every name this server answers
|
||||
# to, and point cupsd at it (see ensure_cupsd_cert_directives below).
|
||||
# to, and make it reachable under every name cupsd looks the certificate up by
|
||||
# (see install_default_cert_copy below).
|
||||
CERT_DIR="${CERT_DIR:-/etc/cups/ssl}"
|
||||
CERT_CRT="${CERT_DIR}/server.crt"
|
||||
CERT_KEY="${CERT_DIR}/server.key"
|
||||
@@ -139,17 +140,63 @@ cert_common_name() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Install a copy of the certificate where cupsd looks by default, i.e.
|
||||
# <name>.crt/.key in the certificate directory, for every name this server
|
||||
# could be known as (host name and Avahi FQDN). CUPS generates its own
|
||||
# certificate there and, at least in CUPS 2.4, keeps serving that file even when
|
||||
# ServerCertificate points elsewhere - the copies make sure the SANs are covered
|
||||
# either way. Re-installed on every start because cupsd may have replaced them.
|
||||
# Local addresses of this host (IPv4 and IPv6), excluding loopback - cupsd
|
||||
# answers loopback connections with the default common name instead.
|
||||
local_addresses() {
|
||||
addrs="$(hostname -I 2>/dev/null || true)"
|
||||
if [ -z "${addrs}" ] && command -v ip >/dev/null 2>&1; then
|
||||
addrs="$(ip -o addr show 2>/dev/null | awk '{ print $4 }' | cut -d/ -f1)"
|
||||
fi
|
||||
printf '%s\n' ${addrs} | grep -vx -e '' -e '127\.0\.0\.1' -e '::1' || true
|
||||
}
|
||||
|
||||
# The file name cupsd derives from a name. http_make_path() in
|
||||
# cups/tls-openssl.c keeps only alphanumerics, '-' and '.', replacing every
|
||||
# other character with '_' (an IPv6 literal thus becomes e.g.
|
||||
# fd26_182_833e_0_be24_11ff_fe21_341c).
|
||||
cert_cups_name() {
|
||||
printf '%s' "$1" | sed 's/[^A-Za-z0-9.-]/_/g'
|
||||
}
|
||||
|
||||
# Every name cupsd may look the server certificate up by. On a server TLS
|
||||
# handshake CUPS 2.4 does *not* use 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 (/etc/cups/ssl). When the lookup
|
||||
# yields a numeric address cupsd keeps it unless it starts with a digit - which
|
||||
# it does not for IPv6 literals. See _httpTLSStart() in cups/tls-openssl.c.
|
||||
# Any name we miss makes cupsd mint a certificate of its own for it, and that
|
||||
# certificate does not cover the name the client actually asked for.
|
||||
cert_lookup_names() {
|
||||
cert_san_list | sed -n 's/^DNS://p' | while IFS= read -r name; do
|
||||
[ -n "${name}" ] || continue
|
||||
printf '%s\n' "$(cert_cups_name "${name}")"
|
||||
done
|
||||
local_addresses | while IFS= read -r addr; do
|
||||
[ -n "${addr}" ] || continue
|
||||
# httpAddrString() spells an IPv6 zone as "+zone" (URI form)...
|
||||
printf '%s\n' "$(cert_cups_name "$(printf '%s' "${addr}" | sed 's/%/+/')")"
|
||||
bare="$(printf '%s' "${addr}" | sed 's/%.*//')"
|
||||
name="$(getent hosts "${bare}" 2>/dev/null | awk 'NR == 1 { print $2 }')"
|
||||
if [ -n "${name}" ]; then
|
||||
printf '%s\n' "$(cert_cups_name "${name}")"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Install a copy of the certificate under every name cupsd looks it up by,
|
||||
# i.e. <name>.crt/.key in the certificate directory. CUPS 2.4 ignores the
|
||||
# ServerCertificate directive (see ensure_cupsd_cert_directives) and resolves
|
||||
# the connection's local address instead, so without these copies it serves a
|
||||
# certificate it generated itself whose SANs do not cover the advertised name.
|
||||
# For example a host-networked container resolves its own LAN address to the
|
||||
# name podman wrote into /etc/hosts ("host.containers.internal"). Re-installed
|
||||
# on every start because cupsd may have written its own certificate since.
|
||||
install_default_cert_copy() {
|
||||
[ -s "${CERT_CRT}" ] || return 0
|
||||
for name in "${CERT_HOSTNAME}" "${CERT_FQDN}"; do
|
||||
cert_lookup_names | sort -u | while IFS= read -r name; do
|
||||
[ -n "${name}" ] || continue
|
||||
case "${name}" in
|
||||
"" | */*) continue ;;
|
||||
*/*) continue ;;
|
||||
esac
|
||||
cp -f "${CERT_CRT}" "${CERT_DIR}/${name}.crt" 2>/dev/null || continue
|
||||
cp -f "${CERT_KEY}" "${CERT_DIR}/${name}.key" 2>/dev/null || continue
|
||||
@@ -214,10 +261,13 @@ ensure_certificate() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Point cupsd at our certificate. Only done when the files exist: cupsd refuses
|
||||
# to start when ServerCertificate cannot be read. Skipped when the directives
|
||||
# are already present, so this also repairs a cupsd.conf previously rewritten by
|
||||
# cupsctl (which drops directives it does not manage).
|
||||
# Point cupsd at our certificate. CUPS 2.4 has dropped ServerCertificate/
|
||||
# ServerKey (cups-files.conf logs an INFO and ignores them); the certificate
|
||||
# lookup there goes through the keychain directory, which install_default_cert_copy()
|
||||
# populates. The directives are kept for older CUPS, and only added when the
|
||||
# files exist: cupsd refuses to start when ServerCertificate cannot be read.
|
||||
# Skipped when already present, so this also repairs a cupsd.conf previously
|
||||
# rewritten by cupsctl (which drops directives it does not manage).
|
||||
ensure_cupsd_cert_directives() {
|
||||
conf=/etc/cups/cupsd.conf
|
||||
[ -f "${conf}" ] || return 0
|
||||
|
||||
Reference in new issue
Block a user