diff --git a/README.md b/README.md index 0084435..57fe267 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ HTTP reverse proxy. ## TODO: +- Docker/podman images are currently not persisted, maybe they should be. + ### Services **For sure**: - Authentik: Authentication diff --git a/example_secrets.yaml b/example_secrets.yaml new file mode 100644 index 0000000..3160664 --- /dev/null +++ b/example_secrets.yaml @@ -0,0 +1,13 @@ +traefik: + acmeEmail: email@example.com + CLOUDFLARE_EMAIL: email@example.com + CLOUDFLARE_DNS_API_TOKEN: AVeryLongAPIKeyHere +authentik: + db_pass: AVeryLongSecurePassword + secret_key: AVeryLongSecretKey + # Some of these are not really secret, but having it in one place is easier + email_host: mail.example.com + email_port: "587" + email_from: mail@example.com + email_username: mail@example.com + email_password: ADifferentVeryLongSecurePassword diff --git a/hosts/nix-test.nix b/hosts/nix-test.nix index 8feee09..8c760b3 100644 --- a/hosts/nix-test.nix +++ b/hosts/nix-test.nix @@ -10,9 +10,12 @@ roles = with roles; [ traefik sonarr + podman + authentik ]; config = { sonarr.domain = "service1.${hlConfig.domain}"; + authentik.domain = "service2.${hlConfig.domain}"; traefik.wildcardDomains = [ hlConfig.domain "pds.${hlConfig.domain}" diff --git a/hosts/pds.nix b/hosts/pds.nix index 68ee9ff..f2710b8 100644 --- a/hosts/pds.nix +++ b/hosts/pds.nix @@ -7,13 +7,15 @@ rec { managed = false; ip = "192.168.10.174"; - traefikRoutes = [ - { - name = "${hostname}"; - rule = "Host(`${hlConfig.domain}`) || HostRegexp(`.+${ - builtins.replaceStrings [ "." ] [ "\\." ] hlConfig.domain - }`)"; - target = "http://${ip}:3000"; - } - ]; + traefikRoutes = + let + domain = "pds.${hlConfig.domain}"; + in + [ + { + name = "${hostname}"; + rule = "Host(`${domain}`) || HostRegexp(`.+${builtins.replaceStrings [ "." ] [ "\\." ] domain}`)"; + target = "http://${ip}:3000"; + } + ]; } diff --git a/roles/authentik.nix b/roles/authentik.nix index 64fc375..88de91a 100644 --- a/roles/authentik.nix +++ b/roles/authentik.nix @@ -17,13 +17,25 @@ { name = "${hostname}-authentik"; rule = "Host(`${config.domain}`)"; - # TODO: Change port - target = "http://${host.ip}:PORTHERE"; + target = "http://${host.ip}:9000"; } ]; nixosModule = - { lib, ... }: + { + lib, + config, + pkgs, + ... + }: + let + AUTHENTIK_VERSION = "2024.12.3"; + publicEnv = pkgs.writeText "authentik-public.env" '' + AUTHENTIK_EMAIL__USE_TLS=false + AUTHENTIK_EMAIL__USE_SSL=true + AUTHENTIK_EMAIL__TIMEOUT=10 + ''; + in { options.authentik = { domain = lib.mkOption { @@ -32,15 +44,140 @@ }; config = { - # Enable the sonarr service - services.sonarr = { - enable = true; - openFirewall = true; - group = "media"; + + # Set up user to run authentik + users.users."authentik" = { + isSystemUser = true; + group = "authentik"; + }; + users.groups."authentik" = { }; + + # TODO: Persist some/all of this into ceph cluster + environment.persistence."/persistent" = { + directories = [ + { + directory = "/appdata/authentik/postgres"; + mode = "0700"; + } + { + directory = "/appdata/authentik/redis"; + mode = "0700"; + } + { + directory = "/appdata/authentik/media"; + mode = "0700"; + } + { + directory = "/appdata/authentik/certs"; + mode = "0700"; + } + ]; }; - # Ensure that the media group exists - users.groups.media = { }; + sops.secrets = { + "authentik/db_pass" = { + owner = "authentik"; + }; + "authentik/secret_key" = { + owner = "authentik"; + }; + "authentik/email_host" = { + owner = "authentik"; + }; + "authentik/email_port" = { + owner = "authentik"; + }; + "authentik/email_from" = { + owner = "authentik"; + }; + "authentik/email_username" = { + owner = "authentik"; + }; + "authentik/email_password" = { + owner = "authentik"; + }; + }; + sops.templates."authentik-secret.env" = { + owner = "authentik"; + content = '' + AUTHENTIK_POSTGRESQL__PASSWORD=${config.sops.placeholder."authentik/db_pass"} + POSTGRES_PASSWORD=${config.sops.placeholder."authentik/db_pass"} + AUTHENTIK_SECRET_KEY="${config.sops.placeholder."authentik/secret_key"}" + AUTHENTIK_EMAIL__HOST="${config.sops.placeholder."authentik/email_host"}" + AUTHENTIK_EMAIL__PORT="${config.sops.placeholder."authentik/email_port"}" + AUTHENTIK_EMAIL__FROM="${config.sops.placeholder."authentik/email_from"}" + AUTHENTIK_EMAIL__USERNAME="${config.sops.placeholder."authentik/email_username"}" + AUTHENTIK_EMAIL__PASSWORD="${config.sops.placeholder."authentik/email_password"}" + ''; + }; + + podman.containers = { + # TODO: Use system postgres here instead of a separate container + "authentik-postgres" = { + image = "docker.io/library/postgres:16-alpine"; + autoStart = true; + volumes = [ + "/appdata/authentik/postgres:/var/lib/postgresql/data" + ]; + environment = { + POSTGRES_USER = "authentik"; + POSTGRES_DB = "authentik"; + }; + environmentFiles = [ + config.sops.templates."authentik-secret.env".path + publicEnv + ]; + }; + "authentik-redis" = { + image = "docker.io/library/redis:7.4.2-alpine"; + autoStart = true; + volumes = [ + "/appdata/authentik/redis:/data" + ]; + }; + "authentik-server" = { + image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}"; + user = "authentik"; + autoStart = true; + cmd = [ "server" ]; + environment = { + AUTHENTIK_REDIS__HOST = "authentik-redis"; + AUTHENTIK_POSTGRESQL__HOST = "authentik-postgres"; + AUTHENTIK_POSTGRESQL__USER = "authentik"; + AUTHENTIK_POSTGRESQL__NAME = "authentik"; + }; + environmentFiles = [ + config.sops.templates."authentik-secret.env".path + publicEnv + ]; + volumes = [ + "/appdata/authentik/media:/media" + ]; + ports = [ + "9000:9000" + ]; + }; + "authentik-worker" = { + image = "ghcr.io/goauthentik/server:${AUTHENTIK_VERSION}"; + user = "authentik"; + autoStart = true; + cmd = [ "worker" ]; + environment = { + AUTHENTIK_REDIS__HOST = "authentik-redis"; + AUTHENTIK_POSTGRESQL__HOST = "authentik-postgres"; + AUTHENTIK_POSTGRESQL__USER = "authentik"; + AUTHENTIK_POSTGRESQL__NAME = "authentik"; + }; + environmentFiles = [ + config.sops.templates."authentik-secret.env".path + publicEnv + ]; + volumes = [ + "/appdata/authentik/media:/media" + "/appdata/authentik/certs:/certs" + ]; + }; + }; }; }; } diff --git a/roles/default.nix b/roles/default.nix index d034834..f72f3e0 100644 --- a/roles/default.nix +++ b/roles/default.nix @@ -5,5 +5,6 @@ { sonarr = utils.mkRole (import ./sonarr.nix); traefik = utils.mkRole (import ./traefik.nix); + podman = utils.mkRole (import ./podman.nix); authentik = utils.mkRole (import ./authentik.nix); } diff --git a/roles/podman.nix b/roles/podman.nix new file mode 100644 index 0000000..39bcd2e --- /dev/null +++ b/roles/podman.nix @@ -0,0 +1,29 @@ +{ + name = "Podman"; + description = '' + Provide support for running docker containers on the system + ''; + + nixosModule = + { lib, config, ... }: + { + options.podman = { + containers = lib.mkOption { + type = lib.types.attrs; + }; + }; + + config = + let + cfg = config.podman; + in + { + virtualisation.containers.enable = true; + virtualisation.podman.enable = true; + virtualisation.podman.defaultNetwork.settings.dns_enabled = true; + virtualisation.oci-containers.backend = "podman"; + + virtualisation.oci-containers.containers = cfg.containers; + }; + }; +} diff --git a/roles/traefik.nix b/roles/traefik.nix index b4be4f2..680d549 100644 --- a/roles/traefik.nix +++ b/roles/traefik.nix @@ -7,7 +7,6 @@ nixosModule = { lib, - pkgs, config, hosts, ... @@ -43,6 +42,16 @@ 443 ]; + environment.persistence."/persistent" = { + directories = [ + { + directory = "/appdata/traefik/acme"; + user = "traefik"; + mode = "0700"; + } + ]; + }; + sops.secrets = { "traefik/acmeEmail" = { owner = "traefik"; @@ -107,7 +116,7 @@ letsencrypt = { acme = { email = "$acmeEmail"; - storage = "/run/traefik/acme.json"; + storage = "/appdata/traefik/acme/acme.json"; dnsChallenge = { provider = "cloudflare"; }; diff --git a/secrets/nix-test.yaml b/secrets/nix-test.yaml index 94ff4e2..dec3445 100644 --- a/secrets/nix-test.yaml +++ b/secrets/nix-test.yaml @@ -2,6 +2,14 @@ traefik: acmeEmail: ENC[AES256_GCM,data:aM2AQADo5s0c1b//UWPXNPlKMXNRRnPFDbM=,iv:RP7Tn8s1nYKJf0B0KO0BQkI4tnz/zUK8KqzQqeNiyZk=,tag:g4+lwK4miUdxOwLHQcUZhg==,type:str] CLOUDFLARE_EMAIL: ENC[AES256_GCM,data:YHQ00Qh0t7owvFE/PXu8o4a8ry1P92/CVA==,iv:z982jUAm8W4Du/5dLopQZE0p5eWi4Ls7TYsiiwUlqvg=,tag:bek2eQ4duYBH8F2LG+Tr+g==,type:str] CLOUDFLARE_DNS_API_TOKEN: ENC[AES256_GCM,data:zyTpv1AGA9GzfGfFyxqO40NKZt8LlHU1YT9kvXPZYAGUc5wE3GVxzg==,iv:W7u5gEeYNkCGO3D0Y+XBZ4PCI081QsNK10ThHKbV68M=,tag:7onKfU+mVz3euCbFrX1mdg==,type:str] +authentik: + db_pass: ENC[AES256_GCM,data:Jkz/wWf+yOm3d+hb+c56XXSGYjYRJbMwJqpcr4HMmu+WVflZCh/ILw==,iv:42uswgv+lIRnonX6kT0MFhs5EYaTgdakrBe9DmFUY2k=,tag:hUxlquZT4RBHlcLKVtHVlg==,type:str] + secret_key: ENC[AES256_GCM,data:JdSbOxLGa0Bqac/YV4HgpN2lD+UEgvWa4YqQ4nQJka8MTRmuFYNDN3eQ5d1bI7JCijy4y7QqyhtKfdpK/puVsNkoSb4Cmh3m7nlqHx/2b9M=,iv:2c5Zk+TLvvlW/JTq2pvdyqT0PNe4qJ9OXEGA20feh1g=,tag:7y2yYlUXrq7gH6qAHaypqA==,type:str] + email_host: ENC[AES256_GCM,data:T5UErdKKbyfYkbd+1V6JEz6yp7h+ww==,iv:o/wvYwDgx+z8v8l9A7OudP0GFGK6ngMrj/X3cLTDN6U=,tag:sR9PZkQc+28rs+rpPV142Q==,type:str] + email_port: ENC[AES256_GCM,data:bPPI,iv:3174C+o4058QF5c46qDWbUMRt+SpDEHtV+vbvQxfTn4=,tag:1oR0WLLzYru5BGdWluKJZg==,type:str] + email_from: ENC[AES256_GCM,data:X6NP2i3uAZQFK7JdeviIMFhNPw==,iv:dwZFyzzzzFNTVfe1nhWebXrTolCa991p+vJUAOxFJf8=,tag:gClo9mZfaVFP35yZath0Nw==,type:str] + email_username: ENC[AES256_GCM,data:c1lu5Tw6N6w96uUujSj1wHh7fQ==,iv:XX2iYXOzz8EhcZ75NlmLsasnZnCrihE9K17qS2nhAyI=,tag:qfhh3bB530IIsJwmjG20Lw==,type:str] + email_password: ENC[AES256_GCM,data:2f/LN5q/5RRIzAc8ol9RByf+RrQ=,iv:gy/UvcKzpvC0r4nQFbTYta8alzTjPWhFWCjGIw/PnuU=,tag:LLOk7NMuQ3VZ2zA779A5dw==,type:str] sops: kms: [] gcp_kms: [] @@ -35,8 +43,8 @@ sops: OHkvUTViMVZSUGFSeDN1ZDcxN3NtNzQK48qiEMcKbsrh8ZhnMD7lkhsy0JRMYiOU EtXwHxEzIXukStQ9kXazfHJJouuqv7mhx12tgv+QKvrfWxCJ5WvE2A== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-12-26T18:39:38Z" - mac: ENC[AES256_GCM,data:2dr8o3njYlYVHiFItM4MrlHfpiw7AurdedXm614MbMiX6b5bkAoIuSJHWjjwmBsQY52yTUwl5GS0oLztRGOZ9OsxiwvGRoxNG5lAPK83t4pralaWvLKVn7CCClU6fyYnUwqPEfw/YFSxlm00iBPz54zRQNvIigrZhhAM3lHswaM=,iv:sgvpiOwz183/GewbTFsW3EV8bHX7p/13b32sDPxRcMw=,tag:ZHHv4fAOT/lPZg/n9rnMvA==,type:str] + lastmodified: "2025-02-10T22:59:35Z" + mac: ENC[AES256_GCM,data:uTzFEW5na0YCBLGb9k/yICjNGKWCefGgiSH8M0QZgrvRf3ioXz1W/rGUJqTmIh3QdQ45evHoj6cKSIys2gU28dR98qGX2sCBmnWYMB2oT57YLXeACsaqXKtXFsOx/YnOy8baQpckUYOwHJpM+dhKf/s348X1jfx1k8TOoNE3aj0=,iv:Jr1HvXKsxKfMBUu2r6ezodySMWduVdlPw+EIckpi5i0=,tag:fDjBfC7nKwkO3mm91iB/HQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.9.2