summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Stachurski <jakub@wilkuu.xyz>2026-06-09 15:08:54 +0200
committerJakub Stachurski <jakub@wilkuu.xyz>2026-06-09 15:08:54 +0200
commitd033778fca80f564c8897df4b79664245a95be84 (patch)
treed36f359b42c8326f74a24312e8bbe29c546b8adf
parent898d1709a1aee731d037e8f8739bc9b72ef87b71 (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.nix28
-rw-r--r--secrets/omega-relay/desec.yaml44
-rw-r--r--services/ddns.nix109
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;
+ };
+}