From 29e183b0c7f8e83017faf32da9868414e8a861b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Tue, 14 Nov 2023 18:44:46 +0100 Subject: [PATCH] feat: use ACME and nginx instead of caddy --- hosts/default.nix | 2 + hosts/frikandel/default.nix | 1 + hosts/frikandel/email.nix | 227 ++++++++++++++++++++------ hosts/frikandel/networking.nix | 11 -- hosts/frikandel/nginx.nix | 23 +++ hosts/frikandel/website.nix | 30 ++-- hosts/pie/ddclient.nix | 44 ----- hosts/pie/default.nix | 2 +- hosts/pie/firefly.nix | 32 ++-- hosts/pie/invoiceplane.nix | 13 ++ hosts/pie/networking.nix | 12 -- hosts/pie/nginx.nix | 25 +++ hosts/pie/paperless.nix | 20 +-- hosts/pie/unbound.nix | 8 +- modules/acme/default.nix | 27 +++ modules/core/networking.nix | 2 +- modules/ddclient/default.nix | 245 ---------------------------- modules/default.nix | 2 +- modules/invoiceplane/default.nix | 22 ++- modules/printing/default.nix | 12 -- pkgs/caddy/default.nix | 1 - secrets/hosting-de-acme-secrets.age | 22 +++ secrets/hosting.de-api.key.age | Bin 908 -> 0 bytes secrets/mail@b12f.io-password.age | Bin 1184 -> 1058 bytes secrets/secrets.nix | 2 +- terraform/b12f.io.tf | 2 +- 26 files changed, 359 insertions(+), 428 deletions(-) create mode 100644 hosts/frikandel/nginx.nix delete mode 100644 hosts/pie/ddclient.nix create mode 100644 hosts/pie/nginx.nix create mode 100644 modules/acme/default.nix delete mode 100644 modules/ddclient/default.nix create mode 100644 secrets/hosting-de-acme-secrets.age delete mode 100644 secrets/hosting.de-api.key.age diff --git a/hosts/default.nix b/hosts/default.nix index d03cea9..b204deb 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -58,6 +58,7 @@ inputs.nixos-hardware.nixosModules.raspberry-pi-4 ./pie self.nixosModules.yule + self.nixosModules.acme self.nixosModules.docker self.nixosModules.invoiceplane ]; @@ -69,6 +70,7 @@ self.nixosModules.base ./frikandel self.nixosModules.yule + self.nixosModules.acme self.nixosModules.docker ]; }; diff --git a/hosts/frikandel/default.nix b/hosts/frikandel/default.nix index 17ed70d..8186bd8 100644 --- a/hosts/frikandel/default.nix +++ b/hosts/frikandel/default.nix @@ -4,6 +4,7 @@ ./configuration.nix ./networking.nix + ./nginx.nix ./wireguard.nix ./email.nix ./website.nix diff --git a/hosts/frikandel/email.nix b/hosts/frikandel/email.nix index c52b667..228f65b 100644 --- a/hosts/frikandel/email.nix +++ b/hosts/frikandel/email.nix @@ -5,16 +5,16 @@ lib, ... }: let - restartMaddyOnCertRenewal = pkgs.writeShellScriptBin "restart-maddy-on-cert-renewal" '' - if [ "$1" == "mail.b12f.io"]; then - ${pkgs.systemd}/bin/systemctl restart maddy.service; - fi + dkimDNSb12fio = '' + default._domainkey IN TXT ( "v=DKIM1; k=rsa; " + "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8WrnxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB" ) ; ''; in { age.secrets."b12f.io-dkim-private-rsa" = { file = "${flake.self}/secrets/b12f.io-dkim-private-rsa.age"; + path = "/var/lib/maddy/dkim_keys/b12f.io_default.key"; mode = "400"; - owner = "rspamd"; + owner = "maddy"; }; age.secrets."mail@b12f.io-password" = { @@ -23,77 +23,204 @@ in { owner = "maddy"; }; - services.caddy = { - globalConfig = '' - events { - on cert_obtained exec ${restartMaddyOnCertRenewal}/bin/restart-maddy-on-cert-renewal {event.data.name} - } - ''; + security.acme.certs = { + "mail.b12f.io" = { + reloadServices = [ "maddy" ]; + group = "maddy"; + }; + "mta-sts.b12f.io" = {}; + }; - virtualHosts = { - "mail.b12f.io".extraConfig = '' - respond "404 Not Found" - ''; - - "mta-sts.b12f.io".extraConfig = '' - encode gzip - file_server - root * ${ - pkgs.runCommand "testdir" {} '' - mkdir -p "$out/.well-known" - echo " - version: STSv1 - mode: enforce - max_age: 604800 - mx: mail.b12f.io - " > "$out/.well-known/mta-sts.txt" - '' - } - ''; + services.nginx.virtualHosts = { + "mta-sts.b12f.io" = { + forceSSL = true; + useACMEHost = "mta-sts.b12f.io"; + locations."/" = { + root = pkgs.runCommand "create-well-known-mta-sts" {} '' + mkdir -p "$out/.well-known" + echo " + version: STSv1 + mode: enforce + max_age: 604800 + mx: mail.b12f.io + " > "$out/.well-known/mta-sts.txt" + ''; + tryFiles = "$uri $uri/ =404"; + }; }; }; + systemd.tmpfiles.rules = [ + "d '/run/maddy' 0750 maddy maddy - -" + ]; + + system.activationScripts.makeMaddyDKIMDNS = lib.stringAfter [ "var" ] '' + mkdir -p /var/lib/maddy/dkim_keys + + echo '${dkimDNSb12fio}' >> /var/lib/maddy/dkim_keys/b12f.io_default.dns + ''; + services.maddy = { - enable = false; - + enable = true; openFirewall = true; - hostname = "mail.b12f.io"; - primaryDomain = "b12f.io"; - ensureAccounts = [ "mail@b12f.io" ]; - ensureCredentials = { # Do not use this in production. This will make passwords world-readable # in the Nix store "mail@b12f.io".passwordFile = config.age.secrets."mail@b12f.io-password".path; }; - tls = { + loader = "file"; certificates = [ { - keyPath = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.b12f.io/mail.b12f.io.key"; - certPath = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.b12f.io/mail.b12f.io.crt"; + keyPath = "${config.security.acme.certs."mail.b12f.io".directory}/key.pem"; + certPath = "${config.security.acme.certs."mail.b12f.io".directory}/cert.pem"; } ]; }; + config = '' + # Minimal configuration with TLS disabled, adapted from upstream example + # configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf + # Do not use this in production! - config = (builtins.replaceStrings ["msgpipeline local_routing {"] [''msgpipeline local_routing { - check { - rspamd { - api_path http://localhost:11334 + auth.pass_table local_authdb { + table sql_table { + driver sqlite3 + dsn credentials.db + table_name passwords + } } - }''] config.services.maddy.config.default) ++ '' - modify { - domains b12f.io - selector default - key_path ${config.age.secrets."b12f.io-dkim-private-rsa".path} + + storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db + } + + table.chain local_rewrites { + optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3" + optional_step static { + entry postmaster postmaster@$(primary_domain) + } + optional_step file /etc/maddy/aliases + } + + msgpipeline local_routing { + check { + rspamd { + api_path http://localhost:11334 + } + } + + destination b12f.io { + modify { + replace_rcpt regexp ".*" "mail@b12f.io" + } + deliver_to &local_mailboxes + } + + destination postmaster $(local_domains) { + modify { + replace_rcpt &local_rewrites + } + deliver_to &local_mailboxes + } + + default_destination { + reject 550 5.1.1 "User doesn't exist" + } + } + + smtp tcp://0.0.0.0:25 { + limits { + all rate 20 1s + all concurrency 10 + } + dmarc yes + check { + require_mx_record + dkim + spf + } + source $(local_domains) { + reject 501 5.1.8 "Use Submission for outgoing SMTP" + } + default_source { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.1.1 "User doesn't exist" + } + } + } + + submission tcp://0.0.0.0:587 { + limits { + all rate 50 1s + } + auth &local_authdb + source $(local_domains) { + check { + authorize_sender { + prepare_email &local_rewrites + user_to_email identity + } + } + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + modify { + dkim $(primary_domain) $(local_domains) default + } + deliver_to &remote_queue + } + } + default_source { + reject 501 5.1.8 "Non-local sender domain" + } + } + + target.remote outbound_delivery { + limits { + destination rate 20 1s + destination concurrency 10 + } + mx_auth { + dane + mtasts { + cache fs + fs_dir mtasts_cache/ + } + local_policy { + min_tls_level encrypted + min_mx_level none + } + } + } + + target.queue remote_queue { + target &outbound_delivery + autogenerated_msg_domain $(primary_domain) + bounce { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.0.0 "Refusing to send DSNs to non-local addresses" + } + } + } + + imap tcp://0.0.0.0:143 { + auth &local_authdb + storage &local_mailboxes } ''; - }; services.rspamd = { diff --git a/hosts/frikandel/networking.nix b/hosts/frikandel/networking.nix index 3090cb0..5d9025c 100644 --- a/hosts/frikandel/networking.nix +++ b/hosts/frikandel/networking.nix @@ -32,15 +32,4 @@ }; networking.firewall.allowedTCPPorts = [ 80 443 ]; - - # Caddy reverse proxy for local services like cups - services.caddy = { - enable = true; - globalConfig = '' - default_bind 128.140.109.213 2a01:4f8:c2c:b60:: - # auto_https off - email acme@benjaminbaedorf.eu - # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory - ''; - }; } diff --git a/hosts/frikandel/nginx.nix b/hosts/frikandel/nginx.nix new file mode 100644 index 0000000..f77ecde --- /dev/null +++ b/hosts/frikandel/nginx.nix @@ -0,0 +1,23 @@ +{ + flake, + config, + pkgs, + lib, + ... +}: { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx = { + enable = true; + + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedTlsSettings = true; + recommendedProxySettings = true; + + defaultListenAddresses = [ + "128.140.109.213" + "[2a01:4f8:c2c:b60::]" + ]; + }; +} diff --git a/hosts/frikandel/website.nix b/hosts/frikandel/website.nix index 4fa28bc..54d28b6 100644 --- a/hosts/frikandel/website.nix +++ b/hosts/frikandel/website.nix @@ -22,25 +22,27 @@ ''; }; in { - services.caddy.virtualHosts = { + security.acme.certs = { + "benjaminbaedorf.eu" = {}; + "b12f.io" = {}; + }; + + services.nginx.virtualHosts = { "benjaminbaedorf.eu" = { - extraConfig = '' - redir https://b12f.io{uri} temporary - ''; + forceSSL = true; + useACMEHost = "benjaminbaedorf.eu"; + locations."/".return = "302 https://b12f.io$request_uri"; }; "b12f.io" = { - extraConfig = '' - handle { - root * ${bbeu} - try_files {path}.html {path} - file_server - } + forceSSL = true; + useACMEHost = "b12f.io"; - handle_errors { - respond "{http.error.status_code} {http.error.status_text}" - } - ''; + locations."/" = { + root = bbeu; + index = "index.html"; + tryFiles = "$uri $uri/ =404"; + }; }; }; } diff --git a/hosts/pie/ddclient.nix b/hosts/pie/ddclient.nix deleted file mode 100644 index 6d8a70b..0000000 --- a/hosts/pie/ddclient.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ - flake, - config, - pkgs, - lib, - ... -}: -with lib; let - psCfg = config.pub-solar; - xdg = config.home-manager.users."${psCfg.user.name}".xdg; - - getIP4 = with pkgs; writeShellScriptBin "getIP" '' - ${curl}/bin/curl -4 https://ipcheck-ds.wieistmeineip.de/callback/ | ${coreutils}/bin/tail -c +2 | ${coreutils}/bin/head -c -1 | ${jq}/bin/jq '.ip' -r - ''; - getIP6 = with pkgs; writeShellScriptBin "getIP" '' - echo "2a02:908:5b1:e3c0:2::" - ''; -in { - imports = [ - flake.self.nixosModules.ddclient - ]; - - services.ddclient = { - enable = true; - protocol = "dyndns1"; - domains = [ - "pie.b12f.io" - "droppie.b12f.io" - ]; - server = "ddns.hosting.de"; - username = "b12f"; - usev4 = "cmdv4, cmdv4=${getIP4}/bin/getIP"; - usev6 = "cmdv6, cmdv6=${getIP6}/bin/getIP"; - verbose = true; - passwordFile = "/run/agenix/dyndns.key"; - interval = "1min"; - }; - - age.secrets."dyndns.key" = { - file = "${flake.self}/secrets/dyndns.key.age"; - mode = "400"; - owner = "root"; - }; -} diff --git a/hosts/pie/default.nix b/hosts/pie/default.nix index a7993a4..86b1956 100644 --- a/hosts/pie/default.nix +++ b/hosts/pie/default.nix @@ -4,12 +4,12 @@ ./configuration.nix ./networking.nix + ./nginx.nix ./wireguard.nix ./backup.nix ./unbound.nix ./dhcpd.nix ./wake-droppie.nix - ./ddclient.nix ./paperless.nix ./firefly.nix ./invoiceplane.nix diff --git a/hosts/pie/firefly.nix b/hosts/pie/firefly.nix index 11bfe9a..acf3fe5 100644 --- a/hosts/pie/firefly.nix +++ b/hosts/pie/firefly.nix @@ -29,24 +29,22 @@ in { mode = "400"; }; - services.caddy = { - enable = true; - extraConfig = '' - firefly.b12f.io { - reverse_proxy 127.0.0.1:8080 + security.acme.certs = { + "firefly.b12f.io" = {}; + "firefly-importer.b12f.io" = {}; + }; - basicauth * { - b12f $2a$05$/xM7USOzLczswXmiacO6UOdg1YNPIw/KJMbMVerEkpsCtNUFwte4a - } - } - firefly-importer.b12f.io { - reverse_proxy 127.0.0.1:8081 - - basicauth * { - b12f $2a$05$/xM7USOzLczswXmiacO6UOdg1YNPIw/KJMbMVerEkpsCtNUFwte4a - } - } - ''; + services.nginx.virtualHosts = { + "firefly.b12f.io" = { + forceSSL = true; + useACMEHost = "firefly.b12f.io"; + locations."/".proxyPass = "http://127.0.0.1:8080"; + }; + "firefly-importer.b12f.io" = { + forceSSL = true; + useACMEHost = "firefly.b12f.io"; + locations."/".proxyPass = "http://127.0.0.1:8081"; + }; }; systemd.services."docker-network-firefly" = let diff --git a/hosts/pie/invoiceplane.nix b/hosts/pie/invoiceplane.nix index 97890de..8bab641 100644 --- a/hosts/pie/invoiceplane.nix +++ b/hosts/pie/invoiceplane.nix @@ -20,7 +20,20 @@ in { mode = "400"; }; + security.acme.certs = { + "invoicing.b12f.io" = {}; + }; + + services.nginx.virtualHosts = { + "invoicing.b12f.io" = { + forceSSL = true; + useACMEHost = "invoicing.b12f.io"; + }; + }; + + services.invoiceplane.webserver = "nginx"; services.invoiceplane.sites."invoicing.b12f.io" = { + # nginx is not supported enable = true; database = { diff --git a/hosts/pie/networking.nix b/hosts/pie/networking.nix index 04bbc6a..b8e61db 100644 --- a/hosts/pie/networking.nix +++ b/hosts/pie/networking.nix @@ -24,17 +24,5 @@ ]; }; - networking.firewall.allowedTCPPorts = [ 80 443 ]; - services.openssh.openFirewall = true; - - # Caddy reverse proxy for local services like cups - services.caddy = { - globalConfig = '' - default_bind 192.168.178.2 2a02:908:5b1:e3c0:2:: 10.0.1.2 fd00:b12f:acab:1312:acab:2:: - # auto_https off - email acme@benjaminbaedorf.eu - # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory - ''; - }; } diff --git a/hosts/pie/nginx.nix b/hosts/pie/nginx.nix new file mode 100644 index 0000000..de58beb --- /dev/null +++ b/hosts/pie/nginx.nix @@ -0,0 +1,25 @@ +{ + flake, + config, + pkgs, + lib, + ... +}: { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx = { + enable = true; + + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedTlsSettings = true; + recommendedProxySettings = true; + + defaultListenAddresses = [ + "192.168.178.2" + # "2a02:908:5b1:e3c0:2::" + "10.0.1.2" + "[fd00:b12f:acab:1312:acab:2::]" + ]; + }; +} diff --git a/hosts/pie/paperless.nix b/hosts/pie/paperless.nix index 24de374..79f1a10 100644 --- a/hosts/pie/paperless.nix +++ b/hosts/pie/paperless.nix @@ -47,18 +47,16 @@ in { }; }; - services.caddy = { - enable = true; - extraConfig = '' - paperless.b12f.io { - request_header Host localhost:${builtins.toString config.services.paperless.port} - reverse_proxy 127.0.0.1:${builtins.toString config.services.paperless.port} + security.acme.certs = { + "paperless.b12f.io" = {}; + }; - basicauth * { - b12f $2a$05$/xM7USOzLczswXmiacO6UOdg1YNPIw/KJMbMVerEkpsCtNUFwte4a - } - } - ''; + services.nginx.virtualHosts = { + "paperless.b12f.io" = { + forceSSL = true; + useACMEHost = "paperless.b12f.io"; + locations."/".proxyPass = "http://127.0.0.1:${builtins.toString config.services.paperless.port}"; + }; }; systemd.tmpfiles.rules = [ diff --git a/hosts/pie/unbound.nix b/hosts/pie/unbound.nix index 4ae5762..310444a 100644 --- a/hosts/pie/unbound.nix +++ b/hosts/pie/unbound.nix @@ -61,16 +61,13 @@ "fd00:b12f:acab:1312::/64 allow" ]; local-zone = [ - "\"b12f.io\" static" + "\"b12f.io\" transparent" "\"local\" static" "\"box\" static" ]; local-data = [ "\"brwb8763f64a364.local. 10800 IN A 192.168.178.4\"" - "\"droppie.local. 10800 IN A 192.168.178.3\"" - "\"droppie.local. 10800 IN AAAA 2a02:908:5b1:e3c0:3::\"" - "\"droppie.b12f.io. 10800 IN A 10.0.1.3\"" "\"droppie.b12f.io. 10800 IN AAAA fd00:b12f:acab:1312:acab:3::\"" "\"backup.b12f.io. 10800 IN A 10.0.1.3\"" @@ -102,6 +99,7 @@ tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt"; }; + forward-zone = [ { name = "."; @@ -111,7 +109,7 @@ "185.253.5.0#dns0.eu" "2a0f:fc81::#dns0.eu" ]; - # forward-tls-upstream = "yes"; + forward-tls-upstream = "yes"; } ]; diff --git a/modules/acme/default.nix b/modules/acme/default.nix new file mode 100644 index 0000000..b95847d --- /dev/null +++ b/modules/acme/default.nix @@ -0,0 +1,27 @@ +{ + flake, + config, + pkgs, + lib, + ... +}: { + age.secrets."hosting-de-acme-secrets" = { + file = "${flake.self}/secrets/hosting-de-acme-secrets.age"; + mode = "400"; + owner = "acme"; + }; + + security.acme = { + acceptTerms = true; + + defaults = { + email = "acme@benjaminbaedorf.eu"; + # server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + dnsProvider = "hostingde"; + dnsPropagationCheck = true; + credentialsFile = config.age.secrets."hosting-de-acme-secrets".path; + group = "nginx"; + webroot = null; + }; + }; +} diff --git a/modules/core/networking.nix b/modules/core/networking.nix index 62ff796..0d93bbe 100644 --- a/modules/core/networking.nix +++ b/modules/core/networking.nix @@ -9,7 +9,7 @@ systemd.services.NetworkManager-wait-online.enable = lib.mkDefault false; systemd.services.systemd-networkd-wait-online.enable = lib.mkDefault false; - networking.hosts = (flake.self.lib.addLocalHostname ["caddy.local"]) // { + networking.hosts = { "128.140.109.213" = [ "vpn.b12f.io" ]; "2a01:4f8:c2c:b60::" = [ "vpn.b12f.io" ]; "2a02:908:5b1:e3c0:2::" = [ "pie-wg.b12f.io" ]; diff --git a/modules/ddclient/default.nix b/modules/ddclient/default.nix deleted file mode 100644 index e3ee366..0000000 --- a/modules/ddclient/default.nix +++ /dev/null @@ -1,245 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: -let - cfg = config.services.ddclient; - boolToStr = bool: if bool then "yes" else "no"; - dataDir = "/var/lib/ddclient"; - StateDirectory = builtins.baseNameOf dataDir; - RuntimeDirectory = StateDirectory; - - usev4 = if cfg.usev4 != "" then "usev4=${cfg.usev4}" else ""; - usev6 = if cfg.usev6 != "" then "usev6=${cfg.usev6}" else ""; - - configFile' = pkgs.writeText "ddclient.conf" '' - # This file can be used as a template for configFile or is automatically generated by Nix options. - use=no - ${usev4} - ${usev6} - cache=${dataDir}/ddclient.cache - foreground=yes - login=${cfg.username} - password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"} - protocol=${cfg.protocol} - ${lib.optionalString (cfg.script != "") "script=${cfg.script}"} - ${lib.optionalString (cfg.server != "") "server=${cfg.server}"} - ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"} - ssl=${boolToStr cfg.ssl} - wildcard=yes - quiet=${boolToStr cfg.quiet} - verbose=${boolToStr cfg.verbose} - ${cfg.extraConfig} - ${lib.concatStringsSep "," cfg.domains} - ''; - configFile = if (cfg.configFile != null) then cfg.configFile else configFile'; - - preStart = '' - install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf - ${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then '' - install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key - '' else if (cfg.passwordFile != null) then '' - "${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf" - '' else '' - sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf - '')} - ''; -in with lib; { - disabledModules = [ - "services/networking/ddclient.nix" - ]; - - imports = [ - (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ] - (config: - let value = getAttrFromPath [ "services" "ddclient" "domain" ] config; - in if value != "" then [ value ] else [])) - (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "") - (mkRemovedOptionModule [ "services" "ddclient" "password" ] "Use services.ddclient.passwordFile instead.") - ]; - - ###### interface - - options = { - services.ddclient = with lib.types; { - enable = mkOption { - default = false; - type = bool; - description = lib.mdDoc '' - Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org). - ''; - }; - - package = mkOption { - type = package; - default = pkgs.ddclient; - defaultText = lib.literalExpression "pkgs.ddclient"; - description = lib.mdDoc '' - The ddclient executable package run by the service. - ''; - }; - - domains = mkOption { - default = [ "" ]; - type = listOf str; - description = lib.mdDoc '' - Domain name(s) to synchronize. - ''; - }; - - username = mkOption { - # For `nsupdate` username contains the path to the nsupdate executable - default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate"; - defaultText = ""; - type = str; - description = lib.mdDoc '' - User name. - ''; - }; - - passwordFile = mkOption { - default = null; - type = nullOr str; - description = lib.mdDoc '' - A file containing the password or a TSIG key in named format when using the nsupdate protocol. - ''; - }; - - interval = mkOption { - default = "10min"; - type = str; - description = lib.mdDoc '' - The interval at which to run the check and update. - See {command}`man 7 systemd.time` for the format. - ''; - }; - - configFile = mkOption { - default = null; - type = nullOr path; - description = lib.mdDoc '' - Path to configuration file. - When set this overrides the generated configuration from module options. - ''; - example = "/root/nixos/secrets/ddclient.conf"; - }; - - protocol = mkOption { - default = "dyndns2"; - type = str; - description = lib.mdDoc '' - Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols). - ''; - }; - - server = mkOption { - default = ""; - type = str; - description = lib.mdDoc '' - Server address. - ''; - }; - - ssl = mkOption { - default = true; - type = bool; - description = lib.mdDoc '' - Whether to use SSL/TLS to connect to dynamic DNS provider. - ''; - }; - - quiet = mkOption { - default = false; - type = bool; - description = lib.mdDoc '' - Print no messages for unnecessary updates. - ''; - }; - - script = mkOption { - default = ""; - type = str; - description = lib.mdDoc '' - script as required by some providers. - ''; - }; - - usev4 = mkOption { - default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '"; - type = str; - description = lib.mdDoc '' - Method to determine the IP address to send to the dynamic DNS provider. - ''; - }; - - usev6 = mkOption { - default = ""; - type = str; - description = lib.mdDoc '' - Method to determine the IP address to send to the dynamic DNS provider. - ''; - }; - - verbose = mkOption { - default = false; - type = bool; - description = lib.mdDoc '' - Print verbose information. - ''; - }; - - zone = mkOption { - default = ""; - type = str; - description = lib.mdDoc '' - zone as required by some providers. - ''; - }; - - extraConfig = mkOption { - default = ""; - type = lines; - description = lib.mdDoc '' - Extra configuration. Contents will be added verbatim to the configuration file. - - ::: {.note} - `daemon` should not be added here because it does not work great with the systemd-timer approach the service uses. - ::: - ''; - }; - }; - }; - - - ###### implementation - - config = mkIf config.services.ddclient.enable { - systemd.services.ddclient = { - description = "Dynamic DNS Client"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - restartTriggers = optional (cfg.configFile != null) cfg.configFile; - - serviceConfig = { - DynamicUser = true; - RuntimeDirectoryMode = "0700"; - inherit RuntimeDirectory; - inherit StateDirectory; - Type = "oneshot"; - ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}"; - ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf"; - }; - }; - - systemd.timers.ddclient = { - description = "Run ddclient"; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = cfg.interval; - OnUnitInactiveSec = cfg.interval; - }; - }; - }; -} diff --git a/modules/default.nix b/modules/default.nix index 695f573..2f04d7e 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -5,13 +5,13 @@ }: { flake = { nixosModules = rec { + acme = import ./acme; adb = import ./adb; arduino = import ./arduino; audio = import ./audio; bluetooth = import ./bluetooth; core = import ./core; crypto = import ./crypto; - ddclient = import ./ddclient; desktop-extended = import ./desktop-extended; docker = import ./docker; gaming = import ./gaming; diff --git a/modules/invoiceplane/default.nix b/modules/invoiceplane/default.nix index a29a5ca..d7b21c0 100644 --- a/modules/invoiceplane/default.nix +++ b/modules/invoiceplane/default.nix @@ -226,7 +226,7 @@ in }; options.webserver = mkOption { - type = types.enum [ "caddy" ]; + type = types.enum [ "caddy" "nginx" ]; default = "caddy"; description = lib.mdDoc '' Which webserver to use for virtual host management. Currently only @@ -358,5 +358,25 @@ in }; }) + (mkIf (cfg.webserver == "nginx") { + services.nginx = { + enable = true; + virtualHosts = mapAttrs' (hostName: cfg: ( + nameValuePair "${hostName}" { + locations."/" = { + root = "${pkg hostName cfg}"; + extraConfig = '' + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}; + include ${pkgs.nginx}/conf/fastcgi_params; + include ${pkgs.nginx}/conf/fastcgi.conf; + ''; + }; + } + )) eachSite; + }; + }) + + ]); } diff --git a/modules/printing/default.nix b/modules/printing/default.nix index 9706aaf..e542f44 100644 --- a/modules/printing/default.nix +++ b/modules/printing/default.nix @@ -21,16 +21,4 @@ ] ++ (if (pkgs.system == "x86_64-linux") then [ pkgs.cups-brother-hl3140cw ] else []); - - networking.hosts = flake.self.lib.addLocalHostname ["cups.local"]; - - services.caddy = { - enable = true; - extraConfig = '' - cups.local { - request_header Host localhost:631 - reverse_proxy unix//run/cups/cups.sock - } - ''; - }; } diff --git a/pkgs/caddy/default.nix b/pkgs/caddy/default.nix index 1afb822..4d0482b 100644 --- a/pkgs/caddy/default.nix +++ b/pkgs/caddy/default.nix @@ -36,7 +36,6 @@ in buildGoModule rec { src = fetchFromGitHub { owner = "caddyserver"; repo = pname; - # https://github.com/NixOS/nixpkgs/blob/nixos-21.11/pkgs/servers/caddy/default.nix rev = "v${version}"; sha256 = "sha256-xNCxzoNpXkj8WF9+kYJfO18ux8/OhxygkGjA49+Q4vY="; }; diff --git a/secrets/hosting-de-acme-secrets.age b/secrets/hosting-de-acme-secrets.age new file mode 100644 index 0000000..a9d4ea8 --- /dev/null +++ b/secrets/hosting-de-acme-secrets.age @@ -0,0 +1,22 @@ +age-encryption.org/v1 +-> ssh-ed25519 8bHz7g 0fwjxxgvY3S/2oKgc+RxTWA9AuA7oS1MnMAqG0AZ2x4 +LReuCNO8r5UMlipXheXT1SLQKlwGMC8Gpu8VtAUI3/4 +-> ssh-ed25519 n71/yQ fiv1s9XA/ATrK/s8OkenXCCp3mUhn8gzp3I3S8h7BG4 +XhzPW7aFm41nzr7ZaBSzS2qfQoVttgODXUqPaF+FvPY +-> ssh-rsa kFDS0A +gLhS2ce3jBP1UbJJt9dJ/4SmkNiL7LBuUik8Zq1WflxHlMqrRslb56Uuc4BJHTlp +KGt2zrzRRNEdSJgzJfK+AT1AHCza++C96ZZsxfhDexL/Gx7Lm6Bwp+D/XR9KYxEn +UF511OWWjoCbDWldUQr17y5cbiG4z+VlMMOuKox8RWHymG0ABe+kZEOg1GBZ/bLj +tUwgxdK/uGGI2/AJIs4tiiemiNo2cMup7fe+wBPqq1b/TcxxTGHfWuJopp3ISl5G +Ov7o7/HVCyemFSlBQTF1K3t68rw4a0ZkMcX86RpEtbiRFrEhS2IuGXnwteEnypER +OiafhbdIh56TnTvCKuxnTJdG1JobM1c15l9FhownRjKMwVjeQb39LKvQlJAkzGOr +eBtkGu42p8UGNpltTawKgWNOTsZNy5BYHvHvS73SXDcuwGVhhP5fbeImUgeBRUyT +2DWoK+vethjGL+tX+NNqeKM/Xr+k8QUGq+qehB6UoiTLL4NiwujeO230b9Afsh2/ +xtzDTMueqTUVTzxA0GbIRSXCKCfDNifdJh6+ebnvizx5NarANvMNlP6sfBQ5gANf +N20IhtgnOxQ5l5URWqK/A5vk+cpP2GGkAGoW40m31gyug9AtuguNpEVRpLHJVtRn +DpHh5k0EMzWz7iQhYIVavLTh3AtxofLg4wp/cQEkcQg +-> OC`4fNF7-grease +YhGHL0+BVF6h6InKVlS3 +--- NXJPtlRt28CuwumxjLQI8qY6i5GSbBPbBANxLpHmsHE +‹×€aqê802.æ–´"R=SÙÚ È³ ËŽlhª@ÄK¢¨F-³ö=;ž @ìåúiÕ°+àÚr¸£àð;4wI}ÕiËÑèõ1o —™ +„¼¬ƒN¼ÂÜòG>Tõ3–Œ7 \ No newline at end of file diff --git a/secrets/hosting.de-api.key.age b/secrets/hosting.de-api.key.age deleted file mode 100644 index fa629c7eb05c7feec4f2b3c56fed44913c9d79fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 908 zcmWlXxy$5q06?uof~Aek?tqOFW+%zy8WAzaOfr*9^2>d!AUS@MWR9GZ;~N)Yv2zz2 zLD<4_JB97kzeKRIu#&~rVjI8hACFhOD1&oYChc8)EvoWS)njQg1|8LjATJ zurlXV-;j2&xC}m>Ibwt5Rd6k9Z0#1_tvmL&{s1&Cp;Q})5Gfi%zf*lr?UyoD=jqK) zlO4@el4ONFZGh0pbY-{-E1GmgaTH_2&LrPk?O8d{%?={wTAA7l&6Xo6u9xDf$q!^a zc(owht_;=f(39kDhYENg4=|=k&!hlG;VRuJylJdu16j(Lg*g^0ofqk(>{&7IEobei zwR58h<>;d0DWNFb3`QAW>ju6Z9rQE}=osx`=hYOVc~@1iWm@q|CWcnCn%wTl4MplI zjcS)B+Yrg}dLNQyZ$JJ>tB>uReLT-v7n$zwRb6euEhlz6hhFrL%UtRaTp9rS7&M>=}3GZOjb!NY&uUthn5!+$Soxq9^G z?~h-8`{ySQV$fILJwCki=4JD~_K#UpPYjv=@cD1=;Xi#{|MB$2U&GIz6;D6?@zqx! T+`{kMSH~ZUcb`4!+TZ^NtQ|26 diff --git a/secrets/mail@b12f.io-password.age b/secrets/mail@b12f.io-password.age index 0e3ead3b6391173767afa06d95e4a05b24d489a6..2fd9e93bbd2f7d3d77e60500f504773ec9b0ce5f 100644 GIT binary patch literal 1058 zcmXxiy9?uV0Kjo^a0oXa$_;jMFx2GNJPUf5N7KBTG;P|X0foGuP1C&EW)MMia`JJ5 z+r2(9hw`~Y9V0z}#@9zp zlZ?Y)LJ(j&Dk*?mj1jvbsU55ZY_jLf4Hr^2ox2nJw4Afs`3Y0Srp;2tzKE+G)WDUM zV>7_j$YqbuQFpJq)grlw{Afp)imw(=0@!!<|lxicnydTh%)HcF7cK=|J$ZhT+a9w7D&v`>9}V zunzBOMG_oV6g^cSjA6m^Zol>gl8VzUHXBF*FHbQtIlB=GdDOWRD1h2TBxy*#H;#B+ z@5d&K%G_;t-8E2S?ozDh_8t4qQ*yZ0Kvz+s87C_l@`^m=OI=d zLaYr~qg)RgB+pd7!2+|YdQd1?r62@Ugyd!^CnC+YtpLoU&M{o&R8spx>Mdo!fuI`TUMC%Ek;S+ya zcxs|@C)He$lzlW+-JDy@MyYm?-$yVmwmr4lrYNrsX>dfmmJX#oAM=gPbx|bkF6Xuj zX8$=(rEy$`ZWBU53?e`pqEG@ASGd(9Nuln6f^g7IAXq06JKw!Y2^~Uv64HRS~^|H5+oA)2`TqEbn}^Cjt@S5QPy91{`M1J;BS0* zbJX4a=uZCm{ZHQi^`#r%B9H$**e}!WmAC)COTGK)l|OEqpS{NY^xMPgsmGrD>fXaI yAAJ4Kd-uqn-@Nhqvwtn`|M2S77he1kq1q3wUw!MF^453OwWsgAbL-ll-~S7>xolkk literal 1184 zcmXxi$*bH10Dy53p^tzdDyX+OnItchWs*$Ro|4HjlVq~bB$=Wp zSVU1QUPOwhsI3cL1Zx#U#0nxFJctJuEQlhlwWkU$i1pxK@EyLN9e5otnHOzVEz{&! zS_IIh3v?dbEz3p6b9)HV#dj053$@ZNYO>Cl$Y9P*O9$&?D^?~v$AWW*4XxS8Ytcfm z1CJE-5?Bk44l1Bc&RWyqFp_Z)vz&8W4ABJXpc7=gveCXRMKkIDT2b1&5j9d^0-!ZI zl0nhZRACk}oSgA)If$UqTIwg57tOYr*2|?*6-`-PKq{25{T^lbDGm=OnK2pyIHMAj zD3iS9*YPp~#fmkXVdJzKPn%d@_PTK|hXmVNl+|+77%f7VFgsHy+zQr)Z2<{^LQcS~ zvBcIQUh&l+D?ELcRj8mT1X)kC)EOo8wu>}DxC-0BrldSd<1Jc2#jM}~POHU~&Ol@t zDxlknjnf+XEoEB4$y zne2)hjPisriMwVwYUzb&!Hy7*)09BcNP-h#yzECTlTOP)n*R)oorO8|e?q^TSuin8a)sU(XG z+cP#+FEFXumRv_xMJn+qpky8?n0-m$)2HdTGoK`-7Mq6pYu8-35cK zkp5KjM&`73(wZ(PT?;DsKgahTzsDn%C(KSzcy{US)(;;!7TpbSjR8_>tzdySOyLPx zdNs`P>LSh6Et2UmsO3VJ388sgv4vM~d6^S~QPOa@9j-~n0xE3fHioY?`jRBJQj6fs z%;}ke8-+NQ4~f=i-E3b4I-Sn$ykJsd9lET*t1CuR*GMy1Py4DDL|R?$E#zj);-#=P zQw$g%y?XI0{p{t>FYNsF(icy^`ocepL)6a6lj==Z&K{1`^A|rmdhPW4f8KFtde?zZ z4*kyF_>ggq5HB2$K7IU&^>YVu_lGxrc`Id3$KRZL2LBT20{8v;uDbIR6&$?u{QZw@ z4|cA6clq$8_pZKj@VB2|JJw(2r*8QA+iLIu`Q(Rl^8A_GK05LC&5!P!5{^6@zj^ET b@4SA?iyuGr$KT7BubtWX@pk-?2c&-inEs;z diff --git a/secrets/secrets.nix b/secrets/secrets.nix index ef851c8..ecce4a9 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -42,7 +42,7 @@ let ]; in { "dyndns.key.age".publicKeys = pieKeys ++ baseKeys; - "hosting.de-api.key.age".publicKeys = baseKeys; + "hosting-de-acme-secrets.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys; "droppie-ssh-root.key.age".publicKeys = droppieKeys ++ baseKeys; diff --git a/terraform/b12f.io.tf b/terraform/b12f.io.tf index f652966..dca4dd3 100644 --- a/terraform/b12f.io.tf +++ b/terraform/b12f.io.tf @@ -112,7 +112,7 @@ resource "hostingde_record" "b12f-dkim" { zone_id = hostingde_zone.b12f.id name = "default._domainkey.b12f.io" type = "TXT" - content = "\"v=DKIM1; k=rsa;\" \"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8Wr\" \"nxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB\"" + content = "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8WrnxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB" ttl = 300 }