diff --git a/flake.lock b/flake.lock index ea43246..5573f77 100644 --- a/flake.lock +++ b/flake.lock @@ -1,21 +1,5 @@ { "nodes": { - "blobs": { - "flake": false, - "locked": { - "lastModified": 1604995301, - "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=", - "owner": "simple-nixos-mailserver", - "repo": "blobs", - "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265", - "type": "gitlab" - }, - "original": { - "owner": "simple-nixos-mailserver", - "repo": "blobs", - "type": "gitlab" - } - }, "colmena": { "inputs": { "flake-compat": "flake-compat", @@ -54,22 +38,6 @@ "type": "github" } }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-utils": { "locked": { "lastModified": 1659877975, @@ -85,54 +53,6 @@ "type": "github" } }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "simple-nixos-mailserver", - "flake-compat" - ], - "gitignore": "gitignore", - "nixpkgs": [ - "simple-nixos-mailserver", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1750779888, - "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "simple-nixos-mailserver", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, "nix-github-actions": { "inputs": { "nixpkgs": [ @@ -170,29 +90,13 @@ "type": "github" } }, - "nixpkgs-25_05": { - "locked": { - "lastModified": 1751741127, - "narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "29e290002bfff26af1db6f64d070698019460302", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-25.05", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs_2": { "locked": { - "lastModified": 1752731184, - "narHash": "sha256-gxXbkDyWNqXH0M2HLmNhP51yY7PSZkXoshXSCh+DVIk=", + "lastModified": 1752644555, + "narHash": "sha256-oeRcp4VEyZ/3ZgfRRoq60/08l2zy0K53l8MdfSIYd24=", "owner": "nixos", "repo": "nixpkgs", - "rev": "558243f9250d98ab34959cff321fa3bfd58ca6dd", + "rev": "9100a4f6bf446603b9575927c8585162f9ec9aa6", "type": "github" }, "original": { @@ -205,34 +109,9 @@ "inputs": { "colmena": "colmena", "nixpkgs": "nixpkgs_2", - "simple-nixos-mailserver": "simple-nixos-mailserver", "sops-nix": "sops-nix" } }, - "simple-nixos-mailserver": { - "inputs": { - "blobs": "blobs", - "flake-compat": "flake-compat_2", - "git-hooks": "git-hooks", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-25_05": "nixpkgs-25_05" - }, - "locked": { - "lastModified": 1752060039, - "narHash": "sha256-MqcbN/PgfXOv8S4q6GcmlORd6kJZ3UlFNhzCvLOEe4I=", - "owner": "simple-nixos-mailserver", - "repo": "nixos-mailserver", - "rev": "80d21ed7a1ab8007597f7cd9adc26ebc98b9611f", - "type": "gitlab" - }, - "original": { - "owner": "simple-nixos-mailserver", - "repo": "nixos-mailserver", - "type": "gitlab" - } - }, "sops-nix": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index 9f15ca5..9e03ab2 100644 --- a/flake.nix +++ b/flake.nix @@ -8,11 +8,6 @@ url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; - - simple-nixos-mailserver = { - url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; - inputs.nixpkgs.follows = "nixpkgs"; - }; # home-manager = { # url = "home-manager"; # inputs.nixpkgs.follows = "nixpkgs"; @@ -27,7 +22,6 @@ sops-nix, # home-manager, colmena, - simple-nixos-mailserver, ... } @ inputs: let overlays = [ diff --git a/hive.nix b/hive.nix index 05f8896..f0b993a 100644 --- a/hive.nix +++ b/hive.nix @@ -2,7 +2,6 @@ inputs @ { self, nixpkgs, sops-nix, - simple-nixos-mailserver, # home-manager, overlays, ... @@ -15,8 +14,11 @@ inputs @ { }; defaults = { + pkgs, lib, name, + nodes, + meta, config, ... }: { @@ -48,12 +50,4 @@ inputs @ { imports = [./machines/${name}/definition.nix]; deployment.tags = ["zitadel" "sso" "ldap"]; }; - - mail = {name, ...}: { - imports = [ - ./machines/${name}/definition.nix - simple-nixos-mailserver.nixosModule - ]; - deployment.tags = ["mail"]; - }; } diff --git a/machines/auth/authelia.nix b/machines/auth/authelia.nix index dc57c96..023fa5c 100644 --- a/machines/auth/authelia.nix +++ b/machines/auth/authelia.nix @@ -14,91 +14,21 @@ in { enable = true; settings = { theme = "auto"; - server = { - buffers = { - read = 16384; - write = 16384; - }; - }; authentication_backend.ldap = { - implementation = "lldap"; address = "ldap://localhost:3890"; base_dn = "dc=procopius,dc=dk"; + users_filter = "(&({username_attribute}={input})(objectClass=person))"; + groups_filter = "(member={dn})"; user = "uid=authelia,ou=people,dc=procopius,dc=dk"; }; - definitions = { - network = { - internal = [ - "192.168.1.0/24" - ]; - }; - }; access_control = { default_policy = "deny"; # We want this rule to be low priority so it doesn't override the others rules = lib.mkAfter [ { - domain = [ - "proxmox.procopius.dk" - "traefik.procopius.dk" - "prometheus.procopius.dk" - "alertmanager.procopius.dk" - ]; - policy = "one_factor"; - subject = [ - ["group:server-admin"] - ]; - } - # bypass /api and /ping - { - domain = ["*.procopius.dk"]; - policy = "bypass"; - resources = [ - "^/api$" - "^/api/" - "^/ping$" - ]; - } - # media - { - domain = [ - "sonarr.procopius.dk" - "radarr.procopius.dk" - "readarr.procopius.dk" - "lidarr.procopius.dk" - "bazarr.procopius.dk" - "prowlarr.procopius.dk" - ]; - policy = "one_factor"; - subject = [ - ["group:media-admin"] - ]; - } - # authenticated - { - domain = [ - "gatus.procopius.dk" - ]; + domain = "*.procopius.dk"; policy = "one_factor"; } - # bypass auth internally - # { - # domain = [ - # "gatus.procopius.dk" - # "prometheus.procopius.dk" - # "alertmanager.procopius.dk" - # "sonarr.procopius.dk" - # "radarr.procopius.dk" - # "readarr.procopius.dk" - # "lidarr.procopius.dk" - # "bazarr.procopius.dk" - # "prowlarr.procopius.dk" - # ]; - # policy = "bypass"; - # networks = [ - # "internal" - # ]; - # } ]; }; storage.postgres = { @@ -128,8 +58,8 @@ in { }; notifier.smtp = { address = "smtp://mail.procopius.dk"; - username = "authelia@procopius.dk"; - sender = "authelia@procopius.dk"; + username = "admin@procopius.dk"; + sender = "auth@procopius.dk"; }; log.level = "info"; # identity_providers.oidc = { @@ -167,9 +97,24 @@ in { environmentVariables = with config.sops; { AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = secrets."authelia/lldap_authelia_password".path; - AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = secrets.smtp-password_authelia.path; + AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = secrets."authelia/smtp_authelia_password".path; }; }; + # caddy = { + # virtualHosts."auth.procopius.cc".extraConfig = '' + # reverse_proxy :9091 + # ''; + # # A Caddy snippet that can be imported to enable Authelia in front of a service + # # Taken from https://www.authelia.com/integration/proxies/caddy/#subdomain + # extraConfig = '' + # (auth) { + # forward_auth :9091 { + # uri /api/authz/forward-auth + # copy_headers Remote-User Remote-Groups Remote-Email Remote-Name + # } + # } + # ''; + # }; }; # Give Authelia access to the Redis socket @@ -197,6 +142,7 @@ in { "authelia/storage_encryption_key".owner = authelia; # The password for the `authelia` LLDAP user "authelia/lldap_authelia_password".owner = authelia; + "authelia/smtp_authelia_password".owner = authelia; smtp-password_authelia = { owner = authelia; key = "service_accounts/authelia/password"; diff --git a/machines/auth/bootstrap/default.nix b/machines/auth/bootstrap/default.nix new file mode 100644 index 0000000..b93965a --- /dev/null +++ b/machines/auth/bootstrap/default.nix @@ -0,0 +1,39 @@ +{ + pkgs, + config, + ... +}: { + systemd.services.lldap-bootstrap = { + description = "Bootstraps LLDAP users"; + requires = ["lldap.service"]; + serviceConfig = { + DynamicUser = true; + Type = "oneshot"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateUsers = true; + PrivateTmp = true; + LoadCredential = "inadyn.conf:${config.sops.templates."inadyn.conf".path}"; + CacheDirectory = "inadyn"; + ExecStart = '' + export LLDAP_URL=http://localhost:8080 + export LLDAP_ADMIN_USERNAME=admin + export LLDAP_ADMIN_PASSWORD=changeme + export USER_CONFIGS_DIR="$(realpath ./configs/user)" + export GROUP_CONFIGS_DIR="$(realpath ./configs/group)" + export USER_SCHEMAS_DIR="$(realpath ./configs/user-schema)" + export GROUP_SCHEMAS_DIR="$(realpath ./configs/group-schema)" + export LLDAP_SET_PASSWORD_PATH="$(realpath ./lldap_set_password)" + export DO_CLEANUP=false + ./bootstrap.sh + + ${pkgs.inadyn}/bin/inadyn \ + --foreground \ + --syslog \ + --once \ + --cache-dir ''${CACHE_DIRECTORY} \ + --config ''${CREDENTIALS_DIRECTORY}/inadyn.conf + ''; + }; + }; +} diff --git a/machines/auth/bootstrap/group-configs.nix b/machines/auth/bootstrap/group-configs.nix index b33bbd0..16d144e 100644 --- a/machines/auth/bootstrap/group-configs.nix +++ b/machines/auth/bootstrap/group-configs.nix @@ -1,24 +1,12 @@ -{ +{config, ...}: { sops.templates."default-groups.json" = { content = '' - { - "name": "mail" - } { "name": "git-user" } { "name": "git-admin" } - { - "name": "media-user" - } - { - "name": "media-admin" - } - { - "name": "server-admin" - } ''; path = "/bootstrap/group-configs/default-groups.json"; owner = "lldap"; diff --git a/machines/auth/bootstrap/lldap-bootstrap.nix b/machines/auth/bootstrap/lldap-bootstrap.nix index e7265b7..a8cbbfb 100644 --- a/machines/auth/bootstrap/lldap-bootstrap.nix +++ b/machines/auth/bootstrap/lldap-bootstrap.nix @@ -7,7 +7,6 @@ cfg = config.services.lldapBootstrap; in { imports = [ - ./service-accounts.nix ./user-configs.nix ./group-configs.nix ]; diff --git a/machines/auth/bootstrap/service-accounts.nix b/machines/auth/bootstrap/service-accounts.nix deleted file mode 100644 index 09a552e..0000000 --- a/machines/auth/bootstrap/service-accounts.nix +++ /dev/null @@ -1,51 +0,0 @@ -{config, ...}: { - sops.secrets."service_accounts/authelia/password" = {}; - sops.secrets."service_accounts/forgejo/password" = {}; - sops.secrets."service_accounts/jellyfin/password" = {}; - sops.secrets."service_accounts/mail/password" = {}; - sops.templates."service-accounts.json" = { - content = '' - { - "id": "authelia", - "email": "authelia@procopius.dk", - "password": "${config.sops.placeholder."service_accounts/authelia/password"}", - "displayName": "Authelia", - "groups": [ - "lldap_password_manager", - "mail" - ] - } - { - "id": "forgejo", - "email": "forgejo@procopius.dk", - "password": "${config.sops.placeholder."service_accounts/forgejo/password"}", - "displayName": "Forgejo", - "groups": [ - "lldap_password_manager", - "mail" - ] - } - { - "id": "jellyfin", - "email": "jellyfin@procopius.dk", - "password": "${config.sops.placeholder."service_accounts/jellyfin/password"}", - "displayName": "Jellyfin", - "groups": [ - "lldap_password_manager" - ] - } - { - "id": "mail", - "email": "mail@procopius.dk", - "password": "${config.sops.placeholder."service_accounts/mail/password"}", - "displayName": "NixOS Mailserver", - "groups": [ - "lldap_password_manager", - "mail" - ] - } - ''; - path = "/bootstrap/user-configs/service-accounts.json"; - owner = "lldap"; - }; -} diff --git a/machines/auth/bootstrap/user-configs.nix b/machines/auth/bootstrap/user-configs.nix index 726ec45..d5c714e 100644 --- a/machines/auth/bootstrap/user-configs.nix +++ b/machines/auth/bootstrap/user-configs.nix @@ -1,47 +1,28 @@ -{ - sops.templates."user-configs.json" = { +{config, ...}: { + sops.secrets."service_accounts/authelia/password" = {}; + sops.secrets."service_accounts/forgejo/password" = {}; + sops.templates."service-accounts.json" = { content = '' { - "id": "plasmagoat", - "email": "david.mikael@proton.me", - "displayName": "David", + "id": "authelia", + "email": "authelia@procopius.dk", + "password": "${config.sops.placeholder."service_accounts/authelia/password"}", + "displayName": "Authelia", "groups": [ - "media-user", - "media-admin", - "git-user", - "git-admin", - "server-admin" + "lldap_password_manager" ] } { - "id": "kurisudanoda", - "email": "iluvmizuki@gmail.com", - "displayName": "Noda", + "id": "forgejo", + "email": "forgejo@procopius.dk", + "password": "${config.sops.placeholder."service_accounts/forgejo/password"}", + "displayName": "Forgejo", "groups": [ - "media-user", - "git-user" - ] - } - { - "id": "dannydannydanny", - "email": "powerhouseplayer@gmail.com", - "displayName": "Danny", - "groups": [ - "media-user", - "git-user" - ] - } - { - "id": "stocksking", - "email": "ethanstocks9@gmail.com", - "displayName": "Ethan", - "groups": [ - "media-user", - "git-user" + "lldap_password_manager" ] } ''; - path = "/bootstrap/user-configs/user-configs.json"; + path = "/bootstrap/user-configs/service-accounts.json"; owner = "lldap"; }; } diff --git a/machines/mail/definition.nix b/machines/mail/definition.nix deleted file mode 100644 index 97607b0..0000000 --- a/machines/mail/definition.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - imports = [ - ./mailserver.nix - ]; - - networking = { - interfaces.eth0.ipv4.addresses = [ - { - address = "192.168.1.25"; - prefixLength = 24; - } - ]; - nameservers = ["192.168.1.53"]; - defaultGateway = "192.168.1.1"; - }; - deployment.targetHost = "192.168.1.25"; - - system.stateVersion = "25.05"; -} diff --git a/machines/mail/mailserver.nix b/machines/mail/mailserver.nix deleted file mode 100644 index 5b0563a..0000000 --- a/machines/mail/mailserver.nix +++ /dev/null @@ -1,37 +0,0 @@ -{config, ...}: { - sops.secrets."service_accounts/mail/password" = {}; - mailserver = { - enable = true; - stateVersion = 3; - fqdn = "mail.procopius.dk"; - domains = ["procopius.dk"]; - localDnsResolver = false; - ldap = { - enable = true; - uris = [ - "ldap://auth.lab:3890" - ]; - bind = { - dn = "cn=mail,ou=people,dc=procopius,dc=dk"; - passwordFile = config.sops.secrets."service_accounts/mail/password".path; - }; - postfix = { - filter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(|(mail=%s)(mail-alias=%s)))"; # Will require MR!351 for aliases to work properly - mailAttribute = "mail"; - }; - - dovecot = { - userFilter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(mail=%u))"; - passFilter = "(&(objectClass=person)(memberOf=cn=mail,ou=groups,dc=procopius,dc=dk)(mail=%u))"; - }; - - searchBase = "ou=people,dc=procopius,dc=dk"; - }; - - # Use Let's Encrypt certificates. Note that this needs to set up a stripped - # down nginx and opens port 80. - certificateScheme = "acme-nginx"; - }; - security.acme.acceptTerms = true; - security.acme.defaults.email = "david.mikael@proton.me"; -} diff --git a/machines/modules/journal-log.nix b/machines/modules/journal-log.nix index 712feeb..260cb73 100644 --- a/machines/modules/journal-log.nix +++ b/machines/modules/journal-log.nix @@ -1,7 +1,7 @@ { lib, config, - # nodes, + nodes, # name, # meta, ... @@ -21,7 +21,7 @@ in { }; clientUrl = mkOption { - type = types.str; + type = types.string; default = "http://monitor.lab:3100/loki/api/v1/push"; }; diff --git a/machines/monitor/gatus.nix b/machines/monitor/gatus.nix index b2d2381..c646ecf 100644 --- a/machines/monitor/gatus.nix +++ b/machines/monitor/gatus.nix @@ -18,51 +18,11 @@ } { name = "sonarr"; - url = "https://sonarr.procopius.dk/ping"; + url = "https://sonarr.procopius.dk/health"; interval = "5m"; conditions = [ "[STATUS] == 200" - "[BODY].status == OK" - "[RESPONSE_TIME] < 300" - ]; - } - { - name = "radarr"; - url = "https://radarr.procopius.dk/ping"; - interval = "5m"; - conditions = [ - "[STATUS] == 200" - "[BODY].status == OK" - "[RESPONSE_TIME] < 300" - ]; - } - { - name = "lidarr"; - url = "https://lidarr.procopius.dk/ping"; - interval = "5m"; - conditions = [ - "[STATUS] == 200" - "[BODY].status == OK" - "[RESPONSE_TIME] < 300" - ]; - } - { - name = "readarr"; - url = "https://readarr.procopius.dk/ping"; - interval = "5m"; - conditions = [ - "[STATUS] == 200" - "[BODY].status == OK" - "[RESPONSE_TIME] < 300" - ]; - } - { - name = "prowlarr"; - url = "https://prowlarr.procopius.dk/ping"; - interval = "5m"; - conditions = [ - "[STATUS] == 200" - "[BODY].status == OK" + "[BODY] == Healthy" "[RESPONSE_TIME] < 300" ]; } diff --git a/machines/monitor/grafana.nix b/machines/monitor/grafana.nix index abf7800..6fa50df 100644 --- a/machines/monitor/grafana.nix +++ b/machines/monitor/grafana.nix @@ -1,4 +1,10 @@ -{config, ...}: { +{ + config, + pkgs, + modulesPath, + lib, + ... +}: { # Add grafana user to the inlfuxdb2 group (for secret) users.users.grafana.extraGroups = ["influxdb2"]; services.grafana.enable = true; diff --git a/machines/monitor/influxdb.nix b/machines/monitor/influxdb.nix index 04e5b7d..3f6d584 100644 --- a/machines/monitor/influxdb.nix +++ b/machines/monitor/influxdb.nix @@ -1,4 +1,8 @@ -{config, ...}: let +{ + config, + pkgs, + ... +}: let influxdbPassword = config.sops.secrets."influxdb/password".path; influxdbToken = config.sops.secrets."influxdb/token".path; in { diff --git a/machines/monitor/prometheus.nix b/machines/monitor/prometheus.nix index 2bdb765..d480e6c 100644 --- a/machines/monitor/prometheus.nix +++ b/machines/monitor/prometheus.nix @@ -1,4 +1,10 @@ -{pkgs, ...}: let +{ + config, + pkgs, + modulesPath, + lib, + ... +}: let monitor_hostname = "monitor.lab"; traefik_hostname = "traefik.lab"; sandbox_hostname = "sandbox.lab"; diff --git a/machines/monitor/promtail.nix b/machines/monitor/promtail.nix index 78471e2..37a5611 100644 --- a/machines/monitor/promtail.nix +++ b/machines/monitor/promtail.nix @@ -1,4 +1,8 @@ -{config, ...}: let +{ + config, + pkgs, + ... +}: let promtail_port = 9080; in { networking.firewall.allowedTCPPorts = [promtail_port]; diff --git a/nixos/README.md b/nixos/README.md index 3c98a20..24d74ca 100644 --- a/nixos/README.md +++ b/nixos/README.md @@ -1,4 +1,4 @@ -nixos-rebuild switch --flake .#traefik --target-host root@traefik.lab --verbose +nixos-rebuild switch --flake .#traefik --target-host root@192.168.1.171 --verbose nixos-rebuild switch --flake .#proxmox --target-host root@192.168.1.205 --verbose nixos-rebuild switch --flake .#sandbox --target-host root@sandbox.lab --verbose nixos-rebuild switch --flake .#monitoring --target-host root@monitor.lab --verbose @@ -6,7 +6,6 @@ nixos-rebuild switch --flake .#forgejo --target-host root@forgejo.lab --verbose nixos-rebuild switch --flake .#dns --target-host root@192.168.1.140 --verbose nixos-rebuild switch --flake .#keycloak --target-host root@keycloak.lab --verbose nixos-rebuild switch --flake .#mail --target-host root@mail.lab --verbose -nixos-rebuild switch --flake .#media --target-host root@media.lab --verbose nixos-rebuild switch --flake .#runner01 --target-host root@forgejo-runner-01.lab --verbose diff --git a/nixos/configuration.nix b/nixos/configuration.nix index 8d386d1..d629197 100644 --- a/nixos/configuration.nix +++ b/nixos/configuration.nix @@ -1,10 +1,6 @@ +{ config, pkgs, modulesPath, lib, ... }: + { - config, - pkgs, - modulesPath, - lib, - ... -}: { ######################################################################## # IMPORTS & PROFILE # @@ -34,8 +30,9 @@ services.qemuGuest.enable = lib.mkDefault true; # GRUB on the “boot drive” + # Both live and template should install a bootloader on /dev/disk/by-label/nixos. boot.loader.grub.enable = lib.mkDefault true; - boot.loader.grub.devices = ["nodev"]; + boot.loader.grub.devices = [ "nodev" ]; # Grow the root partition on first boot boot.growPartition = lib.mkDefault true; @@ -56,16 +53,16 @@ # Root’s SSH authorized_keys (copy your own keys here) # Both live & template will install these, so you can ssh in. users.users.root.openssh.authorizedKeys.keys = [ - # ← Replace these with your actual public keys - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeg/n/vst9KME8byhxX2FhA+FZNQ60W38kkNt45eNzK5zFqBYuwo1nDXVanJSh9unRvB13b+ygpZhrb4sHvkETGWiEioc49MiWr8czEhu6Wpo0vv5MAJkiYvGZUYPdUW52jUzWcYdw8PukG2rowrxL5G0CmsqLwHMPU2FyeCe5aByFI/JZb8R80LoEacgjUiipJcoLWUVgG2koMomHClqGu+16kB8nL5Ja3Kc9lgLfDK7L0A5R8JXhCjrlEsmXbxZmwDKuxvjDAZdE9Sl1VZmMDfWkyrRlenrt01eR3t3Fec6ziRm5ZJk9e2Iu1DPoz+PoHH9aZGVwmlvvnr/gMF3OILxcqb0qx+AYlCCnb6D6pJ9zufhZkKcPRS1Q187F6fz+v2oD1xLZWFHJ92+7ItM0WmbDOHOC29s5EA6wNm3iXZCq86OI3n6T34njDtPqh6Z7Pk2sdK4GBwnFj4KwEWXvdKZKSX1qb2EVlEBE9QI4Gf3eg4SiBu2cAFt3nOSzs8c= asol\\dbs@ALPHA-DBS-P14sG2" - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+U3DWOrklcA8n8wdbLBGyli5LsJI3dpL2Zod8mx8eOdC4H127ZT1hzuk2uSmkic4c73BykPyQv8rcqwaRGW94xdMRanKmHYxnbHXo5FBiGrCkNlNNZuahthAGO49c6sUhJMq0eLhYOoFWjtf15sr5Zu7Ug2YTUL3HXB1o9PZ3c9sqYHo2rC/Il1x2j3jNAMKST/qUZYySvdfNJEeQhMbQcdoKJsShcE3oGRL6DFBoV/mjJAJ+wuDhGLDnqi79nQjYfbYja1xKcrKX+D3MfkFxFl6ZIzomR1t75AnZ+09oaWcv1J7ehZ3h9PpDBFNXvzyLwDBMNS+UYcH6SyFjkUbF David@NZXT" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" + # ← Replace these with your actual public keys + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeg/n/vst9KME8byhxX2FhA+FZNQ60W38kkNt45eNzK5zFqBYuwo1nDXVanJSh9unRvB13b+ygpZhrb4sHvkETGWiEioc49MiWr8czEhu6Wpo0vv5MAJkiYvGZUYPdUW52jUzWcYdw8PukG2rowrxL5G0CmsqLwHMPU2FyeCe5aByFI/JZb8R80LoEacgjUiipJcoLWUVgG2koMomHClqGu+16kB8nL5Ja3Kc9lgLfDK7L0A5R8JXhCjrlEsmXbxZmwDKuxvjDAZdE9Sl1VZmMDfWkyrRlenrt01eR3t3Fec6ziRm5ZJk9e2Iu1DPoz+PoHH9aZGVwmlvvnr/gMF3OILxcqb0qx+AYlCCnb6D6pJ9zufhZkKcPRS1Q187F6fz+v2oD1xLZWFHJ92+7ItM0WmbDOHOC29s5EA6wNm3iXZCq86OI3n6T34njDtPqh6Z7Pk2sdK4GBwnFj4KwEWXvdKZKSX1qb2EVlEBE9QI4Gf3eg4SiBu2cAFt3nOSzs8c= asol\\dbs@ALPHA-DBS-P14sG2" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+U3DWOrklcA8n8wdbLBGyli5LsJI3dpL2Zod8mx8eOdC4H127ZT1hzuk2uSmkic4c73BykPyQv8rcqwaRGW94xdMRanKmHYxnbHXo5FBiGrCkNlNNZuahthAGO49c6sUhJMq0eLhYOoFWjtf15sr5Zu7Ug2YTUL3HXB1o9PZ3c9sqYHo2rC/Il1x2j3jNAMKST/qUZYySvdfNJEeQhMbQcdoKJsShcE3oGRL6DFBoV/mjJAJ+wuDhGLDnqi79nQjYfbYja1xKcrKX+D3MfkFxFl6ZIzomR1t75AnZ+09oaWcv1J7ehZ3h9PpDBFNXvzyLwDBMNS+UYcH6SyFjkUbF David@NZXT" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" ]; # Default filesystem on fileSystems."/" = lib.mkDefault { device = "/dev/disk/by-label/nixos"; - autoResize = true; # grow on first boot + autoResize = true; # grow on first boot fsType = "ext4"; }; @@ -75,23 +72,23 @@ # Default set of packages environment.systemPackages = with pkgs; [ - vim # emergencies - git # pulling flakes, code - curl # downloading things - python3 # for Ansible if needed on live VM + vim # emergencies + git # pulling flakes, code + curl # downloading things + python3 # for Ansible if needed on live VM ]; # Nix settings (cache, experimental, gc) - nix.settings.trusted-users = ["root" "@wheel"]; - nix.settings.experimental-features = ["nix-command" "flakes"]; + nix.settings.trusted-users = [ "root" "@wheel" ]; + nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.extraOptions = '' - experimental-features = nix-command flakes - keep-outputs = true - keep-derivations = true + experimental-features = nix-command flakes + keep-outputs = true + keep-derivations = true ''; nix.gc.automatic = true; - nix.gc.dates = "weekly"; - nix.gc.options = "--delete-older-than 7d"; + nix.gc.dates = "weekly"; + nix.gc.options = "--delete-older-than 7d"; # mDNS with avahi to enable .local dns services.avahi = { @@ -107,7 +104,7 @@ ipv6 = false; }; - networking.firewall.allowedUDPPorts = [5353]; + networking.firewall.allowedUDPPorts = [ 5353 ]; # State version (set to match the Nixpkgs you’re using) system.stateVersion = lib.mkDefault "25.05"; diff --git a/nixos/hosts/dns/dnsmasq.nix b/nixos/hosts/dns/dnsmasq.nix index 9adfe80..5cecdf1 100644 --- a/nixos/hosts/dns/dnsmasq.nix +++ b/nixos/hosts/dns/dnsmasq.nix @@ -30,7 +30,6 @@ # Static IPs "/dns.lab/192.168.1.53" "/traefik.lab/192.168.1.80" - "/mail.lab/192.168.1.25" # "/proxmox-01.lab/192.168.1.205" # "/nas-01.lab/192.168.1.226" diff --git a/nixos/hosts/mail/host.nix b/nixos/hosts/mail/host.nix new file mode 100644 index 0000000..8793fa3 --- /dev/null +++ b/nixos/hosts/mail/host.nix @@ -0,0 +1,14 @@ +{ + config, + pkgs, + modulesPath, + lib, + ... +}: { + imports = [ + ../../templates/base.nix + ./networking.nix + ./sops.nix + ./mailserver.nix + ]; +} diff --git a/nixos/hosts/mail/mailserver.nix b/nixos/hosts/mail/mailserver.nix new file mode 100644 index 0000000..348ea96 --- /dev/null +++ b/nixos/hosts/mail/mailserver.nix @@ -0,0 +1,39 @@ +{ + config, + pkgs, + ... +}: { + imports = [ + (builtins.fetchTarball { + # Pick a release version you are interested in and set its hash, e.g. + url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-25.05/nixos-mailserver-nixos-25.05.tar.gz"; + # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command: + # release="nixos-25.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack + sha256 = "0jpp086m839dz6xh6kw5r8iq0cm4nd691zixzy6z11c4z2vf8v85"; + }) + ]; + + mailserver = { + enable = true; + fqdn = "mail.procopius.dk"; + domains = ["procopius.dk"]; + + # A list of all login accounts. To create the password hashes, use + # nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt' + loginAccounts = { + "admin@procopius.dk" = { + hashedPasswordFile = config.sops.secrets.mailserver-admin-pass.path; + aliases = [ + "@procopius.dk" + "postmaster@procopius.dk" + ]; + }; + }; + + # Use Let's Encrypt certificates. Note that this needs to set up a stripped + # down nginx and opens port 80. + certificateScheme = "acme-nginx"; + }; + security.acme.acceptTerms = true; + security.acme.defaults.email = "david.mikael@proton.me"; +} diff --git a/nixos/hosts/mail/networking.nix b/nixos/hosts/mail/networking.nix new file mode 100644 index 0000000..06a895c --- /dev/null +++ b/nixos/hosts/mail/networking.nix @@ -0,0 +1,8 @@ +{ + config, + lib, + pkgs, + ... +}: { + networking.hostName = "mail"; +} diff --git a/nixos/hosts/mail/sops.nix b/nixos/hosts/mail/sops.nix new file mode 100644 index 0000000..b532a76 --- /dev/null +++ b/nixos/hosts/mail/sops.nix @@ -0,0 +1,8 @@ +{...}: let + mailserverSops = ../../secrets/mailserver/secrets.yml; +in { + sops.secrets.mailserver-admin-pass = { + sopsFile = mailserverSops; + mode = "0440"; + }; +} diff --git a/nixos/hosts/media/nixarr.nix b/nixos/hosts/media/nixarr.nix index 9e54790..715dc8f 100644 --- a/nixos/hosts/media/nixarr.nix +++ b/nixos/hosts/media/nixarr.nix @@ -1,9 +1,7 @@ {config, ...}: { - services.sonarr.settings.auth.method = "External"; - services.radarr.settings.auth.method = "External"; - services.lidarr.settings.auth.method = "External"; - services.readarr.settings.auth.method = "External"; - services.prowlarr.settings.auth.method = "External"; + services.sonarr.settings = { + auth.method = "External"; + }; nixarr = { enable = true; diff --git a/nixos/hosts/traefik/configuration/auth/routers.nix b/nixos/hosts/traefik/configuration/auth/routers.nix index 4b8f879..5ed6407 100644 --- a/nixos/hosts/traefik/configuration/auth/routers.nix +++ b/nixos/hosts/traefik/configuration/auth/routers.nix @@ -13,10 +13,19 @@ tls.certResolver = "letsencrypt"; }; - lldap = { - rule = "Host(`lldap.procopius.dk`)"; - service = "lldap"; + oauth2proxy = { + rule = "Host(`radarr.procopius.dk`) && PathPrefix(`/oauth2/`)"; + service = "oauth2proxy"; entryPoints = ["websecure"]; + middlewares = ["auth-headers"]; + tls.certResolver = "letsencrypt"; + }; + + oauth2route = { + rule = "Host(`oauth.procopius.dk`)"; + service = "oauth2proxy"; + entryPoints = ["websecure"]; + middlewares = ["auth-headers"]; tls.certResolver = "letsencrypt"; }; } diff --git a/nixos/hosts/traefik/configuration/auth/services.nix b/nixos/hosts/traefik/configuration/auth/services.nix index d5e0efc..52a40a8 100644 --- a/nixos/hosts/traefik/configuration/auth/services.nix +++ b/nixos/hosts/traefik/configuration/auth/services.nix @@ -1,6 +1,6 @@ { keycloak.loadBalancer.servers = [{url = "http://keycloak.lab:8080";}]; + oauth2proxy.loadBalancer.servers = [{url = "http://localhost:4180";}]; authelia.loadBalancer.servers = [{url = "http://auth.lab:9091";}]; - lldap.loadBalancer.servers = [{url = "http://auth.lab:17170";}]; } diff --git a/nixos/hosts/traefik/configuration/infra/routers.nix b/nixos/hosts/traefik/configuration/infra/routers.nix index 67ed719..5667d22 100644 --- a/nixos/hosts/traefik/configuration/infra/routers.nix +++ b/nixos/hosts/traefik/configuration/infra/routers.nix @@ -3,7 +3,7 @@ rule = "Host(`traefik.procopius.dk`)"; service = "traefik"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; @@ -25,7 +25,7 @@ rule = "Host(`proxmox.procopius.dk`)"; service = "proxmox"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; nas = { diff --git a/nixos/hosts/traefik/configuration/media-center/routers.nix b/nixos/hosts/traefik/configuration/media-center/routers.nix index 95eec8a..b30e50e 100644 --- a/nixos/hosts/traefik/configuration/media-center/routers.nix +++ b/nixos/hosts/traefik/configuration/media-center/routers.nix @@ -6,18 +6,14 @@ tls.certResolver = "letsencrypt"; }; - jellyseerr = { - rule = "Host(`jellyseerr.procopius.dk`)"; - service = "jellyseerr"; - entryPoints = ["websecure"]; - tls.certResolver = "letsencrypt"; - }; - radarr = { rule = "Host(`radarr.procopius.dk`)"; service = "radarr"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = [ + "oauth-auth" + "restrict-admin" + ]; tls.certResolver = "letsencrypt"; }; @@ -25,39 +21,15 @@ rule = "Host(`sonarr.procopius.dk`)"; service = "sonarr"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; - prowlarr = { - rule = "Host(`prowlarr.procopius.dk`)"; - service = "prowlarr"; + jellyseerr = { + rule = "Host(`jellyseerr.procopius.dk`)"; + service = "jellyseerr"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; - tls.certResolver = "letsencrypt"; - }; - - bazarr = { - rule = "Host(`bazarr.procopius.dk`)"; - service = "bazarr"; - entryPoints = ["websecure"]; - middlewares = ["authelia"]; - tls.certResolver = "letsencrypt"; - }; - - lidarr = { - rule = "Host(`lidarr.procopius.dk`)"; - service = "lidarr"; - entryPoints = ["websecure"]; - middlewares = ["authelia"]; - tls.certResolver = "letsencrypt"; - }; - - readarr = { - rule = "Host(`readarr.procopius.dk`)"; - service = "readarr"; - entryPoints = ["websecure"]; - middlewares = ["authelia"]; + # middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; } diff --git a/nixos/hosts/traefik/configuration/media-center/services.nix b/nixos/hosts/traefik/configuration/media-center/services.nix index 136b502..2fa3a71 100644 --- a/nixos/hosts/traefik/configuration/media-center/services.nix +++ b/nixos/hosts/traefik/configuration/media-center/services.nix @@ -1,11 +1,6 @@ { jellyfin.loadBalancer.servers = [{url = "http://media.lab:8096";}]; - jellyseerr.loadBalancer.servers = [{url = "http://media.lab:5055";}]; - radarr.loadBalancer.servers = [{url = "http://media.lab:7878";}]; sonarr.loadBalancer.servers = [{url = "http://media.lab:8989";}]; - readarr.loadBalancer.servers = [{url = "http://media.lab:8787";}]; - lidarr.loadBalancer.servers = [{url = "http://media.lab:8686";}]; - bazarr.loadBalancer.servers = [{url = "http://media.lab:6767";}]; - prowlarr.loadBalancer.servers = [{url = "http://media.lab:9696";}]; + jellyseerr.loadBalancer.servers = [{url = "http://media.lab:5055";}]; } diff --git a/nixos/hosts/traefik/configuration/middlewares.nix b/nixos/hosts/traefik/configuration/middlewares.nix index d8c81c7..42bba66 100644 --- a/nixos/hosts/traefik/configuration/middlewares.nix +++ b/nixos/hosts/traefik/configuration/middlewares.nix @@ -19,16 +19,25 @@ in { }; }; - authelia = { + oauth-auth = { forwardAuth = { - address = "http://auth.lab:9091/api/authz/forward-auth"; + address = "http://localhost:4180/"; trustForwardHeader = true; authResponseHeaders = [ - "Remote-User" - "Remote-Groups" - "Remote-Email" - "Remote-Name" + "Authorization" + "X-Auth-Request-Access-Token" + "X-Auth-Request-User" + "X-Auth-Request-Email" + "X-Auth-Request-Preferred-Username" # Recommended + "X-Auth-Request-Access-Token" # If you want to pass the token + "X-Auth-Request-Groups" # If you configured a mapper in Keycloak to emit groups ]; }; }; + + restrict-admin = { + forwardAuth = { + address = "http://localhost:4180/oauth2/auth?allowed_groups=role:admin"; + }; + }; } diff --git a/nixos/hosts/traefik/configuration/monitoring/routers.nix b/nixos/hosts/traefik/configuration/monitoring/routers.nix index 1cb88c7..8f30959 100644 --- a/nixos/hosts/traefik/configuration/monitoring/routers.nix +++ b/nixos/hosts/traefik/configuration/monitoring/routers.nix @@ -3,7 +3,7 @@ rule = "Host(`prometheus.procopius.dk`)"; service = "prometheus"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; grafana = { @@ -16,14 +16,14 @@ rule = "Host(`alertmanager.procopius.dk`)"; service = "alertmanager"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; gatus = { rule = "Host(`gatus.procopius.dk`)"; service = "gatus"; entryPoints = ["websecure"]; - middlewares = ["authelia"]; + middlewares = ["oauth-auth"]; tls.certResolver = "letsencrypt"; }; umami = { diff --git a/nixos/hosts/traefik/host.nix b/nixos/hosts/traefik/host.nix index 2af4433..4570d3c 100644 --- a/nixos/hosts/traefik/host.nix +++ b/nixos/hosts/traefik/host.nix @@ -11,5 +11,6 @@ ./traefik.nix ./promtail.nix ./sops.nix + ./oauth2proxy.nix ]; } diff --git a/nixos/hosts/traefik/oauth2proxy.nix b/nixos/hosts/traefik/oauth2proxy.nix new file mode 100644 index 0000000..2250877 --- /dev/null +++ b/nixos/hosts/traefik/oauth2proxy.nix @@ -0,0 +1,76 @@ +# /etc/nixos/configuration.nix +{ + config, + lib, + pkgs, + ... +}: let + oauth2ProxyKeyFile = config.sops.secrets."oauth2-proxy-env".path; +in { + services.oauth2-proxy = { + enable = true; + package = pkgs.oauth2-proxy; + + keyFile = oauth2ProxyKeyFile; + + provider = "keycloak-oidc"; # Use "oidc" for standard OIDC providers like Keycloak + oidcIssuerUrl = "https://keycloak.procopius.dk/realms/homelab"; + clientID = "oauth2-proxy"; # Matches the client ID in Keycloak + + # Public URL for oauth2-proxy itself, where Keycloak redirects back to + redirectURL = "https://oauth.procopius.dk/oauth2/callback"; + upstream = ["static://202"]; + extraConfig = { + code-challenge-method = "S256"; + # email-domain = "*"; + auth-logging = true; + request-logging = true; + whitelist-domain = ".procopius.dk"; + pass-host-header = true; + skip-provider-button = true; + }; + + # Cookie configuration + cookie = { + name = "_oauth2_proxy_homelab"; + domain = ".procopius.dk"; + secure = true; + httpOnly = true; + expire = "24h"; + refresh = "1h"; + }; + + # Listen address for oauth2-proxy internally. Traefik will forward to this. + httpAddress = "http://127.0.0.1:4180"; # Ensure this port is not blocked by your firewall internally + + # Reverse proxy settings for headers + reverseProxy = true; # Set to true because it's behind Traefik + + # Headers to set for the upstream applications after successful authentication + setXauthrequest = true; # Set X-Auth-Request-User, X-Auth-Request-Email etc. + passBasicAuth = true; # Pass HTTP Basic Auth headers + passHostHeader = true; # Pass the original Host header to the upstream + + # Authorization rules for who can access + # You can restrict by email domain (allows everyone from that domain) + email.domains = ["*"]; # Allows any authenticated user from Keycloak + # Or restrict by specific email addresses (if you want tighter control): + # email.addresses = allowedOauth2ProxyEmails; + + # Logging + requestLogging = true; + + # Optional: If you use specific scopes for Keycloak (e.g., if you want groups claim) + # scope = "openid profile email"; + # If you specifically added a 'groups' claim in Keycloak: + scope = "openid profile email"; + + # You can add extra command-line flags here if needed, e.g., for debug logging + # extraConfig = { + # + # }; + }; + + # Expose the internal port for oauth2-proxy if needed for debugging or direct access (less common) + networking.firewall.allowedTCPPorts = [4180]; +} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index 6a8d487..3919e1e 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -21,10 +21,6 @@ service_accounts: password: ENC[AES256_GCM,data:r4Qy09FOhUgD48SHSkWKtrlrMptvXYdScCL8h7gjJNs=,iv:IzsDdV4o35hnS/F2S713cJ5pQ+PGiaVTmTWe6YXgfYc=,tag:OisvmY7QI2Ph7R3g3Xy/Ww==,type:str] forgejo: password: ENC[AES256_GCM,data:HLEoGjx++9fkiJQoLWQvAjgg58mIs1vk1hvUJvr6TiA=,iv:mPIx9cSlHEK+0wLs1/1bjYcsxgdwgLReUoI5JrA4E1k=,tag:TdyznTIGiAFFq8D3Irb0rA==,type:str] - jellyfin: - password: ENC[AES256_GCM,data:PpUHEhNfnR1eg7DmnO7tyNciNE4Tsx/Y4uL92gqiods=,iv:DNKQfymvgEu/iEW8t79m0ZmKTU0Ffasu+gp2KOIAK3o=,tag:lGKw5dbXqImDJNVX6p8kLg==,type:str] - mail: - password: ENC[AES256_GCM,data:6lfziq1zXlFxCAFWv5co3MkBgwaWixjHHX9riQXCbe0=,iv:/t4CnW3bKUDxfpE/qGf1LPs0ciivRMkfgJ1nMseruy4=,tag:TWApzLsm2HV+JMaZLG/Kig==,type:str] sops: age: - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y @@ -36,7 +32,7 @@ sops: QzNYRk5ERmR4aGtLQ3dwQ1lPeDZyaEkKJMLXqv6tBBql7VVnWDIwAh24SfQ2O6Ca CEOQTGEonbqr5doWqTsXUXrdQAS0amL45UdT6ITFtfNAjaHwCMfhZg== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-07-16T15:33:06Z" - mac: ENC[AES256_GCM,data:nZm7N8qfANzHadtW+3eTJljpmYejJdKGFO44iw40CnwlGgb454us9LZbQIAYkNiS7UkivoWa5BqvgLcpObHNAn3tVi+ha0jySIrAmp43y5ilmg76fvL4znel4Nk7eRiGoF3t3xiCR39/3l7PPffx2RJ6PerEyGBpiUZ6mBcWoTE=,iv:UmhSynpMdTnY0R6jwDJts13b0rKsaRFlCizdM2oargE=,tag:Q2xh/QXFOQYqqkxKs7nujA==,type:str] + lastmodified: "2025-07-16T00:07:58Z" + mac: ENC[AES256_GCM,data:kKSjCmLGbr7WaLb+Z1KZL/bjBszNgCCAb67CENaKKpFbqbCk3o5QFok/kVTs3k3JwKmODqbTe0ebP6uMENN/t85+1n4ofnMq5ire/NqCyoE1EJFDmG9xyys24CB+NJJZ2trdxm5CYutme7FfG4bQY0/2OgflmjiZeBsMZcxRxtI=,iv:md6qX+WlFFgMFbDs8MTTKXEOPWKFoVYAUMehWvGF5wk=,tag:Y3AelrfrsBMIJ0wYzFYtLQ==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2