{ pkgs, lib, config, ... }: let cfg = config.crans.reverseProxy; formatJSON = pkgs.formats.json { }; allowAll = formatJSON.generate "allow_all.json" { bots = [ { name = "allow_all"; path_regex = ".*"; action = "ALLOW"; } ]; }; open_graph = formatJSON.generate "opengraph.json" { openGraph = [ { enabled = true; considerHost = true; ttl = "24h"; } ]; }; mainTld = "org"; otherTld = [ "fr" "eu" ]; inherit (lib) literalExpression mkEnableOption mkIf mkOption types ; in { options.crans.reverseProxy = { enable = mkEnableOption "Configuration du reverseproxy."; virtualHosts = mkOption { type = types.attrsOf ( types.submodule { options = { serverAliases = mkOption { type = types.listOf types.str; default = [ ]; example = [ "everything" "voyager" ]; description = '' Déclaration des alias. ''; }; proxyPass = mkOption { type = types.nullOr types.str; default = null; description = '' Indique la destination à proxy. ''; example = "172.16.10.128:8000"; }; serveLocalFiles = mkOption { type = types.nullOr types.path; default = null; description = '' Chemin vers un dossier à exposer statiquement. ''; example = "/var/local/adopter-un-manchot"; }; globalRedirect = mkOption { type = types.nullOr types.str; default = null; description = '' Si définie, toutes les requêtes sont redirigées (via 301) sur cet hôte. ''; example = "perso.crans.org/club"; }; anubisConfig = mkOption { type = types.nullOr types.str; default = null; description = '' Chemin du fichier de configuration ''; example = "/var/www/anubis.conf"; }; anubisOpenGraph = mkOption { type = types.bool; default = true; description = '' Activer openGraph pour l'indexation et l'embedding ''; }; httpOnly = mkOption { type = types.bool; default = false; description = '' Interdit les connexions en https ''; example = "true"; }; proxyWebsockets = mkOption { type = types.bool; default = false; description = '' Activer les websockets ''; example = "true"; }; }; } ); default = { }; example = literalExpression '' { "framadate" = { host = "176.16.10.128:8000"; serverAliases = [ "everything" "voyager" ] }; }; ''; description = "Déclaration des machines."; }; }; config = { systemd.services = lib.mapAttrs (vhostName: vhostConfig: { wantedBy = [ "multi-user.target" ]; }) cfg.virtualHosts; services = mkIf cfg.enable { anubis = { defaultOptions.group = "nginx"; instances = lib.mapAttrs ( vhostName: vhostConfig: mkIf (vhostConfig.anubisConfig != null) { enable = true; settings = { BIND = "/run/anubis/anubis-${vhostName}/socket.sock"; BIND_NETWORK = "unix"; METRICS_BIND = "/run/anubis/anubis-${vhostName}/anubis-${vhostName}-metrics.sock"; TARGET = "unix:///run/nginx/nginx-${vhostName}.sock"; COOKIE_DOMAIN = "crans.org"; REDIRECT_DOMAINS = "${vhostName}.crans.org"; SOCKET_MODE = "0660"; # OpenGraph config OG_PASSTHROUGH = vhostConfig.anubisOpenGraph; OG_EXPIRY_TIME = "24h"; OG_CACHE_CONSIDER_HOST = true; # Policy config POLICY_FNAME = vhostConfig.anubisConfig; }; } ) cfg.virtualHosts; }; nginx = let configVhost = vhostName: vhostConfig: let sslConf = { enableACME = !vhostConfig.httpOnly; forceSSL = !vhostConfig.httpOnly; rejectSSL = vhostConfig.httpOnly; }; # Conf pour l’host en entrès entryExtraConf = { extraConfig = '' set_real_ip_from 172.16.0.0/16; set_real_ip_from fd00::/56; real_ip_header X-Real-Ip; ''; } // sslConf; # Conf supplémentaire pour le proxy principal vhostExtraConf = if vhostConfig.anubisConfig != null then # Il reçoit les requêtes d’Anubis { listen = [ { addr = "unix:/run/nginx/nginx-${vhostName}.sock"; } ]; extraConfig = '' set_real_ip_from unix:; real_ip_header X-Real-IP; ''; } else # Il est en entrée entryExtraConf; # Les alias : vhostName × otherTld U serverAliases × allTld aliases = lib.foldr ( tld: acc: acc ++ (lib.foldr (alias: acc: acc ++ [ "${alias}.crans.${tld}" ]) [ "${vhostName}.crans.${tld}" ] vhostConfig.serverAliases) ) (lib.foldr (alias: acc: acc ++ [ "${alias}.crans.${mainTld}" ]) [ ] vhostConfig.serverAliases) otherTld; in { # Configuration du service à proxy. "${vhostName}" = vhostExtraConf // { serverName = "${vhostName}.crans.${mainTld}"; locations."/" = mkIf (vhostConfig.proxyPass != null) { proxyPass = "http://${vhostConfig.proxyPass}"; proxyWebsockets = vhostConfig.proxyWebsockets; }; root = vhostConfig.serveLocalFiles; globalRedirect = vhostConfig.globalRedirect; }; # Entrée d’Anubis "${vhostName}-anubis" = mkIf (vhostConfig.anubisConfig != null) ( entryExtraConf // { serverName = "${vhostName}.crans.${mainTld}"; locations."/" = { proxyPass = "http://unix:/run/anubis/anubis-${vhostName}/socket.sock"; proxyWebsockets = vhostConfig.proxyWebsockets; }; extraConfig = entryExtraConf.extraConfig + '' access_log /var/log/nginx/anubis.access.log; error_log /var/log/nginx/anubis.error.log; ''; } ); } # Les alias // lib.mergeAttrsList ( lib.map (alias: { "${vhostName}-alias-${alias}" = sslConf // { serverName = alias; globalRedirect = vhostConfig.globalRedirect or "${vhostName}.crans.${mainTld}"; }; }) aliases ); in { enable = true; virtualHosts = lib.concatMapAttrs configVhost cfg.virtualHosts; }; }; }; }