From a90630ecb6bb8f5d8a102ec0b0777f51d6d18670 Mon Sep 17 00:00:00 2001 From: plasmagoat Date: Sat, 5 Jul 2025 11:12:20 +0200 Subject: [PATCH] dump --- .forgejo/workflows/deploy-nixos.yaml | 39 ++++ .forgejo/workflows/proxmox-nixos-deploy.yml | 34 ++++ ansible/books/bootstrap/bootstrap-proxmox.yml | 33 +--- ansible/books/tasks/cloud-init-snippets.yml | 16 ++ .../books/tasks/network-interface-patch.yml | 7 + ansible/books/tasks/proxmox-repos.yml | 20 ++ ansible/books/tasks/terraform-user.yml | 13 ++ nixos/README.md | 10 +- nixos/flake.lock | 94 ++++++--- nixos/flake.nix | 171 ++++++++-------- nixos/hosts/dns/dnsmasq.nix | 2 + nixos/hosts/docker-host.nix | 46 ----- nixos/hosts/forgejo-runner/runner.nix | 24 +-- nixos/hosts/forgejo/database.nix | 13 +- nixos/hosts/forgejo/forgejo.nix | 35 +++- nixos/hosts/forgejo/host.nix | 13 ++ nixos/hosts/keycloak/custom-theme.nix | 15 ++ .../login/resource/css/custom.css | 4 + .../login/resource/theme.properties | 3 + nixos/hosts/keycloak/default.nix | 11 ++ nixos/hosts/keycloak/host.nix | 14 ++ nixos/hosts/keycloak/keycloak.nix | 31 +++ nixos/hosts/keycloak/networking.nix | 8 + nixos/hosts/keycloak/sops.nix | 12 ++ nixos/hosts/mail/host.nix | 14 ++ nixos/hosts/mail/mailserver.nix | 39 ++++ nixos/hosts/mail/networking.nix | 8 + nixos/hosts/mail/sops.nix | 8 + nixos/hosts/media/exportarr.nix | 44 +++++ nixos/hosts/media/host.nix | 12 ++ nixos/hosts/media/jellyfin-exporter.nix | 8 + nixos/hosts/media/networking.nix | 3 + nixos/hosts/media/nixarr.nix | 42 ++++ nixos/hosts/media/recyclarr.yml | 183 ++++++++++++++++++ nixos/hosts/media/sops.nix | 41 ++++ nixos/hosts/media/storage.nix | 23 +++ nixos/hosts/monitoring/alertmanager.nix | 33 ++-- .../monitoring/dashboards/traefik-access.json | 22 +-- .../hosts/monitoring/dashboards/traefik.json | 4 +- nixos/hosts/monitoring/grafana.nix | 70 +++++-- nixos/hosts/monitoring/host.nix | 10 +- nixos/hosts/monitoring/influxdb.nix | 25 +++ nixos/hosts/monitoring/jellyfin-exporter.nix | 14 ++ nixos/hosts/monitoring/prometheus.nix | 177 +++++++++++++---- .../provisioning/notifiers/contact-points.yml | 11 -- nixos/hosts/monitoring/sops.nix | 15 +- nixos/hosts/nixos-builder/builder.nix | 3 + nixos/hosts/nixos-builder/host.nix | 9 + nixos/hosts/nixos-builder/networking.nix | 9 + nixos/hosts/nixos-builder/runner-user.nix | 19 ++ nixos/hosts/sandbox/host.nix | 9 +- nixos/hosts/sandbox/storage.nix | 12 +- nixos/hosts/sandbox/warpgate.nix | 35 ++++ .../traefik/configuration/auth/routers.nix | 24 +++ .../traefik/configuration/auth/services.nix | 5 + .../traefik/configuration/infra/routers.nix | 43 ++++ .../traefik/configuration/infra/services.nix | 13 ++ .../configuration/media-center/routers.nix | 35 ++++ .../configuration/media-center/services.nix | 6 + .../traefik/configuration/middlewares.nix | 43 +++- .../traefik/configuration/misc/routers.nix | 8 + .../traefik/configuration/misc/services.nix | 3 + .../configuration/monitoring/routers.nix | 28 +++ .../configuration/monitoring/services.nix | 6 + .../traefik/configuration/photos/routers.nix | 35 ++++ .../traefik/configuration/photos/services.nix | 7 + nixos/hosts/traefik/configuration/routers.nix | 140 -------------- .../hosts/traefik/configuration/services.nix | 38 ---- nixos/hosts/traefik/configuration/static.nix | 8 +- nixos/hosts/traefik/host.nix | 10 +- nixos/hosts/traefik/oauth2proxy.nix | 76 ++++++++ nixos/hosts/traefik/promtail.nix | 12 +- nixos/hosts/traefik/sops.nix | 10 + nixos/hosts/traefik/traefik.nix | 69 +++++-- nixos/hosts/warpgate/host.nix | 0 nixos/hosts/warpgate/warpgate.nix | 14 ++ nixos/modules/docker-host.nix | 77 -------- nixos/modules/docker.nix | 11 -- nixos/modules/forgejo.nix | 54 ------ nixos/modules/promtail.nix | 84 +++++--- nixos/secrets/README.md | 6 +- nixos/secrets/influxdb/secrets.yml | 17 ++ nixos/secrets/keycloak/secrets.yml | 17 ++ nixos/secrets/mailserver/secrets.yml | 16 ++ nixos/secrets/nixarr/secrets.yml | 24 +++ nixos/secrets/telegram/secrets.yml | 4 +- nixos/secrets/traefik/secrets.yml | 17 ++ nixos/templates/base.nix | 9 +- nixos/templates/docker.nix | 15 -- proxmox-infra/.gitignore | 7 + proxmox-infra/.terraform.lock.hcl | 24 +++ proxmox-infra/main.tf | 52 +++++ proxmox-infra/outputs.tf | 0 proxmox-infra/provider.tf | 9 + proxmox-infra/sandbox.tf | 106 ++++++++++ proxmox-infra/terraform.tfstate.backup | 1 + proxmox-infra/variables.tf | 30 +++ proxmox-infra/versions.tf | 9 + 98 files changed, 2063 insertions(+), 729 deletions(-) create mode 100644 .forgejo/workflows/deploy-nixos.yaml create mode 100644 .forgejo/workflows/proxmox-nixos-deploy.yml create mode 100644 ansible/books/tasks/cloud-init-snippets.yml create mode 100644 ansible/books/tasks/network-interface-patch.yml create mode 100644 ansible/books/tasks/proxmox-repos.yml create mode 100644 ansible/books/tasks/terraform-user.yml delete mode 100644 nixos/hosts/docker-host.nix create mode 100644 nixos/hosts/forgejo/host.nix create mode 100644 nixos/hosts/keycloak/custom-theme.nix create mode 100644 nixos/hosts/keycloak/custom-theme/login/resource/css/custom.css create mode 100644 nixos/hosts/keycloak/custom-theme/login/resource/theme.properties create mode 100644 nixos/hosts/keycloak/default.nix create mode 100644 nixos/hosts/keycloak/host.nix create mode 100644 nixos/hosts/keycloak/keycloak.nix create mode 100644 nixos/hosts/keycloak/networking.nix create mode 100644 nixos/hosts/keycloak/sops.nix create mode 100644 nixos/hosts/mail/host.nix create mode 100644 nixos/hosts/mail/mailserver.nix create mode 100644 nixos/hosts/mail/networking.nix create mode 100644 nixos/hosts/mail/sops.nix create mode 100644 nixos/hosts/media/exportarr.nix create mode 100644 nixos/hosts/media/host.nix create mode 100644 nixos/hosts/media/jellyfin-exporter.nix create mode 100644 nixos/hosts/media/networking.nix create mode 100644 nixos/hosts/media/nixarr.nix create mode 100644 nixos/hosts/media/recyclarr.yml create mode 100644 nixos/hosts/media/sops.nix create mode 100644 nixos/hosts/media/storage.nix create mode 100644 nixos/hosts/monitoring/influxdb.nix create mode 100644 nixos/hosts/monitoring/jellyfin-exporter.nix delete mode 100644 nixos/hosts/monitoring/provisioning/notifiers/contact-points.yml create mode 100644 nixos/hosts/nixos-builder/builder.nix create mode 100644 nixos/hosts/nixos-builder/host.nix create mode 100644 nixos/hosts/nixos-builder/networking.nix create mode 100644 nixos/hosts/nixos-builder/runner-user.nix create mode 100644 nixos/hosts/sandbox/warpgate.nix create mode 100644 nixos/hosts/traefik/configuration/auth/routers.nix create mode 100644 nixos/hosts/traefik/configuration/auth/services.nix create mode 100644 nixos/hosts/traefik/configuration/infra/routers.nix create mode 100644 nixos/hosts/traefik/configuration/infra/services.nix create mode 100644 nixos/hosts/traefik/configuration/media-center/routers.nix create mode 100644 nixos/hosts/traefik/configuration/media-center/services.nix create mode 100644 nixos/hosts/traefik/configuration/misc/routers.nix create mode 100644 nixos/hosts/traefik/configuration/misc/services.nix create mode 100644 nixos/hosts/traefik/configuration/monitoring/routers.nix create mode 100644 nixos/hosts/traefik/configuration/monitoring/services.nix create mode 100644 nixos/hosts/traefik/configuration/photos/routers.nix create mode 100644 nixos/hosts/traefik/configuration/photos/services.nix delete mode 100644 nixos/hosts/traefik/configuration/routers.nix delete mode 100644 nixos/hosts/traefik/configuration/services.nix create mode 100644 nixos/hosts/traefik/oauth2proxy.nix create mode 100644 nixos/hosts/traefik/sops.nix create mode 100644 nixos/hosts/warpgate/host.nix create mode 100644 nixos/hosts/warpgate/warpgate.nix delete mode 100644 nixos/modules/docker-host.nix delete mode 100644 nixos/modules/docker.nix delete mode 100644 nixos/modules/forgejo.nix create mode 100644 nixos/secrets/influxdb/secrets.yml create mode 100644 nixos/secrets/keycloak/secrets.yml create mode 100644 nixos/secrets/mailserver/secrets.yml create mode 100644 nixos/secrets/nixarr/secrets.yml create mode 100644 nixos/secrets/traefik/secrets.yml delete mode 100644 nixos/templates/docker.nix create mode 100644 proxmox-infra/.gitignore create mode 100644 proxmox-infra/.terraform.lock.hcl create mode 100644 proxmox-infra/main.tf create mode 100644 proxmox-infra/outputs.tf create mode 100644 proxmox-infra/provider.tf create mode 100644 proxmox-infra/sandbox.tf create mode 100644 proxmox-infra/terraform.tfstate.backup create mode 100644 proxmox-infra/variables.tf create mode 100644 proxmox-infra/versions.tf diff --git a/.forgejo/workflows/deploy-nixos.yaml b/.forgejo/workflows/deploy-nixos.yaml new file mode 100644 index 0000000..a285d8e --- /dev/null +++ b/.forgejo/workflows/deploy-nixos.yaml @@ -0,0 +1,39 @@ +name: Deploy NixOS VM + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: docker + container: + image: nixos/nix + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install Terraform + run: nix-env -iA nixpkgs.terraform + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Terraform Init & Apply + run: | + terraform init + terraform apply -auto-approve + working-directory: ./terraform + env: + PROXMOX_PASSWORD: ${{ secrets.PROXMOX_PASSWORD }} + + - name: Deploy NixOS via nixos-anywhere + run: | + nix run github:numtide/nixos-anywhere -- \ + --build-on-remote \ + --flake .#new-vm \ + root@ diff --git a/.forgejo/workflows/proxmox-nixos-deploy.yml b/.forgejo/workflows/proxmox-nixos-deploy.yml new file mode 100644 index 0000000..ff9c203 --- /dev/null +++ b/.forgejo/workflows/proxmox-nixos-deploy.yml @@ -0,0 +1,34 @@ +name: Terraform Proxmox NixOS VM Deploy + +on: + workflow_dispatch: + +jobs: + deploy-nixos-vm: + runs-on: nixos-latest + steps: + - name: Install nodejs + run: nix-env -iA nixpkgs.nodejs + + - name: Install terraform + run: nix-env -iA nixpkgs.terraform + + - name: Install sops + run: nix-env -iA nixpkgs.sops + + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Decrypt secrets + env: + SOPS_AGE_KEY_FILE: ${{ secrets.AGE_KEY_FILE }} + run: | + sops --decrypt secrets.yaml.enc > secrets.yaml + + - name: Terraform Init + run: terraform init + + - name: Terraform Apply + env: + PROXMOX_PASSWORD: ${{ secrets.PROXMOX_PASSWORD }} + run: terraform apply -auto-approve diff --git a/ansible/books/bootstrap/bootstrap-proxmox.yml b/ansible/books/bootstrap/bootstrap-proxmox.yml index add0c45..02a8981 100644 --- a/ansible/books/bootstrap/bootstrap-proxmox.yml +++ b/ansible/books/bootstrap/bootstrap-proxmox.yml @@ -3,37 +3,14 @@ become: true pre_tasks: - - name: Remove enterprise repository - ansible.builtin.apt_repository: - update_cache: false - repo: deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise - state: absent - - name: Remove enterprise pbs repository - ansible.builtin.apt_repository: - update_cache: false - repo: deb https://enterprise.proxmox.com/debian/pbs bookworm InRelease - state: absent - - name: Remove enterprise ceph repository - ansible.builtin.apt_repository: - update_cache: false - repo: deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise - state: absent - - name: Add community repository - ansible.builtin.apt_repository: - update_cache: true - repo: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription - state: present + - import_tasks: ../tasks/proxmox-repos.yml tasks: - - name: Ensure ethtool offload post-up is present for eno1 - ansible.builtin.lineinfile: - path: /etc/network/interfaces - line: "\tpost-up ethtool -K eno2 tso off gso off" - insertafter: "^iface eno2 inet manual" - state: present - backup: yes - + - import_tasks: ../tasks/network-interface-patch.yml - import_tasks: ../tasks/packages.yml + - import_tasks: ../tasks/terraform-user.yml + - import_tasks: ../tasks/cloud-init-snippets.yml + # - import_tasks: ../tasks/locale.yml # - import_tasks: ../tasks/keyboard.yml # - import_tasks: ../tasks/users.yml diff --git a/ansible/books/tasks/cloud-init-snippets.yml b/ansible/books/tasks/cloud-init-snippets.yml new file mode 100644 index 0000000..8105b9e --- /dev/null +++ b/ansible/books/tasks/cloud-init-snippets.yml @@ -0,0 +1,16 @@ +- name: Ensure snippets directory exists + ansible.builtin.file: + path: /var/lib/vz/snippets + state: directory + mode: "0755" + +- name: Upload cloud-init snippet + ansible.builtin.copy: + src: cloud-init-user-data.yaml + dest: /var/lib/vz/snippets/cloud-init-user-data.yaml + mode: "0644" + +- name: Verify cloud-init snippet is recognized + ansible.builtin.shell: qm cloudinit dump 9000 user + register: cloudinit_check + ignore_errors: true diff --git a/ansible/books/tasks/network-interface-patch.yml b/ansible/books/tasks/network-interface-patch.yml new file mode 100644 index 0000000..4c07ebc --- /dev/null +++ b/ansible/books/tasks/network-interface-patch.yml @@ -0,0 +1,7 @@ +- name: Ensure ethtool offload post-up is present for eno1 + ansible.builtin.lineinfile: + path: /etc/network/interfaces + line: "\tpost-up ethtool -K eno2 tso off gso off" + insertafter: "^iface eno2 inet manual" + state: present + backup: yes diff --git a/ansible/books/tasks/proxmox-repos.yml b/ansible/books/tasks/proxmox-repos.yml new file mode 100644 index 0000000..c8e3dc5 --- /dev/null +++ b/ansible/books/tasks/proxmox-repos.yml @@ -0,0 +1,20 @@ +- name: Remove enterprise repository + ansible.builtin.apt_repository: + update_cache: false + repo: deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise + state: absent +- name: Remove enterprise pbs repository + ansible.builtin.apt_repository: + update_cache: false + repo: deb https://enterprise.proxmox.com/debian/pbs bookworm InRelease + state: absent +- name: Remove enterprise ceph repository + ansible.builtin.apt_repository: + update_cache: false + repo: deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise + state: absent +- name: Add community repository + ansible.builtin.apt_repository: + update_cache: true + repo: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription + state: present diff --git a/ansible/books/tasks/terraform-user.yml b/ansible/books/tasks/terraform-user.yml new file mode 100644 index 0000000..81cca86 --- /dev/null +++ b/ansible/books/tasks/terraform-user.yml @@ -0,0 +1,13 @@ +- name: Create Terraform user + ansible.builtin.command: pveum user add terraform@pve --password {{ terraform_password }} + args: + creates: "/etc/pve/user.cfg" # Prevent re-run errors + +- name: Add TerraformProvisioner role + ansible.builtin.command: > + pveum role add TerraformProvisioner -privs "VM.Allocate VM.Config.CloudInit VM.Config.Disk VM.Config.Network VM.PowerMgmt VM.Console Datastore.AllocateSpace" + ignore_errors: true # Might already exist + +- name: Assign role to user + ansible.builtin.command: > + pveum aclmod / -user terraform@pve -role TerraformProvisioner diff --git a/nixos/README.md b/nixos/README.md index f19ef37..24d74ca 100644 --- a/nixos/README.md +++ b/nixos/README.md @@ -1,9 +1,13 @@ 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@192.168.1.148 --verbose +nixos-rebuild switch --flake .#sandbox --target-host root@sandbox.lab --verbose nixos-rebuild switch --flake .#monitoring --target-host root@monitor.lab --verbose -nixos-rebuild switch --flake .#forgejo --target-host root@192.168.1.249 --verbose +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 .#runner --target-host root@forgejo-runner-01.lab --override-input runnerId 01 nixos-rebuild switch --flake .#runner01 --target-host root@forgejo-runner-01.lab --verbose + +nixos-rebuild switch --flake .#builder --target-host root@nixos-builder.lab --verbose +nix build .#base --builders "ssh://root@nixos-builder.lab x86_64-linux" diff --git a/nixos/flake.lock b/nixos/flake.lock index ff903c9..becb6f0 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1,42 +1,42 @@ { "nodes": { - "nixlib": { - "locked": { - "lastModified": 1736643958, - "narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixos-generators": { + "nixarr": { "inputs": { - "nixlib": "nixlib", - "nixpkgs": [ - "nixpkgs" - ] + "nixpkgs": "nixpkgs", + "vpnconfinement": "vpnconfinement", + "website-builder": "website-builder" }, "locked": { - "lastModified": 1747663185, - "narHash": "sha256-Obh50J+O9jhUM/FgXtI3he/QRNiV9+J53+l+RlKSaAk=", - "owner": "nix-community", - "repo": "nixos-generators", - "rev": "ee07ba0d36c38e9915c55d2ac5a8fb0f05f2afcc", + "lastModified": 1749909656, + "narHash": "sha256-+BetnYiov7fa/rHNiAq29rFa31Kjfmxh0HrNO1um2Ak=", + "owner": "rasmus-kirk", + "repo": "nixarr", + "rev": "ec1cce4c218f32d8fa209b30e093e5da9d95fb50", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "nixos-generators", + "owner": "rasmus-kirk", + "repo": "nixarr", "type": "github" } }, "nixpkgs": { + "locked": { + "lastModified": 1748662220, + "narHash": "sha256-7gGa49iB9nCnFk4h/g9zwjlQAyjtpgcFkODjcOQS0Es=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "59138c7667b7970d205d6a05a8bfa2d78caa3643", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1748809735, "narHash": "sha256-UR5vKj8rwKQmE8wxKFHgoJKbod05DMoH5phTje4L1l8=", @@ -53,8 +53,8 @@ }, "root": { "inputs": { - "nixos-generators": "nixos-generators", - "nixpkgs": "nixpkgs", + "nixarr": "nixarr", + "nixpkgs": "nixpkgs_2", "sops-nix": "sops-nix" } }, @@ -77,6 +77,42 @@ "repo": "sops-nix", "type": "github" } + }, + "vpnconfinement": { + "locked": { + "lastModified": 1743810720, + "narHash": "sha256-kbv/W4gizUSa6qH2rUQdgPj9AJaeN9k2XSWUYqj7IMU=", + "owner": "Maroka-chan", + "repo": "VPN-Confinement", + "rev": "74ae51e6d18b972ecc918ab43e8bde60c21a65d8", + "type": "github" + }, + "original": { + "owner": "Maroka-chan", + "repo": "VPN-Confinement", + "type": "github" + } + }, + "website-builder": { + "inputs": { + "nixpkgs": [ + "nixarr", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748552643, + "narHash": "sha256-UI3dlA/6WOitW3ejPhwYvB/yxrVWpdTmh96Hl8CEUis=", + "owner": "rasmus-kirk", + "repo": "website-builder", + "rev": "f399b9c89d45a0150ce6230c6df23f62f9c3cf89", + "type": "github" + }, + "original": { + "owner": "rasmus-kirk", + "repo": "website-builder", + "type": "github" + } } }, "root": "root", diff --git a/nixos/flake.nix b/nixos/flake.nix index 5e01942..eadd118 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -1,107 +1,106 @@ { - description = "Unified flake for Proxmox base image + live NixOS VMs"; + description = "NixOS HomeLab"; inputs = { - # Nixpkgs repo for system packages nixpkgs.url = "github:nixos/nixpkgs"; - # nixos-generators lets us produce a "proxmox"-formatted image - nixos-generators = { - url = "github:nix-community/nixos-generators"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - # sops-nix secret management sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + nixarr.url = "github:rasmus-kirk/nixarr"; + # simple-nixos-mailserver = { + # url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; + # inputs.nixpkgs.follwos = "nixpkgs"; + # }; }; - outputs = { self, nixpkgs, nixos-generators, sops-nix,... }: - let - system = "x86_64-linux"; + outputs = inputs @ {...}: let + system = "x86_64-linux"; - ################################################################################ - # A) Define “live” NixOS VM configurations under nixosConfigurations - ################################################################################ - liveVMs = { - traefik = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/traefik/host.nix ]; - }; - - sandbox = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/sandbox/host.nix ]; - }; - - dns = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/dns/host.nix ]; - }; - - monitoring = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/monitoring/host.nix sops-nix.nixosModules.sops ]; - }; - - forgejo = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/forgejo/host.nix sops-nix.nixosModules.sops ]; - }; - - runner01 = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ ./hosts/forgejo-runner/host.nix sops-nix.nixosModules.sops ]; - specialArgs.runnerId = "01"; - }; - - # dockerHost = pkgs.lib.nixosSystem { - # inherit system; - # modules = [ - # ./configuration.nix - # ./users/plasmagoat.nix - # ./hosts/docker-host.nix # Docker‐Host VM settings (shown below) - # ]; - # }; + liveVMs = { + traefik = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/traefik/host.nix + inputs.sops-nix.nixosModules.sops + ]; }; - ################################################################################ - # B) Use nixos-generators to produce “template” images for Proxmox - ################################################################################ - - # 1) Existing Proxmox “base” image generator - proxmoxTemplate = nixos-generators.nixosGenerate { - system = "x86_64-linux"; - modules = [ ./base.nix ]; - format = "proxmox"; # outputs a .vma.zst suitable for qmrestore + sandbox = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/sandbox/host.nix + inputs.sops-nix.nixosModules.sops + ]; }; - # 2) A “docker” generator which builds a Proxmox‐ready template - docker = nixos-generators.nixosGenerate { - system = "x86_64-linux"; - modules = [ ./templates/docker.nix ]; - format = "proxmox"; + mail = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/mail/host.nix + inputs.sops-nix.nixosModules.sops + ]; }; - in - { - ################################################################################ - # 1) Export “live” VM configs so you can run: - # nixos-rebuild switch --flake .#traefik --target-host root@ - # nixos-rebuild switch --flake .#sandbox --target-host root@ - # nixos-rebuild switch --flake .#dockerHost --target-host root@ - ################################################################################ - nixosConfigurations = liveVMs; + keycloak = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/keycloak/host.nix + inputs.sops-nix.nixosModules.sops + ]; + }; - ################################################################################ - # 2) Export Proxmox template images under packages.x86_64-linux: - # - # • proxmox → `nix build .#proxmox` (generic base) - # • docker → `nix build .#docker` (docker template) - ################################################################################ - packages.x86_64-linux = { - proxmoxTemplate = proxmoxTemplate; - docker = docker; + dns = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/dns/host.nix + inputs.sops-nix.nixosModules.sops + ]; + }; + + monitoring = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/monitoring/host.nix + inputs.sops-nix.nixosModules.sops + ]; + }; + + media = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/media/host.nix + inputs.sops-nix.nixosModules.sops + inputs.nixarr.nixosModules.default + ]; + }; + + forgejo = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/forgejo/host.nix + inputs.sops-nix.nixosModules.sops + ]; + }; + + runner01 = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/forgejo-runner/host.nix + inputs.sops-nix.nixosModules.sops + ]; + specialArgs.runnerId = "01"; + }; + + builder = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/nixos-builder/host.nix + inputs.sops-nix.nixosModules.sops + ]; }; }; + in { + nixosConfigurations = liveVMs; + }; } diff --git a/nixos/hosts/dns/dnsmasq.nix b/nixos/hosts/dns/dnsmasq.nix index b076b54..5cecdf1 100644 --- a/nixos/hosts/dns/dnsmasq.nix +++ b/nixos/hosts/dns/dnsmasq.nix @@ -33,6 +33,8 @@ # "/proxmox-01.lab/192.168.1.205" # "/nas-01.lab/192.168.1.226" + "/mail.procopius.dk/213.32.245.247" + # Split Horizon DNS "/procopius.dk/192.168.1.80" "/.procopius.dk/192.168.1.80" diff --git a/nixos/hosts/docker-host.nix b/nixos/hosts/docker-host.nix deleted file mode 100644 index ce76bed..0000000 --- a/nixos/hosts/docker-host.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ config, pkgs, modulesPath, lib, ... }: - -{ - # Pull in all the shared settings from configuration.nix - imports = [ ../configuration.nix ]; - - config = lib.recursiveUpdate config ({ - # (Here, add anything live‐VM‐specific—e.g. NFS mounts, Docker, Compose service, - # static IP, or “import users/plasmagoat.nix” if you prefer.) - - networking.interfaces.enp0s25 = { - useDHCP = false; - ipv4.addresses = [ { address = "192.168.1.50"; prefixLength = 24; } ]; - ipv4.gateway = "192.168.1.1"; - }; - - # Docker + Compose bits, for example: - fileSystems."/mnt/nas" = { - device = "192.168.1.100:/export/docker-volumes"; - fsType = "nfs"; - options = [ "defaults" "nofail" "x-systemd.requires=network-online.target" ]; - }; - - environment.systemPackages = with pkgs; [ - pkgs.docker - pkgs.docker-compose - # …plus anything else you want only on live VM… - ]; - - services.docker.enable = true; - - systemd.services.dockerComposeApp = { - description = "Auto-start Docker‐Compose stack"; - after = [ "network-online.target" "docker.service" ]; - wants = [ "network-online.target" "docker.service" ]; - serviceConfig = { - WorkingDirectory = "/etc/docker-compose-app"; - ExecStart = "${pkgs.docker-compose}/bin/docker-compose -f /etc/docker-compose-app/docker-compose.yml up"; - ExecStop = "${pkgs.docker-compose}/bin/docker-compose -f /etc/docker-compose-app/docker-compose.yml down"; - Restart = "always"; - RestartSec = 10; - }; - wantedBy = [ "multi-user.target" ]; - }; - }); -} diff --git a/nixos/hosts/forgejo-runner/runner.nix b/nixos/hosts/forgejo-runner/runner.nix index 94707a4..e36c380 100644 --- a/nixos/hosts/forgejo-runner/runner.nix +++ b/nixos/hosts/forgejo-runner/runner.nix @@ -1,11 +1,8 @@ -{ config, pkgs,... }: - { - # users.users.forgejo-runner = { - # isSystemUser = true; - # extraGroups = [ "docker" ]; # Optional: if using docker jobs - # }; - + config, + pkgs, + ... +}: { services.gitea-actions-runner = { package = pkgs.forgejo-actions-runner; instances.default = { @@ -25,19 +22,18 @@ ## optionally provide native execution on the host: "native:host" ]; + settings = { + log = { + level = "debug"; + }; + }; }; }; - environment.systemPackages = with pkgs; [ wget nodejs ]; - # systemd.services."forgejo-actions-runner-default".serviceConfig = { - # User = "forgejo-runner"; - # Group = "forgejo-runner"; - # }; - - virtualisation.docker.enable = true; # Optional: if using docker + virtualisation.docker.enable = true; # Optional: if using docker } diff --git a/nixos/hosts/forgejo/database.nix b/nixos/hosts/forgejo/database.nix index 9d6f23e..ebd2bc1 100644 --- a/nixos/hosts/forgejo/database.nix +++ b/nixos/hosts/forgejo/database.nix @@ -1,14 +1,17 @@ -{ lib, pkgs, config, ... }: - { + lib, + pkgs, + config, + ... +}: { systemd.services.forgejo = { - after = [ "postgresql.service" ]; - requires = [ "postgresql.service" ]; + after = ["postgresql.service"]; + requires = ["postgresql.service"]; }; services.postgresql = { enable = true; - ensureDatabases = [ "forgejo" ]; + ensureDatabases = ["forgejo"]; ensureUsers = [ { name = "forgejo"; diff --git a/nixos/hosts/forgejo/forgejo.nix b/nixos/hosts/forgejo/forgejo.nix index 633100c..bbeaf65 100644 --- a/nixos/hosts/forgejo/forgejo.nix +++ b/nixos/hosts/forgejo/forgejo.nix @@ -1,12 +1,15 @@ -{ lib, pkgs, config, ... }: -let +{ + lib, + pkgs, + config, + ... +}: let cfg = config.services.forgejo; srv = cfg.settings.server; domain = "git.procopius.dk"; ssh_domain = "gitssh.procopius.dk"; -in -{ - users.users.plasmagoat.extraGroups = [ "forgejo" ]; +in { + users.users.plasmagoat.extraGroups = ["forgejo"]; services.forgejo = { enable = true; @@ -25,6 +28,15 @@ in SSH_PORT = 2222; SSH_DOMAIN = ssh_domain; }; + mailer = { + ENABLED = true; + FROM = "git@procopius.dk"; + + PROTOCOL = "smtp+starttls"; + SMTP_ADDR = "mail.procopius.dk"; + USER = "admin@procopius.dk"; + PASSWD = "mikael"; + }; database = { DB_TYPE = lib.mkForce "postgres"; HOST = "/run/postgresql"; @@ -33,12 +45,23 @@ in }; service = { DISABLE_REGISTRATION = true; + # ENABLE_INTERNAL_SIGNIN = false; + ENABLE_NOTIFY_MAIL = true; }; metrics = { ENABLED = true; ENABLED_ISSUE_BY_REPOSITORY = true; ENABLED_ISSUE_BY_LABEL = true; }; + actions = { + ZOMBIE_TASK_TIMEOUT = "30m"; + }; + oauth2 = { + }; + oauth2_client = { + ENABLE_AUTO_REGISTRATION = true; + UPDATE_AVATAR = true; + }; # log = { # ROOT_PATH = "/var/log/forgejo"; # MODE = "file"; @@ -63,5 +86,5 @@ in ''; # Optional: firewall - networking.firewall.allowedTCPPorts = [ 3000 2222 ]; + networking.firewall.allowedTCPPorts = [3000 2222]; } diff --git a/nixos/hosts/forgejo/host.nix b/nixos/hosts/forgejo/host.nix new file mode 100644 index 0000000..1883cca --- /dev/null +++ b/nixos/hosts/forgejo/host.nix @@ -0,0 +1,13 @@ +{ config, pkgs, modulesPath, lib, ... }: + +{ + imports = [ + ../../templates/base.nix + ../../secrets/shared-sops.nix + ./sops.nix + ./networking.nix + ./storage.nix + ./forgejo.nix + ./database.nix + ]; +} diff --git a/nixos/hosts/keycloak/custom-theme.nix b/nixos/hosts/keycloak/custom-theme.nix new file mode 100644 index 0000000..8af370a --- /dev/null +++ b/nixos/hosts/keycloak/custom-theme.nix @@ -0,0 +1,15 @@ +{stdenv}: +stdenv.mkDerivation rec { + name = "keycloak_custom_theme"; + version = "1.0"; + + src = ./custom_theme; + + nativeBuildInputs = []; + buildInputs = []; + + installPhase = '' + mkdir -p $out + cp -a login $out + ''; +} diff --git a/nixos/hosts/keycloak/custom-theme/login/resource/css/custom.css b/nixos/hosts/keycloak/custom-theme/login/resource/css/custom.css new file mode 100644 index 0000000..17d771e --- /dev/null +++ b/nixos/hosts/keycloak/custom-theme/login/resource/css/custom.css @@ -0,0 +1,4 @@ +body { + background: red; + color: blue; +} diff --git a/nixos/hosts/keycloak/custom-theme/login/resource/theme.properties b/nixos/hosts/keycloak/custom-theme/login/resource/theme.properties new file mode 100644 index 0000000..33d3768 --- /dev/null +++ b/nixos/hosts/keycloak/custom-theme/login/resource/theme.properties @@ -0,0 +1,3 @@ +parent=base +import=common/keycloak +styles=css/custom.css diff --git a/nixos/hosts/keycloak/default.nix b/nixos/hosts/keycloak/default.nix new file mode 100644 index 0000000..772ca54 --- /dev/null +++ b/nixos/hosts/keycloak/default.nix @@ -0,0 +1,11 @@ +{pkgs, ...}: let + callPackage = pkgs.callPackage; +in { + nixpkgs.overlays = [ + (final: prev: { + custom_keycloak_themes = { + custom = callPackage ./custom_theme.nix {}; + }; + }) + ]; +} diff --git a/nixos/hosts/keycloak/host.nix b/nixos/hosts/keycloak/host.nix new file mode 100644 index 0000000..025e23f --- /dev/null +++ b/nixos/hosts/keycloak/host.nix @@ -0,0 +1,14 @@ +{ + config, + pkgs, + modulesPath, + lib, + ... +}: { + imports = [ + ../../templates/base.nix + ./networking.nix + ./sops.nix + ./keycloak.nix + ]; +} diff --git a/nixos/hosts/keycloak/keycloak.nix b/nixos/hosts/keycloak/keycloak.nix new file mode 100644 index 0000000..59b8d80 --- /dev/null +++ b/nixos/hosts/keycloak/keycloak.nix @@ -0,0 +1,31 @@ +{ + config, + pkgs, + ... +}: { + services.postgresql.enable = true; + + services.keycloak = { + enable = true; + initialAdminPassword = "password"; + database = { + type = "postgresql"; + createLocally = true; + + username = "keycloak"; + passwordFile = config.sops.secrets.keycloak_psql_pass.path; + }; + + settings = { + hostname = "keycloak.procopius.dk"; + # hostname-admin = "http://keycloak.lab:8080"; + # hostname-strict = false; + # hostname-backchannel-dynamic = true; + http-enabled = true; + http-port = 8080; + proxy-headers = "xforwarded"; + }; + }; + + networking.firewall.allowedTCPPorts = [8080]; +} diff --git a/nixos/hosts/keycloak/networking.nix b/nixos/hosts/keycloak/networking.nix new file mode 100644 index 0000000..0705165 --- /dev/null +++ b/nixos/hosts/keycloak/networking.nix @@ -0,0 +1,8 @@ +{ + config, + lib, + pkgs, + ... +}: { + networking.hostName = "keycloak"; +} diff --git a/nixos/hosts/keycloak/sops.nix b/nixos/hosts/keycloak/sops.nix new file mode 100644 index 0000000..b2ae881 --- /dev/null +++ b/nixos/hosts/keycloak/sops.nix @@ -0,0 +1,12 @@ +{...}: let + keycloakSops = ../../secrets/keycloak/secrets.yml; +in { + sops.secrets.keycloak_psql_pass = { + sopsFile = keycloakSops; + mode = "0440"; + }; + sops.secrets.keycloak_admin_pass = { + sopsFile = keycloakSops; + mode = "0440"; + }; +} 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/exportarr.nix b/nixos/hosts/media/exportarr.nix new file mode 100644 index 0000000..01ed792 --- /dev/null +++ b/nixos/hosts/media/exportarr.nix @@ -0,0 +1,44 @@ +{config, ...}: { + services.prometheus.exporters.exportarr-sonarr = { + enable = true; + url = "http://media.lab:8989"; + port = 9707; + openFirewall = true; + apiKeyFile = config.sops.secrets.sonarr-api-key.path; + }; + services.prometheus.exporters.exportarr-readarr = { + enable = true; + url = "http://media.lab:8787"; + port = 9708; + openFirewall = true; + apiKeyFile = config.sops.secrets.readarr-api-key.path; + }; + services.prometheus.exporters.exportarr-radarr = { + enable = true; + url = "http://media.lab:7878"; + port = 9709; + openFirewall = true; + apiKeyFile = config.sops.secrets.radarr-api-key.path; + }; + services.prometheus.exporters.exportarr-prowlarr = { + enable = true; + url = "http://media.lab:9696"; + port = 9710; + openFirewall = true; + apiKeyFile = config.sops.secrets.prowlarr-api-key.path; + }; + services.prometheus.exporters.exportarr-lidarr = { + enable = true; + url = "http://media.lab:8686"; + port = 9711; + openFirewall = true; + apiKeyFile = config.sops.secrets.lidarr-api-key.path; + }; + services.prometheus.exporters.exportarr-bazarr = { + enable = true; + url = "http://media.lab:6767"; + port = 9712; + openFirewall = true; + apiKeyFile = config.sops.secrets.bazarr-api-key.path; + }; +} diff --git a/nixos/hosts/media/host.nix b/nixos/hosts/media/host.nix new file mode 100644 index 0000000..f94483f --- /dev/null +++ b/nixos/hosts/media/host.nix @@ -0,0 +1,12 @@ +{ + imports = [ + ../../templates/base.nix + ../../secrets/shared-sops.nix + ./networking.nix + ./storage.nix + ./nixarr.nix + ./exportarr.nix + ./jellyfin-exporter.nix + ./sops.nix + ]; +} diff --git a/nixos/hosts/media/jellyfin-exporter.nix b/nixos/hosts/media/jellyfin-exporter.nix new file mode 100644 index 0000000..aabe279 --- /dev/null +++ b/nixos/hosts/media/jellyfin-exporter.nix @@ -0,0 +1,8 @@ +{config, ...}: { + services.prometheus.exporters.json = { + enable = true; + configFile = config.sops.secrets.jellyfin-exporter-config.path; + openFirewall = true; + user = "jellyfin"; + }; +} diff --git a/nixos/hosts/media/networking.nix b/nixos/hosts/media/networking.nix new file mode 100644 index 0000000..87bba23 --- /dev/null +++ b/nixos/hosts/media/networking.nix @@ -0,0 +1,3 @@ +{ + networking.hostName = "media"; +} diff --git a/nixos/hosts/media/nixarr.nix b/nixos/hosts/media/nixarr.nix new file mode 100644 index 0000000..56118a3 --- /dev/null +++ b/nixos/hosts/media/nixarr.nix @@ -0,0 +1,42 @@ +{config, ...}: { + nixarr = { + enable = true; + # These two values are also the default, but you can set them to whatever + # else you want + # WARNING: Do _not_ set them to `/home/user/whatever`, it will not work! + mediaDir = "/data/media"; + stateDir = "/data/media/.state/nixarr"; + + vpn = { + enable = true; + # WARNING: This file must _not_ be in the config git directory + # You can usually get this wireguard file from your VPN provider + wgConf = config.sops.secrets.nixarr-vpn-conf.path; + }; + + jellyfin = { + enable = true; + }; + + transmission = { + enable = true; + vpn.enable = true; + peerPort = 51820; # Set this to the port forwarded by your VPN + }; + + # It is possible for this module to run the *Arrs through a VPN, but it + # is generally not recommended, as it can cause rate-limiting issues. + bazarr.enable = true; + lidarr.enable = true; + prowlarr.enable = true; + radarr.enable = true; + readarr.enable = true; + sonarr.enable = true; + jellyseerr.enable = true; + + recyclarr = { + enable = true; + configFile = ./recyclarr.yml; + }; + }; +} diff --git a/nixos/hosts/media/recyclarr.yml b/nixos/hosts/media/recyclarr.yml new file mode 100644 index 0000000..20cffd0 --- /dev/null +++ b/nixos/hosts/media/recyclarr.yml @@ -0,0 +1,183 @@ +sonarr: + series: + base_url: http://localhost:8989 + api_key: !env_var SONARR_API_KEY + include: + - template: sonarr-quality-definition-series + - template: sonarr-v4-quality-profile-web-2160p-alternative + - template: sonarr-v4-custom-formats-web-2160p + - template: sonarr-v4-quality-profile-anime + - template: sonarr-v4-custom-formats-anime + custom_formats: + # HDR Formats + - trash_ids: + # Comment out the next line if you and all of your users' setups are fully DV compatible + - 9b27ab6498ec0f31a3353992e19434ca # DV (WEBDL) + + # HDR10+ Boost - Uncomment the next two lines if any of your devices DO support HDR10+ + - 0dad0a507451acddd754fe6dc3a7f5e7 # HDR10+ Boost + - 385e9e8581d33133c3961bdcdeffb7b4 # DV HDR10+ Boost + assign_scores_to: + - name: WEB-2160p + + # Unwanted + - trash_ids: + - 32b367365729d530ca1c124a0b180c64 # Bad Dual Groups + - 82d40da2bc6923f41e14394075dd4b03 # No-RlsGroup + - e1a997ddb54e3ecbfe06341ad323c458 # Obfuscated + - 06d66ab109d4d2eddb2794d21526d140 # Retags + - 1b3994c551cbb92a2c781af061f4ab44 # Scene + assign_scores_to: + - name: WEB-2160p + + # Optional SDR + # Only ever use ONE of the following custom formats: + # SDR - block ALL SDR releases + # SDR (no WEBDL) - block UHD/4k Remux and Bluray encode SDR releases, but allow SDR WEB + # - trash_ids: + # - 2016d1676f5ee13a5b7257ff86ac9a93 # SDR + # # - 83304f261cf516bb208c18c54c0adf97 # SDR (no WEBDL) + # assign_scores_to: + # - name: WEB-2160p + + - trash_ids: + - 026d5aadd1a6b4e550b134cb6c72b3ca # Uncensored + - b2550eb333d27b75833e25b8c2557b38 # 10bit + assign_scores_to: + - name: Remux-1080p - Anime + score: 1075 # Adjust scoring as desired + + - trash_ids: + - 418f50b10f1907201b6cfdf881f467b7 # Anime Dual Audio + assign_scores_to: + - name: Remux-1080p - Anime + score: 2000 # Adjust scoring as desired + + media_naming: + series: default + season: default + episodes: + rename: true + standard: default + daily: default + anime: default + delete_old_custom_formats: true + +radarr: + movies: + base_url: http://localhost:7878 + api_key: !env_var RADARR_API_KEY + include: + - template: radarr-quality-definition-movie + - template: radarr-custom-formats-remux-web-2160p + - template: radarr-quality-profile-anime + - template: radarr-custom-formats-anime + + quality_profiles: + - name: Remux + WEB 2160p + reset_unmatched_scores: + enabled: true + upgrade: + allowed: true + until_quality: Remux-2160p + until_score: 10000 + min_format_score: 0 + quality_sort: top + qualities: + - name: Remux-2160p + - name: WEB 2160p + qualities: + - WEBDL-2160p + - WEBRip-2160p + - name: Remux-1080p + - name: WEB 1080p + qualities: + - WEBDL-1080p + - WEBRip-1080p + + custom_formats: + - trash_ids: + # Audio + # Uncomment the next section to enable Advanced Audio Formats + - 496f355514737f7d83bf7aa4d24f8169 # TrueHD Atmos + - 2f22d89048b01681dde8afe203bf2e95 # DTS X + - 417804f7f2c4308c1f4c5d380d4c4475 # ATMOS (undefined) + - 1af239278386be2919e1bcee0bde047e # DD+ ATMOS + - 3cafb66171b47f226146a0770576870f # TrueHD + - dcf3ec6938fa32445f590a4da84256cd # DTS-HD MA + - a570d4a0e56a2874b64e5bfa55202a1b # FLAC + - e7c2fcae07cbada050a0af3357491d7b # PCM + - 8e109e50e0a0b83a5098b056e13bf6db # DTS-HD HRA + - 185f1dd7264c4562b9022d963ac37424 # DD+ + - f9f847ac70a0af62ea4a08280b859636 # DTS-ES + - 1c1a4c5e823891c75bc50380a6866f73 # DTS + - 240770601cc226190c367ef59aba7463 # AAC + - c2998bd0d90ed5621d8df281e839436e # DD + assign_scores_to: + - name: Remux + WEB 2160p + + # Movie Versions + - trash_ids: + # Uncomment any of the following lines to prefer these movie versions + # - 0f12c086e289cf966fa5948eac571f44 # Hybrid + # - 570bc9ebecd92723d2d21500f4be314c # Remaster + # - eca37840c13c6ef2dd0262b141a5482f # 4K Remaster + - e0c07d59beb37348e975a930d5e50319 # Criterion Collection + - 9d27d9d2181838f76dee150882bdc58c # Masters of Cinema + - db9b4c4b53d312a3ca5f1378f6440fc9 # Vinegar Syndrome + # - 957d0f44b592285f26449575e8b1167e # Special Edition + # - eecf3a857724171f968a66cb5719e152 # IMAX + # - 9f6cbff8cfe4ebbc1bde14c7b7bec0de # IMAX Enhanced + assign_scores_to: + - name: Remux + WEB 2160p + + # Optional + - trash_ids: + - b6832f586342ef70d9c128d40c07b872 # Bad Dual Groups + - cc444569854e9de0b084ab2b8b1532b2 # Black and White Editions + - ae9b7c9ebde1f3bd336a8cbd1ec4c5e5 # No-RlsGroup + - 7357cf5161efbf8c4d5d0c30b4815ee2 # Obfuscated + - 5c44f52a8714fdd79bb4d98e2673be1f # Retags + - f537cf427b64c38c8e36298f657e4828 # Scene + assign_scores_to: + - name: Remux + WEB 2160p + + - trash_ids: + # Comment out the next line if you and all of your users' setups are fully DV compatible + - 923b6abef9b17f937fab56cfcf89e1f1 # DV (WEBDL) + + # HDR10+ Boost - Uncomment the next two lines if any of your devices DO support HDR10+ + - b17886cb4158d9fea189859409975758 # HDR10Plus Boost + - 55a5b50cb416dea5a50c4955896217ab # DV HDR10+ Boost + assign_scores_to: + - name: Remux + WEB 2160p + + # Optional SDR + # Only ever use ONE of the following custom formats: + # SDR - block ALL SDR releases + # SDR (no WEBDL) - block UHD/4k Remux and Bluray encode SDR releases, but allow SDR WEB + - trash_ids: + - 9c38ebb7384dada637be8899efa68e6f # SDR + # - 25c12f78430a3a23413652cbd1d48d77 # SDR (no WEBDL) + assign_scores_to: + - name: Remux + WEB 2160p + + - trash_ids: + - 064af5f084a0a24458cc8ecd3220f93f # Uncensored + - a5d148168c4506b55cf53984107c396e # 10bit + assign_scores_to: + - name: Remux-1080p - Anime + score: 1075 # Adjust scoring as desired + + - trash_ids: + - 4a3b087eea2ce012fcc1ce319259a3be # Anime Dual Audio + assign_scores_to: + - name: Remux-1080p - Anime + score: 2000 # Adjust scoring as desired + + media_naming: + folder: default + movie: + rename: true + standard: default + delete_old_custom_formats: true diff --git a/nixos/hosts/media/sops.nix b/nixos/hosts/media/sops.nix new file mode 100644 index 0000000..f6c3d52 --- /dev/null +++ b/nixos/hosts/media/sops.nix @@ -0,0 +1,41 @@ +{ + sops.secrets.nixarr-vpn-conf = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.sonarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.radarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.readarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.bazarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.lidarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.prowlarr-api-key = { + sopsFile = ../../secrets/nixarr/secrets.yml; + mode = "0440"; + }; + + sops.secrets.jellyfin-exporter-config = { + sopsFile = ../../secrets/nixarr/secrets.yml; + owner = "jellyfin"; + }; +} diff --git a/nixos/hosts/media/storage.nix b/nixos/hosts/media/storage.nix new file mode 100644 index 0000000..bc7d005 --- /dev/null +++ b/nixos/hosts/media/storage.nix @@ -0,0 +1,23 @@ +{ + boot.supportedFilesystems = ["nfs"]; + + services.rpcbind.enable = true; + + fileSystems."/data/media/library/shows" = { + device = "192.168.1.226:/volume1/Media/TV Shows"; + fsType = "nfs4"; + options = ["x-systemd.automount" "noatime" "_netdev"]; + }; + + fileSystems."/data/media/library/movies" = { + device = "192.168.1.226:/volume1/Media/Movies"; + fsType = "nfs4"; + options = ["x-systemd.automount" "noatime" "_netdev"]; + }; + + fileSystems."/data/media/torrents" = { + device = "192.168.1.226:/volume1/data/torrents"; + fsType = "nfs4"; + options = ["x-systemd.automount" "noatime" "_netdev"]; + }; +} diff --git a/nixos/hosts/monitoring/alertmanager.nix b/nixos/hosts/monitoring/alertmanager.nix index 17d0649..cabd7a0 100644 --- a/nixos/hosts/monitoring/alertmanager.nix +++ b/nixos/hosts/monitoring/alertmanager.nix @@ -1,19 +1,25 @@ -{ config, pkgs, modulesPath, lib, ... }: - { - services.prometheus.alertmanagers = [ { - scheme = "http"; - # path_prefix = "/alertmanager"; - static_configs = [ { - targets = [ - "localhost:9093" + config, + pkgs, + ... +}: { + services.prometheus.alertmanagers = [ + { + scheme = "http"; + # path_prefix = "/alertmanager"; + static_configs = [ + { + targets = [ + "localhost:9093" + ]; + } ]; - } ]; - } ]; + } + ]; services.prometheus.alertmanager = { enable = true; openFirewall = true; - webExternalUrl = "http://monitor.lab:9093"; # optional but helpful + webExternalUrl = "http://monitor.lab:9093"; # optional but helpful configuration = { route = { group_wait = "10s"; @@ -39,11 +45,12 @@ telegram_configs = [ { api_url = "https://api.telegram.org"; - bot_token = config.sops.secrets."telegram-alert-bot-token".path; + # FIX ME! + bot_token = "7597031094:AAHjjo3HL1XdY38pSNlR66-4wCP47o4LlSw"; # config.sops.secrets."telegram-alert-bot-token".path; chat_id = -1002642560007; message_thread_id = 4; parse_mode = "HTML"; - send_resolved = false; + send_resolved = true; message = "{{ template \"telegram.message\". }}"; } ]; diff --git a/nixos/hosts/monitoring/dashboards/traefik-access.json b/nixos/hosts/monitoring/dashboards/traefik-access.json index eb0c31d..b133d2f 100644 --- a/nixos/hosts/monitoring/dashboards/traefik-access.json +++ b/nixos/hosts/monitoring/dashboards/traefik-access.json @@ -162,7 +162,7 @@ "pluginVersion": "7.3.6", "targets": [ { - "expr": "sum(count_over_time({job=\"/var/log/traefik.log\"} |= \"RequestProtocol\" [$__interval]))", + "expr": "sum(count_over_time({job=\"traefik\"} |= \"RequestProtocol\" [$__interval]))", "legendFormat": "", "refId": "A" } @@ -219,7 +219,7 @@ "pluginVersion": "7.3.6", "targets": [ { - "expr": "sum by (OriginStatus) (count_over_time({job=\"/var/log/traefik.log\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval]))", + "expr": "sum by (OriginStatus) (count_over_time({job=\"traefik\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval]))", "legendFormat": "HTTP Status: {{OriginStatus}}", "refId": "A" } @@ -284,7 +284,7 @@ "pluginVersion": "7.3.6", "targets": [ { - "expr": " sum(rate({job=\"/var/log/traefik.log\"} |~ \"RequestProtocol\" | json | OriginStatus >= 400 |__error__=\"\"[$__interval])) / (sum(rate({job=\"/var/log/traefik.log\"} |~ \"RequestProtocol\" | json | __error__=\"\"[$__interval])) / 100)", + "expr": " sum(rate({job=\"traefik\"} |~ \"RequestProtocol\" | json | OriginStatus >= 400 |__error__=\"\"[$__interval])) / (sum(rate({job=\"traefik\"} |~ \"RequestProtocol\" | json | __error__=\"\"[$__interval])) / 100)", "legendFormat": "", "refId": "A" } @@ -367,7 +367,7 @@ "steppedLine": false, "targets": [ { - "expr": " sum by (OriginStatus,ServiceName) (count_over_time({job=\"/var/log/traefik.log\"} |~ \"RequestProtocol\" | json | OriginStatus >= 400 |__error__=\"\"[$__interval]))", + "expr": " sum by (OriginStatus,ServiceName) (count_over_time({job=\"traefik\"} |~ \"RequestProtocol\" | json | OriginStatus >= 400 |__error__=\"\"[$__interval]))", "legendFormat": " {{ServiceName}} / {{OriginStatus}} ", "refId": "A" } @@ -474,7 +474,7 @@ "pluginVersion": "7.3.6", "targets": [ { - "expr": "count(sum by (ClientHost) (count_over_time({job=\"/var/log/traefik.log\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval])))", + "expr": "count(sum by (ClientHost) (count_over_time({job=\"traefik\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval])))", "legendFormat": "", "refId": "A" } @@ -544,7 +544,7 @@ "pluginVersion": "7.3.6", "targets": [ { - "expr": "sum_over_time({job=\"/var/log/traefik.log\"}|= \"RequestProtocol\" | json | OriginStatus=200 | unwrap DownstreamContentSize | __error__=\"\" [$__interval])", + "expr": "sum_over_time({job=\"traefik\"}|= \"RequestProtocol\" | json | OriginStatus=200 | unwrap DownstreamContentSize | __error__=\"\" [$__interval])", "legendFormat": "Bytes sent", "refId": "A" } @@ -638,7 +638,7 @@ "strokeWidth": 1, "targets": [ { - "expr": "sum by (RouterName) (count_over_time({job=\"/var/log/traefik.log\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval]))", + "expr": "sum by (RouterName) (count_over_time({job=\"traefik\"}|= \"RequestProtocol\" | json | __error__=\"\" [$__interval]))", "legendFormat": "{{RouterName}}", "refId": "A" } @@ -675,7 +675,7 @@ }, "targets": [ { - "expr": "{job=\"/var/log/traefik.log\"} |= \"RequestProtocol\"| json | line_format \"Status:{{.OriginStatus}} Client From {{.ClientAddr}} {{.RequestMethod}} {{.RequestAddr}}{{.RequestPath}} Route To {{.ServiceAddr}}\"", + "expr": "{job=\"traefik\"} |= \"RequestProtocol\"| json | line_format \"Status:{{.OriginStatus}} Client From {{.ClientAddr}} {{.RequestMethod}} {{.RequestAddr}}{{.RequestPath}} Route To {{.ServiceAddr}}\"", "legendFormat": "", "refId": "A" } @@ -749,7 +749,7 @@ "steppedLine": false, "targets": [ { - "expr": "quantile_over_time(0.95,{job=\"/var/log/traefik.log\"} |= \"RequestProtocol\"| json | unwrap Duration | __error__=\"\" [$__interval]) by (ServiceName)", + "expr": "quantile_over_time(0.95,{job=\"traefik\"} |= \"RequestProtocol\"| json | unwrap Duration | __error__=\"\" [$__interval]) by (ServiceName)", "hide": false, "legendFormat": " {{ ServiceName }}", "refId": "C" @@ -872,7 +872,7 @@ "steppedLine": false, "targets": [ { - "expr": "max by (ServiceName) (max_over_time({job=\"/var/log/traefik.log\"} |= \"RequestProtocol\" |json | unwrap Duration | __error__=\"\" [$__interval]))", + "expr": "max by (ServiceName) (max_over_time({job=\"traefik\"} |= \"RequestProtocol\" |json | unwrap Duration | __error__=\"\" [$__interval]))", "hide": false, "legendFormat": "{{ ServiceName}}", "refId": "D" @@ -995,7 +995,7 @@ "steppedLine": false, "targets": [ { - "expr": "sum by (ServiceName) (sum_over_time({job=\"/var/log/traefik.log\"} |= \"RequestProtocol\" |json | unwrap RequestContentSize | __error__=\"\" [$__interval]))", + "expr": "sum by (ServiceName) (sum_over_time({job=\"traefik\"} |= \"RequestProtocol\" |json | unwrap RequestContentSize | __error__=\"\" [$__interval]))", "hide": false, "legendFormat": "{{ ServiceName}}", "refId": "D" diff --git a/nixos/hosts/monitoring/dashboards/traefik.json b/nixos/hosts/monitoring/dashboards/traefik.json index f56edd3..0d6779a 100644 --- a/nixos/hosts/monitoring/dashboards/traefik.json +++ b/nixos/hosts/monitoring/dashboards/traefik.json @@ -852,7 +852,7 @@ "uid": "Prometheus" }, "editorMode": "code", - "expr": "sum by(router) (rate(traefik_router_requests_total[$__rate_interval]))", + "expr": "sum by(service) (rate(traefik_service_requests_total[$__rate_interval]))", "instant": false, "legendFormat": "__auto", "range": true, @@ -949,7 +949,7 @@ "uid": "Prometheus" }, "editorMode": "code", - "expr": "sum by(service) (rate(traefik_router_request_duration_seconds_count[$__rate_interval]))", + "expr": "sum by(service) (rate(traefik_service_request_duration_seconds_count[$__rate_interval]))", "instant": false, "legendFormat": "__auto", "range": true, diff --git a/nixos/hosts/monitoring/grafana.nix b/nixos/hosts/monitoring/grafana.nix index 6381378..ff683f5 100644 --- a/nixos/hosts/monitoring/grafana.nix +++ b/nixos/hosts/monitoring/grafana.nix @@ -1,17 +1,30 @@ -{ config, pkgs, modulesPath, lib, ... }: - { + config, + pkgs, + modulesPath, + lib, + ... +}: { services.grafana.enable = true; - services.grafana.settings.server = { - http_port = 3000; - http_addr = "0.0.0.0"; - # Grafana needs to know on which domain and URL it's running - domain = "grafana.lab"; - # root_url = "https://monitor.local/grafana/"; # Not needed if it is `https://your.domain/` - # serve_from_sub_path = true; + services.grafana.settings = { + server = { + http_port = 3000; + http_addr = "0.0.0.0"; + # Grafana needs to know on which domain and URL it's running + domain = "grafana.procopius.dk"; + root_url = "https://grafana.procopius.dk"; # Not needed if it is `https://your.domain/` + # serve_from_sub_path = true; + oauth_auto_login = false; + }; + "auth.generic_oauth" = { + enabled = false; + }; + "auth" = { + disable_login_form = false; + }; }; - networking.firewall.allowedTCPPorts = [ 3000 ]; + networking.firewall.allowedTCPPorts = [3000]; services.grafana = { # declarativePlugins = with pkgs.grafanaPlugins; [ ... ]; @@ -33,22 +46,32 @@ type = "loki"; url = "http://127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}"; } - # Some plugins also can - c.f. https://grafana.com/docs/plugins/yesoreyeram-infinity-datasource/latest/setup/provisioning/ - # { - # name = "Infinity"; - # type = "yesoreyeram-infinity-datasource"; - # } - # But not all - c.f. https://github.com/fr-ser/grafana-sqlite-datasource/issues/141 + { + uid = "influxdb"; + name = "InfluxDB"; + type = "influxdb"; + url = "http://127.0.0.1:8086"; + access = "proxy"; + jsonData = { + dbName = "proxmox"; + httpHeaderName1 = "Authorization"; + }; + secureJsonData = { + httpHeaderValue1 = "Token iY4MTuqUAVJbBkDUiMde"; + }; + } ]; # Note: removing attributes from the above `datasources.settings.datasources` is not enough for them to be deleted on `grafana`; # One needs to use the following option: # datasources.settings.deleteDatasources = [ { name = "prometheus"; orgId = 1; } { name = "loki"; orgId = 1; } ]; - dashboards.settings.providers = [{ - name = "my dashboards"; - options.path = "/etc/grafana-dashboards"; - }]; + dashboards.settings.providers = [ + { + name = "my dashboards"; + options.path = "/etc/grafana-dashboards"; + } + ]; }; }; @@ -59,6 +82,13 @@ mode = "0644"; }; + environment.etc."grafana-dashboards/traefik-access.json" = { + source = ./dashboards/traefik-access.json; + user = "grafana"; + group = "grafana"; + mode = "0644"; + }; + environment.etc."grafana-dashboards/grafana-traefik.json" = { source = ./dashboards/grafana-traefik.json; user = "grafana"; diff --git a/nixos/hosts/monitoring/host.nix b/nixos/hosts/monitoring/host.nix index bccda47..c9fa655 100644 --- a/nixos/hosts/monitoring/host.nix +++ b/nixos/hosts/monitoring/host.nix @@ -1,14 +1,20 @@ -{ config, pkgs, modulesPath, lib, ... }: - { + config, + pkgs, + modulesPath, + lib, + ... +}: { imports = [ ../../templates/base.nix ../../secrets/shared-sops.nix ./networking.nix ./prometheus.nix + ./influxdb.nix ./grafana.nix ./loki.nix ./alertmanager.nix ./sops.nix + ./jellyfin-exporter.nix ]; } diff --git a/nixos/hosts/monitoring/influxdb.nix b/nixos/hosts/monitoring/influxdb.nix new file mode 100644 index 0000000..c7249cb --- /dev/null +++ b/nixos/hosts/monitoring/influxdb.nix @@ -0,0 +1,25 @@ +{ + config, + pkgs, + modulesPath, + lib, + ... +}: { + services.influxdb2 = { + enable = true; + settings = { + }; + provision = { + enable = true; + initialSetup = { + username = "plasmagoat"; + passwordFile = config.sops.secrets.influxdb-password.path; + tokenFile = config.sops.secrets.influxdb-token.path; + organization = "procopius"; + bucket = "proxmox"; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [8086]; +} diff --git a/nixos/hosts/monitoring/jellyfin-exporter.nix b/nixos/hosts/monitoring/jellyfin-exporter.nix new file mode 100644 index 0000000..ff74dc9 --- /dev/null +++ b/nixos/hosts/monitoring/jellyfin-exporter.nix @@ -0,0 +1,14 @@ +{ + virtualisation.oci-containers.containers = { + jellyfin_exporter = { + image = "rebelcore/jellyfin-exporter:latest"; + ports = [ + "9594:9594" + ]; + cmd = [ + "--jellyfin.address=http://media.lab:8096" + "--jellyfin.token=f7c89e5aa307434c9b3ecb329e896335" + ]; + }; + }; +} diff --git a/nixos/hosts/monitoring/prometheus.nix b/nixos/hosts/monitoring/prometheus.nix index 55eabd4..daed4b5 100644 --- a/nixos/hosts/monitoring/prometheus.nix +++ b/nixos/hosts/monitoring/prometheus.nix @@ -1,51 +1,137 @@ -{ config, pkgs, modulesPath, lib, ... }: +{ + config, + pkgs, + modulesPath, + lib, + ... +}: let + monitor_hostname = "monitor.lab"; + traefik_hostname = "traefik.lab"; + sandbox_hostname = "sandbox.lab"; + forgejo_hostname = "forgejo.lab"; + runner01_hostname = "forgejo-runner-01.lab"; + dnsmasq_hostname = "dns.lab"; + media_hostname = "media.lab"; + mail_hostname = "mail.lab"; + keycloak_hostname = "keycloak.lab"; -let - monitor_ip = "monitor.lab"; - traefik_ip = "traefik.lab"; - sandbox_ip = "sandbox.lab"; - forgejo_ip = "forgejo.lab"; - runner01_ip = "forgejo-runner-01.lab"; - dnsmasq_ip = "dns.lab"; + monitored_hosts = [ + monitor_hostname + traefik_hostname + sandbox_hostname + forgejo_hostname + runner01_hostname + dnsmasq_hostname + media_hostname + mail_hostname + keycloak_hostname + ]; + + generateTargets = port: + map (host: "${host}:${toString port}") monitored_hosts; + + instance_relabel_config = [ + { + source_labels = ["__address__"]; + regex = "([^:]+):\\d+"; # Captures everything before the last colon + target_label = "instance"; + replacement = "$1"; + } + ]; + + node_exporter_port = 9100; + node_exporter_job = { + job_name = "node"; + static_configs = [{targets = generateTargets node_exporter_port;}]; + relabel_configs = instance_relabel_config; + }; + + promtail_port = 9080; + promtail_job = { + job_name = "promtail"; + static_configs = [{targets = generateTargets promtail_port;}]; + relabel_configs = instance_relabel_config; + }; - prometheus_exporter_port = 9100; - postgres_exporter_port = 9187; prometheus_port = 9090; alertmanager_port = 9093; grafana_port = 3000; - promtail_port = 9080; - traefik_monitor_port = 8082; - forgejo_monitor_port = 3000; - dnsmasq_exporter_port = 9153; - - exporters = { - node = [ - "${monitor_ip}:${toString prometheus_exporter_port}" - "${traefik_ip}:${toString prometheus_exporter_port}" - "${sandbox_ip}:${toString prometheus_exporter_port}" - "${forgejo_ip}:${toString prometheus_exporter_port}" - "${runner01_ip}:${toString prometheus_exporter_port}" + monitoring_infra_job = { + job_name = "monitoring_infra"; + static_configs = [ + { + targets = [ + "${monitor_hostname}:${toString prometheus_port}" + "${monitor_hostname}:${toString alertmanager_port}" + "${monitor_hostname}:${toString grafana_port}" + ]; + } ]; - promtail = [ - "${monitor_ip}:${toString promtail_port}" - "${traefik_ip}:${toString promtail_port}" - "${sandbox_ip}:${toString promtail_port}" - "${forgejo_ip}:${toString promtail_port}" - "${runner01_ip}:${toString promtail_port}" - ]; - grafana = [ "${monitor_ip}:${toString grafana_port}" ]; - prometheus = [ "${monitor_ip}:${toString prometheus_port}" ]; - alertmanager = [ "${monitor_ip}:${toString alertmanager_port}" ]; - traefik = [ "${traefik_ip}:${toString traefik_monitor_port}" ]; - gitea = [ "${forgejo_ip}:${toString forgejo_monitor_port}" ]; - postgres = [ "${forgejo_ip}:${toString postgres_exporter_port}" ]; - - dnsmasq = [ "${dnsmasq_ip}:${toString dnsmasq_exporter_port}" ]; + relabel_configs = instance_relabel_config; }; + traefik_monitor_port = 8082; + traefik_job = { + job_name = "traefik"; + static_configs = [{targets = ["${traefik_hostname}:${toString traefik_monitor_port}"];}]; + relabel_configs = instance_relabel_config; + }; + forgejo_monitor_port = 3000; + forgejo_job = { + job_name = "forgejo"; + static_configs = [{targets = ["${forgejo_hostname}:${toString forgejo_monitor_port}"];}]; + relabel_configs = instance_relabel_config; + }; + + postgres_exporter_port = 9187; + postgres_job = { + job_name = "postgres"; + static_configs = [{targets = ["${forgejo_hostname}:${toString postgres_exporter_port}"];}]; + relabel_configs = instance_relabel_config; + }; + + dnsmasq_exporter_port = 9153; + dnsmasq_job = { + job_name = "dnsmasq"; + static_configs = [{targets = ["${dnsmasq_hostname}:${toString dnsmasq_exporter_port}"];}]; + relabel_configs = instance_relabel_config; + }; + + # --- Media Stack Scrape Job --- + media_stack_job = { + job_name = "media_stack"; + static_configs = [ + { + targets = [ + "${media_hostname}:9707" # sonarr + "${media_hostname}:9708" # readarr + "${media_hostname}:9709" # radarr + "${media_hostname}:9710" # prowlarr + "${media_hostname}:9711" # lidarr + "${media_hostname}:9712" # bazarr + ]; + } + ]; + relabel_configs = instance_relabel_config; + }; + + jellyfin_port = 8096; + jellyfin_exporter_port = 9594; + jellyfin_job = { + job_name = "jellyfin"; + static_configs = [ + { + targets = [ + "${media_hostname}:${toString jellyfin_port}" + "${monitor_hostname}:${toString jellyfin_exporter_port}" + ]; + } + ]; + relabel_configs = instance_relabel_config; + }; in { - networking.firewall.allowedTCPPorts = [ 9090 ]; + networking.firewall.allowedTCPPorts = [9090]; services.prometheus = { enable = true; @@ -61,10 +147,17 @@ in { "--web.enable-admin-api" ]; - scrapeConfigs = lib.mapAttrsToList (job_name: targets: { - inherit job_name; - static_configs = [ { inherit targets; } ]; - }) exporters; + scrapeConfigs = [ + node_exporter_job + promtail_job + monitoring_infra_job + traefik_job + forgejo_job + postgres_job + dnsmasq_job + media_stack_job + jellyfin_job + ]; # 🔔 Alerts provisioning ruleFiles = [ diff --git a/nixos/hosts/monitoring/provisioning/notifiers/contact-points.yml b/nixos/hosts/monitoring/provisioning/notifiers/contact-points.yml deleted file mode 100644 index c2396d0..0000000 --- a/nixos/hosts/monitoring/provisioning/notifiers/contact-points.yml +++ /dev/null @@ -1,11 +0,0 @@ -# /etc/grafana/provisioning/notifiers/contact-points.yml -apiVersion: 1 - -contactPoints: - - orgId: 1 - name: telegram - type: telegram - settings: - bottoken: "__YOUR_BOT_TOKEN__" - chatid: "__YOUR_CHAT_ID__" - disableResolveMessage: false diff --git a/nixos/hosts/monitoring/sops.nix b/nixos/hosts/monitoring/sops.nix index 2b0c9ab..b63ea15 100644 --- a/nixos/hosts/monitoring/sops.nix +++ b/nixos/hosts/monitoring/sops.nix @@ -1,7 +1,18 @@ -{ config, lib, ... }: { + config, + lib, + ... +}: { sops.secrets."telegram-alert-bot-token" = { sopsFile = ../../secrets/telegram/secrets.yml; - owner = "prometheus"; + mode = "0440"; + }; + sops.secrets."influxdb-password" = { + sopsFile = ../../secrets/influxdb/secrets.yml; + owner = "influxdb2"; + }; + sops.secrets."influxdb-token" = { + sopsFile = ../../secrets/influxdb/secrets.yml; + owner = "influxdb2"; }; } diff --git a/nixos/hosts/nixos-builder/builder.nix b/nixos/hosts/nixos-builder/builder.nix new file mode 100644 index 0000000..f9714fa --- /dev/null +++ b/nixos/hosts/nixos-builder/builder.nix @@ -0,0 +1,3 @@ +{ + virtualisation.kvmgt.enable = true; +} diff --git a/nixos/hosts/nixos-builder/host.nix b/nixos/hosts/nixos-builder/host.nix new file mode 100644 index 0000000..f77c719 --- /dev/null +++ b/nixos/hosts/nixos-builder/host.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ../../templates/base.nix + ../../secrets/shared-sops.nix + ./networking.nix + ./runner-user.nix + ./builder.nix + ]; +} diff --git a/nixos/hosts/nixos-builder/networking.nix b/nixos/hosts/nixos-builder/networking.nix new file mode 100644 index 0000000..1a006e9 --- /dev/null +++ b/nixos/hosts/nixos-builder/networking.nix @@ -0,0 +1,9 @@ +{ + config, + lib, + pkgs, + runnerId, + ... +}: { + networking.hostName = "nixos-builder"; +} diff --git a/nixos/hosts/nixos-builder/runner-user.nix b/nixos/hosts/nixos-builder/runner-user.nix new file mode 100644 index 0000000..7d601c2 --- /dev/null +++ b/nixos/hosts/nixos-builder/runner-user.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + pkgs, + ... +}: { + users.users.runner = { + isNormalUser = true; + description = "forgejo-runner"; + extraGroups = [ + "wheel" + ]; + + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGlzZWik5bbH6/xjiCpwo1SQSJ/J/Cv7y4ZQ45P68GLB forgejo-runner" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air" + ]; + }; +} diff --git a/nixos/hosts/sandbox/host.nix b/nixos/hosts/sandbox/host.nix index 1dea6bf..41e1816 100644 --- a/nixos/hosts/sandbox/host.nix +++ b/nixos/hosts/sandbox/host.nix @@ -1,10 +1,15 @@ -{ config, pkgs, modulesPath, lib, ... }: - { + config, + pkgs, + modulesPath, + lib, + ... +}: { imports = [ ../../templates/base.nix ./networking.nix ./storage.nix ./sandbox.nix + ./warpgate.nix ]; } diff --git a/nixos/hosts/sandbox/storage.nix b/nixos/hosts/sandbox/storage.nix index 0fcb57e..c94b739 100644 --- a/nixos/hosts/sandbox/storage.nix +++ b/nixos/hosts/sandbox/storage.nix @@ -1,11 +1,11 @@ { - boot.supportedFilesystems = [ "nfs" ]; + boot.supportedFilesystems = ["nfs"]; services.rpcbind.enable = true; - fileSystems."/mnt/nas" = { - device = "192.168.1.226:/volume1/docker"; - fsType = "nfs"; - options = [ "noatime" "vers=4" "rsize=8192" "wsize=8192" ]; - }; + # fileSystems."/mnt/nas" = { + # device = "192.168.1.226:/volume1/docker"; + # fsType = "nfs"; + # options = [ "noatime" "vers=4" "rsize=8192" "wsize=8192" ]; + # }; } diff --git a/nixos/hosts/sandbox/warpgate.nix b/nixos/hosts/sandbox/warpgate.nix new file mode 100644 index 0000000..7c24ad8 --- /dev/null +++ b/nixos/hosts/sandbox/warpgate.nix @@ -0,0 +1,35 @@ +{ + virtualisation = { + containers.enable = true; + oci-containers.backend = "podman"; + + podman = { + enable = true; + + # Create a `docker` alias for podman, to use it as a drop-in replacement + dockerCompat = true; + + # Required for containers under podman-compose to be able to talk to each other. + defaultNetwork.settings.dns_enabled = true; + }; + }; + virtualisation.oci-containers.containers = { + warpgate = { + image = "ghcr.io/warp-tech/warpgate"; + ports = [ + "2222:2222" + "8888:8888" + ]; + volumes = [ + "/srv/warpgate/data:/data" + ]; + }; + }; + + systemd.tmpfiles.rules = [ + "d /srv/warpgate 0755 root root -" + "d /srv/warpgate/data 0755 root root -" + ]; + + networking.firewall.allowedTCPPorts = [8888]; +} diff --git a/nixos/hosts/traefik/configuration/auth/routers.nix b/nixos/hosts/traefik/configuration/auth/routers.nix new file mode 100644 index 0000000..0ca42b5 --- /dev/null +++ b/nixos/hosts/traefik/configuration/auth/routers.nix @@ -0,0 +1,24 @@ +{ + keycloak = { + rule = "Host(`keycloak.procopius.dk`)"; + service = "keycloak"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + + 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 new file mode 100644 index 0000000..b5db1d8 --- /dev/null +++ b/nixos/hosts/traefik/configuration/auth/services.nix @@ -0,0 +1,5 @@ +{ + authentik.loadBalancer.servers = [{url = "http://authentik.lab:9000";}]; + keycloak.loadBalancer.servers = [{url = "http://keycloak.lab:8080";}]; + oauth2proxy.loadBalancer.servers = [{url = "http://localhost:4180";}]; +} diff --git a/nixos/hosts/traefik/configuration/infra/routers.nix b/nixos/hosts/traefik/configuration/infra/routers.nix new file mode 100644 index 0000000..5667d22 --- /dev/null +++ b/nixos/hosts/traefik/configuration/infra/routers.nix @@ -0,0 +1,43 @@ +{ + traefik = { + rule = "Host(`traefik.procopius.dk`)"; + service = "traefik"; + entryPoints = ["websecure"]; + middlewares = ["oauth-auth"]; + tls.certResolver = "letsencrypt"; + }; + + mail-acme = { + rule = "Host(`mail.procopius.dk`) && PathPrefix(`/.well-known/acme-challenge/`)"; + service = "mail-acme"; + entryPoints = ["web"]; + priority = 1000; + middlewares = []; + }; + + forgejo = { + rule = "Host(`git.procopius.dk`)"; + service = "forgejo"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + proxmox = { + rule = "Host(`proxmox.procopius.dk`)"; + service = "proxmox"; + entryPoints = ["websecure"]; + middlewares = ["oauth-auth"]; + tls.certResolver = "letsencrypt"; + }; + nas = { + rule = "Host(`nas.procopius.dk`)"; + service = "nas"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + catchAll = { + rule = "HostRegexp(`.+`)"; + service = "nginx"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; +} diff --git a/nixos/hosts/traefik/configuration/infra/services.nix b/nixos/hosts/traefik/configuration/infra/services.nix new file mode 100644 index 0000000..27de8c8 --- /dev/null +++ b/nixos/hosts/traefik/configuration/infra/services.nix @@ -0,0 +1,13 @@ +{ + traefik.loadBalancer.servers = [{url = "http://localhost:8080";}]; + + mail-acme.loadBalancer.servers = [{url = "http://mail.lab:80";}]; + + forgejo.loadBalancer.servers = [{url = "http://forgejo.lab:3000";}]; + proxmox.loadBalancer.servers = [{url = "https://192.168.1.205:8006";}]; + proxmox.loadBalancer.serversTransport = "insecureTransport"; + nas.loadBalancer.servers = [{url = "https://192.168.1.226:5001";}]; + nas.loadBalancer.serversTransport = "insecureTransport"; + nginx.loadBalancer.servers = [{url = "https://192.168.1.226:4433";}]; + nginx.loadBalancer.serversTransport = "insecureTransport"; +} diff --git a/nixos/hosts/traefik/configuration/media-center/routers.nix b/nixos/hosts/traefik/configuration/media-center/routers.nix new file mode 100644 index 0000000..b30e50e --- /dev/null +++ b/nixos/hosts/traefik/configuration/media-center/routers.nix @@ -0,0 +1,35 @@ +{ + jellyfin = { + rule = "Host(`jellyfin.procopius.dk`)"; + service = "jellyfin"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + + radarr = { + rule = "Host(`radarr.procopius.dk`)"; + service = "radarr"; + entryPoints = ["websecure"]; + middlewares = [ + "oauth-auth" + "restrict-admin" + ]; + tls.certResolver = "letsencrypt"; + }; + + sonarr = { + rule = "Host(`sonarr.procopius.dk`)"; + service = "sonarr"; + entryPoints = ["websecure"]; + middlewares = ["oauth-auth"]; + tls.certResolver = "letsencrypt"; + }; + + jellyseerr = { + rule = "Host(`jellyseerr.procopius.dk`)"; + service = "jellyseerr"; + entryPoints = ["websecure"]; + # 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 new file mode 100644 index 0000000..2fa3a71 --- /dev/null +++ b/nixos/hosts/traefik/configuration/media-center/services.nix @@ -0,0 +1,6 @@ +{ + jellyfin.loadBalancer.servers = [{url = "http://media.lab:8096";}]; + radarr.loadBalancer.servers = [{url = "http://media.lab:7878";}]; + sonarr.loadBalancer.servers = [{url = "http://media.lab:8989";}]; + 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 d3995a5..42bba66 100644 --- a/nixos/hosts/traefik/configuration/middlewares.nix +++ b/nixos/hosts/traefik/configuration/middlewares.nix @@ -1,10 +1,43 @@ -{ lib, config, ... }: - let internalNetwork = "192.168.1.0/24"; -in -{ +in { internal-whitelist = { - ipWhiteList.sourceRange = [ internalNetwork ]; + ipWhiteList.sourceRange = [internalNetwork]; + }; + + auth-headers = { + headers = { + sslRedirect = true; + stsSeconds = 315360000; + browserXssFilter = true; + contentTypeNosniff = true; + forceSTSHeader = true; + sslHost = "procopius.dk"; + stsIncludeSubdomains = true; + stsPreload = true; + frameDeny = true; + }; + }; + + oauth-auth = { + forwardAuth = { + address = "http://localhost:4180/"; + trustForwardHeader = true; + authResponseHeaders = [ + "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/misc/routers.nix b/nixos/hosts/traefik/configuration/misc/routers.nix new file mode 100644 index 0000000..054130e --- /dev/null +++ b/nixos/hosts/traefik/configuration/misc/routers.nix @@ -0,0 +1,8 @@ +{ + mesterjakob = { + rule = "Host(`mester.jakobblum.dk`)"; + service = "mesterjakob"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; +} diff --git a/nixos/hosts/traefik/configuration/misc/services.nix b/nixos/hosts/traefik/configuration/misc/services.nix new file mode 100644 index 0000000..3fbb829 --- /dev/null +++ b/nixos/hosts/traefik/configuration/misc/services.nix @@ -0,0 +1,3 @@ +{ + mesterjakob.loadBalancer.servers = [{url = "http://192.168.1.226:4200";}]; +} diff --git a/nixos/hosts/traefik/configuration/monitoring/routers.nix b/nixos/hosts/traefik/configuration/monitoring/routers.nix new file mode 100644 index 0000000..08853cb --- /dev/null +++ b/nixos/hosts/traefik/configuration/monitoring/routers.nix @@ -0,0 +1,28 @@ +{ + prometheus = { + rule = "Host(`prometheus.procopius.dk`)"; + service = "prometheus"; + entryPoints = ["websecure"]; + middlewares = ["oauth-auth"]; + tls.certResolver = "letsencrypt"; + }; + grafana = { + rule = "Host(`grafana.procopius.dk`)"; + service = "grafana"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + alertmanager = { + rule = "Host(`alertmanager.procopius.dk`)"; + service = "alertmanager"; + entryPoints = ["websecure"]; + middlewares = ["oauth-auth"]; + tls.certResolver = "letsencrypt"; + }; + umami = { + rule = "Host(`umami.procopius.dk`)"; + service = "umami"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; +} diff --git a/nixos/hosts/traefik/configuration/monitoring/services.nix b/nixos/hosts/traefik/configuration/monitoring/services.nix new file mode 100644 index 0000000..34cd875 --- /dev/null +++ b/nixos/hosts/traefik/configuration/monitoring/services.nix @@ -0,0 +1,6 @@ +{ + prometheus.loadBalancer.servers = [{url = "http://monitor.lab:9090";}]; + grafana.loadBalancer.servers = [{url = "http://monitor.lab:3000";}]; + alertmanager.loadBalancer.servers = [{url = "http://monitor.lab:9093";}]; + umami.loadBalancer.servers = [{url = "http://192.168.1.226:3333";}]; +} diff --git a/nixos/hosts/traefik/configuration/photos/routers.nix b/nixos/hosts/traefik/configuration/photos/routers.nix new file mode 100644 index 0000000..65da5a3 --- /dev/null +++ b/nixos/hosts/traefik/configuration/photos/routers.nix @@ -0,0 +1,35 @@ +{ + ente = { + rule = "Host(`ente.procopius.dk`)"; + service = "ente"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + + photos = { + rule = "Host(`photos.procopius.dk`)"; + service = "photos"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + + account = { + rule = "Host(`account.procopius.dk`)"; + service = "account"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + + minio = { + rule = "Host(`minio.procopius.dk`)"; + service = "minio"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; + minio-api = { + rule = "Host(`minio-api.procopius.dk`)"; + service = "minio-api"; + entryPoints = ["websecure"]; + tls.certResolver = "letsencrypt"; + }; +} diff --git a/nixos/hosts/traefik/configuration/photos/services.nix b/nixos/hosts/traefik/configuration/photos/services.nix new file mode 100644 index 0000000..7bec8af --- /dev/null +++ b/nixos/hosts/traefik/configuration/photos/services.nix @@ -0,0 +1,7 @@ +{ + ente.loadBalancer.servers = [{url = "http://192.168.1.226:8087";}]; + photos.loadBalancer.servers = [{url = "http://192.168.1.226:3000";}]; + account.loadBalancer.servers = [{url = "http://192.168.1.226:3001";}]; + minio.loadBalancer.servers = [{url = "http://192.168.1.226:3201";}]; + minio-api.loadBalancer.servers = [{url = "http://192.168.1.226:3200";}]; +} diff --git a/nixos/hosts/traefik/configuration/routers.nix b/nixos/hosts/traefik/configuration/routers.nix deleted file mode 100644 index 71a087b..0000000 --- a/nixos/hosts/traefik/configuration/routers.nix +++ /dev/null @@ -1,140 +0,0 @@ -{ lib, config, ... }: - -{ - traefik = { - rule = "Host(`traefik.procopius.dk`)"; - service = "traefik"; - entryPoints = [ "websecure" ]; - middlewares = [ "internal-whitelist" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - proxmox = { - rule = "Host(`proxmox.procopius.dk`)"; - service = "proxmox"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - forgejo = { - rule = "Host(`git.procopius.dk`)"; - service = "forgejo"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - prometheus = { - rule = "Host(`prometheus.procopius.dk`)"; - service = "prometheus"; - entryPoints = [ "websecure" ]; - middlewares = [ "internal-whitelist" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - grafana = { - rule = "Host(`grafana.procopius.dk`)"; - service = "grafana"; - entryPoints = [ "websecure" ]; - middlewares = [ "internal-whitelist" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - alertmanager = { - rule = "Host(`alertmanager.procopius.dk`)"; - service = "alertmanager"; - entryPoints = [ "websecure" ]; - middlewares = [ "internal-whitelist" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - jellyfin = { - rule = "Host(`jellyfin.procopius.dk`)"; - service = "jellyfin"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - sonarr = { - rule = "Host(`sonarr.procopius.dk`)"; - service = "sonarr"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - radarr = { - rule = "Host(`radarr.procopius.dk`)"; - service = "radarr"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - ente = { - rule = "Host(`ente.procopius.dk`)"; - service = "ente"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - photos = { - rule = "Host(`photos.procopius.dk`)"; - service = "photos"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - minio = { - rule = "Host(`minio.procopius.dk`)"; - service = "minio"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - minio-api = { - rule = "Host(`minio-api.procopius.dk`)"; - service = "minio-api"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - account = { - rule = "Host(`account.procopius.dk`)"; - service = "account"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - auth = { - rule = "Host(`auth.procopius.dk`)"; - service = "auth"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - nas = { - rule = "Host(`nas.procopius.dk`)"; - service = "nas"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - umami = { - rule = "Host(`umami.procopius.dk`)"; - service = "umami"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - mesterjakob = { - rule = "Host(`mester.jakobblum.dk`)"; - service = "mesterjakob"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; - - catchAll = { - rule = "HostRegexp(`.+`)"; - service = "nginx"; - entryPoints = [ "websecure" ]; - tls = { certResolver = "letsencrypt"; }; - }; -} diff --git a/nixos/hosts/traefik/configuration/services.nix b/nixos/hosts/traefik/configuration/services.nix deleted file mode 100644 index 971183d..0000000 --- a/nixos/hosts/traefik/configuration/services.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ lib, config, ... }: - -{ - proxmox.loadBalancer.servers = [ { url = "https://192.168.1.205:8006"; } ]; - proxmox.loadBalancer.serversTransport = "insecureTransport"; - - traefik.loadBalancer.servers = [ { url = "http://localhost:8080"; } ]; - - forgejo.loadBalancer.servers = [ { url = "http://forgejo.lab:3000"; } ]; - - nginx.loadBalancer.servers = [ { url = "https://192.168.1.226:4433"; } ]; - nginx.loadBalancer.serversTransport = "insecureTransport"; - - prometheus.loadBalancer.servers = [ { url = "http://monitor.lab:9090"; } ]; - grafana.loadBalancer.servers = [ { url = "http://monitor.lab:3000"; } ]; - alertmanager.loadBalancer.servers = [ { url = "http://monitor.lab:9093"; } ]; - - - # from nginx - account.loadBalancer.servers = [ { url = "http://192.168.1.226:3001"; } ]; - auth.loadBalancer.servers = [ { url = "http://192.168.1.226:3005"; } ]; - ente.loadBalancer.servers = [ { url = "http://192.168.1.226:8087"; } ]; - photos.loadBalancer.servers = [ { url = "http://192.168.1.226:3000"; } ]; - minio.loadBalancer.servers = [ { url = "http://192.168.1.226:3201"; } ]; - minio-api.loadBalancer.servers = [ { url = "http://192.168.1.226:3200"; } ]; - - nas.loadBalancer.servers = [ { url = "https://192.168.1.226:5001"; } ]; - nas.loadBalancer.serversTransport = "insecureTransport"; - - - jellyfin.loadBalancer.servers = [ { url = "http://192.168.1.226:8096"; } ]; - radarr.loadBalancer.servers = [ { url = "http://192.168.1.226:7878"; } ]; - sonarr.loadBalancer.servers = [ { url = "http://192.168.1.226:8989"; } ]; - - umami.loadBalancer.servers = [ { url = "http://192.168.1.226:3333"; } ]; - - mesterjakob.loadBalancer.servers = [ { url = "http://192.168.1.226:4200"; } ]; -} diff --git a/nixos/hosts/traefik/configuration/static.nix b/nixos/hosts/traefik/configuration/static.nix index 22bd1f4..e31f2b2 100644 --- a/nixos/hosts/traefik/configuration/static.nix +++ b/nixos/hosts/traefik/configuration/static.nix @@ -1,11 +1,11 @@ -{ lib, config, ... }: - { entryPoints = { web = { address = ":80"; asDefault = true; + allowACMEByPass = true; http.redirections.entrypoint = { + priority = 10; to = "websecure"; scheme = "https"; }; @@ -21,6 +21,8 @@ }; }; + providers.file.watch = true; + api = { dashboard = true; insecure = true; @@ -37,7 +39,7 @@ dnsChallenge = { provider = "cloudflare"; delayBeforeCheck = 10; - resolvers = [ "1.1.1.1:53" "8.8.8.8:53" ]; + resolvers = ["1.1.1.1:53" "8.8.8.8:53"]; }; }; }; diff --git a/nixos/hosts/traefik/host.nix b/nixos/hosts/traefik/host.nix index 228af8d..4570d3c 100644 --- a/nixos/hosts/traefik/host.nix +++ b/nixos/hosts/traefik/host.nix @@ -1,10 +1,16 @@ -{ config, pkgs, modulesPath, lib, ... }: - { + config, + pkgs, + modulesPath, + lib, + ... +}: { imports = [ ../../templates/base.nix ./networking.nix ./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/nixos/hosts/traefik/promtail.nix b/nixos/hosts/traefik/promtail.nix index 7b4f91b..7d67072 100644 --- a/nixos/hosts/traefik/promtail.nix +++ b/nixos/hosts/traefik/promtail.nix @@ -1,7 +1,9 @@ -{ config, lib, pkgs, ... }: - { - + config, + lib, + pkgs, + ... +}: { # This ensures the directory exists at boot, owned by traefik (writer) and readable by promtail. systemd.tmpfiles.rules = [ "d /var/log/traefik 0755 traefik promtail -" @@ -12,9 +14,9 @@ job_name = "traefik"; static_configs = [ { - targets = [ "localhost" ]; + targets = ["localhost"]; labels = { - job = "/var/log/traefik/*.log"; + job = "traefik"; host = config.networking.hostName; env = "proxmox"; instance = "${config.networking.hostName}.lab"; # prometheus scrape target diff --git a/nixos/hosts/traefik/sops.nix b/nixos/hosts/traefik/sops.nix new file mode 100644 index 0000000..943afc4 --- /dev/null +++ b/nixos/hosts/traefik/sops.nix @@ -0,0 +1,10 @@ +{ + sops.secrets."traefik-env" = { + sopsFile = ../../secrets/traefik/secrets.yml; + mode = "0440"; + }; + sops.secrets."oauth2-proxy-env" = { + sopsFile = ../../secrets/traefik/secrets.yml; + mode = "0440"; + }; +} diff --git a/nixos/hosts/traefik/traefik.nix b/nixos/hosts/traefik/traefik.nix index 52462bd..139161f 100644 --- a/nixos/hosts/traefik/traefik.nix +++ b/nixos/hosts/traefik/traefik.nix @@ -1,23 +1,59 @@ -{ config, lib, pkgs, ... }: - -let - staticConfig = import ./configuration/static.nix { inherit lib config; }; - middlewaresConfig = import ./configuration/middlewares.nix { inherit lib config; }; - routersConfig = import ./configuration/routers.nix { inherit lib config; }; - servicesConfig = import ./configuration/services.nix { inherit lib config; }; -in { + config, + lib, + ... +}: let + # Import router and service declarations grouped in files + infraRouters = import ./configuration/infra/routers.nix; + infraServices = import ./configuration/infra/services.nix; + + monitoringRouters = import ./configuration/monitoring/routers.nix; + monitoringServices = import ./configuration/monitoring/services.nix; + + mediaRouters = import ./configuration/media-center/routers.nix; + mediaServices = import ./configuration/media-center/services.nix; + + photosRouters = import ./configuration/photos/routers.nix; + photosServices = import ./configuration/photos/services.nix; + + authRouters = import ./configuration/auth/routers.nix; + authServices = import ./configuration/auth/services.nix; + + miscRouters = import ./configuration/misc/routers.nix; + miscServices = import ./configuration/misc/services.nix; + + middlewares = import ./configuration/middlewares.nix; + staticConfig = import ./configuration/static.nix; + + # Combine all routers and services from groups + allRouters = lib.foldl' (acc: routers: acc // routers) {} [ + infraRouters + monitoringRouters + mediaRouters + photosRouters + authRouters + miscRouters + ]; + + allServices = lib.foldl' (acc: services: acc // services) {} [ + infraServices + monitoringServices + mediaServices + photosServices + authServices + miscServices + ]; +in { services.traefik = { enable = true; + environmentFiles = [config.sops.secrets."traefik-env".path]; - # ==== Static Configuration ==== staticConfigOptions = staticConfig; - # ==== Dynamic Configuration ==== dynamicConfigOptions.http = { - routers = routersConfig; - services = servicesConfig; - middlewares = middlewaresConfig; + routers = allRouters; + services = allServices; + middlewares = middlewares; serversTransports = { insecureTransport = { @@ -26,11 +62,4 @@ in }; }; }; - - systemd.services.traefik.serviceConfig.Environment = [ - "CLOUDFLARE_DNS_API_TOKEN=gQYyG6cRw-emp_qpsUj9TrkYgoVC1v9UUtv94ozA" - "CLOUDFLARE_ZONE_API_TOKEN=gQYyG6cRw-emp_qpsUj9TrkYgoVC1v9UUtv94ozA" - ]; - - virtualisation.docker.enable = true; } diff --git a/nixos/hosts/warpgate/host.nix b/nixos/hosts/warpgate/host.nix new file mode 100644 index 0000000..e69de29 diff --git a/nixos/hosts/warpgate/warpgate.nix b/nixos/hosts/warpgate/warpgate.nix new file mode 100644 index 0000000..bc48f1e --- /dev/null +++ b/nixos/hosts/warpgate/warpgate.nix @@ -0,0 +1,14 @@ +{ + virtualisation.oci-containers.containers = { + warpgate = { + image = "ghcr.io/warp-tech/warpgate"; + ports = [ + "2222:2222" + "8888:8888" + ]; + volumes = [ + "/srv/warpgate/data:/data" + ]; + }; + }; +} diff --git a/nixos/modules/docker-host.nix b/nixos/modules/docker-host.nix deleted file mode 100644 index 3ab63e5..0000000 --- a/nixos/modules/docker-host.nix +++ /dev/null @@ -1,77 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - # ── Adjust these to your NAS settings ────────────────────────────────────────── - nasServer = "192.168.1.100"; # your NAS IP or hostname - nasExportPath = "/export/docker-volumes"; # path on the NAS - nasMountPoint = "/mnt/nas"; # where to mount inside VM - - # ── Where we drop your Compose file and run it ──────────────────────────────── - composeDir = "/etc/docker-compose-app"; - composeText = lib.readFile ./docker-compose.yml; -in { - ############################################################################## - # A) NETWORKING - # (If you want DHCP, remove this block and let cloud-init assign an IP.) - ############################################################################## - # networking.interfaces.enp0s25 = { - # useDHCP = false; - # ipv4.addresses = [{ - # address = "192.168.1.50"; - # prefixLength = 24; - # }]; - # ipv4.gateway = "192.168.1.1"; - # # optional: ipv4.dns = [ "1.1.1.1" "8.8.8.8" ]; - # }; - - ############################################################################## - # B) MOUNT YOUR NAS VIA NFS - ############################################################################## - # fileSystems."${nasMountPoint}" = { - # device = "${nasServer}:${nasExportPath}"; - # fsType = "nfs"; - # options = [ "defaults" "nofail" "x-systemd.requires=network-online.target" ]; - # }; - # fileSystems."${nasMountPoint}".requiredForBoot = false; - - ############################################################################## - # C) INSTALL DOCKER & DOCKER-COMPOSE - ############################################################################## - environment.systemPackages = with pkgs; [ - docker - docker-compose - ]; - services.docker.enable = true; - - ############################################################################## - # D) DROP IN YOUR docker-compose.yml - ############################################################################## - # systemd.tmpfiles.rules = [ - # # Ensure directory exists before we write the file. - # "D! ${composeDir} 0755 root root - -" - # ]; - # environment.etc."docker-compose-app/docker-compose.yml".text = composeText; - - ############################################################################## - # E) RUN DOCKER-COMPOSE AS A SYSTEMD SERVICE - ############################################################################## - # systemd.services.dockerComposeApp = { - # description = "Auto-start Docker-Compose stack for home server"; - # after = [ "network-online.target" "docker.service" ]; - # wants = [ "network-online.target" "docker.service" ]; - - # serviceConfig = { - # WorkingDirectory = composeDir; - # ExecStart = "${pkgs.docker-compose}/bin/docker-compose -f ${composeDir}/docker-compose.yml up"; - # ExecStop = "${pkgs.docker-compose}/bin/docker-compose -f ${composeDir}/docker-compose.yml down"; - # Restart = "always"; - # RestartSec = 10; - # }; - - # wantedBy = [ "multi-user.target" ]; - # }; - - - - -} diff --git a/nixos/modules/docker.nix b/nixos/modules/docker.nix deleted file mode 100644 index 1da2973..0000000 --- a/nixos/modules/docker.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - config, - pkgs, - inputs, - ... -}: { - virtualisation.docker = { - enable = true; - enableOnBoot = false; - }; -} diff --git a/nixos/modules/forgejo.nix b/nixos/modules/forgejo.nix deleted file mode 100644 index 228f622..0000000 --- a/nixos/modules/forgejo.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ config, pkgs, ... }: - -let - # (Optional) name your Compose app’s directory on the VM: - composeDir = "/etc/docker-compose-app"; - -in { - # 1) Install Docker engine and Docker‐Compose binary: - environment.systemPackages = with pkgs; [ - docker - docker-compose # pulls in the python-based compose - ]; - - # 2) Enable the Docker daemon: - services.docker.enable = true; - - # 3) Create a directory for your Compose file and copy it from the flake: - # If your flake repo has a sibling file `docker-compose.yml`, this will drop - # it into /etc/docker-compose-app/docker-compose.yml on the VM. - environment.etc."docker-compose-app/docker-compose.yml".text = builtins.readFile ./docker-compose.yml; - - # 4) Make sure that directory exists with the right permissions: - systemd.tmpfiles.rules = [ - # D = create directory if missing, mode 0755, owner root:root - "D! /etc/docker-compose-app 0755 root root - -" - ]; - - # 5) Define a systemd service to run `docker-compose up`: - systemd.services.dockerComposeApp = { - description = "docker-compose stack for my application"; - after = [ "network-online.target" "docker.service" ]; - wants = [ "network-online.target" "docker.service" ]; - - serviceConfig = { - # Run in foreground but let systemd restart if it crashes - ExecStart = "${pkgs.docker-compose}/bin/docker-compose -f ${composeDir}/docker-compose.yml up"; - ExecStop = "${pkgs.docker-compose}/bin/docker-compose -f ${composeDir}/docker-compose.yml down"; - WorkingDirectory = composeDir; - Restart = "always"; - RestartSec = 10; - }; - - # Make sure the directory exists before this service starts: - preStart = '' - mkdir -p ${composeDir} - chown root:root ${composeDir} - ''; - - wantedBy = [ "multi-user.target" ]; - }; - - # 6) (Optional) If any volumes need to exist, define them here, for example: - # environment.etc."docker-compose-app/data".source = "/path/to/local/data"; -} diff --git a/nixos/modules/promtail.nix b/nixos/modules/promtail.nix index 84147a0..37a5611 100644 --- a/nixos/modules/promtail.nix +++ b/nixos/modules/promtail.nix @@ -1,9 +1,11 @@ -{ config, pkgs, ... }: -let - promtail_port = 9080; -in { - networking.firewall.allowedTCPPorts = [ promtail_port ]; + config, + pkgs, + ... +}: let + promtail_port = 9080; +in { + networking.firewall.allowedTCPPorts = [promtail_port]; systemd.tmpfiles.rules = [ "d /var/lib/promtail 0755 promtail promtail -" @@ -19,26 +21,60 @@ in positions = { filename = "/var/lib/promtail/positions.yaml"; }; - clients = [{ - url = "http://monitor.lab:3100/loki/api/v1/push"; - }]; - scrape_configs = [{ - job_name = "journal"; - journal = { - path = "/var/log/journal"; - labels = { - job = "promtail"; - host = config.networking.hostName; - env = "proxmox"; - instance = "${config.networking.hostName}.lab"; + clients = [ + { + url = "http://monitor.lab:3100/loki/api/v1/push"; + } + ]; + scrape_configs = [ + { + job_name = "journal"; + journal = { + path = "/var/log/journal"; + labels = { + job = "promtail"; + host = config.networking.hostName; + env = "proxmox"; + instance = "${config.networking.hostName}.lab"; + }; }; - }; - relabel_configs = [{ - source_labels = ["__journal__systemd_unit"]; - target_label = "unit"; - }]; - - }]; + relabel_configs = [ + { + source_labels = ["__journal__systemd_unit"]; + target_label = "unit"; + } + { + source_labels = ["__journal__hostname"]; + target_label = "host"; + } + { + source_labels = ["__journal__systemd_user_unit"]; + target_label = "user_unit"; + } + { + source_labels = ["__journal__transport"]; + target_label = "transport"; + } + { + source_labels = ["__journal_priority_keyword"]; + target_label = "severity"; + } + ]; + } + # { + # job_name = "secure"; + # static_configs = { + # targets = ["localhost"]; + # labels = { + # job = "secure"; + # host = config.networking.hostName; + # env = "proxmox"; + # instance = "${config.networking.hostName}.lab"; + # __path__ = "/var/log/secure"; + # }; + # }; + # } + ]; }; }; } diff --git a/nixos/secrets/README.md b/nixos/secrets/README.md index f14a377..4708e4e 100644 --- a/nixos/secrets/README.md +++ b/nixos/secrets/README.md @@ -23,11 +23,11 @@ secrets/ ### ✅ Encrypt a **new secret file** ```bash -sops --age -e > secrets/myservice/secrets.yaml +sops --age secrets/myservice/secrets.yml ```` Example: ```bash -sops --age $(cat ~/.config/sops/age/keys.txt | grep public) -e > secrets/forgejo/secrets.yaml +sops --age $(cat ~/.config/sops/age/keys.txt | grep public) -e > secrets/forgejo/secrets.yml ``` > Press `i` to enter edit mode if prompted, or fill it using YAML format: ```yaml @@ -40,7 +40,7 @@ db-password: supersecret ### ✏️ Edit secrets in an existing file ```bash -sops secrets/forgejo/secrets.yaml +sops secrets/forgejo/secrets.yml ``` --- diff --git a/nixos/secrets/influxdb/secrets.yml b/nixos/secrets/influxdb/secrets.yml new file mode 100644 index 0000000..e2388ad --- /dev/null +++ b/nixos/secrets/influxdb/secrets.yml @@ -0,0 +1,17 @@ +influxdb-password: ENC[AES256_GCM,data:dpU2unbGc0wLOwaOEbi572+gazA=,iv:wHp62USQS64nMbJm4kmJzD3kqyZlyhHKLH7kj1HDFxI=,tag:v+EeKBeh5OlxYJSP9VFsEw==,type:str] +influxdb-token: ENC[AES256_GCM,data:72bONbuZOBjwMqdDRRBHD9aQZBs=,iv:tTOYz+8c5Dm6bU7ADcYNI1o2m+o5LoVHl+PymBItV2I=,tag:jOY9PkeXGdlUN1OVI64rhg==,type:str] +sops: + age: + - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2SkNlWmxCYzNmd3lJUE04 + d1NDbEltSVdXWW1PWHROSVU1Y0JMaHZHOWhrCm5DS1hQNTFqMnBUTUtDQXhuclNN + eGFFcWRJYXkrRTk2S1h6SDFxdnVDOEUKLS0tIDQ3MWszUlVUT3FzZkFzNkoxenBJ + aEdYYW1GOW5XL2VERGRlU3F6dWlXWG8KBtb7wu2RF6LVfMHG194CvQGoA0+9D9q8 + hWw0iZlsx7AiSxTSWsBDtYLnTO4lEveBmkXegVT+nqwVLUM/I0dWPw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-15T17:41:53Z" + mac: ENC[AES256_GCM,data:U+I/dbYz+TOQ7wVo5+IFOGW/20B2UoT5vp+Iq0QLzUjLLGoBgntCK90p7kZHz4tQRl2IRADZIx5cOu88dTdl8FPWmd80iowmzg+ISvaHqiXLanaUEFYSeqeRISUPtLJf37QJnsGmJbbcbbB9gn1TpAvdcnJZG+DVIOlDyId6tgc=,iv:IkMU2sqrMzIt7A05zx45LSFiRaT2hPJ1nY+uAJNifuE=,tag:JwRjNKiM1zz1xoEJ+GgdlA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/nixos/secrets/keycloak/secrets.yml b/nixos/secrets/keycloak/secrets.yml new file mode 100644 index 0000000..2b2c494 --- /dev/null +++ b/nixos/secrets/keycloak/secrets.yml @@ -0,0 +1,17 @@ +keycloak_psql_pass: ENC[AES256_GCM,data:5qdsIrT74eME6DS1EyWmq20LJcZBz+OK1/TeNwD0,iv:srgAANq6e97jN0bFRwtQIg+KDDBsydpiBogNT2Qi5p0=,tag:iighlJLzqPXeK7kBTdoBQA==,type:str] +keycloak_admin_pass: ENC[AES256_GCM,data:Yp5yRGg/gR+jnerA5bXIFCbJ+0jRUqevKGB4tTlA,iv:Oa8rLcszJ9ijoRo9PDMdf/X+uaPnqF/I4uSZlW/8OmY=,tag:BmVQGV9y3K38n1WB4zCV7w==,type:str] +sops: + age: + - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvQ1gzQXdlK09LWGMyYVRo + bkNQaGgxcU1tcHJ5WUsxbnFjMlRDbE9YcW1JCjV3ZjNOb2srNm9GeHdsSVhoZDhW + N2RFbVdsQ0E2a3d6WjdacXBjVEpxWmcKLS0tIG80TzYvZXBJUC9wM2s3K3Yvampy + aG9xNDF5MERzTE11cGxaQkZFL0N5eGcKXTptz4nnwCkcCDnsM+41vbF0NyKm+S1F + AESwGFiVC67sqxo+pfK/SxgeUg0tbuL0Lh4Sgp2X7kFOuFOLBzDxfA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-11T16:23:10Z" + mac: ENC[AES256_GCM,data:B0U5kmRutk4xE6+hX6eLu8Q5PFcmPC9MhcRaWLFxVr0Ci4M+oZqdpdnfE/u70E/yRRaXzRgAcfQjaz2gSmr51pXv236+OU+cAB4MjDHODZOiAKqJL0cLzkebv+NiN24YA9McWjrgVgRf5fdoD+fyYNZd04N1vOl4IB4pWXcRKNs=,iv:CRrTVfGfuw+Un1K0NkpwOKsNORBulv8Xt/ZfY6BIlRg=,tag:otoIb0P4JVGZ6bDbDfRezA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/nixos/secrets/mailserver/secrets.yml b/nixos/secrets/mailserver/secrets.yml new file mode 100644 index 0000000..da65ef8 --- /dev/null +++ b/nixos/secrets/mailserver/secrets.yml @@ -0,0 +1,16 @@ +mailserver-admin-pass: ENC[AES256_GCM,data:scvGZ1qXHK12hTx9rndkBQxYMzWtYII1GKGEu28D05h6LJyuUmObWuj+7o6KItoY8VnwVH59dDcJt9qB,iv:ZzgQTIX5wBKwDUBfCMmg6vBJ9Bq17vha2YRPdBzAdbs=,tag:nduE5Q+21MXg3cylv9NyvA==,type:str] +sops: + age: + - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1azlSQVV1clVabVcwalVy + MmpKYW9NbVBkU01ITlhrOFRuckFudTQrM1ZNCnVLUmtscFh0MGpaZEIwTDdwNHdm + VXVCaXVaUk9jMU9HUU9GT3R0RnBrdlEKLS0tIGtOK2xjaUVibWwvWHBMckFrYkI3 + S2hXYjRvVGFZMHpOSWtJUXZGV2lUWTgKFcrcHpfRy8uDiPHQpoyC8I2UF5fbIpQK + h3AHOtkEUJqWA+RfEXg7XRtcVkyqV+fLZjeRlAKnUFDK3/uIAsby+A== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-12T18:29:27Z" + mac: ENC[AES256_GCM,data:G+EWfClTj7J1IFyss0HWXkrdJZtmzgQqEBlrgM+1KF9GkdXXmTBDjunD6ePicdIrqNvZ+CAXw7a+UekrZtIxU5H24msoixo8nIkM7PA/dHo7a75ANdGvgY4k/Ow5HGnWy4jU5DtkrKCns/hZclLhsNHyrp1dwCECEyx9usJ0hMA=,iv:xcMn382q85IP85p8CEbWWFuKCwYGZZON31m8Eb7XV14=,tag:f/8FjxFggKQKskwDffoNGA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/nixos/secrets/nixarr/secrets.yml b/nixos/secrets/nixarr/secrets.yml new file mode 100644 index 0000000..61b1527 --- /dev/null +++ b/nixos/secrets/nixarr/secrets.yml @@ -0,0 +1,24 @@ +bazarr-api-key: ENC[AES256_GCM,data:QTDhVRLtp4vkaffp0EoiauHX+1a5yb+0crHnkLobCPc=,iv:p/E631awnfUzm3vXeq7JN77uFSmgT2Vf/bhRmEB0Isw=,tag:wt8v1Q6kH+mIVPZwTd3aCw==,type:str] +jellyseerr-api-key: ENC[AES256_GCM,data:0aLv9mPkq/dTKfAjVF7CeYgtI4I9dLQ9hM0nGR2VD7zP12ClRE2XLCPBrCVf/zAxVeLhhr0thp1nE7gT2pK8VCIBVJk=,iv:uiu3UOmUtBEiA0RiphTJyozfWm3J97Mgha76gLaJBPk=,tag:Wl565BonzL97Y6ZE4tWHuA==,type:str] +lidarr-api-key: ENC[AES256_GCM,data:ZUApwd9hXZ65skUV/myiC06SYMVtPCj7QkOgAxmvwiw=,iv:RZEzs1gPswdCpZUigp3Z5DUv/5tgxxSdP2sECRbmqD4=,tag:k6WNyJMvHalEDdP2P2f7uA==,type:str] +prowlarr-api-key: ENC[AES256_GCM,data:N8dqfBCTBXs+nsuJIrYoizyNuoSTCLTl9mkHT9UE5Y0=,iv:r1HSqc2D8st1Txeqqy3n65SXBBE3tJPOWpg2egGsI3c=,tag:CAXYIHM60fSEYczccUyLaw==,type:str] +radarr-api-key: ENC[AES256_GCM,data:BBl0dr1+gUawzzrgBbSNNt2mGJ4/E4lVP+k1gJqJr/Y=,iv:wXcz/KFUyk3ygbIRMzkNWI3ZhUmKrk9SffIiN6Ryifo=,tag:1zp+kqAg0Voi2Vm/P6yXlw==,type:str] +readarr-api-key: ENC[AES256_GCM,data:JzgdgaS5hXpqcR6pnC9a/Xpe6l8mZ02SzORFcs2+ct0=,iv:K1tK8F78WMk9c9x4ItCqbe0SQFH4/KiIJYY5JqJfkL0=,tag:0weodhzGOZSbaJj20Zszxg==,type:str] +sonarr-api-key: ENC[AES256_GCM,data:xTcw8QiL95iTScPyQmIRa6QJx94PLiCoLX2muQQFG+o=,iv:L+WnSc8l7R6BCK4tEyYyEgcmVTlhRCUeyjvVV+Z5xQA=,tag:4paOxP0LkyPl4qfCQLsfOg==,type:str] +jellyfin-exporter-config: ENC[AES256_GCM,data:5uXoVKlLXgoky0cHt+JSo682TJ4vHOjkFx73uvsZamlxgbpe6nlbUDKBtILzYohLKWCHnBsL0AC7r7MhD+AK5HJnkhFsDYqqt5lGETqir88MUstIKxuv/mfvzoMla6SvSwgJZnrfGaBW/72PjJ46vgiEA0NtOK6dOdnjyYXD7GNlgl1fcUjtfKAqcZuf9qea0cK7O+PRrzRfnKn9O9XBe2Tq/cff+7u/jU0ZeGSG60wEV3FBAYrbjQ0Bl/PkV28naWHpt4ThXMf4GAnJgh51Sg+zOAUlmP+8FobCFUAJ9W64jojiN9yezX6rFU+diwRMHxVV2UGSKOgOM4glkFGUHfuQzZoZN4CdOnwpeB3FEqMHu3QLDJcRKuH/gm7zgga698oENCNw367YA22J2mu+gu6+aydB0PmUO2F/F+R2ioaXqYgybB2g/DPQwwtqds6p0mEem83RycLJrKKpowG5DUhUciu/DW/Vs7iTSAZnX2MYD/Govl16rodpDzZRbBdmzpp7EZOTG+EC3ISXdjwtxH7nHqBCno0lZJeENunlFHT4/4rg54e1zLcfAvL6pBTuqAQjf93ZqxC+ylj5eNtmOvzlTmMLqv1iUaEMCYJz3WtM0VQtZyGFNCpTsDOtJMbAgDEg7zSr/LiMtpJ7MyKkrVLv+iEcomhj/6jSyYpJt6jPDEQNs69iwXUmXI4f87iKXxf2ZWZ+4DNCdn3fzVHh18l/Vfp0TBcd/00zlnJ4QKXwMMcojQhAAy/djCGUrmKF7dLmU7Bg6lODsDRoLzE+wNmAEZEqVXHNdu8knq2PKkW1UQtS/1EMndS7B+zY0eV9ZV+sJ3+TjFZSmrx2Eb4iLvT3dTonmdzbfCsl4OuMn6n0N1uRP+tRJEWaHp8g5jqDZSbQMYLjVe3XAyGvlNXmQaVqyME8NhNfT60Af8jHir7PgThwZxxVB+K3uyYy2GY04o9o9WiREVZusau8cUUFRUC6xKsLogB4t1LPyMtfZS7xNsXDHFW2cvQhm5b8IqhfEoMLvwMt7FZm/oZ+HMkGFHMaCi3C1PWU3J6H9Z+fz0TTC2rg9JWr0j/Dk5gUHvTZplgiU4ynbSCATCDSYPrveipqgIcMlfCfs4ztPsObnzKEt+Be1SU9j+8Hq8IpEdhGXQEt7Y0tQcuYoM/YuRs7BCd+ib/KotP1ZWDmL92bqwY4NowH357qBMv28pP2H0ilssZCGeKffutOljzBJntysmmvDnOUj92QBRBopx2zq8avcFBIZqtxJq/1Yk6LAcDZYvkOVzT+shv//UJ8H8tzCr7XQZEbkdDdF00cpvS3tNdMFjho7JPsN49RLZh39/RXnwFkp3noJ2iHp/X1laafSx4UNflR7QqSo59yjdDh9Q66dZBc5JeOCVhKon5Nq2CVQDPT27dlP8lteOnRwuWPE85f1amAnLCNipgFIX5ImobejXSzXv41GzNdcC01xJeGDcOP8HlFCyXOF3UgtBEyq+9PPanhiKUALRlytBBzV6s7klRGJ+qKsJ9yO3Ef62X7AyT6iz4ElXp42esKrJXdRbwTPo9l0KfwxEDli0bYiQSmuqkedMiJtaQc2ISGP8+ZHVNO33h0gPr41dN0/6tiei6NTsOiOTMGxpgIchY4mX+RkwfDcV+OmIXPxflUGf6mb0bsCmzBJfNXWiMh1Gz+xlyxkdTdx33Jr26gFr2rsLzioAU89N7pkVZ3wFbuir80y15LWQ6/oOHz8q/cMBFz55HdgDeHflckg1sx+MF2ECl+spu6ZtaUJANldDEH5qUblDsXlL2JrRW0LebwoRKyJJl0oGqQR2ZtgFjcABjstkJgmdyBnIuyZczqp+YEbGl6uuqpIh+h+8Y9AzZUYSv7Tz7thH4oSkkQfjOruIljqb7xD2y6/g9bJKjnCFfzB5wZHQpHtqeNucgGNORzno6Wo+qDERr/veeIeZSvCw0JnrKw5wf+e7pxIpDkgBnDtXhtwvsqRjywEBYbj2K2ieirB7qcQTz4Q5ZWosDNIZKOBOTx9RKv,iv:bG+NpKvflSZUf02iZ6tIi6jHeFOO4a9YW/BlHfwBuk4=,tag:dVHnB7zy9HXiYlvba6/Fug==,type:str] +nixarr-vpn-conf: ENC[AES256_GCM,data:SGg0p4lEWrqUu3g1hIkYskk970mxx6W6d+tbFC6y4gsvg3O6dmApkm2jDU2BWppkbQ4FQvcq/xz8t6tJhFGUCTJ5OOGJJ6vm+1jLf7FVe+2MPIwLhvr6plfTfkFwOXBb/YUBoxNE5PaiKZG2BTckt/cFkM1QZjTt+IAP+1xHgWCUjk5BPEIvbZp0A95gDmExhPTunZPHH4eoG9aB4SMtmjbzs/F6LiFH/F7S74r6p0y/K5lXWvVrH5t0s+rcuwljoGq6WiiOn6AFLq5NUBoQ0zeW/Cr8pPwcDCeKeBaHiqlVFPCNdXTe/p2Z9g+ZO7qA9EaPS0dhD2ANso9mfiCXFfIFW4ir1+5kOs7ckSXGNSLeWw22CLoumyJ4wb/hOjNC2NrCDG2o986WODmkAjlO83FKlr2Tc45/VRPrslUb/JBzIQqvb6d61Bt6xc78Dh5he7NSsEkHrrheQAyp03+fCnvAIINAd5xtreI=,iv:ep9F4DMjOIxg8cEzehNEFZQy0ybVFkcOHzUcF79OrAE=,tag:vt1HDE7Kdb7QiiyvsY6BYQ==,type:str] +sops: + age: + - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6empmamp2ckVjaVB1RDgx + RHJlWGNvYmg2dzBXVGc5K29OTWVGNk9oNEFVCnhMbk1ib3ZFMFJIN1ZJZUNsR3ky + ZXRrS1pYNzNvRUVzd0x3ZUJ2eis3VW8KLS0tIDlxa3cwMXI0blVFcFlZR0hDUklQ + cnBmWXlhYlNNQmMrQ3ZPelhsdlNnMEkKqCUzSjxZFLKy35B5CcFjkv0jW+EBKGSP + oleRKQSVsyraOVmnZ8UjBIRh8X5jCEWbMQ9VbH6aqsvo0ESk18XXbw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-18T17:00:52Z" + mac: ENC[AES256_GCM,data:e/BhM6dLTjsmuXXpXGRHVVcKMkGKJeLD78MXFsJ9QcDMXwazImPCuwsIAHBoJU2Krwdp9HWX2wRpNy1r1WIUCCGsnPK5KACyfNCBUlaMQS0XxfwGYiebS36+Si/gQfJ6hW3ue+TSJDH4zJ2+rMptcjCmMtzNbNnmi+Fij6Q1Zk0=,iv:TSFlP7vuBX6ly1V2vsKEf9KQKfZsskPDCswalts1tFA=,tag:1i7DVs3T+8Xpg+cbtIwyzA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/nixos/secrets/telegram/secrets.yml b/nixos/secrets/telegram/secrets.yml index e5d637e..505985d 100644 --- a/nixos/secrets/telegram/secrets.yml +++ b/nixos/secrets/telegram/secrets.yml @@ -10,7 +10,7 @@ sops: UHl6emN0My8wcFZWYlZEaElrb2NidjgKlZols9SJQxgaoOdJJxghqlACBcwuFs94 IGAOoQVUSFhMCWzyXqAQ/1/VkbWqfiUmvqDa3ulEK2Ri+1F+u3mB1Q== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-06-06T21:21:32Z" - mac: ENC[AES256_GCM,data:YS7BLFXkQ/A5PVLVOyMaqRHGavY0YttFps3njzSiYgBUa4VfPHqMcl2fW5vMec5MwM3GKPFGtrSEZKK1NVqLxUWZrfIF6ugAZ4vhRCyWe1Kze2Zs2S0ia2C3mUdhQR2wb7M7YzohI/e7PDZo0UcrcG3YeEzS5NL7qb0hzFsrGLY=,iv:kqzD06q5X0ZkZ1sIoUQz05b6QRDWQVsPqQYxPP2OAl8=,tag:eexvJspUxpDpwJqU1zEMnA==,type:str] + lastmodified: "2025-06-17T21:24:16Z" + mac: ENC[AES256_GCM,data:t7sdBMtlRupTD2oF3kAlBiWuxuZNMEeLu/xLi5QUmVVdcnb0bednZrXUtRti0mTSqXhwsgEM6j67SVb+vRZh7l1ONixoWrQ+EUcKkCSPcw7fFOwBjCY2jy8ttMWNRHqimM93MmxhX0E5KkoS6ZaB9GDi1vDCI51gCR07y/CBCk8=,iv:A+dG00zh9UkIRxsGm6nYjsZAKfCY6VMTwOqsLoFJLcc=,tag:wB8e4/JfwysRjvRyPuP/Pw==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/nixos/secrets/traefik/secrets.yml b/nixos/secrets/traefik/secrets.yml new file mode 100644 index 0000000..0a21a12 --- /dev/null +++ b/nixos/secrets/traefik/secrets.yml @@ -0,0 +1,17 @@ +traefik-env: ENC[AES256_GCM,data:FH0VWUoGKtijZpU6tpOLkhc40/KPAbs4rTIBLchOWBF1cnWCMBoR8F1xn6AROUuW6OTtXjBTwvPzM+8qHYOR07kGawMATtRHAhy5Qd0AYObrjZRfw96atpEkC8lb6t3Fwwu+4MekX7IFcHU3hlYoS0RUWkx5FdZGiF4fHxz/0fRYOL8jfw==,iv:9SkU5yZNejjIMBf5KsycYek3z3X4KRN9fFor4oYdGZM=,tag:6U+cT8wxFaqrJbWNKWGcog==,type:str] +oauth2-proxy-env: ENC[AES256_GCM,data:SvjM/SOzuYUms98hBYWUivD87ERd26VQOowmQS1/SZfcwiziaxHx9bzj/i0a+ta5aJ7qLYj6a5+KZwo+W5HzgCNs4dIExJW/7N8c+MB6dk64ILqXwfPt7PuOXA+txJGj0dKHDz1nYJkE/E9U1CpDDr3bzRATSQJoujsTUISHMJUBAo0DUBeM6A==,iv:XRz6hgU67S1HfLWDjpc83nekm3bSQ1MEtnsvyXsONU4=,tag:Up54ZP6b61/js042C2py9w==,type:str] +sops: + age: + - recipient: age1n20y9kmdh324m3tkclvhmyuc7c8hk4w84zsal725adahwl8nzq0s04aq4y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPUkJJaXpFdmllZHFjRlgr + OS9XaStIWXhPb3J1bXU4QkQ4cWN4NnBGdVF3CnJkQk5TWHFtOGxnaXJzNlN1dWgv + YjNhQVhvY1l0SE43ZE9NSi9sSCttak0KLS0tIDdZTTIrcGhLMW1aY2tucWhmQkhI + OXhMbmNUbEQ5M251YjBQTmprekZ4U2cK7V4F30TK9fucNhlgyCjsU2mQUVtWWSvu + ZAssFXCXI9mABbyXzf/sDYwdBWuPWkoSdRnScnbKbzPzzniYINGqeg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-06-11T23:36:11Z" + mac: ENC[AES256_GCM,data:lpnAgSEzyPgdfR2IdVHwqK1S6rZ3Bcp5DZwixNdzzGCVUzhTblFIPczN+ogWTx5lXwZf8C7QmbQ9n5lMsh3xURkH7xgZO21BPfU/s9lKsHvE+QwPXrfg7MSe7wr6iuDouXImRsMXbpn36Lo6ocx8OW9vMGjBk2ZBfJa0UxCsl/U=,iv:3HJWy/yh+vUYYl1qEiuDymiFncyDWyD6JZJ44FhCrXA=,tag:XbWhDQZrIaVKlUoxk3lwXg==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/nixos/templates/base.nix b/nixos/templates/base.nix index 518f565..ffa5371 100644 --- a/nixos/templates/base.nix +++ b/nixos/templates/base.nix @@ -1,11 +1,16 @@ -{ config, pkgs, modulesPath, lib, ... }: - { + config, + pkgs, + modulesPath, + lib, + ... +}: { # Pull in all the shared settings from configuration.nix imports = [ ../configuration.nix ../modules/node-exporter.nix ../modules/promtail.nix ../users/plasmagoat.nix + ../secrets/shared-sops.nix ]; } diff --git a/nixos/templates/docker.nix b/nixos/templates/docker.nix deleted file mode 100644 index 2e896e3..0000000 --- a/nixos/templates/docker.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ config, pkgs, modulesPath, lib, ... }: - -{ - # Pull in all the shared settings from configuration.nix - imports = [ - ./base.nix - ]; - - config = { - environment.systemPackages = with pkgs; [ - docker - docker-compose - ]; - }; -} diff --git a/proxmox-infra/.gitignore b/proxmox-infra/.gitignore new file mode 100644 index 0000000..9b8ce00 --- /dev/null +++ b/proxmox-infra/.gitignore @@ -0,0 +1,7 @@ +# proxmox-infra/.gitignore +.terraform/ + +*.tfstate +.tfstate. +crash.log +*.tfvars diff --git a/proxmox-infra/.terraform.lock.hcl b/proxmox-infra/.terraform.lock.hcl new file mode 100644 index 0000000..978a610 --- /dev/null +++ b/proxmox-infra/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/telmate/proxmox" { + version = "3.0.2-rc01" + constraints = "3.0.2-rc01" + hashes = [ + "h1:571ROPuTMC0w5lr9hbUXi7NVLsG3SpmZxXXZx8cAT+Q=", + "zh:34d264243a4513f4e30c01fb37cc6a3e592d7823dfd182c5edfb170ac7b7de3a", + "zh:544428311ad20fbb3ad2cd854e893bbf036023cb57c3acc5093d141976dac670", + "zh:5c2396b328edee8de7ac144c15a6b7e668e81063699bc8c110d7c39fb8da70e9", + "zh:5ca8e33476ad06a0259071120a59477e8f107f30c1178ea7b9f6cafe1a461ade", + "zh:5ea56eb8275edc754a01a0180750e9c939cd997d3a50659617770211f4337da9", + "zh:9dd3482df6bbe00a4a6152be3567b6c08d35c3644a327a1f5ac30fd95ccd449f", + "zh:a76075fafadcc94a825151aff169bae4e0c05e3c7717e16dcdcf16ffa61a0780", + "zh:b1d95f97b22f671db762f7adf428b409e6736c078bcf267d8391985b8847d6e3", + "zh:cc94255cd1b18e6a341c15089015c457c8c639c25c426b07f278d5ea9850b3b5", + "zh:ce991103cb69b0b3e275127e3ab92c88bb3b6b0f4e5a2cb082aeaef70a7f7d61", + "zh:d24838bce87b38e12544a1329f5ad30e2be045968e639a3f4ddd5c84aa648e04", + "zh:e106ebd4eea8d62d62e62f261a262febc615e17466b54ac18f7e65c7e79e0008", + "zh:e254ca76c95e6e92da973b7bddc36bfa0a1e31d7c7e758ef4b01315db969388b", + "zh:f1d1d5f4c39267cacebe0ab7e9e06caf9692707f3b5369685541b65bc8b840ce", + ] +} diff --git a/proxmox-infra/main.tf b/proxmox-infra/main.tf new file mode 100644 index 0000000..e9ef2ce --- /dev/null +++ b/proxmox-infra/main.tf @@ -0,0 +1,52 @@ +# # This calls the module to define a new VM (e.g., if you were creating one) +# resource "proxmox_vm_qemu" "sandbox" { +# name = "sandbox" +# desc = "OpenTofu testing" +# target_nodes = [var.proxmox_node] +# vmid= 100 +# full_clone = true +# clone_id = 9100 +# agent = 1 +# scsihw = "virtio-scsi-single" +# ciuser = "root" +# ipconfig0 = "ip=dhcp" +# cpu { +# cores = 2 +# } +# memory = 2048 +# disks { +# virtio { +# virtio0 { +# disk { +# size = "9452M" +# storage = "local-lvm" +# } +# } +# } +# ide { +# ide2 { +# cloudinit { +# storage = "local-lvm" +# } +# } +# } +# } +# network { +# id = 0 +# bridge = "vmbr0" +# model = "virtio" +# } +# serial { +# id = 0 +# } +# } + +# output "sandbox_vmid" { +# description = "sandbox VM ID" +# value = proxmox_vm_qemu.sandbox.id +# } + +# output "sandbox_ipv4" { +# description = "sandbox public IPv4 address" +# value = proxmox_vm_qemu.sandbox.default_ipv4_address +# } diff --git a/proxmox-infra/outputs.tf b/proxmox-infra/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/proxmox-infra/provider.tf b/proxmox-infra/provider.tf new file mode 100644 index 0000000..0d04a2a --- /dev/null +++ b/proxmox-infra/provider.tf @@ -0,0 +1,9 @@ +provider "proxmox" { + pm_tls_insecure = true + pm_api_url = var.proxmox_api_url + pm_user = var.proxmox_user + pm_password = var.proxmox_password + # Or use API token for better security: + # pm_api_token_id = var.proxmox_api_token_id + # pm_api_token_secret = var.proxmox_api_token_secret +} diff --git a/proxmox-infra/sandbox.tf b/proxmox-infra/sandbox.tf new file mode 100644 index 0000000..7932732 --- /dev/null +++ b/proxmox-infra/sandbox.tf @@ -0,0 +1,106 @@ +# proxmox_vm_qemu.sandbox: +resource "proxmox_vm_qemu" "sandbox" { + agent = 1 + bios = "seabios" + boot = " " + ciuser = "root" + cores = 0 + current_node = "proxmox-01" + define_connection_info = false + desc = " generated by NixOS" + force_create = false + full_clone = false + hotplug = "network,disk,usb" + id = "proxmox-01/qemu/100" + ipconfig0 = "ip=dhcp" + kvm = true + linked_vmid = 0 + memory = 2048 + name = "sandbox" + numa = false + onboot = true + protection = false + qemu_os = "l26" + reboot_required = false + scsihw = "virtio-scsi-single" + sockets = 0 + sshkeys = <<-EOT + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCljEOf8Lv7Ptgsc1+CYzXpnrctPy7LFXXOyVZTI9uN7R4HY5aEdZTKEGSsU/+p+JtXWzzI65fnrZU8pTMG/wvCK+gYyNZcEM4g/TXMVa+CWZR3y13zGky88R7dKiBl5L00U4BePDD1ci3EU3/Mjr/GVTQHtkbJfLtvhR9zkCNZzxbu+rySWDroUPWPvE3y60/iLjBsh5ZmHo59CW67lh1jgbAlZjKWZzLWo0Bc5wgbxoQPWcO4BCh17N4g8llrRxGOwJzHeaipBnXn9J1AGIm9Zls6pxT9j6MKltcCOb7tQZwc3hlPOW2ku6f7OHTrziKw37drIDM0UDublAOcnIfBjE+XuWsp5t6ojdIzIDMrzaYW2MyMA3PHuf7VESUQdP4TZ1XUwtRRzOjn5AZJi9DPoowPaxKL92apRpFG+ovaFpWZsG7s8NWXHAC79IpgMUzscEmM15OMQ36RQ5xeytGDVCmVT8DbHGrMT9HUfR5fBSWD3aDQiOOiIIhrbY35m+U65Sz/GpZMk6HlaiV3tKNB0m+xE+84MUEmm4fFzt3B/0N4kscMArnLAm/OMUblihPwbKAUAUWErGRBfP+u+zjRCi1D9/pffpl2OQ2QIuVM82g6/EPa1ZsXZP+4iHooQoJbrqVGzkfiA1EKLfcdGfkP/O4nRl+D5UgkGdqqvm20NQ== root@proxmox-01 + EOT + tablet = true + target_nodes = [ + "proxmox-01", + ] + unused_disk = [] + vcpus = 0 + vm_state = "running" + vmid = 100 + + cpu { + cores = 2 + limit = 0 + numa = false + sockets = 1 + type = "host" + units = 0 + vcores = 0 + } + + disks { + ide { + ide2 { + cloudinit { + storage = "local-lvm" + } + } + } + virtio { + virtio0 { + disk { + backup = true + discard = false + format = "raw" + id = 0 + iops_r_burst = 0 + iops_r_burst_length = 0 + iops_r_concurrent = 0 + iops_wr_burst = 0 + iops_wr_burst_length = 0 + iops_wr_concurrent = 0 + iothread = false + linked_disk_id = -1 + mbps_r_burst = 0 + mbps_r_concurrent = 0 + mbps_wr_burst = 0 + mbps_wr_concurrent = 0 + readonly = false + replicate = true + size = "9452M" + storage = "local-lvm" + } + } + } + } + + network { + bridge = "vmbr0" + firewall = true + id = 0 + link_down = false + macaddr = "bc:24:11:a7:e8:2a" + model = "virtio" + mtu = 0 + queues = 0 + rate = 0 + tag = 0 + } + + serial { + id = 0 + type = "socket" + } + + smbios { + uuid = "37cd09d5-29a5-42e2-baba-f21b691130e8" + } +} diff --git a/proxmox-infra/terraform.tfstate.backup b/proxmox-infra/terraform.tfstate.backup new file mode 100644 index 0000000..8a8181b --- /dev/null +++ b/proxmox-infra/terraform.tfstate.backup @@ -0,0 +1 @@ +{"version":4,"terraform_version":"1.9.1","serial":2,"lineage":"ecd6c5f8-5352-bf30-6117-d55763366399","outputs":{"sandbox_ipv4":{"value":"192.168.1.206","type":"string"},"sandbox_vmid":{"value":"proxmox-01/qemu/999","type":"string"}},"resources":[{"mode":"managed","type":"proxmox_vm_qemu","name":"sandbox","provider":"provider[\"registry.opentofu.org/telmate/proxmox\"]","instances":[{"schema_version":0,"attributes":{"additional_wait":5,"agent":1,"agent_timeout":90,"args":"","automatic_reboot":true,"balloon":0,"bios":"seabios","boot":" ","bootdisk":"","ci_wait":null,"cicustom":null,"cipassword":"","ciupgrade":false,"ciuser":"root","clone":null,"clone_id":9100,"clone_wait":10,"cores":0,"cpu":[{"affinity":"","cores":2,"flags":[],"limit":0,"numa":false,"sockets":1,"type":"host","units":0,"vcores":0}],"cpu_type":"","current_node":"proxmox-01","default_ipv4_address":"192.168.1.206","default_ipv6_address":"2a05:f6c7:2030:0:be24:11ff:feb9:919f","define_connection_info":true,"desc":"OpenTofu testing","disk":[],"disks":[{"ide":[{"ide0":[],"ide1":[],"ide2":[{"cdrom":[],"cloudinit":[{"storage":"local-lvm"}],"disk":[],"ignore":false,"passthrough":[]}],"ide3":[]}],"sata":[],"scsi":[],"virtio":[{"virtio0":[{"cdrom":[],"disk":[{"asyncio":"","backup":true,"cache":"","discard":false,"format":"raw","id":0,"iops_r_burst":0,"iops_r_burst_length":0,"iops_r_concurrent":0,"iops_wr_burst":0,"iops_wr_burst_length":0,"iops_wr_concurrent":0,"iothread":false,"linked_disk_id":-1,"mbps_r_burst":0,"mbps_r_concurrent":0,"mbps_wr_burst":0,"mbps_wr_concurrent":0,"readonly":false,"replicate":false,"serial":"","size":"9452M","storage":"local-lvm","wwn":""}],"ignore":false,"passthrough":[]}],"virtio1":[],"virtio10":[],"virtio11":[],"virtio12":[],"virtio13":[],"virtio14":[],"virtio15":[],"virtio2":[],"virtio3":[],"virtio4":[],"virtio5":[],"virtio6":[],"virtio7":[],"virtio8":[],"virtio9":[]}]}],"efidisk":[],"force_create":false,"force_recreate_on_change_of":null,"full_clone":true,"hagroup":"","hastate":"","hostpci":[],"hotplug":"network,disk,usb","id":"proxmox-01/qemu/999","ipconfig0":"ip=dhcp","ipconfig1":null,"ipconfig10":null,"ipconfig11":null,"ipconfig12":null,"ipconfig13":null,"ipconfig14":null,"ipconfig15":null,"ipconfig2":null,"ipconfig3":null,"ipconfig4":null,"ipconfig5":null,"ipconfig6":null,"ipconfig7":null,"ipconfig8":null,"ipconfig9":null,"kvm":true,"linked_vmid":0,"machine":"","memory":2048,"name":"sandbox2","nameserver":null,"network":[{"bridge":"vmbr0","firewall":false,"id":0,"link_down":false,"macaddr":"bc:24:11:b9:91:9f","model":"virtio","mtu":0,"queues":0,"rate":0,"tag":0}],"numa":false,"onboot":false,"os_network_config":null,"os_type":null,"pci":[],"pcis":[],"pool":"","protection":false,"pxe":null,"qemu_os":"l26","reboot_required":false,"scsihw":"virtio-scsi-single","searchdomain":null,"serial":[{"id":0,"type":"socket"}],"skip_ipv4":false,"skip_ipv6":false,"smbios":[{"family":"","manufacturer":"","product":"","serial":"","sku":"","uuid":"51a93ec4-4afa-428b-911a-daab70390a8c","version":""}],"sockets":0,"ssh_forward_ip":null,"ssh_host":"192.168.1.206","ssh_port":"22","ssh_private_key":null,"ssh_user":null,"sshkeys":null,"startup":"","tablet":true,"tags":"v0.0.2","target_node":null,"target_nodes":["proxmox-01"],"timeouts":null,"tpm_state":[],"unused_disk":[],"usb":[],"usbs":[],"vcpus":0,"vga":[],"vm_state":"running","vmid":999},"sensitive_attributes":[[{"type":"get_attr","value":"cipassword"}],[{"type":"get_attr","value":"ssh_private_key"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWZhdWx0IjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInJlYWQiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19"}]}],"check_results":null} diff --git a/proxmox-infra/variables.tf b/proxmox-infra/variables.tf new file mode 100644 index 0000000..71653f0 --- /dev/null +++ b/proxmox-infra/variables.tf @@ -0,0 +1,30 @@ +# proxmox-infra/variables.tf + +variable "proxmox_api_url" { + description = "The URL of the Proxmox API (e.g., https://192.168.1.10:8006/api2/json)" + type = string + # No default here, so OpenTofu will prompt or expect a .tfvars file/env var +} + +variable "proxmox_user" { + description = "Proxmox user (e.g., root@pam or user@pve)" + type = string +} + +variable "proxmox_password" { + description = "Proxmox user password" + type = string + sensitive = true # Mark as sensitive to hide in logs +} + +variable "proxmox_node" { + description = "The Proxmox node name where VMs will be deployed (e.g., 'pve')" + type = string +} + +# Example for templates - you might have different templates +variable "nixos_template_id" { + description = "VMID of the nixos cloud-init template" + type = number + # Example: default = 100 +} diff --git a/proxmox-infra/versions.tf b/proxmox-infra/versions.tf new file mode 100644 index 0000000..3ca35cc --- /dev/null +++ b/proxmox-infra/versions.tf @@ -0,0 +1,9 @@ +# versions.tf +terraform { + required_providers { + proxmox = { + source = "Telmate/proxmox" + version = "3.0.2-rc01" + } + } +}