yumeami/modules/nixos/services/networking/httpd/default.nix
C4 Patino 34be676dd3
All checks were successful
ci / treefmt (push) Successful in 1m59s
feat: add asciinema server module and CLI tool
2026-06-27 17:43:08 -05:00

290 lines
8 KiB
Nix

{
config,
inputs,
lib,
namespace,
...
}: let
inherit (lib) mkIf mkEnableOption mapAttrsToList listToAttrs replaceStrings mkMerge concatStringsSep filterAttrs optionalString;
inherit (lib.${namespace}) getAttrByNamespace mkOptionsWithNamespace resolveHostIP isGateway flattenHostServices;
inherit (config.networking) hostName;
inherit (config.sops) secrets;
base = "${namespace}.services.networking.httpd";
cfg = getAttrByNamespace config base;
networkingCfg = getAttrByNamespace config "${namespace}.services.networking";
miasmaCfg = getAttrByNamespace config "${namespace}.services.apps.miasma";
networkServices = networkingCfg.network-services;
networkServicesFlat = flattenHostServices networkServices;
mkLocalhostConfig = name: service: let
p = toString service.port;
in ''
# --- ${name} (localhost access) ---
RewriteCond %{HTTP_HOST} ^${name}\.localhost$ [NC]
RewriteRule ^/(.*) http://localhost:${p}/$1 [P,L]
ProxyPassReverse / http://localhost:${p}/
'';
mkVirtualHost = {
domain,
useSSL,
enableAcme ? false,
injectHoneypot ? false,
}: host: name: service: let
hostIP = resolveHostIP networkingCfg.devices host;
p = toString service.port;
certs = replaceStrings ["*" "."] ["wildcard" "_"] domain;
sslConfig =
if useSSL
then
{
forceSSL = true;
}
// (
if enableAcme
then {
useACMEHost = certs;
}
else {
sslServerCert = secrets."ssl/${certs}/cert".path;
sslServerKey = secrets."ssl/${certs}/key".path;
}
)
else {};
miasma = networkServicesFlat.miasma;
honeypotConfig = optionalString injectHoneypot ''
AddOutputFilterByType SUBSTITUTE text/html
SubstituteMaxLineLength 30m
Substitute 's|</body>|<a href="${miasmaCfg.linkPrefix}/" style="display:none" aria-hidden="true" tabindex="-1">Amazing high quality data here!</a></body>|i'
ProxyPass ${miasmaCfg.linkPrefix}/ http://${resolveHostIP networkingCfg.devices miasma.host}:${toString miasma.port}/
ProxyPassReverse ${miasmaCfg.linkPrefix}/ http://${resolveHostIP networkingCfg.devices miasma.host}:${toString miasma.port}/
ProxyPass /robots.txt !
Alias /robots.txt ${inputs.dotfiles}/httpd/robots/${name}.txt
'';
websocketConfig = optionalString service.websocket.enable ''
# --- ${name} (websocket access) ---
ProxyPass ${service.websocket.path} ws://${hostIP}:${p}${service.websocket.path} connectiontimeout=30 timeout=300 retry=0
ProxyPassReverse ${service.websocket.path} ws://${hostIP}:${p}${service.websocket.path}
'';
in {
name = replaceStrings ["*"] [name] domain;
value =
{
acmeRoot = null;
documentRoot = "/var/empty";
extraConfig = ''
UseCanonicalName Off
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
RewriteEngine On
ProxyTimeout 300
ProxyPreserveHost On
${honeypotConfig}
${websocketConfig}
# --- ${name} (subdomain access) ---
ProxyPass / http://${hostIP}:${p}/ connectiontimeout=30 timeout=300 retry=0
ProxyPassReverse / http://${hostIP}:${p}/
'';
}
// sslConfig;
};
in {
options = mkOptionsWithNamespace base {
enable = mkEnableOption "Apache HTTPD";
};
config = mkIf cfg.enable {
services.httpd = {
enable = true;
extraModules = [
"filter"
"proxy"
"proxy_http"
"proxy_wstunnel"
"rewrite"
"substitute"
];
extraConfig = ''
ErrorDocument 400 /400.html
ErrorDocument 401 /401.html
ErrorDocument 403 /403.html
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html
ErrorDocument 503 /503.html
'';
virtualHosts = mkMerge [
{
"localhost" = let
localhostProxyConfig =
networkServices.${hostName}
|> mapAttrsToList mkLocalhostConfig
|> concatStringsSep "\n";
in {
acmeRoot = null;
serverAliases = ["*.localhost"];
documentRoot = "/var/empty";
servedDirs = [
{
dir = "/var/www/error";
urlPath = "/";
}
];
extraConfig = ''
UseCanonicalName Off
RewriteEngine On
${localhostProxyConfig}
RewriteCond %{REQUEST_URI} !^/(400|401|403|404|500|503)\.html$
RewriteRule ^ - [L,R=404]
'';
};
}
(networkServices.${hostName}
|> filterAttrs (_: svc: svc.internal)
|> mapAttrsToList (name: svc:
(mkVirtualHost {
domain = "*.yumeami.sh";
useSSL = true;
})
hostName
name
svc)
|> listToAttrs)
(mkIf (isGateway networkingCfg.devices hostName) (
networkServicesFlat
|> filterAttrs (_: svc: svc.public)
|> mapAttrsToList (name: svc:
(mkVirtualHost {
domain = "*.cpatino.com";
useSSL = true;
enableAcme = true;
injectHoneypot = true;
})
svc.host
name
svc)
|> listToAttrs
))
{
"_default_" = {
documentRoot = "/var/empty";
servedDirs = [
{
dir = "/var/www/error";
urlPath = "/";
}
];
extraConfig = ''
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(400|401|403|404|500|503)\.html$
RewriteRule ^ - [L,R=404]
'';
};
}
(
mkIf (isGateway networkingCfg.devices hostName)
{
"zzz-undefined.cpatino.com" = {
serverAliases = ["*.cpatino.com"];
documentRoot = "/var/empty";
servedDirs = [
{
dir = "/var/www/error";
urlPath = "/";
}
];
extraConfig = ''
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(400|401|403|404|500|503)\.html$
RewriteRule ^ - [L,R=404]
'';
forceSSL = true;
useACMEHost = "wildcard_cpatino_com";
};
}
)
{
"zzz-undefined.yumeami.sh" = {
serverAliases = ["*.yumeami.sh"];
documentRoot = "/var/empty";
servedDirs = [
{
dir = "/var/www/error";
urlPath = "/";
}
];
extraConfig = ''
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(400|401|403|404|500|503)\.html$
RewriteRule ^ - [L,R=404]
'';
forceSSL = true;
sslServerCert = secrets."ssl/wildcard_yumeami_sh/cert".path;
sslServerKey = secrets."ssl/wildcard_yumeami_sh/key".path;
};
}
];
};
networking.firewall.allowedTCPPorts = [80 443];
systemd.tmpfiles.rules = [
"L+ /var/www 555 root root - ${inputs.dotfiles + "/httpd"}"
];
sops.secrets = let
inherit (config.networking) hostName;
inherit (config.users.users) wwwrun;
hostSpecific =
[
"ssl/wildcard_yumeami_sh/cert"
"ssl/wildcard_yumeami_sh/key"
]
|> map (name: {
inherit name;
value = {
sopsFile = "${inputs.self}/secrets/sops/${hostName}.yaml";
owner = wwwrun.name;
group = wwwrun.group;
};
});
in
listToAttrs hostSpecific;
};
}