From 92554d8615a5fe3b45073a62eb5341f4598463d5 Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Thu, 17 Apr 2025 16:18:20 -0600 Subject: sys/web/sites: add host site --- pki/ca.nix | 41 +++++++++++++++++------- sys/mta/default.nix | 4 ++- sys/web/nginx.nix | 2 ++ sys/web/sites/default.nix | 1 + sys/web/sites/host.nix | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 sys/web/sites/host.nix diff --git a/pki/ca.nix b/pki/ca.nix index 70640be..f0ca33d 100644 --- a/pki/ca.nix +++ b/pki/ca.nix @@ -2,7 +2,7 @@ with lib; let cfg = config.local.pki.ca; - inherit (pkgs.buildPackages) openssl; + openssl = getExe pkgs.buildPackages.openssl; certsType = leafOf: with lib.types; attrsOf (submodule ({ config, name, ... }: { options = { @@ -11,9 +11,16 @@ with lib; let readOnly = true; }; - fingerprint.sha256 = mkOption { - type = str; - readOnly = true; + fingerprint = { + sha1-lower = mkOption { + type = str; + readOnly = true; + }; + + sha256-bytes-upper = mkOption { + type = str; + readOnly = true; + }; }; fullchain = mkOption { @@ -53,12 +60,22 @@ with lib; let }; config = { - fingerprint.sha256 = readFile (pkgs.runCommandNoCCLocal "cert-${config.path}-fprint-sha256" { } '' - ${openssl}/bin/openssl x509 -in ${config.cert} -noout -sha256 -fingerprint \ - | sed 's/^.*=//' \ - | tr -d $'\n' \ - >$out - ''); + fingerprint = { + sha1-lower = readFile (pkgs.runCommandNoCCLocal "cert-${config.path}-fprint-sha1-lower" { } '' + ${openssl} x509 -in ${config.cert} -noout -sha1 -fingerprint \ + | sed 's/^.*=//' \ + | tr -d $':\n' \ + | tr '[A-Z]' '[a-z]' \ + >>$out + ''); + + sha256-bytes-upper = readFile (pkgs.runCommandNoCCLocal "cert-${config.path}-fprint-sha256-bytes-upper" { } '' + ${openssl} x509 -in ${config.cert} -noout -sha256 -fingerprint \ + | sed 's/^.*=//' \ + | tr -d $'\n' \ + >>$out + ''); + }; fullchain = pkgs.writeText "${name}-fullchain-crl.pem" (concatStrings (map readFile @@ -67,8 +84,8 @@ with lib; let path = optionalString (config.issuer != null) (cfg.${config.issuer}.path + ".") + name; } // optionalAttrs (leafOf != null) { - commonName = readFile (pkgs.runCommandNoCCLocal "cert-${config.path}-fprint-sha256" { } '' - ${openssl}/bin/openssl x509 -in ${config.cert} -noout -subject -nameopt multiline \ + commonName = readFile (pkgs.runCommandNoCCLocal "cert-${config.path}-common-name" { } '' + ${openssl} x509 -in ${config.cert} -noout -subject -nameopt multiline \ | grep commonName \ | sed 's/^.*=\s*//' \ | tr -d $'\n' \ diff --git a/sys/mta/default.nix b/sys/mta/default.nix index 7a10146..4305f70 100644 --- a/sys/mta/default.nix +++ b/sys/mta/default.nix @@ -118,7 +118,9 @@ in pkgs.writeText "postfix-sender_ccerts" (concatLines (flatten (mapAttrsToList (username: user: map - (alias: "${alias}@${domain} CCERTS ${concatStringsSep "," (map (certPath: config.local.pki.byPath.${certPath}.fingerprint.sha256) user.mail.certs)}") + (alias: "${alias}@${domain} CCERTS ${concatStringsSep "," + (map (certPath: config.local.pki.byPath.${certPath}.fingerprint.sha256-bytes-upper) + user.mail.certs)}") ([ username ] ++ user.hardAliases)) (filterAttrs (_: user: user.mail.certs != [ ]) users)))); diff --git a/sys/web/nginx.nix b/sys/web/nginx.nix index fc24afe..b6e7414 100644 --- a/sys/web/nginx.nix +++ b/sys/web/nginx.nix @@ -50,6 +50,8 @@ in sslDhparam = config.security.dhparams.params.nginx.path; clientMaxBodySize = "42M"; + mapHashBucketSize = 128; + virtualHosts.default = { default = true; diff --git a/sys/web/sites/default.nix b/sys/web/sites/default.nix index a131aaf..ba2835c 100644 --- a/sys/web/sites/default.nix +++ b/sys/web/sites/default.nix @@ -1,6 +1,7 @@ { imports = [ ./home.nix + ./host.nix ./portal.nix ]; } diff --git a/sys/web/sites/host.nix b/sys/web/sites/host.nix new file mode 100644 index 0000000..62abe1a --- /dev/null +++ b/sys/web/sites/host.nix @@ -0,0 +1,79 @@ +{ config, lib, ... }: +with lib; let + cfg = config.local.web.sites.host; + + inherit (config.local) domains users; + inherit (config.local.net) hostname; + + hostDomain = domains.${hostDomainName}; + hostDomainName = "host-${hostname}"; + + userCerts = flatten (flatten (mapAttrsToList + (name: user: map + (cert: { + fprint = config.local.pki.byPath.${cert}.fingerprint.sha1-lower; + inherit name; + }) + user.mail.certs) + users)); +in +{ + options.local.web.sites.host = { + enable = mkEnableOption "host site, restricted to per-user client certs"; + }; + + config = mkIf cfg.enable { + local.web = { + enable = mkDefault true; + ownedCerts = [ hostDomainName ]; + }; + + services = { + nginx = { + appendHttpConfig = '' + map $ssl_client_fingerprint $host_user_from_fprint { + default ""; + ${concatMapStringsSep "\n " (pair: "\"${escapeRegex pair.fprint}\" \"${pair.name}\";") userCerts} + } + ''; + + virtualHosts = { + ${hostDomain.main} = { + forceSSL = true; + useACMEHost = hostDomain.main; + + extraConfig = '' + ssl_verify_depth 2; + ssl_verify_client optional; + ssl_client_certificate ${config.local.pki.ca.mail.fullchain}; + + #if ($ssl_client_verify != "SUCCESS") { + #return 403; + #} + ''; + + locations = { + "/".return = 403; + } // concatMapAttrs + (name: user: + let + userLocation = config: { + extraConfig = '' + if ($host_user_from_fprint != "${name}") { + return 403; + } + '' + config; + }; + in + mapAttrs (_: userLocation) { + "/${name}" = '' + return 404; + ''; + }) + (filterAttrs (_: user: user.mail.certs != [ ]) users); + }; + }; + }; + }; + }; +} -- cgit v1.2.3