caddy support multi domain

This commit is contained in:
pazpi 2025-01-06 18:24:12 +01:00
parent f15e521895
commit fdcc829acf
13 changed files with 344 additions and 141 deletions

View file

@ -6,11 +6,20 @@
}:
let
tailscaleMagicDNS = "neon-dory.ts.net";
publicDomain = "pazpi.top";
tsDomain = "tegola.pro";
email = "pasettodavide@gmail.com";
in
{
age.secrets = {
searx-secret.file = ../../secrets/searx-secret.age;
tailscale-authKey.file = ../../secrets/tailscale-authKey.age;
cloudflare-tegola-apiKey = {
file = ../../secrets/cloudflare-tegola-apiKey.age;
owner = config.services.caddy.user;
group = config.services.caddy.group;
};
};
my = {
@ -18,10 +27,20 @@ in
services = {
dashy = {
enable = true;
settings = import ./dashy-settings.nix;
proxy = {
enable = true;
domain = tsDomain;
host = "caddy.internal";
};
};
media-mgr = {
proxy = {
enable = true;
domain = "tegola.pro";
domain = tsDomain;
host = "arr.internal";
};
};
@ -29,15 +48,25 @@ in
nextcloud = {
proxy = {
enable = true;
domain = "tegola.pro";
domain = tsDomain;
host = "nextcloud.internal";
};
};
searx = {
enable = true;
secretFile = config.age.secrets.searx-secret.path;
proxy = {
enable = true;
domain = tsDomain;
host = "caddy.internal";
};
};
vaultwarden = {
proxy = {
enable = true;
domain = "tegola.pro";
domain = tsDomain;
host = "vaultwarden.internal";
};
@ -48,7 +77,7 @@ in
prometheus = {
proxy = {
enable = true;
domain = "tegola.pro";
domain = tsDomain;
host = "metrics.internal";
};
};
@ -56,7 +85,7 @@ in
grafana = {
proxy = {
enable = true;
domain = "tegola.pro";
domain = tsDomain;
host = "metrics.internal";
};
};
@ -71,14 +100,27 @@ in
caddy = {
enable = true;
email = "pasettodavide@gmail.com";
domain = "tegola.pro";
domainsList = [
{
domain = tsDomain;
email = email;
cloudflareApiKeyFile = config.age.secrets.cloudflare-tegola-apiKey.path;
}
];
# email = "pasettodavide@gmail.com";
# domain = tsDomain;
# claudflareApiKeyFile = config.age.secrets.cloudflare-tegola-apiKey.path;
};
};
virtualisation = {
proxmox.enable = true;
portainer.proxy = {
enable = true;
domain = tsDomain;
host = "portainer.internal";
};
};
};

View file

@ -11,6 +11,10 @@ in
age.secrets = {
tailscale-authKey.file = ../../secrets/tailscale-authKey.age;
grafana-admin-pwd = {
file = ../../secrets/grafana-admin-pwd.age;
owner = "grafana";
};
};
my = {
@ -21,7 +25,10 @@ in
};
monitoring = {
grafana.enable = true;
grafana = {
enable = true;
adminPasswordFile = config.age.secrets.grafana-admin-pwd.path;
};
prometheus.enable = true;
loki.enable = true;
};

View file

@ -5,11 +5,22 @@
...
}:
{
age.secrets = {
nextcloud-admin-pwd = {
file = ../../secrets/nextcloud-admin-pwd.age;
owner = "nextcloud";
group = "nextcloud";
mode = "770";
};
};
my = {
utils.commons.enable = true;
services.nextcloud = {
enable = true;
adminPasswordFile = config.age.secrets.nextcloud-admin-pwd.path;
proxy.domain = "tegola.pro";
};

View file

@ -5,10 +5,14 @@
...
}:
{
age.secrets.vaultwarden-admin-pwd.file = ../../secrets/vaultwarden-admin-pwd.age;
my = {
utils.commons.enable = true;
services.vaultwarden = {
enable = true;
adminPasswordFile = config.age.secrets.vaultwarden-admin-pwd.path;
proxy.domain = "tegola.pro";
};
virtualisation.proxmox.enable = true;

View file

@ -15,6 +15,14 @@ in
options.my.monitoring.grafana = {
enable = lib.mkEnableOption "Enable grafana as a data visualization";
adminPasswordFile = lib.mkOption {
default = "";
type = lib.types.str;
description = ''
Path to the file containing the admin password for Grafana
'';
};
proxy = {
enable = lib.mkEnableOption "Set the proxy entry for this service";
@ -26,6 +34,14 @@ in
'';
};
subdomain = lib.mkOption {
default = "grafana";
type = lib.types.str;
description = ''
The subdomain where Grafana is reachable
'';
};
host = lib.mkOption {
default = "localhost";
type = lib.types.str;
@ -41,13 +57,6 @@ in
config = lib.mkMerge [
(lib.mkIf cfg.enable {
age.secrets = {
grafana-admin-pwd = {
file = ../../secrets/grafana-admin-pwd.age;
owner = "grafana";
};
};
services = {
grafana = {
@ -63,13 +72,14 @@ in
};
security = {
admin_user = "pazpi";
admin_password = "$__file{${config.age.secrets.grafana-admin-pwd.path}}";
admin_password = "$__file{${cfg.adminPasswordFile}}";
};
server = {
domain = "grafana.neon-dory.ts.net";
# domain = "grafana.neon-dory.ts.net";
domain = cfg.proxy.domain;
http_addr = "0.0.0.0";
http_port = 3000;
# root_url = "https://grafana.${cfg.proxy.domain}";
root_url = "https://${cfg.proxy.subdomain}.${cfg.proxy.domain}";
enable_gzip = true;
};
users = {
@ -111,9 +121,9 @@ in
(lib.mkIf cfg.proxy.enable {
services.caddy = with cfg.proxy; {
virtualHosts."grafana.${domain}".extraConfig = ''
virtualHosts."${subdomain}.${domain}".extraConfig = ''
reverse_proxy http://${host}:3000
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -26,6 +26,14 @@ in
'';
};
subdomain = lib.mkOption {
default = "prometheus";
type = lib.types.str;
description = ''
The subdomain where Prometheus is reachable
'';
};
host = lib.mkOption {
default = "localhost";
type = lib.types.str;
@ -94,9 +102,9 @@ in
(lib.mkIf cfg.proxy.enable {
services.caddy = with cfg.proxy; {
virtualHosts."prometheus.${domain}".extraConfig = ''
virtualHosts."${subdomain}.${domain}".extraConfig = ''
reverse_proxy http://${host}:9090
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -52,7 +52,7 @@ in
services.caddy = with cfg.proxy; {
virtualHosts."up.${domain}".extraConfig = ''
reverse_proxy http://${host}:${port}
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -14,53 +14,81 @@ in
options.my.networking.caddy = {
enable = lib.mkEnableOption "Enable caddy as reverse proxy";
domain = lib.mkOption {
default = "example.com";
type = lib.types.str;
domainsList = lib.mkOption {
type = lib.types.listOf (lib.types.attrsOf lib.types.str);
description = ''
The domain where Caddy is reachable
A list of sets, each containing three parameters of type string: domain, email, and cloudflareApiKeyFile.
'';
default = [
{
domain = "example.com";
email = "user@domain.com";
cloudflareApiKeyFile = "/path/to/cloudflare/api/key";
}
];
};
email = lib.mkOption {
default = "user@domain.com";
type = lib.types.str;
description = ''
Email for Certbot
'';
};
# claudflareApiKeyFile = lib.mkOption {
# default = "";
# type = lib.types.str;
# description = ''
# Cloudflare API key file
# '';
# };
# domain = lib.mkOption {
# default = "example.com";
# type = lib.types.str;
# description = ''
# The domain where Caddy is reachable
# '';
# };
# email = lib.mkOption {
# default = "user@domain.com";
# type = lib.types.str;
# description = ''
# Email for Certbot
# '';
# };
};
config = lib.mkIf cfg.enable {
age.secrets = {
cloudflare-tegola-apiKey = {
file = ../../secrets/cloudflare-tegola-apiKey.age;
owner = config.services.caddy.user;
group = config.services.caddy.group;
};
};
# Insted on relying on caddy to provide TLS, we use certbot to get a certificate
# https://aottr.dev/posts/2024/08/homelab-setting-up-caddy-reverse-proxy-with-ssl-on-nixos/
security.acme = {
acceptTerms = true;
defaults.email = cfg.email;
# TESTING ONLY!
# defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory";
certs."${cfg.domain}" = {
group = config.services.caddy.group;
certs = lib.mkMerge (
map (domainConfig: {
"${domainConfig.domain}" = {
group = config.services.caddy.group;
email = domainConfig.email;
domain = domainConfig.domain;
extraDomainNames = [ "*.${domainConfig.domain}" ];
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
dnsPropagationCheck = true;
environmentFile = domainConfig.cloudflareApiKeyFile;
};
}) cfg.domainsList
);
domain = "${cfg.domain}";
extraDomainNames = [ "*.${cfg.domain}" ];
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
dnsPropagationCheck = true;
environmentFile = config.age.secrets.cloudflare-tegola-apiKey.path;
};
# certs."${cfg.domain}" = {
# group = config.services.caddy.group;
# domain = "${cfg.domain}";
# extraDomainNames = [ "*.${cfg.domain}" ];
# dnsProvider = "cloudflare";
# dnsResolver = "1.1.1.1:53";
# dnsPropagationCheck = true;
# environmentFile = cfg.claudflareApiKeyFile;
# };
};
services.caddy = {
@ -72,17 +100,33 @@ in
}
'';
extraConfig =
let
certPath = config.security.acme.certs."${cfg.domain}".directory;
in
''
(cloudflare) {
tls ${certPath}/cert.pem ${certPath}/key.pem {
protocols tls1.3
extraConfig = lib.concatStringsSep "\n" (
map (
domainConfig:
let
certPath = config.security.acme.certs."${domainConfig.domain}".directory;
in
''
(cloudflare_${domainConfig.domain}) {
tls ${certPath}/cert.pem ${certPath}/key.pem {
protocols tls1.3
}
}
}
'';
''
) cfg.domainsList
);
# extraConfig =
# let
# certPath = config.security.acme.certs."${cfg.domain}".directory;
# in
# ''
# (cloudflare) {
# tls ${certPath}/cert.pem ${certPath}/key.pem {
# protocols tls1.3
# }
# }
# '';
};
systemd.services.caddy.serviceConfig = {

View file

@ -135,31 +135,31 @@ in
services.caddy = with cfg.proxy; {
virtualHosts."prowlarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:9696
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."radarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:7878
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."sonarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:8989
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."lidarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:8686
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."readarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:8787
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."bazarr.${domain}".extraConfig = ''
reverse_proxy http://${host}:6767
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."jellyseerr.${domain}".extraConfig = ''
reverse_proxy http://${host}:5055
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -12,6 +12,14 @@ in
options.my.services.nextcloud = {
enable = lib.mkEnableOption "Enable Nextcloud module";
adminPasswordFile = lib.mkOption {
default = "";
type = lib.types.str;
description = ''
Path to the file containing the admin password for Nextcloud
'';
};
proxy = {
enable = lib.mkEnableOption "Set the proxy entry for this service";
@ -23,6 +31,22 @@ in
'';
};
subdomain = lib.mkOption {
default = "nextcloud";
type = lib.types.str;
description = ''
The subdomain where Nextcloud is reachable
'';
};
officeSubdomain = lib.mkOption {
default = "office";
type = lib.types.str;
description = ''
The subdomain where Collabora Online is reachable
'';
};
host = lib.mkOption {
default = "localhost";
type = lib.types.str;
@ -37,15 +61,6 @@ in
config = lib.mkMerge [
(lib.mkIf cfg.enable {
age.secrets = {
nextcloud-admin-pwd = {
file = ../../secrets/nextcloud-admin-pwd.age;
owner = "nextcloud";
group = "nextcloud";
mode = "770";
};
};
services = {
nextcloud = {
@ -90,7 +105,7 @@ in
config = {
dbtype = "pgsql";
adminuser = "admin";
adminpassFile = config.age.secrets.nextcloud-admin-pwd.path;
adminpassFile = cfg.adminPasswordFile;
};
# Let NixOS install and configure the database automatically.
@ -160,12 +175,12 @@ in
(lib.mkIf cfg.proxy.enable {
services.caddy = with cfg.proxy; {
virtualHosts."cloud.${domain}".extraConfig = ''
virtualHosts."${subdomain}.${domain}".extraConfig = ''
reverse_proxy http://${host}:80
import cloudflare
import cloudflare_${domain}
'';
virtualHosts."office.${domain}".extraConfig = ''
import cloudflare
virtualHosts."${officeSubdomain}.${domain}".extraConfig = ''
import cloudflare_${domain}
reverse_proxy http://${host}:${toString config.services.collabora-online.port} {
# Required to circumvent bug of Onlyoffice loading mixed non-https content
header_up X-Forwarded-Proto https

View file

@ -12,6 +12,14 @@ in
options.my.services.searx = {
enable = lib.mkEnableOption "Enable searXNG module";
secretFile = lib.mkOption {
default = "";
type = lib.types.str;
description = ''
Path to the file containing the secret for searXNG
'';
};
proxy = {
enable = lib.mkEnableOption "Set the proxy entry for this service";
@ -37,12 +45,10 @@ in
config = lib.mkMerge [
(lib.mkIf cfg.enable {
age.secrets.searx-secret.file = ../../secrets/searx-secret.age;
services.searcx = {
services.searx = {
enable = true;
redisCreateLocally = true;
environmentFile = config.age.secrets.searx-secret.path;
environmentFile = cfg.secretFile;
settings = {
general = {
open_metrics = "@METRICS_SECRET@";
@ -63,7 +69,7 @@ in
services.caddy = with cfg.proxy; {
virtualHosts."search.${domain}".extraConfig = ''
reverse_proxy http://${host}:8080
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -13,6 +13,14 @@ in
options.my.services.vaultwarden = {
enable = lib.mkEnableOption "Enable Vaultwarden module";
adminPasswordFile = lib.mkOption {
default = "";
type = lib.types.str;
description = ''
Path to the file containing the admin password for Vaultwarden
'';
};
proxy = {
enable = lib.mkEnableOption "Set the proxy entry for this service";
@ -24,6 +32,14 @@ in
'';
};
subdomain = lib.mkOption {
default = "vault";
type = lib.types.str;
description = ''
The subdomain where Vaultwarden is reachable
'';
};
host = lib.mkOption {
default = "localhost";
type = lib.types.str;
@ -38,8 +54,6 @@ in
config = lib.mkMerge [
(lib.mkIf cfg.enable {
age.secrets.vaultwarden-admin-pwd.file = ../../secrets/vaultwarden-admin-pwd.age;
my.services.postgresql = {
enable = true;
ensures = [
@ -53,7 +67,7 @@ in
services.vaultwarden = {
enable = true;
dbBackend = "postgresql";
environmentFile = config.age.secrets.vaultwarden-admin-pwd.path;
environmentFile = cfg.adminPasswordFile;
config = {
DOMAIN = "https://vault.${cfg.proxy.domain}";
SENDS_ALLOWED = true;
@ -75,9 +89,9 @@ in
(lib.mkIf cfg.proxy.enable {
services.caddy = with cfg.proxy; {
virtualHosts."vault.${domain}".extraConfig = ''
virtualHosts."${subdomain}.${domain}".extraConfig = ''
reverse_proxy http://${host}:${toString rocketPort}
import cloudflare
import cloudflare_${domain}
'';
};
})

View file

@ -43,61 +43,103 @@ in
'';
};
};
config = lib.mkIf cfg.enable {
my.virtualisation.docker.enable = true;
virtualisation.oci-containers = {
backend = "docker"; # Use Docker as the backend
containers = {
portainer = {
image = "portainer/portainer-ce:latest";
ports = [ "9000:9000" ];
volumes = [
"/var/run/docker.sock:/var/run/docker.sock"
"${cfg.portainerDataDir}:/data" # Add persistent volume for Portainer data
];
environmentFiles = [ cfg.environmentSecrets ];
labels = {
"com.centurylinklabs.watchtower.enable" = "true";
};
autoStart = true;
};
watchtower = lib.mkIf cfg.enableWatchtower {
image = "containrrr/watchtower";
volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ];
autoStart = true;
environmentFiles = [ cfg.environmentSecrets ];
environment = {
"TZ" = "Europe/Rome";
"WATCHTOWER_CLEANUP" = "true";
"WATCHTOWER_SCHEDULE" = "0 0 4 * * *"; # Run every day at 4am
"WATCHTOWER_LABEL_ENABLE" = "true"; # Only update labeled containers
"WATCHTOWER_NOTIFICATIONS" = "shoutrrr"; # Use shoutrrr for notifications
};
};
proxy = {
enable = lib.mkEnableOption "Set the proxy entry for this service";
domain = lib.mkOption {
default = "example.com";
type = lib.types.str;
description = ''
The domain where Caddy is reachable
'';
};
subdomain = lib.mkOption {
default = "portainer";
type = lib.types.str;
description = ''
The subdomain where Portainer will be reachable
'';
};
host = lib.mkOption {
default = "localhost";
type = lib.types.str;
description = ''
host name where the download manager stack is running
'';
};
};
# Ensure the directory exists and has the correct permissions
systemd.tmpfiles.settings = {
"10-portainerDataDir" = {
${cfg.portainerDataDir} = {
d = {
group = "root";
mode = "0755";
user = "root";
};
config = lib.mkMerge [
(lib.mkIf cfg.enable {
my.virtualisation.docker.enable = true;
virtualisation.oci-containers = {
backend = "docker"; # Use Docker as the backend
containers = {
portainer = {
image = "portainer/portainer-ce:latest";
ports = [ "9000:9000" ];
volumes = [
"/var/run/docker.sock:/var/run/docker.sock"
"${cfg.portainerDataDir}:/data" # Add persistent volume for Portainer data
];
environmentFiles = [ cfg.environmentSecrets ];
labels = {
"com.centurylinklabs.watchtower.enable" = "true";
};
autoStart = true;
};
watchtower = lib.mkIf cfg.enableWatchtower {
image = "containrrr/watchtower";
volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ];
autoStart = true;
environmentFiles = [ cfg.environmentSecrets ];
environment = {
"TZ" = "Europe/Rome";
"WATCHTOWER_CLEANUP" = "true";
"WATCHTOWER_SCHEDULE" = "0 0 4 * * *"; # Run every day at 4am
"WATCHTOWER_LABEL_ENABLE" = "true"; # Only update labeled containers
"WATCHTOWER_NOTIFICATIONS" = "shoutrrr"; # Use shoutrrr for notifications
};
};
};
};
# Ensure the directory exists and has the correct permissions
systemd.tmpfiles.settings = {
"10-portainerDataDir" = {
${cfg.portainerDataDir} = {
d = {
group = "root";
mode = "0755";
user = "root";
};
};
};
};
};
networking.firewall.allowedTCPPorts = [ 9000 ];
networking.firewall.allowedTCPPorts = [ 9000 ];
})
(lib.mkIf cfg.proxy.enable {
services.caddy = with cfg.proxy; {
virtualHosts."${subdomain}.${domain}".extraConfig = ''
reverse_proxy http://${host}:9000
import cloudflare_${domain}
'';
};
})
];
};
}