# Renders `services.hyperhive.gateway.vhosts` into # `services.nginx.virtualHosts`: each declared vhost keeps what its module # wrote and gets the gateway's `listen` (the private one when it sets # `public = false`), the TLS attributes for its name and the security headers. # # Its own module rather than part of ./default.nix because the # "declared while the gateway is off" assertion has to hold when # `gateway.enable` is false, where ./default.nix defines nothing. { lib, config, ... }: let cfg = config.services.hyperhive.gateway; listen = import ./listen.nix { inherit lib; }; # What the gateway owns on a vhost. A declared vhost naming one of these # fails the eval. gatewayOwned = [ "listen" "addSSL" "onlySSL" "enableACME" "useACMEHost" "sslCertificate" "sslCertificateKey" ]; offenders = lib.concatMap ( name: map (attr: "${name}.${attr}") (lib.filter (a: cfg.vhosts.${name} ? ${a}) gatewayOwned) ) (lib.attrNames cfg.vhosts); privateOnly = lib.filter (name: !cfg.vhosts.${name}.public) (lib.attrNames cfg.vhosts); wildcardPrivate = lib.filter listen.isWildcard cfg.privateListenAddresses; render = name: declared: let own = removeAttrs declared [ "_module" "certificate" "public" "securityHeaders" ]; base = if declared.certificate != null then { sslCertificate = declared.certificate.cert; sslCertificateKey = declared.certificate.key; } else cfg.lib.tlsFor name; # nixos asserts on a vhost declaring both `addSSL` and `forceSSL`. tls = if own.forceSSL or false then removeAttrs base [ "addSSL" ] else base; in tls // own // { listen = if declared.public then cfg.lib.listen else cfg.lib.privateListen; extraConfig = lib.optionalString declared.securityHeaders cfg.lib.securityHeaders + (own.extraConfig or ""); } // lib.optionalAttrs (own ? default) { # At `mkDefault` priority, so an operator can turn this vhost's # `default` off with a plain definition. default = lib.mkDefault own.default; }; in { config = lib.mkMerge [ { assertions = [ { assertion = cfg.enable || cfg.vhosts == { }; message = '' services.hyperhive.gateway.vhosts declares ${lib.concatStringsSep ", " (lib.attrNames cfg.vhosts)} while services.hyperhive.gateway.enable is false, so nothing would serve them. Enable the gateway, or drop the service that declares the vhost. ''; } { assertion = offenders == [ ]; message = '' services.hyperhive.gateway.vhosts sets attributes the gateway owns: ${lib.concatStringsSep ", " offenders} The gateway sets the listen set and the TLS attributes of every vhost it fronts. Declare only the vhost's own locations and options; to serve a certificate the gateway does not pick, set `certificate` on the vhost. ''; } { assertion = privateOnly == [ ] || cfg.privateListenAddresses != [ ]; message = '' services.hyperhive.gateway.vhosts sets `public = false` on ${lib.concatStringsSep ", " privateOnly} while services.hyperhive.gateway.privateListenAddresses is empty, so these vhosts would listen nowhere. Set the addresses they should answer on, or drop `public = false`. ''; } { assertion = wildcardPrivate == [ ]; message = '' services.hyperhive.gateway.privateListenAddresses holds ${lib.concatStringsSep ", " wildcardPrivate}, which is every address of the host, not a private one: the vhosts with `public = false` would answer on the public address too. List the host's private addresses (a WireGuard interface's, say). ''; } ]; } (lib.mkIf cfg.enable { services.nginx.virtualHosts = lib.mapAttrs render cfg.vhosts; }) ]; }