cockpit-tls: enable loading multiple TLS certificates (for post-quantum)#22831
cockpit-tls: enable loading multiple TLS certificates (for post-quantum)#22831allisonkarlitskaya wants to merge 3 commits intocockpit-project:mainfrom
Conversation
a87bab1 to
ae0aeb1
Compare
The Credentials type wraps gnutls_certificate_credentials_t, which can hold multiple certificate/key pairs. Renaming to Credentials better reflects this capability and aligns with GnuTLS terminology. This is preparation for supporting multiple certificates (e.g., PQC with RSA fallback). Assisted-by: Claude <noreply@anthropic.com>
Instead of having one "cert" and one "key" file in the runtime directory, we support any number of files named like "n.crt" and "n.key" where n is an integer starting from 0. cockpit-tls reads files until it sees ENOENT. Meanwhile in cockpit-certificate-ensure, we change the names of the files that we output to "0.crt" and "0.key". We still don't support multiple keys yet, but that will come next.
ae0aeb1 to
2cb0d7e
Compare
Add tests to verify that when multiple certificates (RSA and ECDSA) are configured, the server correctly selects the certificate based on the client's signature algorithm preferences. Assisted-by: Claude <noreply@anthropic.com>
2cb0d7e to
af5d513
Compare
|
@allisonkarlitskaya this failure feels relevant?
And then the unit tries to auto-restart, and eventually barfs. the other failure is a flake obviously. |
martinpitt
left a comment
There was a problem hiding this comment.
Thanks! Code looks good to me. I'll ask Copilot for a second opinion, it finds different stuff than us humans.
src/tls/cockpit-certificate-ensure.c
Outdated
| #include <common/cockpitwebcertificate.h> | ||
| #include <common/cockpitmemory.h> | ||
|
|
||
| #include "certificate.h" |
There was a problem hiding this comment.
Hmm, is this really not using anything from credentials.h? Or is that some weird "transient #include" effect?
|
|
||
| #pragma once | ||
|
|
||
| #include <stdbool.h> |
There was a problem hiding this comment.
This commit does not add anything bool-ish here to the include. Leftover?
There was a problem hiding this comment.
Pull request overview
This PR updates cockpit-tls to support loading multiple TLS certificate/key pairs (to enable post-quantum and other multi-certificate deployments) by switching from fixed certificate filenames to a directory-based, numbered certificate set.
Changes:
- Replace single certificate/key loading with directory-based loading of sequential
N.crt/N.keypairs. - Update certificate ensure/helper outputs and tests to use
0.crt/0.key(and.sourcesymlinks) naming. - Add TLS tests that verify certificate selection (RSA vs ECDSA) via GnuTLS priority configuration.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| test/verify/check-connection | Adjust expected .source symlink name to 0.key.source. |
| src/tls/test-server.c | Extend test fixtures to provision multiple certs and validate selected server cert algorithm. |
| src/tls/test-cockpit-certificate-ensure.c | Update assertions and file loading to use 0.crt/0.key. |
| src/tls/main.c | Open certificate directory, initialize TLS from dirfd, and delete numbered cert/key files after loading. |
| src/tls/credentials.h | Introduce new credentials API header for multi-cert loading. |
| src/tls/credentials.c | Implement directory-based loading of sequential N.crt/N.key into GnuTLS credentials. |
| src/tls/connection.h | Update connection_crypto_init() signature to accept a cert directory fd. |
| src/tls/connection.c | Switch from Certificate to Credentials and initialize TLS from directory-loaded credentials. |
| src/tls/cockpit-certificate-ensure.c | Write 0.crt/0.key and corresponding .source symlinks instead of legacy names. |
| src/tls/certificate.h | Removed legacy single-certificate API. |
| src/tls/certificate.c | Removed legacy single-certificate implementation. |
| src/tls/Makefile-tls.am | Replace certificate sources with credentials sources in the TLS library build. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!load_file (dirfd, crt_name, &crt_data)) | ||
| break; | ||
|
|
||
| debug (SERVER, "Adding certificate %s", cert_name); |
There was a problem hiding this comment.
cert_name is not defined here; this should likely log crt_name. As-is, this is a build-breaking compile error in credentials_load_directory().
| debug (SERVER, "Adding certificate %s", cert_name); | |
| debug (SERVER, "Adding certificate %s", crt_name); |
| * | ||
| * Returns true on success, false if the file doesn't exist. | ||
| * Exits on any other failure. | ||
| * On success, data->data must be freed with gnutls_free(). |
There was a problem hiding this comment.
The comment says data->data must be freed with gnutls_free(), but this function allocates via mallocx() and later frees with free(). Please update the comment to match the actual ownership/deallocator (or switch to gnutls allocators if that’s the intent).
| * On success, data->data must be freed with gnutls_free(). | |
| * On success, data->data must be freed with free(). |
| credentials_load (const char *certificate_filename, | ||
| const char *key_filename); | ||
|
|
||
| Credentials * | ||
| credentials_new_empty (void); | ||
|
|
||
| Credentials * |
There was a problem hiding this comment.
credentials.h declares credentials_load() and credentials_new_empty(), but there are no definitions in src/tls/credentials.c (and no other definitions in the repo). This advertises APIs that don’t exist and will cause link failures if any code starts using them; please either implement them or remove the declarations.
| credentials_load (const char *certificate_filename, | |
| const char *key_filename); | |
| Credentials * | |
| credentials_new_empty (void); | |
| Credentials * |
| * Loads all certificate/key pairs from the directory. Files ending in | ||
| * ".crt" or ".cert" are treated as certificates, with corresponding | ||
| * ".key" files for private keys. |
There was a problem hiding this comment.
The doc comment describes loading arbitrary *.crt/*.cert + *.key pairs from the directory, but credentials_load_directory() actually only looks for sequentially numbered %d.crt and %d.key. Please update this comment (and parameter docs if needed) to reflect the numbered file convention actually required.
| /* Clean up certificate files after loading */ | ||
| for (int i = 0; ; i++) | ||
| { | ||
| char cert_name[16], key_name[16]; | ||
| snprintf (cert_name, sizeof cert_name, "%d.crt", i); | ||
| snprintf (key_name, sizeof key_name, "%d.key", i); | ||
|
|
||
| /* There's absolutely no need to keep these around */ | ||
| if (unlink ("/run/cockpit/tls/server/cert") != 0) | ||
| err (EXIT_FAILURE, "unlink: /run/cockpit/tls/server/cert"); | ||
| if (unlinkat (cert_dirfd, cert_name, 0) != 0) | ||
| break; | ||
| unlinkat (cert_dirfd, key_name, 0); | ||
| } |
There was a problem hiding this comment.
The cleanup loop breaks on any unlinkat(cert) error. That can silently ignore unexpected failures (e.g. EACCES/EPERM/EBUSY) and leave partially cleaned state. Consider breaking only on errno == ENOENT and treating other errors as fatal (and likewise check/report failures for the key unlink if cleanup is intended to be strict).
| allow_unencrypted, client_cert_mode); | ||
| int cert_dirfd = open (cert_dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC); | ||
| if (cert_dirfd == -1) | ||
| err (EXIT_FAILURE, "open: %s", cert_dir); |
There was a problem hiding this comment.
err(EXIT_FAILURE, ...) already appends the strerror(errno) text; using the format string "open: %s" makes the message a bit redundant/confusing. Consider err(EXIT_FAILURE, "%s", cert_dir) or a clearer message like "Failed to open %s".
| err (EXIT_FAILURE, "open: %s", cert_dir); | |
| err (EXIT_FAILURE, "Failed to open %s", cert_dir); |
(incomplete)