From d5969ca92336e889fbc363c4af56424849bb4544 Mon Sep 17 00:00:00 2001 From: Alejandro Angulo Date: Sun, 16 Jul 2023 10:53:02 -0700 Subject: [PATCH] Refactored how SSL certs are configured for nginx Made a separate ACME module to handle requesting certs from multiple machines. Right now, the module only supports exactly one wildcard cert. It might make sense to have cache.kilonull.com use a cert specific to its subdomain rather than also requesting a wildcard cert (or maybe the nginx on its host shouldn't care about TLS and it should be node's responsibility). --- modules/nix/default.nix | 2 +- modules/security/acme/default.nix | 56 +++++++++++++++++++++++ modules/services/adguardhome/default.nix | 47 +++++++------------ modules/services/nextcloud/default.nix | 12 ++++- modules/services/nix-serve/default.nix | 19 ++++++-- secrets/cf_dns_kilonull.age | Bin 566 -> 687 bytes secrets/secrets.nix | 3 +- systems/x86_64-linux/gospel/default.nix | 12 ++++- systems/x86_64-linux/node/default.nix | 18 +++++++- 9 files changed, 128 insertions(+), 41 deletions(-) create mode 100644 modules/security/acme/default.nix diff --git a/modules/nix/default.nix b/modules/nix/default.nix index c8be95e..a603959 100644 --- a/modules/nix/default.nix +++ b/modules/nix/default.nix @@ -7,7 +7,7 @@ }: with lib; let cfg = config.aa.nix; - selfHostedCacheHost = "http://192.168.113.69/"; + selfHostedCacheHost = "https://cache.kilonull.com/"; in { options.aa.nix = with types; { enable = mkEnableOption "manage nix configuration."; diff --git a/modules/security/acme/default.nix b/modules/security/acme/default.nix new file mode 100644 index 0000000..740eea9 --- /dev/null +++ b/modules/security/acme/default.nix @@ -0,0 +1,56 @@ +{ + options, + config, + lib, + pkgs, + format, + ... +}: +with lib; let + cfg = config.aa.security.acme; +in { + options.aa.security.acme = with types; { + enable = mkEnableOption "Automatic Certificate Management Environment (ACME)"; + useStaging = mkOption { + type = bool; + description = '' + Use the staging environment (use when configuring for the first time to + avoid being locked out). + ''; + default = false; + }; + domainName = mkOption { + type = str; + description = "The domain to request a wildcard cert for."; + }; + dnsCredentialsFile = mkOption { + type = path; + description = "The path to the credentials file for the DNS provider."; + }; + }; + + # Only supports exactly one wildcard cert using Cloudflare (only use case I have) + config = mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults = { + email = config.aa.user.email; + group = "nginx"; + server = mkIf cfg.useStaging "https://acme-staging-v02.api.letsencrypt.org/directory"; + }; + + # Wildcard cert + certs."${cfg.domainName}" = { + group = "nginx"; + dnsProvider = "cloudflare"; + # Private network resolves *.kilonull.com to private servers but `lego` + # (acme client under the hood) needs to find the cloudflare nameservers + # to determine the correct zone to apply changes in. Use cloudflare's + # own DNS to make `lego` happy (will resolve names to a public IP). + dnsResolver = "1.1.1.1:53"; + credentialsFile = cfg.dnsCredentialsFile; + extraDomainNames = [("*." + cfg.domainName)]; + }; + }; + }; +} diff --git a/modules/services/adguardhome/default.nix b/modules/services/adguardhome/default.nix index e9dcc9b..6cecd6e 100644 --- a/modules/services/adguardhome/default.nix +++ b/modules/services/adguardhome/default.nix @@ -11,6 +11,14 @@ with lib; let in { options.aa.services.adguardhome = with types; { enable = mkEnableOption "adguardhome"; + acmeCertName = mkOption { + type = str; + default = ""; + description = '' + If set to a non-empty string, forces SSL with the supplied acme + certificate. + ''; + }; }; config = mkIf cfg.enable { @@ -26,37 +34,16 @@ in { services.nginx = { enable = true; recommendedProxySettings = true; - virtualHosts."adguardhome.kilonull.com" = { - forceSSL = true; - useACMEHost = "kilonull.com"; - locations."/" = { - proxyPass = "http://127.0.0.1:3000"; + virtualHosts."adguardhome.kilonull.com" = + { + locations."/" = { + proxyPass = "http://127.0.0.1:3000"; + }; + } + // lib.optionalAttrs (cfg.acmeCertName != "") { + forceSSL = true; + useACMEHost = cfg.acmeCertName; }; - }; - }; - - # So that nginx has access to the ACME certs - users.users.nginx.extraGroups = ["acme"]; - - age.secrets.cf_dns_kilonull.file = ../../../secrets/cf_dns_kilonull.age; - - security.acme = { - # NOTE: Uncomment line below when testing changes - # defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; - acceptTerms = true; - defaults.email = "iam@alejandr0angul0.dev"; - - # Wildcard cert - certs."kilonull.com" = { - dnsProvider = "cloudflare"; - # Private network resolves *.kilonull.com to private servers but `lego` - # (acme client under the hood) needs to find the cloudflare nameservers - # to determine the correct zone to apply changes in. Use cloudflare's - # own DNS to make `lego` happy (will resolve names to a public IP). - dnsResolver = "1.1.1.1:53"; - credentialsFile = config.age.secrets.cf_dns_kilonull.path; - extraDomainNames = ["*.kilonull.com"]; - }; }; networking.firewall = { diff --git a/modules/services/nextcloud/default.nix b/modules/services/nextcloud/default.nix index 69a6685..5463605 100644 --- a/modules/services/nextcloud/default.nix +++ b/modules/services/nextcloud/default.nix @@ -11,6 +11,14 @@ with lib; let in { options.aa.services.nextcloud = with types; { enable = mkEnableOption "nextcloud"; + acmeCertName = mkOption { + type = str; + default = ""; + description = '' + If set to a non-empty string, forces SSL with the supplied acme + certificate. + ''; + }; }; config = mkIf cfg.enable { @@ -47,9 +55,9 @@ in { }; # nextcloud module configures nginx, just need to specify SSL stuffs here - services.nginx.virtualHosts.${config.services.nextcloud.hostName} = { + services.nginx.virtualHosts.${config.services.nextcloud.hostName} = mkIf (cfg.acmeCertName != "") { forceSSL = true; - useACMEHost = "kilonull.com"; + useACMEHost = cfg.acmeCertName; }; networking.firewall.allowedTCPPorts = [80 443]; diff --git a/modules/services/nix-serve/default.nix b/modules/services/nix-serve/default.nix index d8c24b2..894ef18 100644 --- a/modules/services/nix-serve/default.nix +++ b/modules/services/nix-serve/default.nix @@ -19,6 +19,14 @@ in { type = str; description = "The subdomain to use."; }; + acmeCertName = mkOption { + type = str; + default = ""; + description = '' + If set to a non-empty string, forces SSL with the supplied acme + certificate. + ''; + }; }; config = mkIf cfg.enable { @@ -38,8 +46,8 @@ in { nginx = { enable = true; - virtualHosts = { - "${cfg.subdomain_name}.${cfg.domain_name}" = { + virtualHosts."${cfg.subdomain_name}.${cfg.domain_name}" = + { serverAliases = ["${cfg.subdomain_name}"]; locations."/".extraConfig = '' proxy_pass http://localhost:${toString config.services.nix-serve.port}; @@ -47,13 +55,16 @@ in { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ''; + } + // lib.optionalAttrs (cfg.acmeCertName != "") { + forceSSL = true; + useACMEHost = cfg.acmeCertName; }; - }; }; }; networking.firewall = { - allowedTCPPorts = [80]; + allowedTCPPorts = [80 443]; }; }; } diff --git a/secrets/cf_dns_kilonull.age b/secrets/cf_dns_kilonull.age index 315f0911b0d13d15b1f27c8532176ef4643213d9..249d5cea4946d5354183a1cba9364ccb7c221558 100644 GIT binary patch delta 655 zcmW;FOKZ~r003an`5*|J2T{a96m@DA(x|!qTK&-)Wn)X*TUa zM4Ur5@Zw>hprE4YU=B}$bBKZ;B)0{HUs&^ zcA(-fLItAZuhRfL0ydiyu%V-Vzc&bq8BXMQFrdj1-SR-Jt0b6-5)tcCXhtlzgs2>I zf@Lk&OxTQ$84OXO0VGZ&C%7!9wWAgm3Z{9THYwm}%kjq8lp;3DWDT`Z%0%d{5Rz>Qqk)dVkOpJv zaXXYZ(HwykePd%Gw-punYAu}b+L%@~VyvTE|FirkoNpN8O#?Ja@@WvS21P*P!>Qav zG*wf04<#^?s=JseHVhd{O5-FVs-$i5C`+}8Bt~&q9mB$AHvyr+@uto4HtPcdq7{;| zGM-EF5GNxRrCQ#);v)67FPp2OP(D!tU>FAZNVV+R8iBL-1N^=@bHJ);i1UOc<-`Q)3qgX>>Dj@;hr7|Y_Xmv?+Ob2ly$EgH&X aHs|@N!i@HF*cWcyvluWpPBMKU>9WiM4h3QS~5YDHyL zY-nLnLU(LrNp4hhP)KQTV^~s7LQiOLM?+ylW=~dQIB{2Z3N1b$L1Haua%Ew2WgtCT zdM-R}3S&xTS9el3b2mmdbuVamPfS8We?fM1FJ&u4XjxEZRyA={XF^SANkw;NPd6)1 zPI_ZwM_5cVW-m2tVnTFcS4Rp>XliL>YArgNJU3xGert5EiE8p za7QpjW@A@nPB=4IXjNi)V_0=cW>IJ}P*p2yHECmKcSLw-YcX#$Ls1IX3(AFGf1I{l z8|&~yH~(YZWrtMJx3A& z2oZ})SXYG$EklbL==mOs4-