diff options
| author | Jakub Stachurski <jakub@wilkuu.xyz> | 2026-06-09 15:08:54 +0200 |
|---|---|---|
| committer | Jakub Stachurski <jakub@wilkuu.xyz> | 2026-06-09 15:08:54 +0200 |
| commit | d033778fca80f564c8897df4b79664245a95be84 (patch) | |
| tree | d36f359b42c8326f74a24312e8bbe29c546b8adf | |
| parent | 898d1709a1aee731d037e8f8739bc9b72ef87b71 (diff) | |
feat: Desec dynamic DNS and DNS01 acme certs
This feature will allow for a more flexible DNS management through
Desec as opposed to OXXA/Ferox.
| -rw-r--r-- | hosts/omega-relay/default.nix | 28 | ||||
| -rw-r--r-- | secrets/omega-relay/desec.yaml | 44 | ||||
| -rw-r--r-- | services/ddns.nix | 109 |
3 files changed, 178 insertions, 3 deletions
diff --git a/hosts/omega-relay/default.nix b/hosts/omega-relay/default.nix index 8c5bfbe..b1489a5 100644 --- a/hosts/omega-relay/default.nix +++ b/hosts/omega-relay/default.nix @@ -18,6 +18,7 @@ ../../services/continuwuity.nix ../../services/bulwark.nix ../../services/mail2.nix + ../../services/ddns.nix ]; addons = { @@ -39,18 +40,31 @@ chawan ]; + sops.secrets."desec/token" = { + sopsFile = ../../secrets/${config.networking.hostName}/desec.yaml; + mode = "0440"; + }; + wilkuu.services = let isVM = config.addons.virtualisation.isTestVM; in { + desecDyn = { + enable = true; + domains."wilkuu.dedyn.io" = { + doWildcard = true; + enableDDNS = true; + enableACME = true; + }; + tokenSopsName = "desec/token"; + }; test_endpoint = { enable = true; domain = if isVM then "omega-relay.local" else "omega-relay.wilkuu.xyz"; doACME = !isVM; port = 8080; }; - mail = { enable = true; startupMode = "normal"; @@ -64,8 +78,6 @@ extraConfig = []; extraCreate = []; }; - - bulwark = { enable = true; jmap_servers = [ "mail.wilkuu.xyz" ]; @@ -158,6 +170,16 @@ tryFiles = "$uri $uri/ =404"; }; }; + virualHosts."wilkuu.dedyn.io" = { + enableACME = true; + forceSSL = true; + useACMEHost = "wilkuu.dedyn.io"; + root = "/srv/www/wilkuu.dedyn.io/"; + location."/" = { + index = "index.html"; + tryFiles = "$uri $uri/ =404"; + }; + }; }; networking.hostName = "omega-relay"; diff --git a/secrets/omega-relay/desec.yaml b/secrets/omega-relay/desec.yaml new file mode 100644 index 0000000..2d409c1 --- /dev/null +++ b/secrets/omega-relay/desec.yaml @@ -0,0 +1,44 @@ +desec: + token: ENC[AES256_GCM,data:NM3nh8FNd0Xy0Iw4s8N0xdKR7jOIlnnw6S2oDg==,iv:ETgIEHoV4fboFZDALT3jlxFppscaurgpLz9LPRI+xXc=,tag:XAzBbuOjR0p24/8Vutwsfg==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpVUd3WFQ2a3l1a0tFd0Zn + aVlsTXExSHZrbGZXNDA5V3NaMW9IU0tDaDE0CmRUSjlMSUdmL0tkME8wOTJuY1hE + bXRmNWZ2ajErRzM2VTdHdmoyak1jZkkKLS0tIFFpbFF5cE55M3JHYXlxanpHMTVU + NlhBQTJHbmI4ZE1NM3FqWGdzbm9SMWMKT44qiiDAZM6IBtImTsjfWaz/+mClLH2n + eyNaTqc3gONiOXNY1OPg1cN0u0lB/+IrnL1JuHlsZqQlvO5sed1o0g== + -----END AGE ENCRYPTED FILE----- + recipient: age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaQytWVlMyM3NYZ2JNUHB4 + UEwxNVdGVTUzSjM1TG84Tld5cTJOVmQwR0hVClNnRm5yb20xY0ZQWkRNdm1HY2VD + SVFwcEJqRzY1Smp1TjRLdzR5dXNGSTgKLS0tIEQ1ZnYrN1oybWhwc0NnMm1qUE5T + UFFjTmNoR0NtaEkzb24wUjJVTDUxVG8KczBdUI8Fb5UocO5W387rIsreajkh2CNd + B3DE2aAgf7XLgH8LVVy2qSTtcnR0w1qLtLjivy1f7Urwy8SKspPiiw== + -----END AGE ENCRYPTED FILE----- + recipient: age1n20lpr5x2aep3l3ta62x2c9cnuz3m495f2dmguqs08ezlqgu4vwsqfskyg + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqZHVIWEdWQzgrbmtWVkUr + bkhuTCtiWmMxUVJ0L3BkRnJHVS9IWVRRcjFjCjFRbTdjdnJVUWJXV1hRVU5Tb25H + Y2FOU3k0MHN2K0ZvdWFQaTdCUmo5T1kKLS0tIC9VOGV0V1RYeVRCQVp3QTRRTURY + VWlMOGJxOGtyeWF3Q2NjaUhVZmFUT1UKd1p03qvlGNbTxpIYYHgFa4RNm5LtdGs/ + EhfcLb3Kovqf6gehuQY4ZJIs3fBoHjdNR17N/DIG5+rOXN2ntsking== + -----END AGE ENCRYPTED FILE----- + recipient: age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4 + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYREZKYjN1L2tGVm9FY01n + YnlaZmNsMkNpck5LTkFkcmJnandiRDEzZFdrCnJINllRMEViNzlJckVzbzBtRm8r + OGQyL0c5MFFRREZjTU1rOUN0N3FDQzQKLS0tIFlBdExFWlJFVVN3aDFjS1BsWjNl + UVRlQjZjZnRpcTluaDh6eU8xNC9NdWsKD4eqsbk/rZyv75iakydKtO65ZzMXsnvu + Wfcz+DURBsqrPGkUhdss8kwzD+ngrjBvLr9e4u/ew94rFiSzTCNpPA== + -----END AGE ENCRYPTED FILE----- + recipient: age18u22lycxtcasvlm298qwzwuc97z56kkhw8ssy9yx4gzlkskuwpyqdz80g4 + lastmodified: "2026-06-07T19:57:02Z" + mac: ENC[AES256_GCM,data:8n3YQFFeg7BMuxSiyEMdXN7jMVwT04sbpswfjfNLgpb6/JaAwLwLyfqnJwDqn9bdU2QpeXtpWCauSbzAxZ6n6MrkWDc3ZMXdGn/YqrJW0iBShAu0snfcMNrG0/av3JoD0Xf5A6JhLnhs8mxw+gCT36kItJxdEKtDV3jESTDxMW4=,iv:iEHvyqFXnrHPrnlMUMKnTlnLEPK8Sms/3Cxbj/Pka0k=,tag:g8lMSdCyJPnq9IMxVw3KuQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/services/ddns.nix b/services/ddns.nix new file mode 100644 index 0000000..08135e1 --- /dev/null +++ b/services/ddns.nix @@ -0,0 +1,109 @@ +{ lib, config, ... }: +let + inherit (lib) + mkOption + types + mkEnableOption + mkIf + ; + + cfg = config.wilkuu.services.desecDyn; +in +{ + options.wilkuu.services.desecDyn = { + enable = mkEnableOption "Dynamic DNS management through desec.io"; + + domains = mkOption { + type = types.attrsOf ( + types.submodule { + options = { + # TODO: Decide on defaults + doWildcard = mkEnableOption "requesting a wildcard zone/cert"; + enableDDNS = mkEnableOption "setting A/AAAA records for the domain"; + enableACME = mkEnableOption "requesting certificates for the domain"; + }; + } + ); + description = "List of all domains to be managed through desec.io"; + example = { + "example.dedyn.io" = { + doWildcard = true; + enableDDNS = true; + enableACME = true; + }; + }; + default = { }; + }; + tokenSopsName = mkOption { + type = types.str; + description = "Sops-nix secret name"; + }; + }; + config = + let + ddns-updater-configs = builtins.concatLists ( + builtins.attrValues ( + lib.mapAttrs ( + _name: dcfg: + ( + lib.optional dcfg.doWildcard ({ + provider = "desec"; + domain = "*.${cfg.domain}"; + token = config.sops.placeholder.${cfg.tokenSopsName}; + ip_version = "ipv4"; + }) + ++ [ + { + provider = "desec"; + domain = cfg.domain; + token = config.sops.placeholder.${cfg.tokenSopsName}; + ip_version = "ipv4"; + } + ] + ) + ) (lib.filterAttrs (_: dcfg: dcfg.enableDDNS) cfg.domains) + ) + ); + enableDDNS = ddns-updater-configs != [ ]; + + acme-certs = ( + lib.mapAttrs (dname: dcfg: { + domain = dname; + # TODO: Make a special group so that you can give access to other stuff. + group = config.services.nginx.group; + dnsProvider = "desec"; + environmentFile = config.sops.templates.acme-envfile.path; + extraDomainNames = (lib.optional dcfg.doWildcard "*.${dname}"); + }) (lib.filterAttrs (_: dcfg: dcfg.enableACME) cfg.domains) + ); + + in + mkIf cfg.enable { + users.groups.ddns = mkIf enableDDNS { }; + sops.templates.ddns-updater-config = mkIf (enableDDNS) { + mode = "0440"; + group = "ddns"; + content = lib.generators.toJSON { } { + settings = ddns-updater-configs; + }; + }; + sops.templates.acme-envfile = { + content = '' + DESEC_TOKEN=${config.sops.placeholder.${cfg.tokenSopsName}} + ''; + }; + systemd.services.ddns-updater.serviceConfig = { + SupplementaryGroups = [ "ddns" ]; + }; + services.ddns-updater = { + enable = enableDDNS; + environment = { + SERVER_ENABLED = "no"; + PERIOD = "60m"; + CONFIG_FILEPATH = config.sops.templates.ddns-updater-config.path; + }; + }; + + security.acme.certs = acme-certs; + }; +} |
