nixos/modules/services/webserver/default.nix

182 lines
5 KiB
Nix
Raw Normal View History

# public webserver with reverseproxy
{
config,
lib,
pkgs,
...
}:
let
cfg = config.my.services.webserver;
inherit (config.networking) domain;
virtualHostOption = lib.types.submodule {
options = {
subdomain = lib.mkOption {
type = lib.types.str;
example = "dev";
description = ''
Which subdomain, under config.networking.domain, to use
for this virtual host.
'';
};
port = lib.mkOption {
type = with lib.types; nullOr port;
default = null;
example = 8080;
description = ''
Which port to proxy to, through localhost, for this virtual host.
This option is incompatible with `root`.
'';
};
root = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
example = "/var/www/blog";
description = ''
The root folder for this virtual host. This option is incompatible
with `port`.
'';
};
extraConfig = lib.mkOption {
type = with lib.types; nullOr lines;
example = lib.literalExpression ''
{
locations."/socket" = {
proxyPass = "http://localhost:8096/";
proxyWebsockets = true;
};
}
'';
default = null;
description = ''
Any extra configuration that should be applied to this virtual host.
'';
};
};
};
in
{
options.my.services.webserver = {
enable = lib.mkEnableOption "webserver";
virtualHosts = lib.mkOption {
type = lib.types.listOf virtualHostOption;
default = [ ];
example = lib.literalExpression ''
[
{
subdomain = "gitea";
port = 8080;
}
{
subdomain = "dev";
root = "/var/www/dev";
}
{
subdomain = "jellyfin";
port = 8096;
extraConfig = {
locations."/socket" = {
proxyPass = "http://localhost:8096/";
proxyWebsockets = true;
};
};
}
]
'';
description = ''
List of virtual hosts to set-up using default settings.
'';
};
};
config = lib.mkIf cfg.enable {
services = {
nginx.enable = false;
caddy = {
enable = true;
email = "server@buehler.rocks";
globalConfig = ''
servers{
metrics
}
'';
extraConfig = ''
(compress) {
encode gzip zstd
}
(headers) {
header {
# enable CORS
Access-Control-Allow-Origin "https://${config.networking.domain}"
# disable FLoC tracking
Permissions-Policy interest-cohort=()
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options "nosniff"
# clickjacking protection
X-Frame-Options "DENY"
# enable XSS protection
X-XSS-Protection "1; mode=block"
# referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
}
}
(common) {
import headers
import compress
}
'';
virtualHosts =
let
mkVHost =
{ subdomain, ... }@args:
lib.nameValuePair "${subdomain}.${domain}" (
lib.foldl lib.recursiveUpdate { } [
{
useACMEHost = domain;
extraConfig = ''
import common
${lib.optionalString (args.root != null) ''
root * ${args.root}
file_server
''}
${lib.optionalString (args.port != null) ''
reverse_proxy localhost:${toString args.port} {
# remove CORS headers from proxied server, because duplicate headers are not allowed
# remove after new release: https://github.com/navidrome/navidrome/commit/657fe11f5327ff7a3cb6aa9308b0bb7c71eea5c6
header_down -Access-Control-Allow-Origin
}
''}
${lib.optionalString (args.extraConfig != null) args.extraConfig}
'';
}
]
);
in
lib.listToAttrs (map mkVHost cfg.virtualHosts);
};
prometheus.scrapeConfigs = [
{
job_name = "caddy";
static_configs = [
{
targets = [ "localhost:2019" ];
labels.instance = config.networking.hostName;
}
];
}
];
};
networking.firewall.allowedTCPPorts = [
80
443
];
};
}