{ me, config, pkgs, lib, util, ... }: let inherit (lib) mergeAttrsList mkIf mkEnableOption mkOption concatLines optional ; inherit (config.age) secrets; cfg = config.foundation.www; rnrdUrl = if me.is.renard then "rnrd.eu" else "${me.name}.rnrd.eu"; default-page-index = pkgs.replaceVars ../../../assets/base.html { me = util.titleCase me.name; }; default-page-index-w-goat = pkgs.concatText "base.html" [ default-page-index ../../../assets/goat.html ]; default-page = pkgs.linkFarm "www-base" { "index.html" = default-page-index-w-goat; "favicon.png" = ../../../assets/favicon.png; }; certificate = domain: settings: { ${domain} = { domain = "*.${domain}"; extraDomainNames = [ domain ]; } // settings; }; # our zones have wildcard cnames, which lego would follow to # place the challenge somewhere else. keep it where it belongs. noCnameFollowing = pkgs.writeText "lego-no-cname" '' LEGO_DISABLE_CNAME_SUPPORT=true ''; # edge addresses of the cdn providers we use, to allow extraction # of the real addresses connecting to us. edgeAddresses = let fetch = url: hash: pkgs.fetchurl { inherit url hash; }; in { cloudflare = pkgs.runCommand "cloudflare-edge-servers.conf" { v4 = fetch "https://www.cloudflare.com/ips-v4" "sha256-8Cxtg7wBqwroV3Fg4DbXAMdFU1m84FTfiE5dfZ5Onns="; v6 = fetch "https://www.cloudflare.com/ips-v6" "sha256-np054+g7rQDE3sr9U8Y/piAp89ldto3pN9K+KCNMoKk="; } '' awk 'NF { print "set_real_ip_from " $0 ";" }' $v4 $v6 > $out ''; bunny = pkgs.runCommand "bunny-edge-servers.conf" { nativeBuildInputs = [ pkgs.jq ]; v4 = fetch "https://bunnycdn.com/api/system/edgeserverlist" "sha256-a27vEUILkiSN8ieWYcHnquuqUsS4i2xv5Lv9AA9iJxU="; v6 = fetch "https://bunnycdn.com/api/system/edgeserverlist/IPv6" "sha256-3qwGHkAnnrbeDvvv1wUnMU3Hi1vNog4alu5y0nOA6Do="; } '' jq -r '.[] | "set_real_ip_from \(.);"' $v4 $v6 > $out ''; }; defaultHost = domain: certificate: base: log: { default = true; serverName = domain; forceSSL = true; useACMEHost = certificate; root = base; extraConfig = '' access_log /var/log/nginx/${log}.access.log json_combined; ''; }; in { imports = [ ./tailnet.nix ]; options.foundation.www = { enable = mkEnableOption "www server"; public = mkEnableOption "public access through rnrd.eu url"; defaultPage = mkOption { type = lib.types.package; default = default-page; }; behindCloudflare = mkOption { type = lib.types.bool; description = '' Whether to add real_ip directives to unwrap Cloudflare CDN edge addresses. ''; default = true; }; behindBunny = mkOption { type = lib.types.bool; description = '' Whether to add real_ip directives to unwrap bunny.net CDN edge addresses. ''; default = false; }; }; config = mkIf cfg.enable { age.secrets = { bunny-dns.file = ../../../secrets/bunny-dns.age; cloudflare-dns.file = ../../../secrets/cloudflare-dns.age; }; security.acme = { acceptTerms = true; defaults = { email = "mel@rnrd.eu"; # our certificates are really only used with Nginx group = config.services.nginx.group; reloadServices = [ "nginx.service" ]; environmentFile = noCnameFollowing; }; # yes, we generate both certificates, even if they are not # used by every machine, but as long as it doesn't cause # any problems... :) certs = mergeAttrsList [ (certificate "rnrd.eu" { dnsProvider = "bunny"; credentialFiles.BUNNY_API_KEY_FILE = secrets.bunny-dns.path; }) (certificate "rnrd.fyi" { dnsProvider = "cloudflare"; credentialFiles.CLOUDFLARE_DNS_API_TOKEN_FILE = secrets.cloudflare-dns.path; }) ]; }; services.nginx = { enable = true; recommendedGzipSettings = true; recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; statusPage = true; commonHttpConfig = let logs = '' log_format json_combined escape=json '{' '"time_local":"$time_local",' '"remote_addr":"$remote_addr",' '"remote_user":"$remote_user",' '"request":"$request",' '"status": "$status",' '"body_bytes_sent":"$body_bytes_sent",' '"request_length":"$request_length",' '"request_time":"$request_time",' '"http_referrer":"$http_referer",' '"http_user_agent":"$http_user_agent",' '"upstream_response_time":"$upstream_response_time",' '"upstream_addr":"$upstream_addr",' '"upstream_status":"$upstream_status",' '"cf_connecting_ip":"$http_cf_connecting_ip"' '}'; access_log /var/log/nginx/access.log json_combined; error_log /var/log/nginx/error.log warn; ''; cloudflare = '' include ${edgeAddresses.cloudflare}; real_ip_header CF-Connecting-IP; ''; bunny = '' include ${edgeAddresses.bunny}; real_ip_header X-Real-IP; ''; in concatLines ( [ logs ] ++ (optional cfg.behindCloudflare cloudflare) ++ (optional cfg.behindBunny bunny) ); virtualHosts = { base = mkIf cfg.public (defaultHost rnrdUrl "rnrd.eu" cfg.defaultPage "base"); }; }; }; }