summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2025-04-17 16:18:20 -0600
committerAlejandro Soto <alejandro@34project.org>2025-04-19 11:10:00 -0600
commit92554d8615a5fe3b45073a62eb5341f4598463d5 (patch)
tree9b4ac40931680d43c78a7e36902d32566c78fe9f
parent94082eb5943f483dda4c1c71e50e57ee665bcddf (diff)
sys/web/sites: add host site
-rw-r--r--pki/ca.nix41
-rw-r--r--sys/mta/default.nix4
-rw-r--r--sys/web/nginx.nix2
-rw-r--r--sys/web/sites/default.nix1
-rw-r--r--sys/web/sites/host.nix79
5 files changed, 114 insertions, 13 deletions
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);
+ };
+ };
+ };
+ };
+ };
+}