nixos/acme: Add useRoot option

This commit is contained in:
Lucas Savva 2021-12-04 19:01:18 +00:00
parent 8d01b0862d
commit 41fb8d71ab
No known key found for this signature in database
GPG key ID: E4EC5BF2E2F116A2
2 changed files with 31 additions and 5 deletions

View file

@ -3,6 +3,7 @@ with lib;
let let
cfg = config.security.acme; cfg = config.security.acme;
opt = options.security.acme; opt = options.security.acme;
user = if cfg.useRoot then "root" else "acme";
# Used to calculate timer accuracy for coalescing # Used to calculate timer accuracy for coalescing
numCerts = length (builtins.attrNames cfg.certs); numCerts = length (builtins.attrNames cfg.certs);
@ -23,7 +24,7 @@ let
# security.acme.certs.<cert>.group on some of the services. # security.acme.certs.<cert>.group on some of the services.
commonServiceConfig = { commonServiceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "acme"; User = user;
Group = mkDefault "acme"; Group = mkDefault "acme";
UMask = 0022; UMask = 0022;
StateDirectoryMode = 750; StateDirectoryMode = 750;
@ -101,12 +102,12 @@ let
# is configurable on a per-cert basis. # is configurable on a per-cert basis.
userMigrationService = let userMigrationService = let
script = with builtins; '' script = with builtins; ''
chown -R acme .lego/accounts chown -R ${user} .lego/accounts
'' + (concatStringsSep "\n" (mapAttrsToList (cert: data: '' '' + (concatStringsSep "\n" (mapAttrsToList (cert: data: ''
for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do
if [ -d "$fixpath" ]; then if [ -d "$fixpath" ]; then
chmod -R u=rwX,g=rX,o= "$fixpath" chmod -R u=rwX,g=rX,o= "$fixpath"
chown -R acme:${data.group} "$fixpath" chown -R ${user}:${data.group} "$fixpath"
fi fi
done done
'') certConfigs)); '') certConfigs));
@ -268,7 +269,7 @@ let
cat key.pem fullchain.pem > full.pem cat key.pem fullchain.pem > full.pem
# Group might change between runs, re-apply it # Group might change between runs, re-apply it
chown 'acme:${data.group}' * chown '${user}:${data.group}' *
# Default permissions make the files unreadable by group + anon # Default permissions make the files unreadable by group + anon
# Need to be readable by group # Need to be readable by group
@ -403,7 +404,7 @@ let
mv domainhash.txt certificates/ mv domainhash.txt certificates/
# Group might change between runs, re-apply it # Group might change between runs, re-apply it
chown 'acme:${data.group}' certificates/* chown '${user}:${data.group}' certificates/*
# Copy all certs to the "real" certs directory # Copy all certs to the "real" certs directory
if ! cmp -s 'certificates/${keyName}.crt' out/fullchain.pem; then if ! cmp -s 'certificates/${keyName}.crt' out/fullchain.pem; then
@ -723,6 +724,18 @@ in {
''; '';
}; };
useRoot = mkOption {
type = types.bool;
default = false;
description = ''
Whether to use the root user when generating certs. This is not recommended
for security + compatiblity reasons. If a service requires root owned certificates
consider following the guide on "Using ACME with services demanding root
owned certificates" in the NixOS manual, and only using this as a fallback
or for testing.
'';
};
defaults = mkOption { defaults = mkOption {
type = types.submodule { options = inheritableOpts {}; }; type = types.submodule { options = inheritableOpts {}; };
description = '' description = ''

View file

@ -232,6 +232,13 @@ in {
} }
]; ];
use-root.configuration = { ... }: lib.mkMerge [
webserverBasicConfig
{
security.acme.useRoot = true;
}
];
# Test compatibility with Nginx # Test compatibility with Nginx
} // (mkServerConfigs { } // (mkServerConfigs {
server = "nginx"; server = "nginx";
@ -450,6 +457,12 @@ in {
webserver.wait_for_unit("nginx.service") webserver.wait_for_unit("nginx.service")
check_connection(client, "slow.example.com") check_connection(client, "slow.example.com")
with subtest("Can set useRoot to true and still use certs normally"):
switch_to(webserver, "use-root")
webserver.wait_for_unit("nginx.service")
webserver.succeed("test \"$(stat -c '%U' /var/lib/acme/* | uniq)\" = \"root\"")
check_connection(client, "a.example.com")
domains = ["http", "dns", "wildcard"] domains = ["http", "dns", "wildcard"]
for server, logsrc in [ for server, logsrc in [
("nginx", "journalctl -n 30 -u nginx.service"), ("nginx", "journalctl -n 30 -u nginx.service"),