globalized port/vhost config: move modules

This commit is contained in:
Grimmauld 2024-05-09 14:55:02 +02:00
parent 181f108308
commit dca29c0156
Signed by: Grimmauld
GPG Key ID: C2946668769F91FB
11 changed files with 156 additions and 286 deletions

View File

@ -26,25 +26,68 @@ in
options.serverConfig = with lib; { options.serverConfig = with lib; {
ports = mkOption { ports = mkOption {
type = types.attrsOf types.int; type = types.attrsOf (
types.submodule (
{ config, ... }:
rec {
options = {
port = mkOption {
type = types.int;
description = "port to define";
};
open = mkEnableOption "whether to open the port" // {
default = true;
};
};
}
)
);
default = { }; default = { };
description = "ports associated with services"; description = "ports associated with services";
}; };
vhosts = mkOption { vhosts = mkOption {
type = types.attrsOf ( type = types.attrsOf (
types.submodule { types.submodule (
{ config, ... }:
rec {
options = { options = {
port = mkOption { port = mkOption {
type = types.int; type = types.int;
default = 80;
description = "port to redirect to this vhost"; description = "port to redirect to this vhost";
}; };
host = mkOption { host = mkOption {
type = types.nonEmptyStr; type = types.nonEmptyStr;
description = "name if the vhost"; description = "name if the vhost";
}; };
accessType = mkOption {
type = types.enum [
"proxy"
"redirect"
"custom"
"none"
];
default = "proxy";
description = "nginx template to use";
};
extraNginx = mkOption {
type = types.attrs;
default =
if config.accessType == "redirect" then
{ locations."/".return = "307 https://${domain}"; }
else
(
if config.accessType == "proxy" then
{ locations."/".proxyPass = "http://127.0.0.1:${builtins.toString config.port}"; }
else
{ }
);
description = "location definition for nginx";
};
}; };
} }
)
); );
default = { }; default = { };
description = "vhosts associated with services"; description = "vhosts associated with services";
@ -52,13 +95,11 @@ in
}; };
config = { config = {
networking.firewall.allowedTCPPorts = networking.firewall.allowedTCPPorts = [
[
80 80
443 443
] ] ++ (lib.mapAttrsToList (n: v: v.port) (lib.filterAttrs (n: v: v.open) config.serverConfig.ports));
++ lib.attrValues config.serverConfig.ports # ++ (lib.mapAttrsToList (n: v: v.port) (lib.filterAttrs (n: v: !v.disableWebAccess) config.serverConfig.vhosts));
++ (lib.mapAttrsToList (n: v: v.port) config.serverConfig.vhosts);
services.nginx.virtualHosts = services.nginx.virtualHosts =
{ {
@ -76,16 +117,28 @@ in
serverName = host.host; serverName = host.host;
forceSSL = true; forceSSL = true;
useACMEHost = domain; useACMEHost = domain;
locations."/" = { enableACME = lib.mkForce false;
proxyPass = "http://127.0.0.1:${builtins.toString host.port}"; } // host.extraNginx;
}; }) (lib.filterAttrs (n: v: v.accessType != "none") config.serverConfig.vhosts));
};
}) config.serverConfig.vhosts);
serverConfig = { serverConfig = {
ports = { ports = {
puffer_sftp_port = 5657; puffer_sftp_port.port = 5657;
gitea_ssh_port.port = 2222;
node_exporter = {
port = 9002;
open = false;
}; };
discord_matrix_bridge_port = {
port = 9005;
open = false;
};
redis_nextcloud_port = {
port = 6379;
open = false;
};
};
vhosts = { vhosts = {
puffer_host = { puffer_host = {
port = 8080; port = 8080;
@ -95,6 +148,37 @@ in
port = 8100; port = 8100;
host = "tlemap.${domain}"; host = "tlemap.${domain}";
}; };
mail_host = {
host = "mail.${domain}";
accessType = "redirect";
};
gitea_host = {
host = "git.${domain}";
port = 8081;
};
matrix_host = {
accessType = "redirect";
host = "matrix.${domain}";
};
prometheus_host = {
host = "prometheus.${domain}";
port = 9090;
accessType = "redirect";
};
grafana_host = {
host = "grafana.${domain}";
port = 8082;
};
nextcloud_host = rec {
host = "cloud.${domain}";
port = 8083;
accessType = "custom";
extraNginx.serverName = host;
};
mastodon_host = {
host = "mastodon.${domain}";
accessType = "none";
};
}; };
}; };

View File

@ -5,7 +5,7 @@
... ...
}: }:
let let
bridge_port = 9005; # netstat -nlp | grep 9005 inherit (config.serverConfig) ports vhosts;
in in
{ {
age.secrets.matrix_discord_bridge_token.file = ../secrets/matrix_discord_bridge_token.age; age.secrets.matrix_discord_bridge_token.file = ../secrets/matrix_discord_bridge_token.age;
@ -22,15 +22,15 @@ in
}; };
bridge = { bridge = {
enableSelfServiceBridging = true; enableSelfServiceBridging = true;
inherit (config.networking) domain; domain = config.services.matrix-synapse-next.settings.server_name;
homeserverUrl = "https://${config.networking.domain}"; homeserverUrl = config.services.matrix-synapse-next.settings.public_baseurl;
disablePresence = true; disablePresence = true;
disableTypingNotifications = true; disableTypingNotifications = true;
}; };
# logging.console = "silly"; # logging.console = "silly";
}; };
serviceDependencies = [ "matrix-synapse.target" ]; serviceDependencies = [ "matrix-synapse.target" ];
port = bridge_port; port = ports.discord_matrix_bridge_port.port;
localpart = "_discord_"; localpart = "_discord_";
package = pkgs.matrix-appservice-discord; package = pkgs.matrix-appservice-discord;
environmentFile = config.age.secrets.matrix_discord_bridge_token.path; environmentFile = config.age.secrets.matrix_discord_bridge_token.path;

View File

@ -1,15 +1,13 @@
{ config, ... }: { config, ... }:
let let
inherit (config.serverConfig) ports vhosts;
inherit (config.networking) domain; inherit (config.networking) domain;
mail_host = "mail.${domain}";
in in
{ {
security.acme.certs."${domain}".extraDomainNames = [ mail_host ];
# services.dovecot2.sieve.extensions = [ "fileinto" ]; # sives break without this for some reason # services.dovecot2.sieve.extensions = [ "fileinto" ]; # sives break without this for some reason
mailserver = { mailserver = {
enable = true; enable = true;
fqdn = mail_host; fqdn = vhosts.mail_host.host;
domains = [ domain ]; domains = [ domain ];
# A list of all login accounts. To create the password hashes, use # A list of all login accounts. To create the password hashes, use
@ -33,17 +31,4 @@ in
certificateFile = "/var/lib/acme/${domain}/fullchain.pem"; certificateFile = "/var/lib/acme/${domain}/fullchain.pem";
keyFile = "/var/lib/acme/${domain}/key.pem"; keyFile = "/var/lib/acme/${domain}/key.pem";
}; };
services.nginx = {
enable = true;
virtualHosts."${mail_host}" = {
# you should NOT be here from a browser :P
serverName = mail_host;
forceSSL = true;
useACMEHost = domain;
locations."/" = {
return = "307 https://${domain}";
};
};
};
} }

View File

@ -7,9 +7,7 @@
}: }:
let let
inherit (config.networking) domain; inherit (config.networking) domain;
gitea_host = "git.${domain}"; inherit (config.serverConfig) ports vhosts;
gitea_port = 8081;
gitea_ssh_port = 2222;
in in
{ {
services.gitea = { services.gitea = {
@ -17,13 +15,13 @@ in
settings = { settings = {
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
server = { server = {
HTTP_PORT = gitea_port; HTTP_PORT = vhosts.gitea_host.port;
ROOT_URL = "https://${gitea_host}/"; ROOT_URL = "https://${vhosts.gitea_host.host}/";
DISABLE_SSH = false; DISABLE_SSH = false;
SSH_DOMAIN = domain; SSH_DOMAIN = domain;
START_SSH_SERVER = true; START_SSH_SERVER = true;
BUILTIN_SSH_SERVER_USER = "git"; BUILTIN_SSH_SERVER_USER = "git";
SSH_PORT = gitea_ssh_port; SSH_PORT = ports.gitea_ssh_port.port;
# SSH_LISTEN_HOST="::"; # fixme? # SSH_LISTEN_HOST="::"; # fixme?
# SSH_AUTHORIZED_PRINCIPALS_ALLOW="username"; # SSH_AUTHORIZED_PRINCIPALS_ALLOW="username";
}; };
@ -35,19 +33,4 @@ in
}; };
environment.systemPackages = with pkgs; [ gitea ]; environment.systemPackages = with pkgs; [ gitea ];
security.acme.certs."${domain}".extraDomainNames = [ gitea_host ];
networking.firewall.allowedTCPPorts = [ gitea_ssh_port ];
services.nginx = {
enable = true;
virtualHosts."${gitea_host}" = {
serverName = gitea_host;
forceSSL = true;
useACMEHost = domain;
locations."/" = {
proxyPass = "http://127.0.0.1:${builtins.toString config.services.gitea.settings.server.HTTP_PORT}";
};
};
};
} }

View File

@ -1,8 +1,7 @@
{ config, ... }: { config, ... }:
let let
inherit (config.serverConfig) ports vhosts;
inherit (config.networking) domain; inherit (config.networking) domain;
grafana_host = "grafana.${domain}";
grafana_port = 8082;
in in
{ {
age.secrets.grafana_admin_pass = { age.secrets.grafana_admin_pass = {
@ -12,7 +11,6 @@ in
mode = "0600"; mode = "0600";
}; };
security.acme.certs."${domain}".extraDomainNames = [ grafana_host ];
services.grafana = { services.grafana = {
enable = true; enable = true;
settings = { settings = {
@ -22,22 +20,9 @@ in
admin_password = "$__file{${config.age.secrets.grafana_admin_pass.path}}"; admin_password = "$__file{${config.age.secrets.grafana_admin_pass.path}}";
}; };
server = { server = {
domain = grafana_host; domain = vhosts.grafana_host.host;
root_url = "https://${grafana_host}"; root_url = "https://${vhosts.grafana_host.host}";
http_port = grafana_port; http_port = vhosts.grafana_host.port;
};
};
};
services.nginx = {
enable = true;
virtualHosts."${grafana_host}" = {
serverName = grafana_host;
forceSSL = true;
useACMEHost = domain;
locations."/" = {
proxyPass = "http://127.0.0.1:${builtins.toString config.services.grafana.settings.server.http_port}";
proxyWebsockets = true;
}; };
}; };
}; };

View File

@ -1,13 +1,12 @@
{ config, ... }: { config, ... }:
let let
inherit (config.serverConfig) ports vhosts;
inherit (config.networking) domain; inherit (config.networking) domain;
mastodon_host = "mastodon.${domain}";
in in
{ {
security.acme.certs."${domain}".extraDomainNames = [ mastodon_host ];
services.mastodon = { services.mastodon = {
enable = true; enable = true;
localDomain = mastodon_host; localDomain = vhosts.mastodon_host.host;
streamingProcesses = 7; streamingProcesses = 7;
configureNginx = true; configureNginx = true;
smtp = { smtp = {

View File

@ -7,7 +7,7 @@
}: }:
let let
inherit (config.networking) domain; inherit (config.networking) domain;
matrix_host = "matrix.${domain}"; inherit (config.serverConfig) ports vhosts;
in in
{ {
services.postgresql = { services.postgresql = {
@ -89,7 +89,7 @@ in
name = "psycopg2"; name = "psycopg2";
args = { args = {
host = "localhost"; host = "localhost";
port = 5432; port = config.services.postgresql.port;
dbname = "synapse"; dbname = "synapse";
user = "synapse"; user = "synapse";
cp_min = 5; cp_min = 5;
@ -129,17 +129,17 @@ in
enable = true; enable = true;
virtualHosts."${domain}" = { virtualHosts."${domain}" = {
forceSSL = true; forceSSL = true;
enableACME = false; # use the cert above, not some weird one that matrix-synapse module supplies enableACME = lib.mkForce false; # use the cert above, not some weird one that matrix-synapse module supplies
useACMEHost = domain; useACMEHost = domain;
locations."/.well-known/matrix/server" = { locations."/.well-known/matrix/server" = {
return = "200 '{\"m.server\":\"${matrix_host}:443\"}'"; return = "200 '{\"m.server\":\"${vhosts.matrix_host.host}:443\"}'";
extraConfig = '' extraConfig = ''
default_type application/json; default_type application/json;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Accept-Ranges bytes;''; add_header Accept-Ranges bytes;'';
}; };
locations."/.well-known/matrix/client" = { locations."/.well-known/matrix/client" = {
return = "200 '{\"m.homeserver\": {\"base_url\": \"https://${matrix_host}\"}}'"; return = "200 '{\"m.homeserver\": {\"base_url\": \"https://${vhosts.matrix_host.host}\"}}'";
extraConfig = '' extraConfig = ''
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
default_type application/json; default_type application/json;

View File

@ -5,9 +5,7 @@
... ...
}: }:
let let
inherit (config.networking) domain; inherit (config.serverConfig) ports vhosts;
nextcloud_host = "cloud.${domain}";
nextcloud_port = 8083;
in in
{ {
services.postgresql = { services.postgresql = {
@ -21,7 +19,6 @@ in
]; ];
}; };
security.acme.certs."${domain}".extraDomainNames = [ nextcloud_host ];
age.secrets = { age.secrets = {
nextcloud_admin_pass = { nextcloud_admin_pass = {
file = ../secrets/nextcloud_admin_pass.age; file = ../secrets/nextcloud_admin_pass.age;
@ -34,13 +31,13 @@ in
services.redis.servers.nextcloud = { services.redis.servers.nextcloud = {
enable = true; enable = true;
bind = "::1"; bind = "::1";
port = 6379; port = ports.redis_nextcloud_port.port;
}; };
systemd.services.nextcloud-setup.serviceConfig.ExecStartPost = pkgs.writeScript "nextcloud-redis.sh" '' systemd.services.nextcloud-setup.serviceConfig.ExecStartPost = pkgs.writeScript "nextcloud-redis.sh" ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
nextcloud-occ config:system:set redis 'host' --value '::1' --type string nextcloud-occ config:system:set redis 'host' --value '::1' --type string
nextcloud-occ config:system:set redis 'port' --value 6379 --type integer nextcloud-occ config:system:set redis '${builtins.toString config.services.redis.servers.nextcloud.port}' --value 6379 --type integer
nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string
nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string
''; '';
@ -48,13 +45,14 @@ in
services.nextcloud = { services.nextcloud = {
enable = true; enable = true;
https = true; https = true;
hostName = nextcloud_host; hostName = vhosts.nextcloud_host.host;
package = pkgs.nextcloud28; package = pkgs.nextcloud28;
caching.redis = true; caching.redis = true;
# extraApps = with config.services.nextcloud.package.packages.apps; [ extraApps = {
# news contacts calendar tasks; inherit (config.services.nextcloud.package.packages.apps) calendar;
# ]; };
config = { config = {
adminpassFile = config.age.secrets.nextcloud_admin_pass.path; adminpassFile = config.age.secrets.nextcloud_admin_pass.path;
dbuser = "nextcloud"; dbuser = "nextcloud";
@ -67,7 +65,7 @@ in
filelocking.enabled = true; filelocking.enabled = true;
redis = { redis = {
host = "localhost"; host = "localhost";
port = 6379; port = config.services.redis.servers.nextcloud.port;
timeout = 0.0; timeout = 0.0;
}; };
}; };
@ -75,13 +73,4 @@ in
"opcache.interned_strings_buffer" = "12"; "opcache.interned_strings_buffer" = "12";
}; };
}; };
services.nginx = {
enable = true;
virtualHosts."${nextcloud_host}" = {
serverName = nextcloud_host;
forceSSL = true;
useACMEHost = domain;
};
};
} }

View File

@ -1,26 +1,22 @@
{ config, ... }: { config, lib, ... }:
let let
inherit (config.networking) domain; inherit (config.serverConfig) ports vhosts;
prometheus_host = "prometheus.${domain}";
prometheus_port = 9090; # netstat -nlp | grep 9090
in in
{ {
security.acme.certs."${domain}".extraDomainNames = [ prometheus_host ];
services.prometheus = { services.prometheus = {
enable = true; enable = true;
port = prometheus_port; port = vhosts.prometheus_host.port;
globalConfig.scrape_interval = "15s"; globalConfig.scrape_interval = "15s";
scrapeConfigs = [ scrapeConfigs = [
{ {
job_name = "chrysalis"; job_name = "chrysalis";
static_configs = [ static_configs = [
{ {
targets = [ targets =
"127.0.0.1:${toString config.services.prometheus.exporters.node.port}" with lib;
"127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}" map (v: "127.0.0.1:${toString v.port}") (
"127.0.0.1:${toString config.services.prometheus.exporters.postgres.port}" filter (v: (isAttrs v) && v.enable) (attrValues config.services.prometheus.exporters)
]; );
} }
]; ];
} }
@ -35,20 +31,7 @@ in
node = { node = {
enable = true; enable = true;
enabledCollectors = [ "systemd" ]; enabledCollectors = [ "systemd" ];
port = 9002; port = ports.node_exporter.port;
};
};
};
services.nginx = {
enable = true;
virtualHosts."${prometheus_host}" = {
serverName = prometheus_host;
forceSSL = true;
useACMEHost = domain;
locations."/" = {
# proxyPass = "http://127.0.0.1:${builtins.toString config.services.prometheus.port}";
return = "307 https://${domain}"; # nuh uh, no raw prometheus access for you!
}; };
}; };
}; };

View File

@ -1,137 +0,0 @@
{ config, pkgs, ... }:
let
inherit (config.networking) domain;
root_email = "contact@${domain}";
ptero_host = "ptero.${domain}";
DATA_DIR = "/var/lib/pterodactylpanel";
panel_user = "pterodactyl";
local_bridge = "ptero-local-br";
ptero_ver = "1.11.5";
ptero_port = "8042";
in
{
users.users.${panel_user} = {
isSystemUser = true;
extraGroups = [ "docker" ];
group = panel_user;
};
users.groups.${panel_user} = { };
age.secrets.ptero_env = {
file = ../secrets/ptero_env.age;
};
systemd.services.init-ptero-data-dir = {
description = "Create the pterodactyl panel data dir";
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
script = ''
mkdir -p ${DATA_DIR}/database
mkdir -p ${DATA_DIR}/cache
mkdir -p ${DATA_DIR}/panel/var
mkdir -p ${DATA_DIR}/panel/logs
mkdir -p ${DATA_DIR}/panel/nginx
chown ${panel_user}:${panel_user} -R ${DATA_DIR}
chmod +777 -R ${DATA_DIR}
'';
};
virtualisation.oci-containers.backend = "podman";
systemd.services.init-ptero-local-network = {
description = "Create the network bridge ${local_bridge} for ptero.";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
script =
let
podmancli = "${config.virtualisation.podman.package}/bin/podman";
in
''
check=$(${podmancli} pod ls | grep "ptero" || true)
if [ -z "$check" ]; then
${podmancli} pod create -p "${ptero_port}:80" ptero
else
echo "ptero pod already exists"
fi
'';
};
virtualisation.oci-containers.containers."ptero-mysql" = {
image = "library/mysql:8.0";
workdir = "${DATA_DIR}/database";
extraOptions = [ "--pod=ptero" ];
environment = {
"MYSQL_USER" = "pterodactyl";
"MYSQL_DATABASE" = "panel";
};
environmentFiles = [ config.age.secrets.ptero_env.path ];
volumes = [
"${DATA_DIR}/database:/var/lib/mysql"
"${DATA_DIR}/database:${DATA_DIR}/database"
];
cmd = [ "--default-authentication-plugin=mysql_native_password" ];
};
virtualisation.oci-containers.containers."ptero-cache" = {
image = "redis:alpine";
workdir = "${DATA_DIR}/cache";
volumes = [ "${DATA_DIR}/cache:${DATA_DIR}/cache" ];
extraOptions = [ "--pod=ptero" ];
};
virtualisation.oci-containers.containers."ptero-panel" = {
image = "ghcr.io/pterodactyl/panel:v${ptero_ver}";
volumes = [
"${DATA_DIR}/panel/var/:/app/var/"
"${DATA_DIR}/panel/logs/:/app/storage/logs"
"${DATA_DIR}/panel/nginx/:/etc/nginx/conf.d/"
];
extraOptions = [ "--pod=ptero" ];
environment = {
"APP_URL" = "https://${ptero_host}";
"APP_TIMEZONE" = "Europe/Berlin";
"APP_SERVICE_AUTHOR" = root_email;
"MAIL_FROM" = "noreply@${domain}";
"MAIL_DRIVER" = "smtp";
"MAIL_HOST" = "mail";
"MAIL_PORT" = "25";
"MAIL_USERNAME" = "";
"MAIL_PASSWORD" = "";
"MAIL_ENCRYPTION" = "true";
"APP_ENV" = "production";
"APP_ENVIRONMENT_ONLY" = "false";
"CACHE_DRIVER" = "redis";
"SESSION_DRIVER" = "redis";
"QUEUE_DRIVER" = "redis";
"REDIS_HOST" = "127.0.0.1";
"DB_HOST" = "127.0.0.1";
"TRUSTED_PROXIES" = "*";
};
labels = {
"traefik.http.routers.pterodactyl_panel.entrypoints" = "web";
};
environmentFiles = [ config.age.secrets.ptero_env.path ];
};
security.acme.certs."${domain}".extraDomainNames = [ ptero_host ];
services.nginx = {
enable = true;
virtualHosts."${ptero_host}" = {
serverName = ptero_host;
forceSSL = true;
useACMEHost = domain;
locations."/" = {
proxyPass = "http://127.0.0.1:${ptero_port}";
};
};
};
environment.systemPackages = with pkgs; [
(writeShellScriptBin "ptero-nix" "${config.virtualisation.podman.package}/bin/podman exec -it ptero-panel php artisan $@")
];
}

View File

@ -6,7 +6,6 @@
... ...
}: }:
let let
inherit (config.networking) domain;
inherit (config.serverConfig) ports vhosts; inherit (config.serverConfig) ports vhosts;
in in
{ {
@ -14,7 +13,7 @@ in
enable = true; enable = true;
environment = { environment = {
PUFFER_WEB_HOST = ":${builtins.toString vhosts.puffer_host.port}"; PUFFER_WEB_HOST = ":${builtins.toString vhosts.puffer_host.port}";
PUFFER_DAEMON_SFTP_HOST = ":${builtins.toString ports.puffer_sftp_port}"; PUFFER_DAEMON_SFTP_HOST = ":${builtins.toString ports.puffer_sftp_port.port}";
}; };
extraPackages = with pkgs; [ ]; extraPackages = with pkgs; [ ];
extraGroups = [ "docker" ]; extraGroups = [ "docker" ];