Skip to content

Commit

Permalink
tls: copy cert/key in cockpit-certificate-ensure
Browse files Browse the repository at this point in the history
When starting up cockpit-tls, we have an existing helper called
cockpit-certificate-ensure which is responsible for checking that we
have a valid TLS certificate, and creating a self-signed one if not.
This helper runs as root so that it can create the certificate, if
needed.

We want to enable cockpit-tls to read certificates which are not owned
by the cockpit-ws user, which is useful in situations where we want to
share certificates with other services.

We can't do the normal "read as root and drop permissions after" trick
because the environment that systemd launches cockpit-tls in never has
the required permissions.  cockpit-certificate-ensure does, however.

Add a new "secret" --for-cockpit-tls option to
cockpit-certificate-ensure which creates a new server/ subdirectory in
the runtime directory of the service, and copies the certificate and key
into that directory, to be read by cockpit-tls.  This directory exists
in a tmpfs, and the files created inside of it are owned by the
cockpit-ws user and inaccessible to anyone else.  Even still, we remove
as soon as we're done reading them at startup.

As an added bonus, we store symlinks into the same directory, which can
be useful for determining the certificate that a running cockpit-tls is
using.
  • Loading branch information
allisonkarlitskaya committed Nov 10, 2021
1 parent b83d91e commit 644116a
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/tls/cockpit-certificate-ensure.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,36 @@ read_file (const char *filename,
close (fd);
}

static void
write_file (int dirfd,
const char *dirfd_filename,
const char *filename,
const gnutls_datum_t *data,
uid_t uid,
gid_t gid)
{
/* Just open the file directly: it doesn't exist yet and nobody will
* look at it until after we're done here.
*/
int fd = openat (dirfd, filename, O_CREAT | O_EXCL | O_WRONLY, 0400);

if (fd == -1)
err (EXIT_FAILURE, "%s/%s: creat", dirfd_filename, filename);

size_t s = write (fd, data->data, data->size);
if (s == -1)
err (EXIT_FAILURE, "%s/%s: write", dirfd_filename, filename);
if (s != data->size)
errx (EXIT_FAILURE, "%s/%s: write: wrote %zu bytes, expecting %zu",
dirfd_filename, filename, s, (size_t) data->size);

/* This is actually making the file more accessible, to do it last */
if (fchown (fd, uid, gid) != 0)
err (EXIT_FAILURE, "%s/%s: fchown", dirfd_filename, filename);

close (fd);
}

static bool
is_selfsigned (const char *certificate_filename)
{
Expand Down Expand Up @@ -162,6 +192,45 @@ certificate_and_key_clear (CertificateKeyPair *self)
self->filename_for_errors = NULL;
}

static void
certificate_and_key_write (const CertificateKeyPair *self,
const char *directory)
{
int dirfd = open (directory, O_PATH | O_DIRECTORY | O_NOFOLLOW);
if (dirfd == -1)
err (EXIT_FAILURE, "open: %s", directory);

struct stat buf;
if (fstat (dirfd, &buf) != 0)
err (EXIT_FAILURE, "fstat: %s", directory);

int r = mkdirat (dirfd, "server", 0700);
if (r != 0)
err (EXIT_FAILURE, "mkdir: %s/%s", directory, "server");

/* fchown() won't accept file descriptors opened O_PATH */
int fd = openat (dirfd, "server", O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (fd == -1)
err (EXIT_FAILURE, "open: %s", directory);

/* Copy the owner/group from the parent directory */
if (fchown (fd, buf.st_uid, buf.st_gid) != 0)
err (EXIT_FAILURE, "%s: fchown", directory);

if (symlinkat (self->certificate_filename, fd, "cert.source") != 0)
err (EXIT_FAILURE, "%s/%s: symlinkat", directory, "certificate.source");

if (symlinkat (self->key_filename, fd, "key.source") != 0)
err (EXIT_FAILURE, "%s/%s: symlinkat", directory, "key.source");

write_file (fd, directory, "cert", &self->certificate, buf.st_uid, buf.st_gid);

write_file (fd, directory, "key", &self->key, buf.st_uid, buf.st_gid);

close (dirfd);
close (fd);
}

static bool
certificate_and_key_split (CertificateKeyPair *self)
{
Expand Down Expand Up @@ -321,11 +390,14 @@ main (int argc, char **argv)
{
CertificateKeyPair result = { };
bool check = false;
bool for_cockpit_tls = false;

if (argc == 1)
;
else if (argc == 2 && strcmp (argv[1], "--check") == 0)
check = true;
else if (argc == 2 && strcmp (argv[1], "--for-cockpit-tls") == 0)
for_cockpit_tls = true;
else
errx (EXIT_FAILURE, "usage: %s [--check]", argv[0]);

Expand All @@ -343,6 +415,15 @@ main (int argc, char **argv)
if (check)
printf ("Would use certificate %s", result.certificate_filename);

if (for_cockpit_tls)
{
const char *runtime_directory = getenv ("RUNTIME_DIRECTORY");
if (runtime_directory == NULL)
errx (EXIT_FAILURE, "--for-cockpit-tls cannot be used unless RUNTIME_DIRECTORY is set");

certificate_and_key_write (&result, runtime_directory);
}

certificate_and_key_clear (&result);

return 0;
Expand Down

0 comments on commit 644116a

Please sign in to comment.