nixos/modules/services/reverseproxy.nix

322 lines
10 KiB
Nix
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
lib,
config,
...
}:
let
cfg = config.crans.reverseProxy;
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;
# On fait un certificat commun pour tous les hosts
security.acme.certs."all.crans.all" = mkIf cfg.enable {
domain = "*.crans.${mainTld}";
dnsProvider = "rfc2136";
# Contient le serveur à contacter avec le protocole
# et le mot de passe
environmentFile = config.age.secrets.acme-env.path;
extraDomainNames =
lib.concatMap (tld: [
"*.crans.${tld}"
"crans.${tld}"
]) otherTld
++ [
"crans.${mainTld}"
];
group = config.services.nginx.group;
};
# Création des dossiers de logs
system.activationScripts = {
nginx-logs = {
text =
lib.concatMapStrings (f: "mkdir -p /var/log/nginx/${f}/\n") (
[ "anubis" ] ++ (lib.map (hn: "host/${hn}") (builtins.attrNames cfg.virtualHosts))
)
+ "chown -R ${config.services.nginx.user}:${config.services.nginx.group} /var/log/nginx/\n";
};
};
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.${mainTld}";
REDIRECT_DOMAINS = "${vhostName}.crans.${mainTld}";
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;
};
logrotate.settings =
let
# https://github.com/NixOS/nixpkgs/blob/nixos-26.05/nixos/modules/services/web-servers/nginx/default.nix
base = {
su = "${config.services.nginx.user} ${config.services.nginx.group}";
postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
frequency = "daily";
ifempty = true;
compress = true;
delaycompress = true;
dateyesterday = true;
};
in
{
nginx-anubis = base // {
files = [ "/var/log/nginx/anubis/*.log" ];
rotate = 7; # une semaine de log pour débugguer
};
nginx-hosts = base // {
files = [ "/var/log/nginx/host/*/*.log" ];
rotate = 173; # 6 mois de logs
dateext = true;
dateformat = "%Y-%m-%d";
};
};
nginx =
let
configVhost =
vhostName: vhostConfig:
let
sslConf = {
forceSSL = !vhostConfig.httpOnly;
rejectSSL = vhostConfig.httpOnly;
useACMEHost = mkIf (!vhostConfig.httpOnly) "all.crans.all";
acmeRoot = null;
};
# Conf pour lhôte en entrée
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 dAnubis
{
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;
logHostConf = ''
access_log /var/log/nginx/host/${vhostName}/access.log;
error_log /var/log/nginx/host/${vhostName}/error.log;
'';
# Les alias : vhostName × otherTld U serverAliases × allTld
mkHostName = als: tld: if als == "." then "crans.${tld}" else "${als}.crans.${tld}";
aliases =
lib.foldr
(
tld: acc:
acc
++ (lib.foldr (alias: acc: acc ++ [ (mkHostName alias tld) ]) [
(mkHostName vhostName tld)
] vhostConfig.serverAliases)
)
(lib.foldr (alias: acc: acc ++ [ (mkHostName alias mainTld) ]) [ ] vhostConfig.serverAliases)
otherTld;
in
{
# Configuration du service à proxy.
"${vhostName}" = vhostExtraConf // {
serverName = mkHostName vhostName mainTld;
locations."/" = mkIf (vhostConfig.proxyPass != null) {
proxyPass = "http://${vhostConfig.proxyPass}";
proxyWebsockets = vhostConfig.proxyWebsockets;
};
root = vhostConfig.serveLocalFiles;
globalRedirect = vhostConfig.globalRedirect;
extraConfig = vhostExtraConf.extraConfig + logHostConf;
};
# Entrée dAnubis
"${vhostName}-anubis" = mkIf (vhostConfig.anubisConfig != null) (
entryExtraConf
// rec {
serverName = mkHostName vhostName mainTld;
locations."/" = {
proxyPass = "http://unix:/run/anubis/anubis-${vhostName}/socket.sock";
proxyWebsockets = vhostConfig.proxyWebsockets;
};
locations."/.within.website/x/cmd/anubis/api/honeypot/" = locations."/" // {
extraConfig = ''
add_header 'X-Robots-Tag' 'noindex,nofollow' always;
'';
};
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 = lib.defaultTo "${vhostName}.crans.${mainTld}" vhostConfig.globalRedirect;
extraConfig = logHostConf;
};
}) aliases
);
in
{
enable = true;
virtualHosts = lib.concatMapAttrs configVhost cfg.virtualHosts;
};
};
};
}