From 12015f8589f83882fca484c70814925c02f888b3 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 4 Oct 2025 19:59:03 +0200 Subject: [PATCH 1/6] feat(sops): Setup sops --- .sops.yaml | 8 ++++++++ flake.nix | 10 ++++++++-- secrets/secrets.yaml | 19 +++++++++++++++++++ users/admin/default.nix | 13 ++++++++++--- 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 .sops.yaml create mode 100644 secrets/secrets.yaml diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..8d0d445 --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,8 @@ +keys: + - &tdpeuter_Tibo-NixTop age1qzutny0mqpcccqw6myyfntu6wcskruu9ghzvt6r4te7afkqwnguq05ex37 + +creation_rules: + - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *tdpeuter_Tibo-NixTop diff --git a/flake.nix b/flake.nix index 7701f3a..446f4ce 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,10 @@ nixpkgs.url = "nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; utils = { url = "github:gytis-ivaskevicius/flake-utils-plus"; inputs.flake-utils.follows = "flake-utils"; @@ -13,11 +17,11 @@ outputs = inputs@{ self, nixpkgs, - flake-utils, utils, + flake-utils, sops-nix, utils, ... }: let - system = "x86_64-linux"; + system = utils.lib.system.x86_64-linux; in utils.lib.mkFlake { inherit self inputs; @@ -28,6 +32,8 @@ modules = [ ./modules ./users + + sops-nix.nixosModules.sops ]; }; diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml new file mode 100644 index 0000000..e17dab1 --- /dev/null +++ b/secrets/secrets.yaml @@ -0,0 +1,19 @@ +users: + admin: + authorized_keys: + NixOS: ENC[AES256_GCM,data:sj2hkUkWp628KuXp+AnncLdawHpxb9fH1ZHnIisP0x9Tght9+/X2sWHpuMSeqi2i/R8B+Wgte66QkuwAOB0j+oB9N+66EhehmWZlK5hD/22p,iv:z18U+LvAQgPDfBBewE3lJmWZd0NGCPwJIe/h3tupuZc=,tag:ZJar3spO66JbDXygdTHh2w==,type:str] +sops: + age: + - recipient: age1qzutny0mqpcccqw6myyfntu6wcskruu9ghzvt6r4te7afkqwnguq05ex37 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjUSt2REk2Mmd0bk9ubjJk + dXFiY2JNR1dyZW9qTUdzaWZhY3c3amVwQzA0CkZHNVpZVjhsWXhVQVNaR0xONzhh + Y0lQaWNaNmpYYVdrRnZIZUhvUFUzcWMKLS0tIDAvSmF0VmpxcnZEQStXUjNCUE5Z + RnA2Lzk2WHFxOEh6dHN0aGhVSVpLTW8KA7IOvGDMBtgo4pe0Sw3Lol243xCDAJ4i + PhcJFiUObVRFZN7ISlULnOlTO3pT9jWvvmC5rDZWId3PQ8qjPvnOUg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-10-04T17:33:22Z" + mac: ENC[AES256_GCM,data:I7I7uDFEWfw9+4KROtjHMVhaxYrVK5QmLfFZShSajF0A2Zxu9lg+fDGiMHk40JC5zD31P70QS/ipye1mBGQbCbLEA7uBUhNzZ7G1g58cIXF6vSGmt0fovm0MVSxEJ44r05fx6uT4OJu5BYVxYSlG84gTj9rCFXxxcBJMrh+6yaI=,iv:c1vudsp9bg0Pc2ddRyvWn6Tf0LhqNuEjxG9D4PpHqxs=,tag:K/1PSHhrTdsNPcPmRv/2Ew==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/users/admin/default.nix b/users/admin/default.nix index 552909b..bc3ccc7 100644 --- a/users/admin/default.nix +++ b/users/admin/default.nix @@ -2,6 +2,8 @@ let cfg = config.homelab.users.admin; + + owner = config.users.users.admin.name; in { options.homelab.users.admin.enable = lib.mkEnableOption "user System Administrator"; @@ -10,6 +12,12 @@ in { config.users.users.admin.name ]; + sops.secrets."users/admin/authorized_keys" = { + format = "yaml"; + sopsFile = ../../secrets/secrets.yaml; + inherit owner; + }; + users.users.admin = { description = "System Administrator"; isNormalUser = true; @@ -17,9 +25,8 @@ in { config.users.groups.wheel.name # Enable 'sudo' for the user. ]; initialPassword = "ChangeMe"; - openssh.authorizedKeys.keys = [ - # TODO ChangeMe - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPrG+ldRBdCeHEXrsy/qHXIJYg8xQXVuiUR0DxhFjYNg" + openssh.authorizedKeys.keyFiles = [ + /run/secrets/users/admin/authorized_keys ]; packages = with pkgs; [ curl From a37c5ae83a4beb84e46d04bac7bb0ba9bde9a12d Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 4 Oct 2025 20:21:59 +0200 Subject: [PATCH 2/6] fix(sops): Add Tibo-NixFat --- .sops.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.sops.yaml b/.sops.yaml index 8d0d445..02cc451 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,8 +1,10 @@ keys: + - &tdpeuter_Tibo-NixFatDesk age1fva6s64s884z0q2w7de024sp69ucvqu0pg9shrhhqsn3ewlpjfpsh6md7y - &tdpeuter_Tibo-NixTop age1qzutny0mqpcccqw6myyfntu6wcskruu9ghzvt6r4te7afkqwnguq05ex37 creation_rules: - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ key_groups: - age: + - *tdpeuter_Tibo-NixFatDesk - *tdpeuter_Tibo-NixTop From 4ab3848c830a2a1254e78189909cd316011f5817 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 4 Oct 2025 20:25:09 +0200 Subject: [PATCH 3/6] fix(Development): Replace authorized_keys --- users/admin/default.nix | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/users/admin/default.nix b/users/admin/default.nix index bc3ccc7..4038266 100644 --- a/users/admin/default.nix +++ b/users/admin/default.nix @@ -2,8 +2,6 @@ let cfg = config.homelab.users.admin; - - owner = config.users.users.admin.name; in { options.homelab.users.admin.enable = lib.mkEnableOption "user System Administrator"; @@ -12,12 +10,6 @@ in { config.users.users.admin.name ]; - sops.secrets."users/admin/authorized_keys" = { - format = "yaml"; - sopsFile = ../../secrets/secrets.yaml; - inherit owner; - }; - users.users.admin = { description = "System Administrator"; isNormalUser = true; @@ -25,8 +17,9 @@ in { config.users.groups.wheel.name # Enable 'sudo' for the user. ]; initialPassword = "ChangeMe"; - openssh.authorizedKeys.keyFiles = [ - /run/secrets/users/admin/authorized_keys + openssh.authorizedKeys.keys = [ + # HomeLab > NixOS > admin > ssh + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGWIOOEqTy8cWKpENVbzD4p7bsQgQb/Dgpzk8i0dZ00T" ]; packages = with pkgs; [ curl From 81a07af1521169a7ffac5e0850a0e236d9580248 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 4 Oct 2025 20:26:01 +0200 Subject: [PATCH 4/6] nix flake update --- flake.lock | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 78e4127..ca6e418 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1756787288, - "narHash": "sha256-rw/PHa1cqiePdBxhF66V7R+WAP8WekQ0mCDG4CFqT8Y=", + "lastModified": 1759381078, + "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d0fc30899600b9b3466ddb260fd83deb486c32f1", + "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", "type": "github" }, "original": { @@ -37,9 +37,30 @@ "inputs": { "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", + "sops-nix": "sops-nix", "utils": "utils" } }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759188042, + "narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, From ef55596de6a09ceeb5441ebe71920a53ad9fcf0f Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Tue, 7 Oct 2025 23:02:48 +0200 Subject: [PATCH 5/6] feat(coder): Add module --- hosts/Development/default.nix | 5 + modules/apps/bind9/db.depeuter.dev | 5 +- modules/apps/coder/default.nix | 131 +++++++++++++++++++++++++++ modules/apps/default.nix | 1 + modules/apps/vaultwarden/default.nix | 2 +- 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 modules/apps/coder/default.nix diff --git a/hosts/Development/default.nix b/hosts/Development/default.nix index b2237b7..608a31c 100644 --- a/hosts/Development/default.nix +++ b/hosts/Development/default.nix @@ -7,6 +7,11 @@ bind9.enable = true; traefik.enable = true; plex.enable = true; + coder = { + enable = true; + accessUrl = "https://code.depeuter.dev"; + wildcardAccessUrl = "*.code.depeuter.dev"; + }; }; virtualisation.guest.enable = true; }; diff --git a/modules/apps/bind9/db.depeuter.dev b/modules/apps/bind9/db.depeuter.dev index 72f3825..d3b6b42 100644 --- a/modules/apps/bind9/db.depeuter.dev +++ b/modules/apps/bind9/db.depeuter.dev @@ -1,6 +1,6 @@ $TTL 604800 @ IN SOA ns1 admin ( - 15 ; Serial + 18 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire @@ -40,6 +40,9 @@ sonarr IN A 192.168.0.33 ; Development VM plex IN A 192.168.0.91 +code IN A 192.168.0.91 +*.code IN A 192.168.0.91 + ; Catchalls *.production IN A 192.168.0.31 *.development IN A 192.168.0.91 diff --git a/modules/apps/coder/default.nix b/modules/apps/coder/default.nix new file mode 100644 index 0000000..98e0a63 --- /dev/null +++ b/modules/apps/coder/default.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.homelab.apps.coder; + + postgresUser = "coder"; + postgresPassword = "ChangeMe"; + postgresDb = "coder"; + + networkName = "coder"; + proxyNet = config.homelab.apps.traefik.sharedNetworkName; + + coderVersion = "v2.25.3"; + coderDbVersion = "17.6"; +in { + options.homelab.apps.coder = { + enable = lib.mkEnableOption "Coder (Docker)"; + port = lib.mkOption { + type = lib.types.port; + default = 7080; + description = "Port to expose Coder on."; + }; + accessUrl = lib.mkOption { + type = lib.types.str; + description = "The URL to access Coder at."; + }; + wildcardAccessUrl = lib.mkOption { + type = lib.types.str; + description = "A wildcard URL to access Coder at (e.g. for workspaces)."; + }; + + db.port = lib.mkOption { + type = lib.types.either lib.types.bool lib.types.port; + default = false; + description = "Port to expose the database on. Set to false to not expose."; + }; + }; + + config = lib.mkIf cfg.enable { + homelab.virtualisation.containers.enable = true; + + systemd.services."docker-${networkName}-create-network" = { + description = "Create Docker network for ${networkName}"; + requiredBy = [ + "docker-coder.service" + "docker-coderDb.service" + ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then + ${pkgs.docker}/bin/docker network create ${networkName} + fi + ''; + }; + + virtualisation.oci-containers.containers = { + coder = let + coderPort = 7080; + in { + hostname = "coder"; + image = "ghcr.io/coder/coder:${coderVersion}"; + autoStart = true; + dependsOn = [ + "coderDb" + ]; + ports = [ + "${toString cfg.port}:${toString coderPort}/tcp" + ]; + networks = [ + networkName + proxyNet + ]; + volumes = [ + "/var/run/docker.sock:/var/run/docker.sock" + ]; + labels = { + "traefik.enable" = "true"; + "traefik.docker.network" = proxyNet; + "traefik.http.routers.coder.rule" = "Host(`code.depeuter.dev`)"; + "traefik.http.services.coder.loadbalancer.server.port" = toString coderPort; + }; + environment = { + CODER_PG_CONNECTION_URL = "postgresql://${postgresUser}:${postgresPassword}@database/${postgresDb}?sslmode=disable"; + + # Required if you are not using the tunnel + CODER_ACCESS_URL = cfg.accessUrl; + CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; + CODER_DISABLE_PATH_APPS = "true"; + + CODER_HTTP_ADDRESS = "0.0.0.0:${toString coderPort}"; + CODER_TLS_ENABLE = "false"; + + # TODO Enable me! + #CODER_REDIRECT_TO_ACCESS_URL = "true"; + + # Disable telemetry + CODER_TELEMETRY_ENABLED = "false"; + }; + }; + + coderDb = { + hostname = "coder-db"; + image = "postgres:${coderDbVersion}"; + autoStart = true; + ports = lib.mkIf cfg.db.port [ + "${toString cfg.db.port}:5432/tcp" + ]; + networks = [ + networkName + ]; + extraOptions = [ + ''--health-cmd="pg_isready -U ${postgresUser} -d ${postgresDb}"'' + "--health-interval=5s" + "--health-timeout=5s" + "--health-retries=5" + ]; + volumes = [ + "coder_data:/var/lib/postgresql/data" + ]; + environment = { + POSTGRES_USER = postgresUser; + POSTGRES_PASSWORD = postgresPassword; + POSTGRES_DB = postgresDb; + }; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/apps/default.nix b/modules/apps/default.nix index 7c8b8f8..9974f68 100644 --- a/modules/apps/default.nix +++ b/modules/apps/default.nix @@ -4,6 +4,7 @@ ./bind9 ./calibre ./changedetection + ./coder ./freshrss ./gitea ./jellyfin diff --git a/modules/apps/vaultwarden/default.nix b/modules/apps/vaultwarden/default.nix index 4510299..4b1016e 100644 --- a/modules/apps/vaultwarden/default.nix +++ b/modules/apps/vaultwarden/default.nix @@ -8,7 +8,7 @@ in { options.homelab.apps.vaultwarden = { enable = lib.mkEnableOption "Vaultwarden"; port = lib.mkOption { - type = lib.types.int; + type = lib.types.port; default = 10102; description = "Vaultwarden WebUI port"; }; From ae777ec4600a3fa02fa30579588033109603aac2 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Sat, 11 Oct 2025 15:29:58 +0200 Subject: [PATCH 6/6] fix(coder): Reverse proxy domains --- modules/apps/coder/default.nix | 37 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/modules/apps/coder/default.nix b/modules/apps/coder/default.nix index 98e0a63..18751c8 100644 --- a/modules/apps/coder/default.nix +++ b/modules/apps/coder/default.nix @@ -66,6 +66,12 @@ in { dependsOn = [ "coderDb" ]; + extraOptions = [ + "--group-add" "131" # Add docker group to access the socket + + # Modify DNS + "--dns=192.168.0.91" + ]; ports = [ "${toString cfg.port}:${toString coderPort}/tcp" ]; @@ -79,16 +85,16 @@ in { labels = { "traefik.enable" = "true"; "traefik.docker.network" = proxyNet; - "traefik.http.routers.coder.rule" = "Host(`code.depeuter.dev`)"; + "traefik.http.routers.coder.rule" = "HostRegexp(`.+\.code\.depeuter\.dev`) || Host(`code.depeuter.dev`)"; "traefik.http.services.coder.loadbalancer.server.port" = toString coderPort; }; environment = { - CODER_PG_CONNECTION_URL = "postgresql://${postgresUser}:${postgresPassword}@database/${postgresDb}?sslmode=disable"; + CODER_PG_CONNECTION_URL = "postgresql://${postgresUser}:${postgresPassword}@coder-db/${postgresDb}?sslmode=disable"; # Required if you are not using the tunnel CODER_ACCESS_URL = cfg.accessUrl; CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; - CODER_DISABLE_PATH_APPS = "true"; + CODER_DISABLE_PATH_APPS = "false"; # TODO Enable me! CODER_HTTP_ADDRESS = "0.0.0.0:${toString coderPort}"; CODER_TLS_ENABLE = "false"; @@ -105,18 +111,18 @@ in { hostname = "coder-db"; image = "postgres:${coderDbVersion}"; autoStart = true; - ports = lib.mkIf cfg.db.port [ - "${toString cfg.db.port}:5432/tcp" - ]; - networks = [ - networkName - ]; extraOptions = [ ''--health-cmd="pg_isready -U ${postgresUser} -d ${postgresDb}"'' "--health-interval=5s" "--health-timeout=5s" "--health-retries=5" ]; + ports = lib.mkIf cfg.db.port [ + "${toString cfg.db.port}:5432/tcp" + ]; + networks = [ + networkName + ]; volumes = [ "coder_data:/var/lib/postgresql/data" ]; @@ -126,6 +132,17 @@ in { POSTGRES_DB = postgresDb; }; }; + + traefik.cmd = [ + "--entrypoints.websecure.http.tls.domains[2].main=code.depeuter.dev" + "--entrypoints.websecure.http.tls.domains[2].sans=*.code.depeuter.dev" + ]; + }; + + virtualisation.docker.daemon.settings = { + dns = [ + "192.168.0.91" + ]; }; }; -} \ No newline at end of file +}