nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix

Merge pull request #121750 from m1cr0man/master

nixos/acme: Ensure certs are always protected

authored by

Martin Weinelt and committed by
GitHub
dc940ecd f51caec3

+27 -13
+10 -6
nixos/modules/security/acme.nix
··· 46 serviceConfig = commonServiceConfig // { 47 StateDirectory = "acme/.minica"; 48 BindPaths = "/var/lib/acme/.minica:/tmp/ca"; 49 }; 50 51 # Working directory will be /tmp ··· 55 --ca-key ca/key.pem \ 56 --ca-cert ca/cert.pem \ 57 --domains selfsigned.local 58 - 59 - chmod 600 ca/* 60 ''; 61 }; 62 ··· 195 196 serviceConfig = commonServiceConfig // { 197 Group = data.group; 198 199 StateDirectory = "acme/${cert}"; 200 ··· 220 cat cert.pem chain.pem > fullchain.pem 221 cat key.pem fullchain.pem > full.pem 222 223 - chmod 640 * 224 - 225 # Group might change between runs, re-apply it 226 chown 'acme:${data.group}' * 227 ''; 228 }; 229 ··· 342 fi 343 344 mv domainhash.txt certificates/ 345 - chmod 640 certificates/* 346 - chmod -R u=rwX,g=,o= accounts/* 347 348 # Group might change between runs, re-apply it 349 chown 'acme:${data.group}' certificates/* ··· 357 ln -sf fullchain.pem out/cert.pem 358 cat out/key.pem out/fullchain.pem > out/full.pem 359 fi 360 ''; 361 }; 362 };
··· 46 serviceConfig = commonServiceConfig // { 47 StateDirectory = "acme/.minica"; 48 BindPaths = "/var/lib/acme/.minica:/tmp/ca"; 49 + UMask = 0077; 50 }; 51 52 # Working directory will be /tmp ··· 54 --ca-key ca/key.pem \ 55 --ca-cert ca/cert.pem \ 56 --domains selfsigned.local 57 ''; 58 }; 59 ··· 196 197 serviceConfig = commonServiceConfig // { 198 Group = data.group; 199 + UMask = 0027; 200 201 StateDirectory = "acme/${cert}"; 202 ··· 220 cat cert.pem chain.pem > fullchain.pem 221 cat key.pem fullchain.pem > full.pem 222 223 # Group might change between runs, re-apply it 224 chown 'acme:${data.group}' * 225 + 226 + # Default permissions make the files unreadable by group + anon 227 + # Need to be readable by group 228 + chmod 640 * 229 ''; 230 }; 231 ··· 340 fi 341 342 mv domainhash.txt certificates/ 343 344 # Group might change between runs, re-apply it 345 chown 'acme:${data.group}' certificates/* ··· 357 ln -sf fullchain.pem out/cert.pem 358 cat out/key.pem out/fullchain.pem > out/full.pem 359 fi 360 + 361 + # By default group will have no access to the cert files. 362 + # This chmod will fix that. 363 + chmod 640 out/* 364 ''; 365 }; 366 };
+17 -7
nixos/tests/acme.nix
··· 330 331 with subtest("Can request certificate with HTTPS-01 challenge"): 332 webserver.wait_for_unit("acme-finished-a.example.test.target") 333 - check_fullchain(webserver, "a.example.test") 334 - check_issuer(webserver, "a.example.test", "pebble") 335 - check_connection(client, "a.example.test") 336 337 with subtest("Certificates and accounts have safe + valid permissions"): 338 group = "${nodes.webserver.config.security.acme.certs."a.example.test".group}" 339 webserver.succeed( 340 - f"test $(stat -L -c \"%a %U %G\" /var/lib/acme/a.example.test/* | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5" 341 ) 342 webserver.succeed( 343 - f"test $(stat -L -c \"%a %U %G\" /var/lib/acme/.lego/a.example.test/**/* | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5" 344 ) 345 webserver.succeed( 346 - f"test $(stat -L -c \"%a %U %G\" /var/lib/acme/a.example.test | tee /dev/stderr | grep '750 acme {group}' | wc -l) -eq 1" 347 ) 348 webserver.succeed( 349 - f"test $(find /var/lib/acme/accounts -type f -exec stat -L -c \"%a %U %G\" {{}} \\; | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0" 350 ) 351 352 with subtest("Can generate valid selfsigned certs"): 353 webserver.succeed("systemctl clean acme-a.example.test.service --what=state") 354 webserver.succeed("systemctl start acme-selfsigned-a.example.test.service") 355 check_fullchain(webserver, "a.example.test") 356 check_issuer(webserver, "a.example.test", "minica") 357 # Will succeed if nginx can load the certs 358 webserver.succeed("systemctl start nginx-config-reload.service") 359 ··· 384 webserver.wait_for_unit("acme-finished-a.example.test.target") 385 check_connection_key_bits(client, "a.example.test", "384") 386 webserver.succeed("grep testing /var/lib/acme/a.example.test/test") 387 388 with subtest("Correctly implements OCSP stapling"): 389 switch_to(webserver, "ocsp-stapling")
··· 330 331 with subtest("Can request certificate with HTTPS-01 challenge"): 332 webserver.wait_for_unit("acme-finished-a.example.test.target") 333 334 with subtest("Certificates and accounts have safe + valid permissions"): 335 group = "${nodes.webserver.config.security.acme.certs."a.example.test".group}" 336 webserver.succeed( 337 + f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5" 338 ) 339 webserver.succeed( 340 + f"test $(stat -L -c '%a %U %G' /var/lib/acme/.lego/a.example.test/**/a.example.test* | tee /dev/stderr | grep '600 acme {group}' | wc -l) -eq 4" 341 ) 342 webserver.succeed( 343 + f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test | tee /dev/stderr | grep '750 acme {group}' | wc -l) -eq 1" 344 ) 345 webserver.succeed( 346 + f"test $(find /var/lib/acme/accounts -type f -exec stat -L -c '%a %U %G' {{}} \\; | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0" 347 ) 348 349 + with subtest("Certs are accepted by web server"): 350 + webserver.succeed("systemctl start nginx.service") 351 + check_fullchain(webserver, "a.example.test") 352 + check_issuer(webserver, "a.example.test", "pebble") 353 + check_connection(client, "a.example.test") 354 + 355 + # Selfsigned certs tests happen late so we aren't fighting the system init triggering cert renewal 356 with subtest("Can generate valid selfsigned certs"): 357 webserver.succeed("systemctl clean acme-a.example.test.service --what=state") 358 webserver.succeed("systemctl start acme-selfsigned-a.example.test.service") 359 check_fullchain(webserver, "a.example.test") 360 check_issuer(webserver, "a.example.test", "minica") 361 + # Check selfsigned permissions 362 + webserver.succeed( 363 + f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5" 364 + ) 365 # Will succeed if nginx can load the certs 366 webserver.succeed("systemctl start nginx-config-reload.service") 367 ··· 376 webserver.wait_for_unit("acme-finished-a.example.test.target") 377 check_connection_key_bits(client, "a.example.test", "384") 378 webserver.succeed("grep testing /var/lib/acme/a.example.test/test") 379 + # Clean to remove the testing file (and anything else messy we did) 380 + webserver.succeed("systemctl clean acme-a.example.test.service --what=state") 381 382 with subtest("Correctly implements OCSP stapling"): 383 switch_to(webserver, "ocsp-stapling")