diff --git a/nixos/access/kanidm.nix b/nixos/access/kanidm.nix index 0eca0e48..3e8e2eaa 100644 --- a/nixos/access/kanidm.nix +++ b/nixos/access/kanidm.nix @@ -13,7 +13,7 @@ let inherit (config.networking.access) cidrForNetwork; cfg = config.services.kanidm; access = config.services.nginx.access.kanidm; - proxyPass = mkDefault "http://${access.host}:${toString access.port}"; + proxyPass = mkDefault "https://${access.host}:${toString access.port}"; locations = { "/" = { inherit proxyPass; @@ -50,12 +50,28 @@ in { type = str; default = "id.tail.${config.networking.domain}"; }; + ldapDomain = mkOption { + type = str; + default = "ldap.${config.networking.domain}"; + }; + ldapLocalDomain = mkOption { + type = str; + default = "ldap.local.${config.networking.domain}"; + }; + ldapTailDomain = mkOption { + type = str; + default = "ldap.tail.${config.networking.domain}"; + }; port = mkOption { type = port; }; ldapPort = mkOption { type = port; }; + ldapEnable = mkOption { + type = bool; + default = true; + }; useACMEHost = mkOption { type = nullOr str; default = virtualHosts.${access.domain}.useACMEHost; @@ -68,6 +84,7 @@ in { host = mkOptionDefault "localhost"; port = mkOptionDefault cfg.server.frontend.port; ldapPort = mkOptionDefault cfg.server.ldap.port; + ldapEnable = mkDefault cfg.server.ldap.enable; }; streamConfig = let inherit (config.security.acme) certs; @@ -79,7 +96,7 @@ in { ssl_certificate ${cfg.serverSettings.tls_chain}; ssl_certificate_key ${cfg.serverSettings.tls_key}; ''; - in '' + in mkIf access.ldapEnable '' server { listen 0.0.0.0:389; listen [::]:389; diff --git a/nixos/acme.nix b/nixos/acme.nix index f8eb9588..1600f085 100644 --- a/nixos/acme.nix +++ b/nixos/acme.nix @@ -30,7 +30,7 @@ in { dnsProvider = mkDefault "cloudflare"; credentialFiles = { CLOUDFLARE_EMAIL_FILE = config.sops.secrets.acme_cloudflare_email.path; - CLOUDFLARE_API_KEY_FILE = config.sops.secrets.acme_cloudflare_api_key.path; + CLOUDFLARE_DNS_API_TOKEN_FILE = config.sops.secrets.acme_cloudflare_token.path; }; }; }; @@ -46,6 +46,6 @@ in { path = accountDir + "/keys/${cfg.defaults.email}.key"; } ]; acme_cloudflare_email = acmeSecret; - acme_cloudflare_api_key = acmeSecret; + acme_cloudflare_token = acmeSecret; }; } diff --git a/nixos/ddclient.nix b/nixos/ddclient.nix new file mode 100644 index 00000000..54931519 --- /dev/null +++ b/nixos/ddclient.nix @@ -0,0 +1,54 @@ +{ + pkgs, + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkMerge mkDefault mkAfter mkForce; + cfg = config.services.ddclient; +in { + services.ddclient = { + enable = mkDefault true; + quiet = mkDefault true; + username = mkDefault "token"; + protocol = mkDefault "cloudflare"; + zone = mkDefault config.networking.domain; + use = "no"; + domains = [ ]; + extraConfig = mkMerge [ (mkIf config.networking.enableIPv6 '' + usev6=webv6, webv6=https://ipv6.nsupdate.info/myip + '') '' + usev4=webv4, webv4=https://ipv4.nsupdate.info/myip + max-interval=1d + '' ]; + passwordFile = config.sops.secrets.dyndns_cloudflare_token.path; + }; + systemd.services.ddclient = mkIf cfg.enable rec { + wants = [ "network-online.target" ]; + after = wants; + wantedBy = mkForce [ ]; + serviceConfig = { + ExecStartPre = let + inherit (config.systemd.services.ddclient.serviceConfig) RuntimeDirectory; + prestart-domains = pkgs.writeShellScript "ddclient-prestart-domains" '' + cat ${config.sops.secrets.dyndns_ddclient_domains.path} >> /run/${RuntimeDirectory}/ddclient.conf + ''; + in mkAfter [ "!${prestart-domains}" ]; + TimeoutStartSec = 90; + LogFilterPatterns = [ + "~WARNING" + ]; + }; + }; + + sops.secrets = let + sopsFile = mkDefault ./secrets/dyndns.yaml; + in { + dyndns_cloudflare_token = { + inherit sopsFile; + }; + dyndns_ddclient_domains = { + inherit sopsFile; + }; + }; +} diff --git a/nixos/secrets/acme.yaml b/nixos/secrets/acme.yaml index 6340f228..6ec95637 100644 --- a/nixos/secrets/acme.yaml +++ b/nixos/secrets/acme.yaml @@ -1,7 +1,7 @@ acme_account_key: ENC[AES256_GCM,data:2UobeSLGw/GMXEeB+fdjG4jnDhWtm1j3U5SOZYwwJEF70g5z9DW1r2yqkj2/nBBgYUR6UvDo2f66iRCurRJtBPIf3KOOwtef1+MiD7QARgqDQoKCf8IUTS4SittHC0nYLFp08uZ3zXI8P4aqXwZdQT+DH5ARtZo4CaRWXa7JW2fkCe5v/Myz7LhvDZmpcitvPFTOpsZoHnUdPC2/cnTdUw/h2rEt1iNYVnchhHAt+AF9IMLZT5USotc9H4vpfXAqTcMwgI6tHvsJnnxorSVg7yfSYJUEiaFgYaqO6sITZffW+CXeuWdrg+EZB03VHmxJ5fwee2tCDyogSQyB6fDcnySK/bdzZhJBIE5fsfr9D7xuR6bUptuqjR/5STnqCYGO,iv:wJe/ta9hxeppqUI5hBErBS6rffhki8eiogSGwPDI/nE=,tag:gzJz4guO/hz9zxwSbs85Uw==,type:str] acme_account_url: ENC[AES256_GCM,data:wsZvniUCTS8EcjCCKzUjKgeTCUiNNS5nGI4jq1N7R9tzr+Ng2o2rpMLkXsQhVHHlEA38pvLA/+pe,iv:XxS2kzFEWA33QQR+RkHQo0JN22MIliETYOqNSnS7rTc=,tag:GOJ9sgRx5u+7siCN7Fb3LQ==,type:str] acme_cloudflare_email: ENC[AES256_GCM,data:AwOryq31gjMWyEbEOA==,iv:SHNpv3d8fj47o0t/k3Q5JwjJwlA+UKW8pJC5uUJjuJw=,tag:AZbV8wciD0b6o3lcRnywcQ==,type:str] -acme_cloudflare_api_key: ENC[AES256_GCM,data:nadEPYM6QTgRiP2gmER1wN9tPBiW6ToKaIcIOGfQkBXZvzrlMw==,iv:a+3ujE4Cobvh3VSXmSH4iLsXggM5m4uOPj8ygQvPRGc=,tag:h+ZwCPUiMca42nK1JybLFQ==,type:str] +acme_cloudflare_token: ENC[AES256_GCM,data:tpxnRCQHbO0gXRTL8oIcHnoXSsiT2/vFkRkavIqfh2LU5hPBtn4LkA==,iv:zvs1qsk7BVXvVTNkz9LQivhPcoXoO45fnqdp5p3ntNI=,tag:Qd85Akl3dhj/DknmfRyjTA==,type:str] sops: shamir_threshold: 1 kms: [] @@ -36,8 +36,8 @@ sops: TEdEOGdyQ2tBUEkxS0h0Yk1UbjJDWFUKtLA3MJAWMjfd03rKBaW3aIIMJS/OkRqL Tu4JrcL5Lw/Tj7SU0dwxTsp+fGHuXsvQSO2z9BQmy9h7k7hSgSrRSw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-01-23T18:41:11Z" - mac: ENC[AES256_GCM,data:45SskHZXCnEMERHc3R4hD4cQ/ihWnUdFunTPgrBGYdo2fyCMbWyAdSQOMl0u6FkC+NbG66lzgBVXBPnNfw2GX8iVLJ3VQyRW5WA4YW1x1wLbocO+XkMtJmwtiPiOrJfjbs3mZfWmt5hQZI/gQsonQrccD9/UxWip+Hal4prhY50=,iv:1ka9lW88NoaAEgRp5TxC2L6q+kVR25HxxQGVsPlaGho=,tag:gPYKvKcZp5k4TA1Dfl8C8Q==,type:str] + lastmodified: "2024-01-23T21:15:22Z" + mac: ENC[AES256_GCM,data:zf23GtkwB7ueKrJSp3pkh/uCivf+P8lX2f1o0RvoZ/LxRjZfren6qeM5I6iL20T0gK5tlgBgTUE8SNyDiWMHmTOqjE9ncXl4UegvwJliEHSx3Oplc7XrseDkHQ+HmFbOmZXV4sKqxhtVgi2UCkv27CFFQC7ZQuxDmgFooXl6YHQ=,iv:Ooc71yVG8iRwxf2yV4k/TSIc+KXjfhqD3gASKe+uT9A=,tag:MqYU0JjNVTFAjK61+/ieRA==,type:str] pgp: - created_at: "2024-01-23T17:18:24Z" enc: |- diff --git a/nixos/secrets/dyndns.yaml b/nixos/secrets/dyndns.yaml new file mode 100644 index 00000000..8a181b8d --- /dev/null +++ b/nixos/secrets/dyndns.yaml @@ -0,0 +1,78 @@ +dyndns_cloudflare_token: ENC[AES256_GCM,data:Rq9fGq9QHBxZkeoXMFSR7AHJ1NHQavcJTEbDWt6hUs3/HRf75FecIg==,iv:Lo6C5QjjkKhKt69J3kFZZC4H+NGMFhI6hHBEFuV31Vw=,tag:v69iw1KbfEjyjnl5j17q5w==,type:str] +dyndns_cloudflare_record_a: ENC[AES256_GCM,data:pcgYAVkH2PwJrdk/2BSAZRPagzGjIdY8QfyFmNL6r8I=,iv:PCKesuu9it/jhog2MdZGGppmsPOORj7wY6dep7pRJxU=,tag:heGDCnq5h+zIPJ2nhwtN0A==,type:str] +dyndns_cloudflare_record_aaaa: ENC[AES256_GCM,data:2/wCICJhTlN5eBAiwRZYPNQ6EL3TOHivWVn9oXAXuWg=,iv:DyWj3NhKWyvwy6hLv4kqdWUtLi92M9fJjFOoDCqNE6E=,tag:tM3iJAt0NN2twnL3/M6B4w==,type:str] +dyndns_ddclient_domains: ENC[AES256_GCM,data:Ic4oUU+U+QY5ngQF5e7ZbmwkOWA=,iv:h0mnRzHqVaRrenh+9nTlgE7YIYzoILuwMypEKmzaPng=,tag:3h0IeQHMZ495ZQKj4Wy1Dw==,type:str] +sops: + shamir_threshold: 1 + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age12ze362pu5mza6ef9akrptr7hfe4auaqul4rkta7kyy2tnrstqensgmujeq + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBETlJsejAwYnpFRnhDbERM + d0cxcTZjMkp4MDFkTk8yZyt1RVZ5SUJNbEZFCkVSS0k4MGhqK2NzZjdlcCtWTDBw + TzgzQnloRmZXZll6UE1JOEdxaUxvbjgKLS0tIGNMdmw4WmhtblFzcHBHZXFRVFJr + NXZvN2xVVXdGaE10aVZ0NXhGT01OTlEKIoPZUHbWi12tiQ5te5K4ttoICk5k2ZBJ + htYByCo+7/w8qet0HrrxaXNy7z1dm86aipAFI3rlpdVWctnBO7jr5A== + -----END AGE ENCRYPTED FILE----- + - recipient: age1a2quf2ekkj94ygu7wgvhrvh44fwn32c0l2cwvgvjh23wst90s54szdsvgr + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwa1RQcWQrcjNRa2RoMWhq + WEQzZDJDSVlZTkZzcGcvSEZySmpkZi93MzJjCjZuYzBZUmVPRTFOZCtZR0tEL3dO + L2xhWHZUVmM2K0ZjaWpjem00L3BLLzgKLS0tIFVJalNsNjZIT3dYRnNaWVBUdC9y + cU9KU25rOHBVZDVDNS9TVG5qTFhNRFUK/MVX3YnjN83/iCIXliidnGVikdQG3Ek2 + lDT5s2jCzf1ENs+0B4kQJJrpz9Gsm1Dn1O3czXdl5StN0U7VXCWRhg== + -----END AGE ENCRYPTED FILE----- + - recipient: age16klpkaut5759dut8mdm3jn0rnp8w6kxyvs9n6ntqrdsayjtd7upqlvw489 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIRGk4cEtnaU91NGlZVUlO + ZTRzQ1NiYkl4My9yUFI1WkRWcUoyWDNjREdZCjFQWGUyaVhRbkQyMjVLU3B2R1hP + RldVSmxaYnBBdVRmZEVucFhzQVVram8KLS0tIHo2UHVXNmFwTmt5TjRKM3VkVGNR + L3IwMW84SUJRNGtOa3FFUWU1QUdwMlkK3deMhJC9PiugMcwFDVZZ9E3FKn4tyi1C + G3b/Rq5xPpfixiQY/Z2bmulDKPxmVijLeqbfDJdX9z3eWjbHFZQ8Og== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-01-23T20:22:51Z" + mac: ENC[AES256_GCM,data:GKlI3gU2or3XWNgQlwRhYUPrPuc7UM+KcSDlQ90gvEY7av9e9KYqk2dtia8xicpKy+TagXg5p0BgkWpIXScUunf2srLwcZF2TH7Ycbhj1SOjlCU+MH2oJs4Qt7QDweXWUspG1YrWy9yS5xknXwd6mCeGAEynqbXq/veuumSAjfo=,iv:LTuguxgxPXf9wrj8QrI8w6JzowLJLQUvkLlI/lbsEuY=,tag:rFA5fN0gCeCB2MFyNDHkNg==,type:str] + pgp: + - created_at: "2024-01-23T19:59:03Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA82M54yws73UARAAqr1vzzQnzPlC9TBsIcfuzuYy+GwAu1Dg66IyhLFsM+id + vzi2V99fyZVqDPn0aoSd5QENzXdsyMqz5Gixb5MeUWiKDG6ommKS1TlypKe0MbH6 + ggnYY+1mORcQ2o4yMc2yhACEfpdlsiycickmWhBR1V8uhr7GH0FeHNCb54LQZ25u + 28W6V2AeMKyGqQ2EWcpMQqQdGyC1bQkPoTar26HkiamoKSLVa5McWATJmgd2a1QT + OHsNw1aE1r1tNacs+Ia6VTqBVM9eLVJVxlAqdfDkd/WWAjoHn3Xmj+7VcrDVJ6HB + 3oJCSiFktLwVflhZS8MEGerNHvp8RvG2AORBQQ1EDUPiR9sW0ROUkwn8LamU92h2 + Jv382pDraSjOeHYfVyW6iK+c8nrtOl2+R5j/qNsejwNR3uo3mjbd5Ayf2hgJA632 + KS6Wg55DXoJO/L1tjXCmry2CVn8fBbY25g+PKUkQ9xlUrOlVcHtlHybJGuYvW+fC + NPM5okLGiqTpidf3J8t251vTzW8AUtB3gmf4dA5Kj2huPo5rbsvwA2MCgLvTCYKL + bGxKfBnPeKT8WE1Ep2fAaRyjxuNRDRM96uCTTnpTGrdss0TGXpZDi3KhExTqJN/z + pgi+PkU2n9YE0GXykDdkoK2M/IsR0n5Mk6Af5Kdzgr1AOHb8j8dRsQ7gxCNdVinS + XgGqFLM4Xzr8Jjq780nWzAcX36Xm5NJVdrv0pa171SDYBOnB3MAOVqYkRiOrDpQ2 + QgvKYF3uAWhPO/bpdelkZkVrFWROpC9nb888LcIkvYc4FePcATZ9jIrfTXugzvA= + =CAIG + -----END PGP MESSAGE----- + fp: CD8CE78CB0B3BDD4 + - created_at: "2024-01-23T19:59:03Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQEMA2W9MER3HLb7AQf/XjK2wv7gxUsVN5l5f1IOF0cOM7sOXNy54sxPUL917kEN + n2xe2jYd+HLe4BzOgQjHrMK3VZcv6lhHi7TqF5SmapK3yB+MBQfM5IwXsfF4wR+g + REZPYgj+EwamSydZ6Dt7j5V8o6HL+UMiMWk3IyNFVyN5Gx7ZQuLrCWrUZMA2FlP8 + 5C3uDYZZIv/NuS5EKAFZJ7lnMBCvDpsiGBmyUP6pMdBq5ZXCegZT4LELbtkAl3Af + 7iWag4pnpWvDo/TLLy+7camf7xRS6Tz6Em7hUdzl+EPGzG830K1duhU/65wKsrfk + zhkoyI3Hx84MsNy4h20oNKTKf19U/SGYt2mOCUrfStJeAZxToUDSiZHvQpmLssjm + 8usBJPfYuu/FYrBhFTlh1YwLaJShr6+NSJv3USngJYJFpOgw7LA0qg75+93gQD3L + w93BrVl28iUt9XO3Yj1zOdfVyASg2z9c4e32x6ZV2w== + =cUs0 + -----END PGP MESSAGE----- + fp: 65BD3044771CB6FB + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index 813ef237..854298f0 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -8,6 +8,8 @@ inherit (lib.modules) mkIf mkMerge; mediabox = access.systemFor "mediabox"; tei = access.systemFor "tei"; + inherit (mediabox.services) plex; + inherit (tei.services) kanidm; in { imports = let inherit (meta) nixos; @@ -17,6 +19,7 @@ in { nixos.reisen-ct nixos.tailscale nixos.cloudflared + nixos.ddclient nixos.acme nixos.nginx nixos.access.global @@ -42,21 +45,32 @@ in { }; security.acme.certs = let - inherit (config.services) nginx; + inherit (config.services) nginx tailscale; inherit (nginx) access; in { ${access.kanidm.domain} = { inherit (nginx) group; extraDomainNames = mkMerge [ [ access.kanidm.localDomain ] - (mkIf config.services.tailscale.enable [ access.kanidm.tailDomain ]) + (mkIf kanidm.server.ldap.enable [ + access.kanidm.ldapDomain + access.kanidm.ldapLocalDomain + ]) + (mkIf tailscale.enable [ + access.kanidm.tailDomain + ]) + (mkIf (kanidm.server.ldap.enable && tailscale.enable) [ + access.kanidm.ldapTailDomain + ]) ]; }; ${access.proxmox.domain} = { inherit (nginx) group; extraDomainNames = mkMerge [ [ access.proxmox.localDomain ] - (mkIf config.services.tailscale.enable [ access.proxmox.tailDomain ]) + (mkIf config.services.tailscale.enable [ + access.proxmox.tailDomain + ]) ]; }; ${access.plex.domain} = { @@ -67,8 +81,6 @@ in { services.nginx = let inherit (config.services.nginx) access; - inherit (mediabox.services) plex; - inherit (tei.services) kanidm; in { access.plex = assert plex.enable; { url = "http://${mediabox.networking.access.hostnameForNetwork.local}:32400"; @@ -78,6 +90,7 @@ in { host = tei.networking.access.hostnameForNetwork.local; port = kanidm.server.frontend.port; ldapPort = kanidm.server.ldap.port; + ldapEnable = kanidm.server.ldap.enable; }; virtualHosts = { ${access.kanidm.domain} = { diff --git a/tf/cloudflare_dns.tf b/tf/cloudflare_dns.tf index 2189166a..1aaf9368 100644 --- a/tf/cloudflare_dns.tf +++ b/tf/cloudflare_dns.tf @@ -29,6 +29,10 @@ resource "cloudflare_record" "dyndns_a" { type = "A" value = "127.0.0.1" zone_id = cloudflare_zone.gensokyo-zone_zone.id + + lifecycle { + ignore_changes = [value] + } } resource "cloudflare_record" "dyndns_aaaa" { @@ -38,6 +42,10 @@ resource "cloudflare_record" "dyndns_aaaa" { type = "AAAA" value = "::1" zone_id = cloudflare_zone.gensokyo-zone_zone.id + + lifecycle { + ignore_changes = [value] + } } output "cloudflare_dyndns_record_a" { diff --git a/tf/cloudflare_records.tf b/tf/cloudflare_records.tf index 00fc43ae..830706ac 100644 --- a/tf/cloudflare_records.tf +++ b/tf/cloudflare_records.tf @@ -18,6 +18,11 @@ module "hakurei_system_records" { local_subdomains = [ "prox", "id", + "ldap", + ] + global_subdomains = [ + "plex", + "ldap", ] } diff --git a/tf/system/records/cnames.tf b/tf/system/records/cnames.tf index debc80bb..14bac112 100644 --- a/tf/system/records/cnames.tf +++ b/tf/system/records/cnames.tf @@ -3,6 +3,11 @@ variable "local_subdomains" { default = [] } +variable "global_subdomains" { + type = list(string) + default = [] +} + locals { cname_records = concat( [for subdomain in var.local_subdomains : { @@ -13,6 +18,10 @@ locals { name = "${subdomain}.tail", value = "${local.tailscale_name}.${var.zone_zone}", }] : [], + [for subdomain in var.global_subdomains : { + name = subdomain, + value = "${local.global_name}.${var.zone_zone}", + }], ) } @@ -20,7 +29,7 @@ resource "cloudflare_record" "cname_records" { for_each = { for i, cname in local.cname_records : cname.name => i } name = local.cname_records[each.value].name proxied = false - ttl = 360 + ttl = 600 type = "CNAME" value = local.cname_records[each.value].value zone_id = var.zone_id