From 2633ab6d08305a3d02ef47e2be1a955d203b1c8e Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 31 Dec 2023 13:12:09 -0300 Subject: [PATCH 01/43] refactor(terraform-nix): Flake improvements Move LXC configurations to legacyPackages so it plays nice with `nix flake show` Dynamically load services from the services folder --- terraform-nix/flake.nix | 54 +++++++++++-------- terraform-nix/modules/base.nix | 3 -- terraform-nix/{modules => services}/caddy.nix | 0 3 files changed, 32 insertions(+), 25 deletions(-) delete mode 100644 terraform-nix/modules/base.nix rename terraform-nix/{modules => services}/caddy.nix (100%) diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 2aa3464..cf7b88d 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -2,6 +2,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/23.05"; nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; nixos-generators = { url = "github:nix-community/nixos-generators"; @@ -12,34 +13,43 @@ outputs = { self, flake-utils, nixpkgs, nixpkgs-unstable, nixos-generators, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let - commonPkgConfig = { - inherit system; - config.allowUnfree = true; - }; - pkgsUnstable = import nixpkgs-unstable commonPkgConfig; - pkgs = import nixpkgs-unstable commonPkgConfig; - makeProxmoxLxc = modules: - nixpkgs.lib.nixosSystem { + makePkgs = nixpkgs: + import nixpkgs { inherit system; - modules = [ - nixos-generators.nixosModules.proxmox-lxc - ./modules/base.nix - ] ++ modules; + config = { + allowUnfree = true; + allowUnfreePredicate = _: true; + }; }; - in { - packages = rec { - default = base-proxmox-lxc; - base-proxmox-lxc = nixos-generators.nixosGenerate { + pkgs = makePkgs nixpkgs; + pkgsUnstable = makePkgs nixpkgs-unstable; + makeNixosConfig = modules: + nixpkgs.lib.nixosSystem { inherit system; - modules = [ ./modules/base.nix ]; + modules = [ nixos-generators.nixosModules.proxmox-lxc ] ++ modules; + }; + makeProxmoxLxc = modules: + nixos-generators.nixosGenerate { + inherit system modules; format = "proxmox-lxc"; - pkgs = nixpkgs.${system}; + pkgs = nixpkgs.legacyPackages.${system}; lib = nixpkgs.legacyPackages.${system}.lib; + specialArgs = { inherit inputs pkgs system; }; }; - nixosConfigurations = rec { - default = caddy; - caddy = makeProxmoxLxc [ ./modules/caddy.nix ]; - }; + in { + legacyPackages.nixosConfigurations = + let + servicesPath = ./services; + services = builtins.readDir servicesPath; + in + pkgs.lib.mapAttrs' + (file: _: pkgs.lib.nameValuePair + (pkgs.lib.removeSuffix ".nix" file) + (makeNixosConfig [ "${servicesPath}/${file}" ])) + services; + packages = rec { + default = base-proxmox-lxc; + base-proxmox-lxc = makeProxmoxLxc []; }; devShells.default = with pkgsUnstable; diff --git a/terraform-nix/modules/base.nix b/terraform-nix/modules/base.nix deleted file mode 100644 index 5b542b9..0000000 --- a/terraform-nix/modules/base.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - system.stateVersion = "23.05"; -} diff --git a/terraform-nix/modules/caddy.nix b/terraform-nix/services/caddy.nix similarity index 100% rename from terraform-nix/modules/caddy.nix rename to terraform-nix/services/caddy.nix From 536c77b705e40f1675c75d19116ecfafafffcce6 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Mon, 1 Jan 2024 10:51:13 -0300 Subject: [PATCH 02/43] fix(terraform-nix): Add workaround for LXC online detection Make it so systemd-networkd-wait-online works as expected. It would consistently report the LXC as having no internet connection even though it was working just fine. --- terraform-nix/flake.nix | 5 ++++- terraform-nix/modules/proxmox-lxc-base.nix | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 terraform-nix/modules/proxmox-lxc-base.nix diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index cf7b88d..043fbcf 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -26,7 +26,10 @@ makeNixosConfig = modules: nixpkgs.lib.nixosSystem { inherit system; - modules = [ nixos-generators.nixosModules.proxmox-lxc ] ++ modules; + modules = [ + nixos-generators.nixosModules.proxmox-lxc + ./modules/proxmox-lxc-base.nix + ] ++ modules; }; makeProxmoxLxc = modules: nixos-generators.nixosGenerate { diff --git a/terraform-nix/modules/proxmox-lxc-base.nix b/terraform-nix/modules/proxmox-lxc-base.nix new file mode 100644 index 0000000..cdb22db --- /dev/null +++ b/terraform-nix/modules/proxmox-lxc-base.nix @@ -0,0 +1,3 @@ +{ + systemd.network.networks.eth0.linkConfig.RequiredForOnline = "routable"; # Workaround for the LXC sometimes not detecting a valid internet connection even though it's clearly up +} From 1a092ebf987f5f1811ea230148a973513c67d81f Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Mon, 1 Jan 2024 10:52:50 -0300 Subject: [PATCH 03/43] feat(terraform-nix): Build service tarballs Services can now be used with `nix build` as well --- terraform-nix/flake.nix | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 043fbcf..ba57b30 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -23,37 +23,37 @@ }; pkgs = makePkgs nixpkgs; pkgsUnstable = makePkgs nixpkgs-unstable; - makeNixosConfig = modules: + commonModules = [ { system.stateVersion = "23.05"; } ]; + makeProxmoxLxcConfig = modules: nixpkgs.lib.nixosSystem { inherit system; - modules = [ - nixos-generators.nixosModules.proxmox-lxc + modules = commonModules ++ modules ++ [ ./modules/proxmox-lxc-base.nix - ] ++ modules; + nixos-generators.nixosModules.proxmox-lxc + ]; + specialArgs = { inherit inputs pkgs system; }; }; - makeProxmoxLxc = modules: + makeProxmoxLxcTarball = modules: nixos-generators.nixosGenerate { - inherit system modules; + inherit system; + modules = commonModules ++ modules; format = "proxmox-lxc"; pkgs = nixpkgs.legacyPackages.${system}; lib = nixpkgs.legacyPackages.${system}.lib; specialArgs = { inherit inputs pkgs system; }; }; - in { - legacyPackages.nixosConfigurations = - let - servicesPath = ./services; - services = builtins.readDir servicesPath; - in + servicesPath = ./services; + services = builtins.readDir servicesPath; + makeAttrsetFromServices = action: pkgs.lib.mapAttrs' - (file: _: pkgs.lib.nameValuePair - (pkgs.lib.removeSuffix ".nix" file) - (makeNixosConfig [ "${servicesPath}/${file}" ])) + (file: _: pkgs.lib.nameValuePair (pkgs.lib.removeSuffix ".nix" file) (action "${servicesPath}/${file}")) services; + in { + legacyPackages.nixosConfigurations = makeAttrsetFromServices (path: makeProxmoxLxcConfig [ path ]); packages = rec { default = base-proxmox-lxc; - base-proxmox-lxc = makeProxmoxLxc []; - }; + base-proxmox-lxc = makeProxmoxLxcTarball []; + } // makeAttrsetFromServices (path: makeProxmoxLxcTarball [ path ]); devShells.default = with pkgsUnstable; mkShell { @@ -61,7 +61,7 @@ (terraform.withPlugins (b: with b; [ external local - b.null + null proxmox ])) ]; From 563890bdbf8b16fbe956eb168fbc006e6fa21bcf Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Mon, 1 Jan 2024 10:54:53 -0300 Subject: [PATCH 04/43] feat(terraform-nix): Update terraform-nix Rework the main logic to work with a list of LXC definitions --- terraform-nix/main.tf | 44 +++++++++++++++++---------- terraform-nix/terraform.tfvars.sample | 8 ++++- terraform-nix/variables.tf | 17 +++++++++-- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 45538b7..fc03d6c 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -27,19 +27,31 @@ provider "proxmox" { } } -resource "proxmox_lxc" "nixos-caddy" { - vmid = "241" - tags = "nixos,caddy" - memory = var.default_mem - cores = 4 +locals { + lxcs = [ + for lxc in var.lxcs : merge(lxc, { + flake = coalesce(lxc.flake, lxc.name) + }) if lxc.enable + ] +} + +resource "proxmox_lxc" "nixos_lxc" { + count = length(local.lxcs) - hostname = "nixos-caddy" + vmid = local.lxcs[count.index].vmid + tags = join(";", sort(concat([ "nixos", "terraform" ], local.lxcs[count.index].tags))) + + cores = local.lxcs[count.index].cores + memory = local.lxcs[count.index].memory + swap = local.lxcs[count.index].swap + + hostname = "nixos-${local.lxcs[count.index].name}" network { name = "eth0" - bridge = "vmbr0" - ip = "10.10.2.41/${var.network_subnet}" - ip6 = "dhcp" - gw = var.network_gateway + bridge = "vmbr0" + ip = "${local.lxcs[count.index].ip}/${var.network_subnet}" + ip6 = "dhcp" + gw = var.network_gateway } target_node = var.pm_node_name @@ -59,20 +71,20 @@ resource "proxmox_lxc" "nixos-caddy" { } module "nixos" { + count = length(local.lxcs) source = "github.com/Gabriella439/terraform-nixos-ng/nixos" - host = "root@10.10.2.41" - - flake = ".#caddy" + host = "${local.lxcs[count.index].user}@${local.lxcs[count.index].ip}" + flake = ".#${local.lxcs[count.index].flake}" arguments = [ # You can build on another machine, including the target machine, by # enabling this option, but if you build on the target machine then make # sure that the firewall and security group permit outbound connections. - # "--build-host", "root@${var.caddy_ip}", + "--build-host", "${local.lxcs[count.index].user}@${local.lxcs[count.index].ip}", ] - ssh_options = "-o StrictHostKeyChecking=accept-new" + ssh_options = "-o StrictHostKeyChecking=no" - depends_on = [proxmox_lxc.nixos-caddy] + depends_on = [proxmox_lxc.nixos_lxc] } diff --git a/terraform-nix/terraform.tfvars.sample b/terraform-nix/terraform.tfvars.sample index 92e95cd..78b4875 100644 --- a/terraform-nix/terraform.tfvars.sample +++ b/terraform-nix/terraform.tfvars.sample @@ -9,4 +9,10 @@ network_subnet = "8" authorized_keys = < Date: Tue, 2 Jan 2024 00:35:33 -0300 Subject: [PATCH 05/43] chore(terraform-nix): Update nixpkgs to 23.11 --- terraform-nix/flake.lock | 8 ++++---- terraform-nix/flake.nix | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/terraform-nix/flake.lock b/terraform-nix/flake.lock index 60618df..0e6f093 100644 --- a/terraform-nix/flake.lock +++ b/terraform-nix/flake.lock @@ -56,16 +56,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1685566663, - "narHash": "sha256-btHN1czJ6rzteeCuE/PNrdssqYD2nIA4w48miQAFloM=", + "lastModified": 1701282334, + "narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4ecab3273592f27479a583fb6d975d4aba3486fe", + "rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e", "type": "github" }, "original": { "owner": "NixOS", - "ref": "23.05", + "ref": "23.11", "repo": "nixpkgs", "type": "github" } diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index ba57b30..686bf0a 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/23.05"; + nixpkgs.url = "github:NixOS/nixpkgs/23.11"; nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; @@ -23,7 +23,7 @@ }; pkgs = makePkgs nixpkgs; pkgsUnstable = makePkgs nixpkgs-unstable; - commonModules = [ { system.stateVersion = "23.05"; } ]; + commonModules = [ { system.stateVersion = "23.11"; } ]; makeProxmoxLxcConfig = modules: nixpkgs.lib.nixosSystem { inherit system; From 0fe144e505f11a46696dc2ea26adf6b71785f71d Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 09:49:48 -0300 Subject: [PATCH 06/43] feat(terraform-nix): Terraform enhancements Allow defining custom mountpoints Allow adding extra config for specifying vmid.conf lines the API doesn't cover Explicitly define rootfs with optional custom size Change from count to for_each for cleaner code --- terraform-nix/main.tf | 70 ++++++++++++++++++++++++++++++-------- terraform-nix/variables.tf | 38 +++++++++++++++------ 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index fc03d6c..036b2f3 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -31,25 +31,47 @@ locals { lxcs = [ for lxc in var.lxcs : merge(lxc, { flake = coalesce(lxc.flake, lxc.name) + mountpoints = [for mp in lxc.mountpoints : merge(mp, { + volume = coalesce(mp.volume, tostring(mp.storage)) + })] }) if lxc.enable ] } -resource "proxmox_lxc" "nixos_lxc" { - count = length(local.lxcs) +resource "proxmox_lxc" "nixos" { + for_each = { for key, val in local.lxcs : key => val } - vmid = local.lxcs[count.index].vmid - tags = join(";", sort(concat([ "nixos", "terraform" ], local.lxcs[count.index].tags))) + vmid = each.value.vmid + tags = join(";", sort(concat(["nixos", "terraform"], each.value.tags))) + start = true + onboot = each.value.onboot + unprivileged = !each.value.privileged - cores = local.lxcs[count.index].cores - memory = local.lxcs[count.index].memory - swap = local.lxcs[count.index].swap + cores = each.value.cores + memory = each.value.memory + swap = each.value.swap - hostname = "nixos-${local.lxcs[count.index].name}" + rootfs { + storage = "local-lvm" + size = each.value.rootfs_size + } + dynamic "mountpoint" { + for_each = each.value.mountpoints + content { + slot = mountpoint.value["slot"] + mp = mountpoint.value["mp"] + storage = mountpoint.value["storage"] + volume = mountpoint.value["volume"] + key = mountpoint.value["key"] + size = mountpoint.value["size"] + } + } + + hostname = each.value.name network { name = "eth0" bridge = "vmbr0" - ip = "${local.lxcs[count.index].ip}/${var.network_subnet}" + ip = "${each.value.ip}/${var.network_subnet}" ip6 = "dhcp" gw = var.network_gateway } @@ -57,34 +79,52 @@ resource "proxmox_lxc" "nixos_lxc" { target_node = var.pm_node_name ostemplate = var.pm_lxc_template ssh_public_keys = var.authorized_keys - start = true cmode = "console" features { nesting = true } + lifecycle { ignore_changes = [ rootfs ] } + + connection { + type = "ssh" + user = var.pm_user + host = var.pm_host + private_key = file("~/.ssh/id_rsa") + } + + provisioner "remote-exec" { + inline = length(each.value.extra_config) > 0 ? concat( + [for line in each.value.extra_config : "echo '${line}' >> /etc/pve/lxc/${each.value.vmid}.conf"], + [ + "awk -i inplace '!a[$0]++' /etc/pve/lxc/${each.value.vmid}.conf", # Removes duplicate lines, just in case + "sleep 2 && pct reboot ${each.value.vmid}" + ] + ) : [] + } } module "nixos" { - count = length(local.lxcs) + for_each = { for key, val in local.lxcs : key => val } + source = "github.com/Gabriella439/terraform-nixos-ng/nixos" - host = "${local.lxcs[count.index].user}@${local.lxcs[count.index].ip}" - flake = ".#${local.lxcs[count.index].flake}" + host = "${each.value.user}@${each.value.ip}" + flake = ".#${each.value.flake}" arguments = [ # You can build on another machine, including the target machine, by # enabling this option, but if you build on the target machine then make # sure that the firewall and security group permit outbound connections. - "--build-host", "${local.lxcs[count.index].user}@${local.lxcs[count.index].ip}", + "--build-host", "${each.value.user}@${each.value.ip}", ] ssh_options = "-o StrictHostKeyChecking=no" - depends_on = [proxmox_lxc.nixos_lxc] + depends_on = [proxmox_lxc.nixos] } diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index 76f1479..f5d3627 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -9,6 +9,12 @@ variable "pm_host" { type = string } +variable "pm_user" { + description = "PVE node user" + type = string + default = "root" +} + variable "pm_node_name" { description = "Name of the proxmox node to create the VMs on" type = string @@ -38,15 +44,27 @@ variable "authorized_keys" { variable "lxcs" { description = "List of maps describing LXC containers" type = list(object({ - name = string - vmid = number - ip = string - enable = optional(bool, true) - user = optional(string, "root") - flake = optional(string, null) - tags = optional(list(string), []) - memory = optional(number, 1024) - swap = optional(number, 0) - cores = optional(number, 6) + name = string + vmid = number + ip = string + enable = optional(bool, true) + onboot = optional(bool, true) + rootfs_size = optional(string, "8G") + privileged = optional(bool, false) + user = optional(string, "root") + flake = optional(string, null) + tags = optional(list(string), []) + memory = optional(number, 1024) + swap = optional(number, 0) + cores = optional(number, 6) + mountpoints = optional(list(object({ + slot = number + mp = string + storage = string + key = optional(string, "") + volume = optional(string) + size = optional(string, "0T") + })), []) + extra_config = optional(list(string), []) })) } From 9d8694e954125fdf19c252bd2b52e43f7cce529d Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 10:05:53 -0300 Subject: [PATCH 07/43] chore(terraform-nix): Flake enhancements Extract shared logic from makeProxmox* methods to make it more DRY Move platform-specific proxmox LXC code to dedicated platform dir --- terraform-nix/flake.nix | 27 ++++++++++--------- .../modules/platforms/proxmox-lxc/default.nix | 12 +++++++++ terraform-nix/modules/proxmox-lxc-base.nix | 3 --- 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 terraform-nix/modules/platforms/proxmox-lxc/default.nix delete mode 100644 terraform-nix/modules/proxmox-lxc-base.nix diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 686bf0a..990d923 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -23,25 +23,26 @@ }; pkgs = makePkgs nixpkgs; pkgsUnstable = makePkgs nixpkgs-unstable; - commonModules = [ { system.stateVersion = "23.11"; } ]; - makeProxmoxLxcConfig = modules: - nixpkgs.lib.nixosSystem { + + makeCommonConfig = modules: { inherit system; - modules = commonModules ++ modules ++ [ - ./modules/proxmox-lxc-base.nix - nixos-generators.nixosModules.proxmox-lxc - ]; + modules = [ { system.stateVersion = "23.11"; } ] ++ modules; specialArgs = { inherit inputs pkgs system; }; - }; + }; + makeProxmoxLxcConfig = modules: + nixpkgs.lib.nixosSystem ( + makeCommonConfig (modules ++ [ + ./modules/platforms/proxmox-lxc + nixos-generators.nixosModules.proxmox-lxc + ]) + ); makeProxmoxLxcTarball = modules: - nixos-generators.nixosGenerate { - inherit system; - modules = commonModules ++ modules; + nixos-generators.nixosGenerate (makeCommonConfig modules // { format = "proxmox-lxc"; pkgs = nixpkgs.legacyPackages.${system}; lib = nixpkgs.legacyPackages.${system}.lib; - specialArgs = { inherit inputs pkgs system; }; - }; + }); + servicesPath = ./services; services = builtins.readDir servicesPath; makeAttrsetFromServices = action: diff --git a/terraform-nix/modules/platforms/proxmox-lxc/default.nix b/terraform-nix/modules/platforms/proxmox-lxc/default.nix new file mode 100644 index 0000000..90bd405 --- /dev/null +++ b/terraform-nix/modules/platforms/proxmox-lxc/default.nix @@ -0,0 +1,12 @@ +{ + systemd.network.networks.eth0 = { + matchConfig.Name = "eth0"; + linkConfig.RequiredForOnline = "routable"; # Workaround for the LXC sometimes not detecting a valid internet connection even though it's clearly up + networkConfig.Description = "Default connection"; + }; + + proxmoxLXC = { + manageNetwork = false; + manageHostName = false; + }; +} diff --git a/terraform-nix/modules/proxmox-lxc-base.nix b/terraform-nix/modules/proxmox-lxc-base.nix deleted file mode 100644 index cdb22db..0000000 --- a/terraform-nix/modules/proxmox-lxc-base.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - systemd.network.networks.eth0.linkConfig.RequiredForOnline = "routable"; # Workaround for the LXC sometimes not detecting a valid internet connection even though it's clearly up -} From 84191666e7200d170b72f215bc14b7a3861bc051 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 10:09:40 -0300 Subject: [PATCH 08/43] feat(terraform-nix): Add Jellyfin service Fully working with intel hardware acceleration support out of the box Uses same folder structure as the existing legacy setup --- terraform-nix/services/jellyfin.nix | 50 ++++++++++++++++++++++ terraform-nix/terraform.tfvars.sample | 61 +++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 terraform-nix/services/jellyfin.nix diff --git a/terraform-nix/services/jellyfin.nix b/terraform-nix/services/jellyfin.nix new file mode 100644 index 0000000..9922780 --- /dev/null +++ b/terraform-nix/services/jellyfin.nix @@ -0,0 +1,50 @@ +# Documentation: https://nixos.wiki/wiki/Jellyfin + +{ pkgs, ... }: + +{ + proxmoxLXC.privileged = true; + + users = { + extraUsers.jellyfin = { + uid = 1000; + group = "jellyfin"; + extraGroups = [ "host-render" "host-video" ]; + }; + extraGroups = { + host-render.gid = 103; + host-video.gid = 44; + jellyfin.gid = 1000; + }; + }; + + environment.systemPackages = with pkgs; [ + intel-gpu-tools + jellyfin + jellyfin-web + jellyfin-ffmpeg + libva-utils + ]; + + services.jellyfin = { + enable = true; + openFirewall = true; + }; + + nixpkgs.config.packageOverrides = pkgs: { + vaapiIntel = pkgs.vaapiIntel.override { + enableHybridCodec = true; + }; + }; + + hardware.opengl = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver + vaapiIntel + vaapiVdpau + libvdpau-va-gl + intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) + ]; + }; +} diff --git a/terraform-nix/terraform.tfvars.sample b/terraform-nix/terraform.tfvars.sample index 78b4875..bf8a046 100644 --- a/terraform-nix/terraform.tfvars.sample +++ b/terraform-nix/terraform.tfvars.sample @@ -11,8 +11,63 @@ SSH lxcs = [ { - name = "caddy" - vmid = "241" - ip = "10.10.2.41" + name = "caddy" + vmid = "241" + ip = "10.10.2.41" + tags = ["networking"] + memory = 256 + cores = 2 + }, + { + name = "jellyfin" + privileged = true + vmid = "810" + ip = "10.10.8.10" + tags = ["media"] + memory = 4096 + cores = 6 + extra_config = [ + "lxc.cgroup2.devices.allow: c 226:0 rwm", + "lxc.cgroup2.devices.allow: c 226:128 rwm", + "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file,mode=0666", + "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file", + ] + mountpoints = [ + { + slot = 0 + mp = "/data" + storage = "/srv/storage" + }, + { + slot = 1 + mp = "/config" + storage = "/srv/storage/AppData/config/jellyfin" + }, + { + slot = 2 + mp = "/var/lib/jellyfin/data" + storage = "/srv/storage/AppData/config/jellyfin/data/data" + }, + { + slot = 3 + mp = "/var/lib/jellyfin/metadata" + storage = "/srv/storage/AppData/config/jellyfin/data/metadata" + }, + { + slot = 4 + mp = "/var/lib/jellyfin/plugins" + storage = "/srv/storage/AppData/config/jellyfin/data/plugins" + }, + { + slot = 5 + mp = "/var/lib/jellyfin/root" + storage = "/srv/storage/AppData/config/jellyfin/data/root" + }, + { + slot = 6 + mp = "/var/cache/jellyfin" + storage = "/srv/storage/AppData/config/jellyfin/cache" + }, + ] } ] From d007c6c5954ee36a63f98a76278ce9adc15adef2 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 11:50:08 -0300 Subject: [PATCH 09/43] chore(terraform-nix): Make mountpoint slot optional Automatically determined by the index as a fallback, works well most of the time and still allows for overriding as needed --- terraform-nix/main.tf | 2 +- terraform-nix/variables.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 036b2f3..118e40e 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -58,7 +58,7 @@ resource "proxmox_lxc" "nixos" { dynamic "mountpoint" { for_each = each.value.mountpoints content { - slot = mountpoint.value["slot"] + slot = coalesce(mountpoint.value["slot"], mountpoint.key) mp = mountpoint.value["mp"] storage = mountpoint.value["storage"] volume = mountpoint.value["volume"] diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index f5d3627..56b4b89 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -58,9 +58,9 @@ variable "lxcs" { swap = optional(number, 0) cores = optional(number, 6) mountpoints = optional(list(object({ - slot = number mp = string storage = string + slot = optional(number) key = optional(string, "") volume = optional(string) size = optional(string, "0T") From 85c6cac8e8c5f099023db59a7810a2e3306c1431 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 12:27:40 -0300 Subject: [PATCH 10/43] feat(terraform-nix): Overhaul LXC definition LXCs are now defined within Nix, which then auto generates a terraform-compatible JSON file. Add terraform-vars package to generate the JSON file with `nix build .#terraform-vars`, and the generateTerraformVars app to write it to nix.auto.tfvars.json with `nix run .#generateTerraformVars` Add terraform init, apply and destroy wrappers, executed with `nix run .#init/apply/destroy` --- terraform-nix/.gitignore | 1 + terraform-nix/flake.nix | 50 ++++++++++++++++++++++++++--- terraform-nix/lxcs/default.nix | 36 +++++++++++++++++++++ terraform-nix/services/jellyfin.nix | 4 +-- 4 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 terraform-nix/lxcs/default.nix diff --git a/terraform-nix/.gitignore b/terraform-nix/.gitignore index 0860713..8165ffc 100644 --- a/terraform-nix/.gitignore +++ b/terraform-nix/.gitignore @@ -1,5 +1,6 @@ .terraform logs/* +nix.auto.tfvars.json result terraform.tfvars !.gitkeep diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 990d923..b7d4c8a 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -25,9 +25,9 @@ pkgsUnstable = makePkgs nixpkgs-unstable; makeCommonConfig = modules: { - inherit system; - modules = [ { system.stateVersion = "23.11"; } ] ++ modules; - specialArgs = { inherit inputs pkgs system; }; + inherit system; + modules = [{ system.stateVersion = "23.11"; }] ++ modules; + specialArgs = { inherit inputs pkgs system; }; }; makeProxmoxLxcConfig = modules: nixpkgs.lib.nixosSystem ( @@ -49,12 +49,49 @@ pkgs.lib.mapAttrs' (file: _: pkgs.lib.nameValuePair (pkgs.lib.removeSuffix ".nix" file) (action "${servicesPath}/${file}")) services; - in { + in + { + apps = + let + makeDefaultTerraformCmd = cmd: { + type = "app"; + program = toString (pkgs.writers.writeBash cmd '' + ${self.apps.${system}.generateTerraformVars.program} + ${pkgs.terraform}/bin/terraform ${cmd} + ''); + }; + in + rec { + default = apply; + init = makeDefaultTerraformCmd "init"; + apply = makeDefaultTerraformCmd "apply"; + destroy = makeDefaultTerraformCmd "destroy"; + generateTerraformVars = { + type = "app"; + program = + let + tfVarsFile = "nix.auto.tfvars.json"; + in + toString (pkgs.writers.writeBash "generateTerraformVars" '' + if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi + cp ${self.packages.${system}.terraform-vars} ${tfVarsFile} + ''); + }; + }; + legacyPackages.nixosConfigurations = makeAttrsetFromServices (path: makeProxmoxLxcConfig [ path ]); packages = rec { default = base-proxmox-lxc; - base-proxmox-lxc = makeProxmoxLxcTarball []; + base-proxmox-lxc = makeProxmoxLxcTarball [ ]; + terraform-vars = + let + tfVars = { lxcs = import ./lxcs; }; + in + pkgs.runCommand "terraform-vars" { } '' + echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out + ''; } // makeAttrsetFromServices (path: makeProxmoxLxcTarball [ path ]); + devShells.default = with pkgsUnstable; mkShell { @@ -65,6 +102,9 @@ null proxmox ])) + rnix-lsp + nixfmt + nixpkgs-fmt ]; }; } diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix new file mode 100644 index 0000000..33cddc6 --- /dev/null +++ b/terraform-nix/lxcs/default.nix @@ -0,0 +1,36 @@ +[ + { + name = "caddy"; + enable = false; + vmid = "241"; + ip = "10.10.2.41"; + tags = [ "networking" ]; + cores = 2; + memory = 256; + } + { + name = "jellyfin"; + enable = true; + privileged = true; + vmid = "810"; + ip = "10.10.8.10"; + tags = [ "media" ]; + memory = 4096; + cores = 6; + extra_config = [ + "lxc.cgroup2.devices.allow: c 226:0 rwm" + "lxc.cgroup2.devices.allow: c 226:128 rwm" + "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file,mode=0666" + "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" + ]; + mountpoints = [ + { mp = "/data"; storage = "/srv/storage"; } + { mp = "/config"; storage = "/srv/storage/AppData/config/jellyfin"; } + { mp = "/var/lib/jellyfin/data"; storage = "/srv/storage/AppData/config/jellyfin/data/data"; } + { mp = "/var/lib/jellyfin/metadata"; storage = "/srv/storage/AppData/config/jellyfin/data/metadata"; } + { mp = "/var/lib/jellyfin/plugins"; storage = "/srv/storage/AppData/config/jellyfin/data/plugins"; } + { mp = "/var/lib/jellyfin/root"; storage = "/srv/storage/AppData/config/jellyfin/data/root"; } + { mp = "/var/cache/jellyfin"; storage = "/srv/storage/AppData/config/jellyfin/cache"; } + ]; + } +] diff --git a/terraform-nix/services/jellyfin.nix b/terraform-nix/services/jellyfin.nix index 9922780..125f3a6 100644 --- a/terraform-nix/services/jellyfin.nix +++ b/terraform-nix/services/jellyfin.nix @@ -32,9 +32,7 @@ }; nixpkgs.config.packageOverrides = pkgs: { - vaapiIntel = pkgs.vaapiIntel.override { - enableHybridCodec = true; - }; + vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; }; hardware.opengl = { From 5dbe7c20eabbd21b1341a37ec0ef464fed6818ef Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 12:27:58 -0300 Subject: [PATCH 11/43] chore(terraform-nix): Tweak vscode settings --- terraform-nix/.vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 terraform-nix/.vscode/settings.json diff --git a/terraform-nix/.vscode/settings.json b/terraform-nix/.vscode/settings.json new file mode 100644 index 0000000..5935d5a --- /dev/null +++ b/terraform-nix/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "nix.enableLanguageServer": true, + "nix.serverPath": "rnix-lsp", + "nix.formatterPath": "nixpkgs-fmt", +} From 72fb028b9ac68e8ce6c219f251b2adc1796e91d7 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 12:28:50 -0300 Subject: [PATCH 12/43] fix(terraform-nix): Ignore mountpoint["storage"] It always seems to be detected as a change even when it's clearly not meant to be --- terraform-nix/main.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 118e40e..5bfcafc 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -87,7 +87,8 @@ resource "proxmox_lxc" "nixos" { lifecycle { ignore_changes = [ - rootfs + rootfs, + mountpoint["storage"], ] } From 29f7c54bb557848356170e8e3c0757c6bd8a02c8 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 13:04:33 -0300 Subject: [PATCH 13/43] fix(terraform-nix): Mountpoints state For some reason, the "storage" key isn't part of the stored state, so terraform always sees it as in need of updating. Using "volume" instead and passing a blank "storage" value seems to make it behave as expected. --- terraform-nix/lxcs/default.nix | 14 +++++++------- terraform-nix/main.tf | 4 ---- terraform-nix/variables.tf | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 33cddc6..0f6f86d 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -24,13 +24,13 @@ "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" ]; mountpoints = [ - { mp = "/data"; storage = "/srv/storage"; } - { mp = "/config"; storage = "/srv/storage/AppData/config/jellyfin"; } - { mp = "/var/lib/jellyfin/data"; storage = "/srv/storage/AppData/config/jellyfin/data/data"; } - { mp = "/var/lib/jellyfin/metadata"; storage = "/srv/storage/AppData/config/jellyfin/data/metadata"; } - { mp = "/var/lib/jellyfin/plugins"; storage = "/srv/storage/AppData/config/jellyfin/data/plugins"; } - { mp = "/var/lib/jellyfin/root"; storage = "/srv/storage/AppData/config/jellyfin/data/root"; } - { mp = "/var/cache/jellyfin"; storage = "/srv/storage/AppData/config/jellyfin/cache"; } + { mp = "/data"; volume = "/srv/storage"; } + { mp = "/config"; volume = "/srv/storage/AppData/config/jellyfin"; } + { mp = "/var/lib/jellyfin/data"; volume = "/srv/storage/AppData/config/jellyfin/data/data"; } + { mp = "/var/lib/jellyfin/metadata"; volume = "/srv/storage/AppData/config/jellyfin/data/metadata"; } + { mp = "/var/lib/jellyfin/plugins"; volume = "/srv/storage/AppData/config/jellyfin/data/plugins"; } + { mp = "/var/lib/jellyfin/root"; volume = "/srv/storage/AppData/config/jellyfin/data/root"; } + { mp = "/var/cache/jellyfin"; volume = "/srv/storage/AppData/config/jellyfin/cache"; } ]; } ] diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 5bfcafc..23f4e44 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -31,9 +31,6 @@ locals { lxcs = [ for lxc in var.lxcs : merge(lxc, { flake = coalesce(lxc.flake, lxc.name) - mountpoints = [for mp in lxc.mountpoints : merge(mp, { - volume = coalesce(mp.volume, tostring(mp.storage)) - })] }) if lxc.enable ] } @@ -88,7 +85,6 @@ resource "proxmox_lxc" "nixos" { lifecycle { ignore_changes = [ rootfs, - mountpoint["storage"], ] } diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index 56b4b89..b6f74ff 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -59,10 +59,10 @@ variable "lxcs" { cores = optional(number, 6) mountpoints = optional(list(object({ mp = string - storage = string + volume = string slot = optional(number) key = optional(string, "") - volume = optional(string) + storage = optional(string, "") size = optional(string, "0T") })), []) extra_config = optional(list(string), []) From 797e3f843976fff26a940cf668d06126fd3b6065 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 16:22:28 -0300 Subject: [PATCH 14/43] chore(terraform-nix): Move services to modules dir --- terraform-nix/{ => modules}/services/caddy.nix | 2 +- terraform-nix/{ => modules}/services/jellyfin.nix | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename terraform-nix/{ => modules}/services/caddy.nix (88%) rename terraform-nix/{ => modules}/services/jellyfin.nix (100%) diff --git a/terraform-nix/services/caddy.nix b/terraform-nix/modules/services/caddy.nix similarity index 88% rename from terraform-nix/services/caddy.nix rename to terraform-nix/modules/services/caddy.nix index f945c58..86f55c9 100644 --- a/terraform-nix/services/caddy.nix +++ b/terraform-nix/modules/services/caddy.nix @@ -2,7 +2,7 @@ { services.caddy = { - enable = true; + enable = true; virtualHosts."localhost".extraConfig = '' respond "Hello, world!" ''; diff --git a/terraform-nix/services/jellyfin.nix b/terraform-nix/modules/services/jellyfin.nix similarity index 100% rename from terraform-nix/services/jellyfin.nix rename to terraform-nix/modules/services/jellyfin.nix From 5a20fcfc68685c032f60dd67993ec2b5f7284612 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 17:17:41 -0300 Subject: [PATCH 15/43] chore(terraform-nix): Module refactor Refactor platforms and services into modules --- terraform-nix/.vscode/settings.json | 4 + terraform-nix/flake.nix | 26 +++--- terraform-nix/modules/default.nix | 6 ++ terraform-nix/modules/platforms/default.nix | 5 ++ .../modules/platforms/proxmox-lxc/default.nix | 28 +++++-- terraform-nix/modules/services/caddy.nix | 22 +++-- terraform-nix/modules/services/default.nix | 6 ++ terraform-nix/modules/services/jellyfin.nix | 84 +++++++++++-------- 8 files changed, 115 insertions(+), 66 deletions(-) create mode 100644 terraform-nix/modules/default.nix create mode 100644 terraform-nix/modules/platforms/default.nix create mode 100644 terraform-nix/modules/services/default.nix diff --git a/terraform-nix/.vscode/settings.json b/terraform-nix/.vscode/settings.json index 5935d5a..a8e10ae 100644 --- a/terraform-nix/.vscode/settings.json +++ b/terraform-nix/.vscode/settings.json @@ -2,4 +2,8 @@ "nix.enableLanguageServer": true, "nix.serverPath": "rnix-lsp", "nix.formatterPath": "nixpkgs-fmt", + "[nix]": { + "editor.insertSpaces": true, + "editor.tabSize": 2 + } } diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index b7d4c8a..d6ff1d2 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -26,13 +26,13 @@ makeCommonConfig = modules: { inherit system; - modules = [{ system.stateVersion = "23.11"; }] ++ modules; + modules = [{ system.stateVersion = "23.11"; } ./modules] ++ modules; specialArgs = { inherit inputs pkgs system; }; }; makeProxmoxLxcConfig = modules: nixpkgs.lib.nixosSystem ( makeCommonConfig (modules ++ [ - ./modules/platforms/proxmox-lxc + { my.platforms.proxmox-lxc.enable = true; } nixos-generators.nixosModules.proxmox-lxc ]) ); @@ -43,13 +43,10 @@ lib = nixpkgs.legacyPackages.${system}.lib; }); - servicesPath = ./services; - services = builtins.readDir servicesPath; - makeAttrsetFromServices = action: - pkgs.lib.mapAttrs' - (file: _: pkgs.lib.nameValuePair (pkgs.lib.removeSuffix ".nix" file) (action "${servicesPath}/${file}")) - services; + serviceFiles = builtins.removeAttrs (builtins.readDir ./modules/services) [ "default.nix" ]; + services = builtins.map (name: pkgs.lib.removeSuffix ".nix" name) (builtins.attrNames serviceFiles); in + with pkgs.lib; { apps = let @@ -61,11 +58,8 @@ ''); }; in - rec { - default = apply; - init = makeDefaultTerraformCmd "init"; - apply = makeDefaultTerraformCmd "apply"; - destroy = makeDefaultTerraformCmd "destroy"; + { + default = self.apps.${system}.apply; generateTerraformVars = { type = "app"; program = @@ -77,9 +71,9 @@ cp ${self.packages.${system}.terraform-vars} ${tfVarsFile} ''); }; - }; + } // genAttrs [ "init" "plan" "apply" "destroy" ] makeDefaultTerraformCmd; - legacyPackages.nixosConfigurations = makeAttrsetFromServices (path: makeProxmoxLxcConfig [ path ]); + legacyPackages.nixosConfigurations = genAttrs services (name: makeProxmoxLxcConfig [{ config.my.services.${name}.enable = true; }]); packages = rec { default = base-proxmox-lxc; base-proxmox-lxc = makeProxmoxLxcTarball [ ]; @@ -90,7 +84,7 @@ pkgs.runCommand "terraform-vars" { } '' echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out ''; - } // makeAttrsetFromServices (path: makeProxmoxLxcTarball [ path ]); + } // genAttrs services (name: makeProxmoxLxcTarball [{ config.my.services.${name}.enable = true; }]); devShells.default = with pkgsUnstable; diff --git a/terraform-nix/modules/default.nix b/terraform-nix/modules/default.nix new file mode 100644 index 0000000..d721524 --- /dev/null +++ b/terraform-nix/modules/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./platforms + ./services + ]; +} diff --git a/terraform-nix/modules/platforms/default.nix b/terraform-nix/modules/platforms/default.nix new file mode 100644 index 0000000..f434cd0 --- /dev/null +++ b/terraform-nix/modules/platforms/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./proxmox-lxc + ]; +} diff --git a/terraform-nix/modules/platforms/proxmox-lxc/default.nix b/terraform-nix/modules/platforms/proxmox-lxc/default.nix index 90bd405..c3526e5 100644 --- a/terraform-nix/modules/platforms/proxmox-lxc/default.nix +++ b/terraform-nix/modules/platforms/proxmox-lxc/default.nix @@ -1,12 +1,24 @@ -{ - systemd.network.networks.eth0 = { - matchConfig.Name = "eth0"; - linkConfig.RequiredForOnline = "routable"; # Workaround for the LXC sometimes not detecting a valid internet connection even though it's clearly up - networkConfig.Description = "Default connection"; +{ config, lib, options, pkgs, ... }: + +with lib; + +let + cfg = config.my.platforms.proxmox-lxc; +in { + options.my.platforms.proxmox-lxc = { + enable = mkEnableOption "custom LXC tweaks"; }; - proxmoxLXC = { - manageNetwork = false; - manageHostName = false; + config = mkIf cfg.enable { + systemd.network.networks.eth0 = { + matchConfig.Name = "eth0"; + linkConfig.RequiredForOnline = "routable"; # Workaround for the LXC sometimes not detecting a valid internet connection even though it's clearly up + networkConfig.Description = "Default connection"; + }; + + proxmoxLXC = { + manageNetwork = false; + manageHostName = false; + }; }; } diff --git a/terraform-nix/modules/services/caddy.nix b/terraform-nix/modules/services/caddy.nix index 86f55c9..89ec428 100644 --- a/terraform-nix/modules/services/caddy.nix +++ b/terraform-nix/modules/services/caddy.nix @@ -1,10 +1,22 @@ # Documentation: https://nixos.wiki/wiki/Caddy +{ config, lib, options, ... }: + +let + cfg = config.my.services.caddy; +in +with lib; { - services.caddy = { - enable = true; - virtualHosts."localhost".extraConfig = '' - respond "Hello, world!" - ''; + options.my.services.caddy = { + enable = mkEnableOption "caddy"; + }; + + config = mkIf cfg.enable { + services.caddy = { + enable = true; + virtualHosts."localhost".extraConfig = '' + respond "Hello, world!" + ''; + }; }; } diff --git a/terraform-nix/modules/services/default.nix b/terraform-nix/modules/services/default.nix new file mode 100644 index 0000000..7a1a493 --- /dev/null +++ b/terraform-nix/modules/services/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./caddy.nix + ./jellyfin.nix + ]; +} diff --git a/terraform-nix/modules/services/jellyfin.nix b/terraform-nix/modules/services/jellyfin.nix index 125f3a6..288b4e9 100644 --- a/terraform-nix/modules/services/jellyfin.nix +++ b/terraform-nix/modules/services/jellyfin.nix @@ -1,48 +1,58 @@ # Documentation: https://nixos.wiki/wiki/Jellyfin -{ pkgs, ... }: +{ config, lib, options, pkgs, ... }: +let + cfg = config.my.services.jellyfin; +in +with lib; { - proxmoxLXC.privileged = true; - - users = { - extraUsers.jellyfin = { - uid = 1000; - group = "jellyfin"; - extraGroups = [ "host-render" "host-video" ]; - }; - extraGroups = { - host-render.gid = 103; - host-video.gid = 44; - jellyfin.gid = 1000; - }; + options.my.services.jellyfin = { + enable = mkEnableOption "jellyfin"; }; - environment.systemPackages = with pkgs; [ - intel-gpu-tools - jellyfin - jellyfin-web - jellyfin-ffmpeg - libva-utils - ]; - - services.jellyfin = { - enable = true; - openFirewall = true; - }; + config = mkIf cfg.enable { + proxmoxLXC.privileged = true; - nixpkgs.config.packageOverrides = pkgs: { - vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; - }; + users = { + extraUsers.jellyfin = { + uid = 1000; + group = "jellyfin"; + extraGroups = [ "host-render" "host-video" ]; + }; + extraGroups = { + host-render.gid = 103; + host-video.gid = 44; + jellyfin.gid = 1000; + }; + }; - hardware.opengl = { - enable = true; - extraPackages = with pkgs; [ - intel-media-driver - vaapiIntel - vaapiVdpau - libvdpau-va-gl - intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) + environment.systemPackages = with pkgs; [ + intel-gpu-tools + jellyfin + jellyfin-web + jellyfin-ffmpeg + libva-utils ]; + + services.jellyfin = { + enable = true; + openFirewall = true; + }; + + nixpkgs.config.packageOverrides = pkgs: { + vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; + }; + + hardware.opengl = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver + vaapiIntel + vaapiVdpau + libvdpau-va-gl + intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) + ]; + }; }; } From 6b27864a79b6192c4109f1f008739cb2c0e6f9c5 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 19:30:04 -0300 Subject: [PATCH 16/43] chore(terraform-nix): Tweak devShell Change to stable nixpkgs as unstable isn't needed anymore for the proxmox provider Remove rnix-lsp, nixfmt Add nixd --- terraform-nix/.vscode/settings.json | 2 +- terraform-nix/flake.nix | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/terraform-nix/.vscode/settings.json b/terraform-nix/.vscode/settings.json index a8e10ae..1525a5c 100644 --- a/terraform-nix/.vscode/settings.json +++ b/terraform-nix/.vscode/settings.json @@ -1,6 +1,6 @@ { "nix.enableLanguageServer": true, - "nix.serverPath": "rnix-lsp", + "nix.serverPath": "nixd", "nix.formatterPath": "nixpkgs-fmt", "[nix]": { "editor.insertSpaces": true, diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index d6ff1d2..832e648 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -86,21 +86,18 @@ ''; } // genAttrs services (name: makeProxmoxLxcTarball [{ config.my.services.${name}.enable = true; }]); - devShells.default = - with pkgsUnstable; - mkShell { - buildInputs = [ - (terraform.withPlugins (b: with b; [ - external - local - null - proxmox - ])) - rnix-lsp - nixfmt - nixpkgs-fmt - ]; - }; + devShells.default = with pkgs; mkShell { + buildInputs = [ + (terraform.withPlugins (b: with b; [ + external + local + null + proxmox + ])) + nixd + nixpkgs-fmt + ]; + }; } ); } From 43c725b8310dc337422d48fc83b65e5db6dce4e8 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 2 Jan 2024 19:32:19 -0300 Subject: [PATCH 17/43] chore(terraform-nix): More sensible flake apps --- terraform-nix/flake.nix | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 832e648..87c76ea 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -48,30 +48,24 @@ in with pkgs.lib; { - apps = - let - makeDefaultTerraformCmd = cmd: { - type = "app"; - program = toString (pkgs.writers.writeBash cmd '' - ${self.apps.${system}.generateTerraformVars.program} - ${pkgs.terraform}/bin/terraform ${cmd} + apps = { + default = self.apps.${system}.apply; + generateTerraformVars = { + type = "app"; + program = + let tfVarsFile = "nix.auto.tfvars.json"; + in toString (pkgs.writers.writeBash "generateTerraformVars" '' + if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi + cp ${self.packages.${system}.terraform-vars} ${tfVarsFile} ''); - }; - in - { - default = self.apps.${system}.apply; - generateTerraformVars = { - type = "app"; - program = - let - tfVarsFile = "nix.auto.tfvars.json"; - in - toString (pkgs.writers.writeBash "generateTerraformVars" '' - if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi - cp ${self.packages.${system}.terraform-vars} ${tfVarsFile} - ''); - }; - } // genAttrs [ "init" "plan" "apply" "destroy" ] makeDefaultTerraformCmd; + }; + } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { + type = "app"; + program = toString (pkgs.writers.writeBash cmd '' + ${self.apps.${system}.generateTerraformVars.program} + ${pkgs.terraform}/bin/terraform ${cmd} + ''); + }); legacyPackages.nixosConfigurations = genAttrs services (name: makeProxmoxLxcConfig [{ config.my.services.${name}.enable = true; }]); packages = rec { From 084b9cdd90a127b827a8e11d9f29860e344252f4 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Wed, 3 Jan 2024 14:42:05 -0300 Subject: [PATCH 18/43] fix(jellyfin): Nixpkgs 23.05 compatibility Change nixpkgs.config.packageOverrides to an overlay due to a breaking change --- terraform-nix/modules/services/jellyfin.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform-nix/modules/services/jellyfin.nix b/terraform-nix/modules/services/jellyfin.nix index 288b4e9..41d296c 100644 --- a/terraform-nix/modules/services/jellyfin.nix +++ b/terraform-nix/modules/services/jellyfin.nix @@ -40,9 +40,9 @@ with lib; openFirewall = true; }; - nixpkgs.config.packageOverrides = pkgs: { - vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; - }; + nixpkgs.overlays = [ (final: prev: { + vaapiIntel = prev.vaapiIntel.override { enableHybridCodec = true; }; + }) ]; hardware.opengl = { enable = true; From c4b2087dc7780c0638f710cbc59c646f39cde244 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Wed, 3 Jan 2024 14:48:57 -0300 Subject: [PATCH 19/43] feat(terraform-nix): Add sops-nix secret management Initial implementation with sops-nix Change deploy strategy to only setup base proxmox LXCs by default since SOPS needs the public SSH host keys to encrypt the secrets. After that's done, the `deploy` command deploys the NixOS configurations as usual. --- terraform-nix/.gitignore | 3 +- terraform-nix/.sops.yaml | 10 +++ terraform-nix/config.nix | 3 + terraform-nix/flake.lock | 54 ++++++++++++++- terraform-nix/flake.nix | 81 ++++++++++++++--------- terraform-nix/main.tf | 2 +- terraform-nix/modules/default.nix | 1 + terraform-nix/modules/secrets/default.nix | 10 +++ terraform-nix/secrets/default.yaml | 0 terraform-nix/variables.tf | 6 ++ 10 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 terraform-nix/.sops.yaml create mode 100644 terraform-nix/config.nix create mode 100644 terraform-nix/modules/secrets/default.nix create mode 100644 terraform-nix/secrets/default.yaml diff --git a/terraform-nix/.gitignore b/terraform-nix/.gitignore index 8165ffc..53d4db4 100644 --- a/terraform-nix/.gitignore +++ b/terraform-nix/.gitignore @@ -1,6 +1,7 @@ .terraform +keys.txt logs/* -nix.auto.tfvars.json +nix-*.auto.tfvars.json result terraform.tfvars !.gitkeep diff --git a/terraform-nix/.sops.yaml b/terraform-nix/.sops.yaml new file mode 100644 index 0000000..16ee9f0 --- /dev/null +++ b/terraform-nix/.sops.yaml @@ -0,0 +1,10 @@ +keys: + - &primary age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h + - &jellyfin age1c4sghnqh77k930j4y3z690tfpud627wz60y75440vzxk8u8hkuvs438jvl +creation_rules: + - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - pgp: [] + age: + - *primary + - *jellyfin diff --git a/terraform-nix/config.nix b/terraform-nix/config.nix new file mode 100644 index 0000000..e352241 --- /dev/null +++ b/terraform-nix/config.nix @@ -0,0 +1,3 @@ +{ + domain = "lpcha.im"; +} diff --git a/terraform-nix/flake.lock b/terraform-nix/flake.lock index 0e6f093..afc348f 100644 --- a/terraform-nix/flake.lock +++ b/terraform-nix/flake.lock @@ -70,6 +70,22 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1703950681, + "narHash": "sha256-veU5bE4eLOmi7aOzhE7LfZXcSOONRMay0BKv01WHojo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0aad9113182747452dbfc68b93c86e168811fa6c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-unstable": { "locked": { "lastModified": 1701718080, @@ -85,12 +101,48 @@ "type": "indirect" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1703499205, + "narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", "nixos-generators": "nixos-generators", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable" + "nixpkgs-unstable": "nixpkgs-unstable", + "sops-nix": "sops-nix" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": "nixpkgs_2", + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1703991717, + "narHash": "sha256-XfBg2dmDJXPQEB8EdNBnzybvnhswaiAkUeeDj7fa/hQ=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "cfdbaf68d00bc2f9e071f17ae77be4b27ff72fa6", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" } }, "systems": { diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 87c76ea..976e719 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -8,11 +8,14 @@ url = "github:nix-community/nixos-generators"; inputs.nixpkgs.follows = "nixpkgs"; }; + sops-nix.url = "github:Mic92/sops-nix"; }; - outputs = { self, flake-utils, nixpkgs, nixpkgs-unstable, nixos-generators, ... }@inputs: + outputs = { self, flake-utils, nixpkgs, nixpkgs-unstable, nixos-generators, sops-nix, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let + my.config = import ./config.nix; + makePkgs = nixpkgs: import nixpkgs { inherit system; @@ -26,8 +29,8 @@ makeCommonConfig = modules: { inherit system; - modules = [{ system.stateVersion = "23.11"; } ./modules] ++ modules; - specialArgs = { inherit inputs pkgs system; }; + modules = [{ system.stateVersion = "23.11"; } ./modules sops-nix.nixosModules.sops] ++ modules; + specialArgs = { inherit inputs my pkgs system; }; }; makeProxmoxLxcConfig = modules: nixpkgs.lib.nixosSystem ( @@ -48,50 +51,68 @@ in with pkgs.lib; { - apps = { - default = self.apps.${system}.apply; - generateTerraformVars = { - type = "app"; - program = - let tfVarsFile = "nix.auto.tfvars.json"; - in toString (pkgs.writers.writeBash "generateTerraformVars" '' - if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi - cp ${self.packages.${system}.terraform-vars} ${tfVarsFile} + apps = + let + makeTfVarsPackage = tfVars: pkgs.runCommand "terraform-vars" { } '' + echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out + ''; + makeGenerateTfVars = name: package: + let tfVarsFile = "${name}.auto.tfvars.json"; + in { + type = "app"; + program = toString (pkgs.writers.writeBash "package-${package.name}" '' + if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi + cp ${package} ${tfVarsFile} + ''); + }; + enableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = true; }); + disableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = false; }); + generateTerraformVars = makeGenerateTfVars "nix-lxcs" (makeTfVarsPackage { lxcs = import ./lxcs; }); + in + { + default = self.apps.${system}.deploy; + deploy = { + type = "app"; + program = toString (pkgs.writers.writeBash "deploy" '' + ${enableBuild.program} + ${generateTerraformVars.program} + ${pkgs.terraform}/bin/terraform apply ''); - }; - } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { - type = "app"; - program = toString (pkgs.writers.writeBash cmd '' - ${self.apps.${system}.generateTerraformVars.program} - ${pkgs.terraform}/bin/terraform ${cmd} - ''); - }); + }; + ageFromSsh = { + type = "app"; + program = toString (pkgs.writers.writeBash "ageFromSsh" '' + ssh-keyscan "$1" | ${pkgs.ssh-to-age}/bin/ssh-to-age + ''); + }; + } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { + type = "app"; + program = toString (pkgs.writers.writeBash cmd '' + ${disableBuild.program} + ${generateTerraformVars.program} + ${pkgs.terraform}/bin/terraform ${cmd} + ''); + }); legacyPackages.nixosConfigurations = genAttrs services (name: makeProxmoxLxcConfig [{ config.my.services.${name}.enable = true; }]); packages = rec { default = base-proxmox-lxc; base-proxmox-lxc = makeProxmoxLxcTarball [ ]; - terraform-vars = - let - tfVars = { lxcs = import ./lxcs; }; - in - pkgs.runCommand "terraform-vars" { } '' - echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out - ''; } // genAttrs services (name: makeProxmoxLxcTarball [{ config.my.services.${name}.enable = true; }]); devShells.default = with pkgs; mkShell { buildInputs = [ + age (terraform.withPlugins (b: with b; [ external local - null + b.null proxmox ])) nixd nixpkgs-fmt + sops ]; }; - } - ); + }); } diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 23f4e44..73f88db 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -107,7 +107,7 @@ resource "proxmox_lxc" "nixos" { } module "nixos" { - for_each = { for key, val in local.lxcs : key => val } + for_each = { for key, val in local.lxcs : key => val if var.build } source = "github.com/Gabriella439/terraform-nixos-ng/nixos" diff --git a/terraform-nix/modules/default.nix b/terraform-nix/modules/default.nix index d721524..99ba27e 100644 --- a/terraform-nix/modules/default.nix +++ b/terraform-nix/modules/default.nix @@ -1,6 +1,7 @@ { imports = [ ./platforms + ./secrets ./services ]; } diff --git a/terraform-nix/modules/secrets/default.nix b/terraform-nix/modules/secrets/default.nix new file mode 100644 index 0000000..9a92ffd --- /dev/null +++ b/terraform-nix/modules/secrets/default.nix @@ -0,0 +1,10 @@ +# Documentation: https://github.com/Mic92/sops-nix + +{ config, options, ... }: + +{ + sops = { + defaultSopsFile = ../../secrets/default.yaml; + age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + }; +} diff --git a/terraform-nix/secrets/default.yaml b/terraform-nix/secrets/default.yaml new file mode 100644 index 0000000..e69de29 diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index b6f74ff..b8d9156 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -68,3 +68,9 @@ variable "lxcs" { extra_config = optional(list(string), []) })) } + +variable "build" { + description = "Whether to build the NixOS configurations" + type = bool + default = false +} From 8368b23cf7242d2b2fb58f20413470a463fc0876 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:46:59 -0300 Subject: [PATCH 20/43] feat(terraform-nix): Initial traefik implementation Add overlayed traefik package with some plugins included Add overlayed package cloudflare-ips containing the cloudflare IP ranges pulled from the official mmproxy package Rework config file into module containing configuration constants --- terraform-nix/.sops.yaml | 13 +- terraform-nix/config.nix | 3 - terraform-nix/flake.lock | 23 +- terraform-nix/flake.nix | 5 + terraform-nix/modules/config/default.nix | 24 ++ terraform-nix/modules/services/default.nix | 1 + .../modules/services/traefik/default.nix | 210 ++++++++++++++++++ terraform-nix/overlays/cloudflare-ips.nix | 7 + terraform-nix/overlays/default.nix | 7 + terraform-nix/overlays/traefik/default.nix | 19 ++ terraform-nix/overlays/traefik/plugins.json | 16 ++ terraform-nix/secrets/traefik/traefik.env | 11 + 12 files changed, 328 insertions(+), 11 deletions(-) delete mode 100644 terraform-nix/config.nix create mode 100644 terraform-nix/modules/config/default.nix create mode 100644 terraform-nix/modules/services/traefik/default.nix create mode 100644 terraform-nix/overlays/cloudflare-ips.nix create mode 100644 terraform-nix/overlays/default.nix create mode 100644 terraform-nix/overlays/traefik/default.nix create mode 100644 terraform-nix/overlays/traefik/plugins.json create mode 100644 terraform-nix/secrets/traefik/traefik.env diff --git a/terraform-nix/.sops.yaml b/terraform-nix/.sops.yaml index 16ee9f0..1549371 100644 --- a/terraform-nix/.sops.yaml +++ b/terraform-nix/.sops.yaml @@ -1,10 +1,13 @@ keys: - - &primary age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h - - &jellyfin age1c4sghnqh77k930j4y3z690tfpud627wz60y75440vzxk8u8hkuvs438jvl + - &primary age1dsuede07h7akeaucp53uyxu6wa5y3yapxy3c0nh34vcdhxnanacqr6q3tc + - &traefik age1k53pxzjtln8ds72ys5crlqz48q3flr8kawjhfmu34w4306mahscqgdamrx creation_rules: - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ key_groups: - - pgp: [] - age: + - age: - *primary - - *jellyfin + - path_regex: secrets/traefik/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *primary + - *traefik diff --git a/terraform-nix/config.nix b/terraform-nix/config.nix deleted file mode 100644 index e352241..0000000 --- a/terraform-nix/config.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - domain = "lpcha.im"; -} diff --git a/terraform-nix/flake.lock b/terraform-nix/flake.lock index afc348f..f1f4cb1 100644 --- a/terraform-nix/flake.lock +++ b/terraform-nix/flake.lock @@ -18,6 +18,22 @@ "type": "github" } }, + "mmproxy": { + "flake": false, + "locked": { + "lastModified": 1641486126, + "narHash": "sha256-elus7Jh4ZPZGNnY5FgCC34qBXVvmU+TovlRFW67UFNk=", + "owner": "cloudflare", + "repo": "mmproxy", + "rev": "ac4b6083dc180758b3caf15b64a157f8ce5199c7", + "type": "github" + }, + "original": { + "owner": "cloudflare", + "repo": "mmproxy", + "type": "github" + } + }, "nixlib": { "locked": { "lastModified": 1693701915, @@ -88,11 +104,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1701718080, - "narHash": "sha256-6ovz0pG76dE0P170pmmZex1wWcQoeiomUZGggfH9XPs=", + "lastModified": 1704194953, + "narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2c7f3c0fb7c08a0814627611d9d7d45ab6d75335", + "rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6", "type": "github" }, "original": { @@ -120,6 +136,7 @@ "root": { "inputs": { "flake-utils": "flake-utils", + "mmproxy": "mmproxy", "nixos-generators": "nixos-generators", "nixpkgs": "nixpkgs", "nixpkgs-unstable": "nixpkgs-unstable", diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 976e719..b618a89 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -4,6 +4,10 @@ nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + mmproxy = { + flake = false; + url = "github:cloudflare/mmproxy"; + }; nixos-generators = { url = "github:nix-community/nixos-generators"; inputs.nixpkgs.follows = "nixpkgs"; @@ -23,6 +27,7 @@ allowUnfree = true; allowUnfreePredicate = _: true; }; + overlays = (import ./overlays { inherit inputs pkgs; }); }; pkgs = makePkgs nixpkgs; pkgsUnstable = makePkgs nixpkgs-unstable; diff --git a/terraform-nix/modules/config/default.nix b/terraform-nix/modules/config/default.nix new file mode 100644 index 0000000..eb7b2bc --- /dev/null +++ b/terraform-nix/modules/config/default.nix @@ -0,0 +1,24 @@ +{ lib, pkgs, ... }@args: + +with lib; +let + mkReadonlyOption = default: mkOption { inherit default; readOnly = true; }; +in +{ + options.my = { + domain = mkReadonlyOption "lpcha.im"; + email = mkReadonlyOption "lpchaim@gmail.com"; + networking = mkReadonlyOption { + defaultGateway = "10.0.0.1"; + defaultPrefixLength = "8"; + cidrs = rec { + trusted = private ++ cloudflare; + private = [ "10.0.0.0/8" "fe80::/10" ]; + cloudflare = builtins.filter + (x: stringLength(x) > 0) + (splitString "\n" (builtins.readFile pkgs.cloudflare-ips)); + }; + }; + lxcs = mkReadonlyOption (import ../../lxcs args); + }; +} diff --git a/terraform-nix/modules/services/default.nix b/terraform-nix/modules/services/default.nix index 7a1a493..192ff1e 100644 --- a/terraform-nix/modules/services/default.nix +++ b/terraform-nix/modules/services/default.nix @@ -2,5 +2,6 @@ imports = [ ./caddy.nix ./jellyfin.nix + ./traefik ]; } diff --git a/terraform-nix/modules/services/traefik/default.nix b/terraform-nix/modules/services/traefik/default.nix new file mode 100644 index 0000000..05e611d --- /dev/null +++ b/terraform-nix/modules/services/traefik/default.nix @@ -0,0 +1,210 @@ +# Documentation: https://nixos.wiki/wiki/Traefik + +{ config, inputs, lib, options, pkgs, system, ... }: + +with lib; +let + cfg = config.my.services.traefik; + myCfg = config.my; + + dataDir = config.services.traefik.dataDir; + domain = myCfg.domain; + lxcs = filterAttrs (_: lxc: lxc.nixConfig.reverseProxy.enable or false) config.my.lxcs; + + trustedIpsPrivate = myCfg.networking.cidrs.private; + trustedIps = myCfg.networking.cidrs.trusted; +in +with lib; +{ + options.my.services.traefik = { + enable = mkEnableOption "traefik"; + }; + + config = mkIf cfg.enable { + sops = { + defaultSopsFile = ../../../secrets/traefik/traefik.env; + defaultSopsFormat = "dotenv"; + secrets."traefik.env" = { }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 8080 ]; + + services.traefik = { + enable = true; + package = pkgs.traefik-custom; + environmentFiles = [ "/run/secrets/traefik.env" ]; + dynamicConfigOptions = { + http = { + routers = { + api = { + rule = "Host(`traefik.${domain}`)"; + entrypoints = [ "websecure" ]; + service = "api@internal"; + middlewares = [ "auth@file" "default@file" ]; + tls.certResolver = "letsEncrypt"; + }; + } // mapAttrs' + (_: lxc: nameValuePair lxc.name { + rule = "Host(`${lxc.name}.${domain}`)"; + entrypoints = [ "websecure" ]; + service = lxc.name; + middlewares = [ "default@file" ]; + tls.certResolver = "letsEncrypt"; + }) + lxcs; + services = mapAttrs' + (_: lxc: nameValuePair lxc.name { + loadBalancer.servers = map (url: { inherit url; }) lxc.nixConfig.reverseProxy.servers; + }) + lxcs; + middlewares = { + default.chain.middlewares = [ + "cloudflarewarp" + # "crowdsec" + "default-security-headers" + "gzip" + ]; + auth.basicAuth.users = [ "{{ env `TRAEFIK_PASSWORD_HASHED` }}" ]; + cloudflarewarp.plugin.cloudflarewarp = { + disableDefault = true; + trustIp = trustedIps; + }; + # crowdsec.plugin.bouncer = { # TODO Enable crowdsec + # enabled = true; + # logLevel = "INFO"; + # crowdsecMode = "live"; # live | stream | alone + # updateIntervalSeconds = 60; # For stream mode + # defaultDecisionSeconds = 60; # For live mode + # crowdsecLapiKey = "$CROWDSEC_BOUNCER_API_KEY"; + # crowdsecLapiHost = "crowdsec:8080"; + # crowdsecLapiScheme = "http"; + # crowdsecLapiTLSInsecureVerify = false; + # forwardedHeadersTrustedIPs: trustedIps + # clientTrustedIPs: trustedIpsPrivate + # forwardedHeadersCustomName = "X-Forwarded-For"; + # }; + default-security-headers.headers = { + browserXssFilter = true; # X-XSS-Protection=1; mode=block + contentTypeNosniff = true; # X-Content-Type-Options=nosniff + forceSTSHeader = true; # Add the Strict-Transport-Security header even when the connection is HTTP + frameDeny = false; # X-Frame-Options=deny + customFrameOptionsValue = "SAMEORIGIN"; + referrerPolicy = "no-referrer-when-downgrade"; + sslRedirect = true; # Allow only https requests + stsIncludeSubdomains = true; # Add includeSubdomains to the Strict-Transport-Security header + stsPreload = true; # Add preload flag appended to the Strict-Transport-Security header + stsSeconds = "31536000"; # Set the max-age of the Strict-Transport-Security header (63072000 = 2 years) + customResponseHeaders = { + "X-Robots-Tags" = "noindex, nofollow"; + }; + }; + gzip.compress = { }; + httpsRedirect.redirectScheme.scheme = "https"; + nextcloudSecureHeaders.headers = { + hostsProxyHeaders = [ + "X-Forwarded-Host" + ]; + referrerPolicy = "same-origin"; + }; + }; + serversTransports.ignoreCert.insecureSkipVerify = true; + }; + tls.options = { + modern = { + minVersion = "versionTLS12"; + sniStrict = true; + }; + intermediate = { + cipherSuites = [ + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" + ]; + minVersion = "versionTLS13"; + sniStrict = true; + }; + old = { + cipherSuites = [ + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" + "TLS_RSA_WITH_AES_128_GCM_SHA256" + "TLS_RSA_WITH_AES_256_GCM_SHA384" + "TLS_RSA_WITH_AES_128_CBC_SHA256" + "TLS_RSA_WITH_AES_128_CBC_SHA" + "TLS_RSA_WITH_AES_256_CBC_SHA" + "TLS_RSA_WITH_3DES_EDE_CBC_SHA" + ]; + minVersion = "TLDv1"; + sniStrict = true; + }; + }; + }; + staticConfigOptions = { + api = { + dashboard = true; + insecure = true; + }; + certificatesResolvers.letsEncrypt.acme = { + email = myCfg.email; + storage = "${dataDir}/acme/acme.json"; + dnsChallenge.provider = "cloudflare"; + }; + entryPoints = { + web = { + address = ":80"; + http.redirections.entryPoint = { + to = "websecure"; + scheme = "https"; + }; + }; + websecure = { + address = ":443"; + http.tls.certResolver = "letsEncrypt"; + forwardedHeaders.trustedIps = trustedIps; + }; + }; + global = { + checknewversion = false; + sendanonymoususage = false; + }; + # providers = { # TODO Consider how to properly implement docker + # docker = { + # endpoint = "ssh://root@10.10.10.0:22"; # Listen to the UNIX Docker socket + # exposedByDefault = false; # Only expose container that are explicitly enabled (using label traefik.enabled) + # network = "external"; # Default network to use for connections to all containers. + # watch = true; # Watch Docker Swarm events + # }; + # providersThrottleDuration = 10; + # }; + experimental.localPlugins = { + bouncer.moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"; + cloudflarewarp.moduleName = "github.com/BilikoX/cloudflarewarp"; + }; + log = { }; + accessLog = { }; + }; + }; + + systemd.services.traefik = { + serviceConfig.WorkingDirectory = dataDir; + preStart = '' + umask 077 + mkdir -p ${dataDir}/{acme,plugins-storage} + ln -sfn ${config.services.traefik.package}/bin/plugins-local ${dataDir} + ''; + }; + }; +} diff --git a/terraform-nix/overlays/cloudflare-ips.nix b/terraform-nix/overlays/cloudflare-ips.nix new file mode 100644 index 0000000..2c4009e --- /dev/null +++ b/terraform-nix/overlays/cloudflare-ips.nix @@ -0,0 +1,7 @@ +{ inputs, pkgs, ... }: + +(final: prev: { + cloudflare-ips = pkgs.runCommand "cloudflare-ips" { } '' + cat ${inputs.mmproxy}/cloudflare-ip-ranges.txt > $out + ''; +}) diff --git a/terraform-nix/overlays/default.nix b/terraform-nix/overlays/default.nix new file mode 100644 index 0000000..8b087ed --- /dev/null +++ b/terraform-nix/overlays/default.nix @@ -0,0 +1,7 @@ +args: + +builtins.map + (f: (import (./. + "/${f}") args)) + (builtins.filter + (f: f != "default.nix") + (builtins.attrNames (builtins.readDir ./.))) diff --git a/terraform-nix/overlays/traefik/default.nix b/terraform-nix/overlays/traefik/default.nix new file mode 100644 index 0000000..d12724b --- /dev/null +++ b/terraform-nix/overlays/traefik/default.nix @@ -0,0 +1,19 @@ +# See https://github.com/NixOS/nixpkgs/issues/265496 + +{ pkgs, ... }: + +let plugins = builtins.fromJSON (builtins.readFile ./plugins.json); +in +with pkgs.lib; +(final: prev: { + traefik-custom = prev.traefik.overrideAttrs (oldAttrs: { + postInstall = (oldAttrs.postInstall or "") + strings.concatMapStrings + (plugin: + let site = plugin.site or "github.com"; + in with plugin; '' + mkdir -p $out/bin/plugins-local/src/${site}/${owner}/ + cp -r ${ pkgs.fetchFromGitHub { inherit owner repo rev sha256; } } $out/bin/plugins-local/src/${site}/${owner}/${repo} + '') + plugins; + }); +}) diff --git a/terraform-nix/overlays/traefik/plugins.json b/terraform-nix/overlays/traefik/plugins.json new file mode 100644 index 0000000..97f2196 --- /dev/null +++ b/terraform-nix/overlays/traefik/plugins.json @@ -0,0 +1,16 @@ +[ + { + "name": "crowdsec-bouncer", + "owner": "maxlerebourg", + "repo": "crowdsec-bouncer-traefik-plugin", + "rev": "fc3da2fc2d24626eb1be956882e185abef048cb8", + "sha256": "sha256-3164ANmCaxf6bbmdij5eL+N+TGTtRnFJ2gnkS5+5blc=" + }, + { + "name": "cloudflarewarp", + "owner": "BilikoX", + "repo": "cloudflarewarp", + "rev": "94ed32a45dcd5656e9b5539e8cd564bd3d7babaa", + "sha256": "sha256-AU/AgeYLi1e5CaIcXaDoDRWSRyfKHZYfIsp4lPOqnTI=" + } +] diff --git a/terraform-nix/secrets/traefik/traefik.env b/terraform-nix/secrets/traefik/traefik.env new file mode 100644 index 0000000..bea9125 --- /dev/null +++ b/terraform-nix/secrets/traefik/traefik.env @@ -0,0 +1,11 @@ +CF_DNS_API_TOKEN=ENC[AES256_GCM,data:4iuMYMJUyfKG0Ty0CIr2EgNliPbcIXm7hY7JFD5iSsavWuxGkTjBIZkN,iv:5QhM2LbnNT9yy7I6+oWxQMk0GeCT1fQBCCkfrdzT1UM=,tag:CnHV138H+37rfsS07niCBA==,type:str] +TRAEFIK_PASSWORD_HASHED=ENC[AES256_GCM,data:sFaOgg9U5eRYMKRNGiYGf7gUMxDhw31zc7e8EqvyrMNfacUgzll/XyVGal/8,iv:+mwtWw1CLeoTXd7j8f8BNAsJg7/5enEBsN21N1kD0rE=,tag:j8bya01Huye30vL9BNrFSQ==,type:str] +CROWDSEC_BOUNCER_API_KEY=ENC[AES256_GCM,data:oBAdDjQJ38aOcxIWtKAwtBIxgBToyHt5jGAkgj7D2mUwzw==,iv:/anjeCiChvkAhtcjHhLAD7MFsn6tDXKhjuYCXwpEmi0=,tag:Y5g9h538/BBFj++fmZbFzw==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWGl2QzFQekVqOFVIdzhl\nT0lSaUZHckJyTC9tc1VFNEdxRERQZnkvQ1M4CjJQbDFkZVo0aStJYk4vYVNzQzNK\nNUVLT25NRFR1Q2taWmdUMzAxNlU4dFEKLS0tIE5FRGlDYkdmcTNqWU1SY1pDRFRu\naFdoYnJBSmkyWGp1cUhnalFCbFp3cDgKgzxTVC5WdCkpCbTAEZpHBxBuV1NfC3pW\n8VZNaA8EPBtRjMSwqqTjix7fli0z0h3F9SPIcVay4/RzM1McxeH81g==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1dsuede07h7akeaucp53uyxu6wa5y3yapxy3c0nh34vcdhxnanacqr6q3tc +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwV3Z1UWp1bnZ1c01BWGxt\nejVnd0hIVWRPZXFydDgyaldaRWJ6dHozRWprCjIrbVpVTkQvNTVBSXJDbkcvK0ts\nZlp5ZUVGM1plc0ZadGg3UkJraStZUjgKLS0tIExGUldBSy9JMWFYSThDR1FrUmJk\nS3U2UkNtbEdaTjBaMVN5cXgwem95b3cK4vcAnlT0L/wQ05S5NeX15ckrgtt6+X3T\nSDsqhoCz1inU+uMnAk/PKY02A/BP4nc0ObsWvwFWcHBbjQKl/FRQOw==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1k53pxzjtln8ds72ys5crlqz48q3flr8kawjhfmu34w4306mahscqgdamrx +sops_lastmodified=2024-01-06T15:25:39Z +sops_mac=ENC[AES256_GCM,data:30bnM4XgerD09gT8BkbADWdQOJnUu3lGoxZfCefKZh+km2i2oWEyUknsXlrikMUHuNbaQ99VSVz+6YD7QBoua68yNFwsqN/ZdwLxTwyNROWCj3klp5viP5tCGPy6WnNP/9li2Pmr2/sBcy2eUWhXhFmISey8fEjLszPqN9PApWc=,iv:KmmzB6IiRLCeqWyS2xWUWTdww9Gl3tcCEhTr7hMIHKY=,tag:5cTgx2slFs/jclBgC+7EWQ==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.8.1 From fb58b362ea052dcdaece02c7959133599bf39c37 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:49:16 -0300 Subject: [PATCH 21/43] feat(terraform-nix): Initial crowdsec implementation --- terraform-nix/modules/services/crowdsec.nix | 57 +++++++++++++++++++++ terraform-nix/modules/services/default.nix | 1 + 2 files changed, 58 insertions(+) create mode 100644 terraform-nix/modules/services/crowdsec.nix diff --git a/terraform-nix/modules/services/crowdsec.nix b/terraform-nix/modules/services/crowdsec.nix new file mode 100644 index 0000000..f60bb71 --- /dev/null +++ b/terraform-nix/modules/services/crowdsec.nix @@ -0,0 +1,57 @@ +# Documentation: https://nixos.wiki/wiki/Traefik + +{ config, inputs, lib, options, pkgs, system, ... }: + +let + cfg = config.my.services.crowdsec; + myCfg = config.my; +in +with lib; +{ + options.my.services.crowdsec = { + enable = mkEnableOption "crowdsec"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + crowdsec + ]; + + systemd.services.crowdsec = { + enable = true; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" "crowdsec.service" ]; + startLimitIntervalSec = 86400; + startLimitBurst = 5; + serviceConfig = { + ExecStart = "${pkgs.crowdsec}/bin/crowdsec"; + ExecStartPre = pkgs.writeShellScript "pre-start" '' + umask 077 + mkdir -p /var/lib/crowdsec/data + ''; + Type = "simple"; + User = "crowdsec"; + Group = "crowdsec"; + Restart = "on-failure"; + AmbientCapabilities = "cap_net_bind_service"; + CapabilityBoundingSet = "cap_net_bind_service"; + NoNewPrivileges = true; + LimitNPROC = 64; + LimitNOFILE = 1048576; + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = "full"; + ReadWriteDirectories = "/var/lib/crowdsec"; + RuntimeDirectory = "crowdsec"; + }; + }; + + users.users.crowdsec = { + group = "crowdsec"; + uid = 1000; + isSystemUser = true; + }; + users.groups.crowdsec.gid = 1000; + }; +} diff --git a/terraform-nix/modules/services/default.nix b/terraform-nix/modules/services/default.nix index 192ff1e..bebf5e2 100644 --- a/terraform-nix/modules/services/default.nix +++ b/terraform-nix/modules/services/default.nix @@ -1,6 +1,7 @@ { imports = [ ./caddy.nix + ./crowdsec.nix ./jellyfin.nix ./traefik ]; From c1a14c089bd53fece9e424bbf96809af7a311fa9 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:51:03 -0300 Subject: [PATCH 22/43] feat(terraform-nix): Implement base module Provides basic configuration, functionality and niceties Enables firewall by default --- terraform-nix/modules/base.nix | 47 +++++++++++++++++++++ terraform-nix/modules/default.nix | 4 ++ terraform-nix/modules/services/caddy.nix | 3 ++ terraform-nix/modules/services/jellyfin.nix | 3 ++ 4 files changed, 57 insertions(+) create mode 100644 terraform-nix/modules/base.nix diff --git a/terraform-nix/modules/base.nix b/terraform-nix/modules/base.nix new file mode 100644 index 0000000..b9cefbf --- /dev/null +++ b/terraform-nix/modules/base.nix @@ -0,0 +1,47 @@ +{ lib, pkgs, ... }: + +with lib; +{ + # Packages and services + environment.systemPackages = with pkgs; [ + bat + helix + vim + ]; + services.avahi = { + enable = true; + openFirewall = true; + }; + + # Environment + environment.sessionVariables = { + EDITOR = "hx"; + NIXPKGS_ALLOW_UNFREE = "1"; + }; + + # Package manager + nix = { + package = pkgs.nixFlakes; + extraOptions = '' + experimental-features = flakes nix-command + ''; + settings = { + keep-outputs = true; + }; + gc = { + automatic = true; + dates = "daily"; + }; + }; + + # Networking + networking.firewall.enable = mkDefault true; + networking.firewall.allowedTCPPorts = [ 22 ]; + networking.enableIPv6 = mkDefault false; + + # Internationalization + time.timeZone = "America/Sao_Paulo"; + i18n.defaultLocale = "en_US.UTF-8"; + console.useXkbConfig = true; + services.xserver.xkb.layout = "br"; +} diff --git a/terraform-nix/modules/default.nix b/terraform-nix/modules/default.nix index 99ba27e..3ac8bb6 100644 --- a/terraform-nix/modules/default.nix +++ b/terraform-nix/modules/default.nix @@ -1,5 +1,9 @@ +{ pkgs, ... }: + { imports = [ + ./base.nix + ./config ./platforms ./secrets ./services diff --git a/terraform-nix/modules/services/caddy.nix b/terraform-nix/modules/services/caddy.nix index 89ec428..864c0ab 100644 --- a/terraform-nix/modules/services/caddy.nix +++ b/terraform-nix/modules/services/caddy.nix @@ -12,6 +12,9 @@ with lib; }; config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + networking.enableIPv6 = true; + services.caddy = { enable = true; virtualHosts."localhost".extraConfig = '' diff --git a/terraform-nix/modules/services/jellyfin.nix b/terraform-nix/modules/services/jellyfin.nix index 41d296c..6ea2c8c 100644 --- a/terraform-nix/modules/services/jellyfin.nix +++ b/terraform-nix/modules/services/jellyfin.nix @@ -12,6 +12,9 @@ with lib; }; config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 8096 8920 ]; + networking.firewall.allowedUDPPorts = [ 1900 7359 ]; + proxmoxLXC.privileged = true; users = { From bc96b4f6446263a7894cdadc2857290956dbaff4 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:58:04 -0300 Subject: [PATCH 23/43] feat(terraform-nix): Misc flake tweaks Remove unstable nixpkgs as it's unused at the moment Small changes to outputs --- terraform-nix/flake.lock | 16 ---------- terraform-nix/flake.nix | 68 ++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/terraform-nix/flake.lock b/terraform-nix/flake.lock index f1f4cb1..812d918 100644 --- a/terraform-nix/flake.lock +++ b/terraform-nix/flake.lock @@ -102,21 +102,6 @@ "type": "github" } }, - "nixpkgs-unstable": { - "locked": { - "lastModified": 1704194953, - "narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" - } - }, "nixpkgs_2": { "locked": { "lastModified": 1703499205, @@ -139,7 +124,6 @@ "mmproxy": "mmproxy", "nixos-generators": "nixos-generators", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable", "sops-nix": "sops-nix" } }, diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index b618a89..84b24b8 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -1,7 +1,6 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/23.11"; - nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; mmproxy = { @@ -15,11 +14,9 @@ sops-nix.url = "github:Mic92/sops-nix"; }; - outputs = { self, flake-utils, nixpkgs, nixpkgs-unstable, nixos-generators, sops-nix, ... }@inputs: + outputs = { self, flake-utils, nixpkgs, nixos-generators, sops-nix, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let - my.config = import ./config.nix; - makePkgs = nixpkgs: import nixpkgs { inherit system; @@ -30,40 +27,62 @@ overlays = (import ./overlays { inherit inputs pkgs; }); }; pkgs = makePkgs nixpkgs; - pkgsUnstable = makePkgs nixpkgs-unstable; - makeCommonConfig = modules: { + makeCommonConfig = { modules ? [ ], pkgs ? pkgs }: { inherit system; modules = [{ system.stateVersion = "23.11"; } ./modules sops-nix.nixosModules.sops] ++ modules; - specialArgs = { inherit inputs my pkgs system; }; + specialArgs = { inherit inputs pkgs system; }; }; - makeProxmoxLxcConfig = modules: - nixpkgs.lib.nixosSystem ( - makeCommonConfig (modules ++ [ - { my.platforms.proxmox-lxc.enable = true; } - nixos-generators.nixosModules.proxmox-lxc - ]) + makeProxmoxLxcConfig = { modules ? [ ], pkgs ? pkgs, generator ? nixpkgs.lib.nixosSystem }: + generator ( + makeCommonConfig { + inherit pkgs; + modules = (modules ++ [ + { my.platforms.proxmox-lxc.enable = true; } + nixos-generators.nixosModules.proxmox-lxc + ]); + } ); - makeProxmoxLxcTarball = modules: - nixos-generators.nixosGenerate (makeCommonConfig modules // { + makeProxmoxLxcTarball = { pkgs, modules ? [ ] }: + nixos-generators.nixosGenerate ({ format = "proxmox-lxc"; pkgs = nixpkgs.legacyPackages.${system}; lib = nixpkgs.legacyPackages.${system}.lib; + } // makeCommonConfig { + inherit pkgs; + modules = modules; }); serviceFiles = builtins.removeAttrs (builtins.readDir ./modules/services) [ "default.nix" ]; services = builtins.map (name: pkgs.lib.removeSuffix ".nix" name) (builtins.attrNames serviceFiles); in with pkgs.lib; - { + rec { + nixosConfigurations = genAttrs + services + (name: makeProxmoxLxcConfig { + inherit pkgs; + modules = [{ config.my.services.${name}.enable = true; }]; + }); + + packages = rec { + default = lxc-base; + lxc-base = makeProxmoxLxcTarball { inherit pkgs; }; + } // genAttrs services (name: makeProxmoxLxcTarball { + modules = [{ config.my.services.${name}.enable = true; }]; + inherit pkgs; + }); + legacyPackages.nixosConfigurations = nixosConfigurations; # Workaround for the Terraform provider + apps = let makeTfVarsPackage = tfVars: pkgs.runCommand "terraform-vars" { } '' - echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out - ''; + echo '${builtins.toJSON tfVars}' | ${pkgs.jq}/bin/jq > $out + ''; makeGenerateTfVars = name: package: let tfVarsFile = "${name}.auto.tfvars.json"; - in { + in + { type = "app"; program = toString (pkgs.writers.writeBash "package-${package.name}" '' if [[ -e ${tfVarsFile} ]]; then rm -f ${tfVarsFile}; fi @@ -72,7 +91,7 @@ }; enableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = true; }); disableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = false; }); - generateTerraformVars = makeGenerateTfVars "nix-lxcs" (makeTfVarsPackage { lxcs = import ./lxcs; }); + generateTerraformVars = makeGenerateTfVars "nix-lxcs" (makeTfVarsPackage { lxcs = import ./lxcs { lib = pkgs.lib; }; }); in { default = self.apps.${system}.deploy; @@ -87,7 +106,7 @@ ageFromSsh = { type = "app"; program = toString (pkgs.writers.writeBash "ageFromSsh" '' - ssh-keyscan "$1" | ${pkgs.ssh-to-age}/bin/ssh-to-age + (ssh-keyscan "$1" | ${pkgs.ssh-to-age}/bin/ssh-to-age) 2>/dev/null ''); }; } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { @@ -99,12 +118,6 @@ ''); }); - legacyPackages.nixosConfigurations = genAttrs services (name: makeProxmoxLxcConfig [{ config.my.services.${name}.enable = true; }]); - packages = rec { - default = base-proxmox-lxc; - base-proxmox-lxc = makeProxmoxLxcTarball [ ]; - } // genAttrs services (name: makeProxmoxLxcTarball [{ config.my.services.${name}.enable = true; }]); - devShells.default = with pkgs; mkShell { buildInputs = [ age @@ -116,6 +129,7 @@ ])) nixd nixpkgs-fmt + rnix-lsp sops ]; }; From ac4417166d1d259d65360bb4ea5448092e3206d2 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:58:26 -0300 Subject: [PATCH 24/43] feat(terraform-nix): vscode setting tweaks --- terraform-nix/.vscode/settings.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/terraform-nix/.vscode/settings.json b/terraform-nix/.vscode/settings.json index 1525a5c..7a7d94d 100644 --- a/terraform-nix/.vscode/settings.json +++ b/terraform-nix/.vscode/settings.json @@ -1,9 +1,20 @@ { "nix.enableLanguageServer": true, - "nix.serverPath": "nixd", + "nix.serverPath": "rnix-lsp", "nix.formatterPath": "nixpkgs-fmt", "[nix]": { "editor.insertSpaces": true, "editor.tabSize": 2 + }, + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + ".direnv": true, + "result": true, + "/nix": true } } From f76e28d7d8a806a0fbb6103f769676b7504020f2 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 10:58:48 -0300 Subject: [PATCH 25/43] feat(terraform-nix): Secret module tweaks --- terraform-nix/modules/secrets/default.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/terraform-nix/modules/secrets/default.nix b/terraform-nix/modules/secrets/default.nix index 9a92ffd..5fb1a63 100644 --- a/terraform-nix/modules/secrets/default.nix +++ b/terraform-nix/modules/secrets/default.nix @@ -1,10 +1,11 @@ # Documentation: https://github.com/Mic92/sops-nix -{ config, options, ... }: +{ config, lib, options, ... }: +with lib; { sops = { - defaultSopsFile = ../../secrets/default.yaml; + defaultSopsFile = mkDefault ../../secrets/default.yaml; age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; }; } From 5f211760a0a1c26c0f011cba484d084d20fa28bb Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 11:01:04 -0300 Subject: [PATCH 26/43] feat(terraform-nix): TF deploy enhancements Lxcs variable now indexed by vmid Each VM can now specify whether to perform a local or remote build --- terraform-nix/lxcs/default.nix | 94 +++++++++++++++++++++------------- terraform-nix/main.tf | 25 +++++---- terraform-nix/variables.tf | 4 +- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 0f6f86d..e352646 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -1,36 +1,58 @@ -[ - { - name = "caddy"; - enable = false; - vmid = "241"; - ip = "10.10.2.41"; - tags = [ "networking" ]; - cores = 2; - memory = 256; - } - { - name = "jellyfin"; - enable = true; - privileged = true; - vmid = "810"; - ip = "10.10.8.10"; - tags = [ "media" ]; - memory = 4096; - cores = 6; - extra_config = [ - "lxc.cgroup2.devices.allow: c 226:0 rwm" - "lxc.cgroup2.devices.allow: c 226:128 rwm" - "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file,mode=0666" - "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" - ]; - mountpoints = [ - { mp = "/data"; volume = "/srv/storage"; } - { mp = "/config"; volume = "/srv/storage/AppData/config/jellyfin"; } - { mp = "/var/lib/jellyfin/data"; volume = "/srv/storage/AppData/config/jellyfin/data/data"; } - { mp = "/var/lib/jellyfin/metadata"; volume = "/srv/storage/AppData/config/jellyfin/data/metadata"; } - { mp = "/var/lib/jellyfin/plugins"; volume = "/srv/storage/AppData/config/jellyfin/data/plugins"; } - { mp = "/var/lib/jellyfin/root"; volume = "/srv/storage/AppData/config/jellyfin/data/root"; } - { mp = "/var/cache/jellyfin"; volume = "/srv/storage/AppData/config/jellyfin/cache"; } - ]; - } -] +{ lib, ... }: + +with lib; +let + lxcs = { + "241" = { + name = "caddy"; + enable = false; + ip = "10.10.2.41"; + tags = [ "networking" ]; + cores = 2; + memory = 256; + }; + "300" = { + name = "traefik"; + enable = false; + remotebuild = false; + ip = "10.10.3.0"; + tags = [ "networking" ]; + cores = 2; + memory = 512; + mountpoints = [ + { mp = "/etc/crowdsec"; volume = "/srv/storage/AppData/config/crowdsec"; } + { mp = "/var/lib/crowdsec"; volume = "/srv/storage/AppData/data/crowdsec"; } + ]; + }; + "810" = { + name = "jellyfin"; + privileged = true; + ip = "10.10.8.10"; + tags = [ "media" ]; + memory = 4096; + cores = 6; + extra_config = [ + "lxc.cgroup2.devices.allow: c 226:0 rwm" + "lxc.cgroup2.devices.allow: c 226:128 rwm" + "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file,mode=0666" + "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" + ]; + mountpoints = [ + { mp = "/data"; volume = "/srv/storage"; } + { mp = "/config"; volume = "/srv/storage/AppData/config/jellyfin"; } + { mp = "/var/lib/jellyfin/data"; volume = "/srv/storage/AppData/config/jellyfin/data/data"; } + { mp = "/var/lib/jellyfin/metadata"; volume = "/srv/storage/AppData/config/jellyfin/data/metadata"; } + { mp = "/var/lib/jellyfin/plugins"; volume = "/srv/storage/AppData/config/jellyfin/data/plugins"; } + { mp = "/var/lib/jellyfin/root"; volume = "/srv/storage/AppData/config/jellyfin/data/root"; } + { mp = "/var/cache/jellyfin"; volume = "/srv/storage/AppData/config/jellyfin/cache"; } + ]; + nixConfig = let self = lxcs."810"; in + { + reverseProxy = { + enable = true; + servers = [ "http://${self.ip}:8096" ]; + }; + }; + }; + }; +in lxcs diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index 73f88db..fcb9d5a 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -28,17 +28,17 @@ provider "proxmox" { } locals { - lxcs = [ - for lxc in var.lxcs : merge(lxc, { + lxcs = { + for vmid, lxc in var.lxcs : vmid => merge(lxc, { flake = coalesce(lxc.flake, lxc.name) }) if lxc.enable - ] + } } resource "proxmox_lxc" "nixos" { - for_each = { for key, val in local.lxcs : key => val } + for_each = local.lxcs - vmid = each.value.vmid + vmid = each.key tags = join(";", sort(concat(["nixos", "terraform"], each.value.tags))) start = true onboot = each.value.onboot @@ -69,7 +69,6 @@ resource "proxmox_lxc" "nixos" { name = "eth0" bridge = "vmbr0" ip = "${each.value.ip}/${var.network_subnet}" - ip6 = "dhcp" gw = var.network_gateway } @@ -97,12 +96,12 @@ resource "proxmox_lxc" "nixos" { provisioner "remote-exec" { inline = length(each.value.extra_config) > 0 ? concat( - [for line in each.value.extra_config : "echo '${line}' >> /etc/pve/lxc/${each.value.vmid}.conf"], + [for line in each.value.extra_config : "echo '${line}' >> /etc/pve/lxc/${each.key}.conf"], [ - "awk -i inplace '!a[$0]++' /etc/pve/lxc/${each.value.vmid}.conf", # Removes duplicate lines, just in case - "sleep 2 && pct reboot ${each.value.vmid}" + "awk -i inplace '!a[$0]++' /etc/pve/lxc/${each.key}.conf", # Removes duplicate lines, just in case + "pct reboot ${each.key}" ] - ) : [] + ) : [":"] } } @@ -114,14 +113,14 @@ module "nixos" { host = "${each.value.user}@${each.value.ip}" flake = ".#${each.value.flake}" - arguments = [ + arguments = each.value.remotebuild ? [ # You can build on another machine, including the target machine, by # enabling this option, but if you build on the target machine then make # sure that the firewall and security group permit outbound connections. "--build-host", "${each.value.user}@${each.value.ip}", - ] + ] : [] - ssh_options = "-o StrictHostKeyChecking=no" + ssh_options = "-o StrictHostKeyChecking=accept-new" depends_on = [proxmox_lxc.nixos] } diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index b8d9156..605313d 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -43,11 +43,11 @@ variable "authorized_keys" { variable "lxcs" { description = "List of maps describing LXC containers" - type = list(object({ + type = map(object({ name = string - vmid = number ip = string enable = optional(bool, true) + remotebuild = optional(bool, true) onboot = optional(bool, true) rootfs_size = optional(string, "8G") privileged = optional(bool, false) From e1a333c4ddb6107d52ddadcde888e45b386b59b5 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 13:03:38 -0300 Subject: [PATCH 27/43] feat(terraform-nix): Output generation improvements Flake outputs are now generated based on the LXC definition file as opposed to from manually reading the service directory --- terraform-nix/flake.nix | 41 ++++++++++++------- terraform-nix/lxcs/default.nix | 24 ++++++----- .../modules/services/traefik/default.nix | 4 +- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 84b24b8..23bdf39 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -14,7 +14,7 @@ sops-nix.url = "github:Mic92/sops-nix"; }; - outputs = { self, flake-utils, nixpkgs, nixos-generators, sops-nix, ... }@inputs: + outputs = { self, flake-utils, nixpkgs, nixos-generators, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let makePkgs = nixpkgs: @@ -30,7 +30,11 @@ makeCommonConfig = { modules ? [ ], pkgs ? pkgs }: { inherit system; - modules = [{ system.stateVersion = "23.11"; } ./modules sops-nix.nixosModules.sops] ++ modules; + modules = [ + { system.stateVersion = "23.11"; } + inputs.sops-nix.nixosModules.sops + ./modules + ] ++ modules; specialArgs = { inherit inputs pkgs system; }; }; makeProxmoxLxcConfig = { modules ? [ ], pkgs ? pkgs, generator ? nixpkgs.lib.nixosSystem }: @@ -53,25 +57,26 @@ modules = modules; }); - serviceFiles = builtins.removeAttrs (builtins.readDir ./modules/services) [ "default.nix" ]; - services = builtins.map (name: pkgs.lib.removeSuffix ".nix" name) (builtins.attrNames serviceFiles); + lxcs = import ./lxcs { inherit (pkgs) lib; }; in with pkgs.lib; rec { - nixosConfigurations = genAttrs - services - (name: makeProxmoxLxcConfig { + nixosConfigurations = mapAttrs + (_: lxc: makeProxmoxLxcConfig { inherit pkgs; - modules = [{ config.my.services.${name}.enable = true; }]; - }); + modules = lxc.nix.modules or []; + }) + lxcs.byName; packages = rec { default = lxc-base; lxc-base = makeProxmoxLxcTarball { inherit pkgs; }; - } // genAttrs services (name: makeProxmoxLxcTarball { - modules = [{ config.my.services.${name}.enable = true; }]; - inherit pkgs; - }); + } // mapAttrs + (_: lxc: makeProxmoxLxcTarball { + inherit pkgs; + modules = lxc.nix.modules or []; + }) + lxcs.byName; legacyPackages.nixosConfigurations = nixosConfigurations; # Workaround for the Terraform provider apps = @@ -91,7 +96,7 @@ }; enableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = true; }); disableBuild = makeGenerateTfVars "nix-build" (makeTfVarsPackage { build = false; }); - generateTerraformVars = makeGenerateTfVars "nix-lxcs" (makeTfVarsPackage { lxcs = import ./lxcs { lib = pkgs.lib; }; }); + generateTerraformVars = makeGenerateTfVars "nix-lxcs" (makeTfVarsPackage { lxcs = lxcs.byId; }); in { default = self.apps.${system}.deploy; @@ -100,7 +105,7 @@ program = toString (pkgs.writers.writeBash "deploy" '' ${enableBuild.program} ${generateTerraformVars.program} - ${pkgs.terraform}/bin/terraform apply + ${pkgs.terraform}/bin/terraform apply "$([ "$1" ] && echo "-target=module.nixos["1010"]" || echo "")" ''); }; ageFromSsh = { @@ -109,6 +114,12 @@ (ssh-keyscan "$1" | ${pkgs.ssh-to-age}/bin/ssh-to-age) 2>/dev/null ''); }; + buildOsConfig = { + type = "app"; + program = toString (pkgs.writers.writeBash "buildosconfig" '' + nix build ".#legacyPackages.x86_64-linux.nixosConfigurations.$1.config.system.build.toplevel" --show-trace + ''); + }; } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { type = "app"; program = toString (pkgs.writers.writeBash cmd '' diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index e352646..01d751e 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -2,7 +2,8 @@ with lib; let - lxcs = { + makeDefaultModules = name: [{ config.my.services.${name}.enable = true; }]; + byId = { "241" = { name = "caddy"; enable = false; @@ -10,6 +11,7 @@ let tags = [ "networking" ]; cores = 2; memory = 256; + nix.modules = makeDefaultModules "caddy"; }; "300" = { name = "traefik"; @@ -23,11 +25,13 @@ let { mp = "/etc/crowdsec"; volume = "/srv/storage/AppData/config/crowdsec"; } { mp = "/var/lib/crowdsec"; volume = "/srv/storage/AppData/data/crowdsec"; } ]; + nix.modules = makeDefaultModules "traefik"; }; - "810" = { + "810" = rec { name = "jellyfin"; privileged = true; ip = "10.10.8.10"; + services = [ "http://${ip}:8096" ]; tags = [ "media" ]; memory = 4096; cores = 6; @@ -46,13 +50,13 @@ let { mp = "/var/lib/jellyfin/root"; volume = "/srv/storage/AppData/config/jellyfin/data/root"; } { mp = "/var/cache/jellyfin"; volume = "/srv/storage/AppData/config/jellyfin/cache"; } ]; - nixConfig = let self = lxcs."810"; in - { - reverseProxy = { - enable = true; - servers = [ "http://${self.ip}:8096" ]; - }; - }; + nix.modules = makeDefaultModules "jellyfin"; }; }; -in lxcs +in +rec { + inherit byId; + asList = attrValues byId; + byName = mapAttrs' (_: lxc: nameValuePair lxc.name lxc) byId; + byRole = { reverseProxy = byName.traefik; }; +} diff --git a/terraform-nix/modules/services/traefik/default.nix b/terraform-nix/modules/services/traefik/default.nix index 05e611d..19e0138 100644 --- a/terraform-nix/modules/services/traefik/default.nix +++ b/terraform-nix/modules/services/traefik/default.nix @@ -9,7 +9,7 @@ let dataDir = config.services.traefik.dataDir; domain = myCfg.domain; - lxcs = filterAttrs (_: lxc: lxc.nixConfig.reverseProxy.enable or false) config.my.lxcs; + lxcs = filterAttrs (_: lxc: lxc ? services) config.my.lxcs; trustedIpsPrivate = myCfg.networking.cidrs.private; trustedIps = myCfg.networking.cidrs.trusted; @@ -54,7 +54,7 @@ with lib; lxcs; services = mapAttrs' (_: lxc: nameValuePair lxc.name { - loadBalancer.servers = map (url: { inherit url; }) lxc.nixConfig.reverseProxy.servers; + loadBalancer.servers = map (url: { inherit url; }) lxc.services; }) lxcs; middlewares = { From adf9522264c0233134d5e4ff5fab016637c710e7 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 14:59:20 -0300 Subject: [PATCH 28/43] feat(terraform-nix): Change package outputs Change from "name" to both "lxc-${name}" and "lxc-{vmid}" --- terraform-nix/flake.nix | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 23bdf39..2bddfae 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -71,12 +71,25 @@ packages = rec { default = lxc-base; lxc-base = makeProxmoxLxcTarball { inherit pkgs; }; - } // mapAttrs - (_: lxc: makeProxmoxLxcTarball { - inherit pkgs; - modules = lxc.nix.modules or []; - }) - lxcs.byName; + } + // mapAttrs' + (name: lxc: nameValuePair + "lxc-${name}" + (makeProxmoxLxcTarball { + inherit pkgs; + modules = lxc.nix.modules or [ ]; + }) + ) + lxcs.byName + // mapAttrs' + (vmid: lxc: nameValuePair + "lxc-${vmid}" + (makeProxmoxLxcTarball { + inherit pkgs; + modules = lxc.nix.modules or [ ]; + }) + ) + lxcs.byId; legacyPackages.nixosConfigurations = nixosConfigurations; # Workaround for the Terraform provider apps = From bad257755960464017d6522d862a51f1d9ee3066 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sun, 7 Jan 2024 17:44:17 -0300 Subject: [PATCH 29/43] feat(terraform-nix): Configuration improvements Networking and storage configurations moved to their own files so that LXC definitions can import and refer to them --- terraform-nix/lxcs/default.nix | 28 ++++++++++++++------- terraform-nix/modules/config/default.nix | 15 +++-------- terraform-nix/modules/config/networking.nix | 11 ++++++++ terraform-nix/modules/config/storage.nix | 6 +++++ 4 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 terraform-nix/modules/config/networking.nix create mode 100644 terraform-nix/modules/config/storage.nix diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 01d751e..72e407e 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -2,7 +2,13 @@ with lib; let + config.my = { + networking = import ../modules/config/networking.nix; + storage = import ../modules/config/storage.nix; + }; + makeDefaultModules = name: [{ config.my.services.${name}.enable = true; }]; + byId = { "241" = { name = "caddy"; @@ -41,15 +47,19 @@ let "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file,mode=0666" "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" ]; - mountpoints = [ - { mp = "/data"; volume = "/srv/storage"; } - { mp = "/config"; volume = "/srv/storage/AppData/config/jellyfin"; } - { mp = "/var/lib/jellyfin/data"; volume = "/srv/storage/AppData/config/jellyfin/data/data"; } - { mp = "/var/lib/jellyfin/metadata"; volume = "/srv/storage/AppData/config/jellyfin/data/metadata"; } - { mp = "/var/lib/jellyfin/plugins"; volume = "/srv/storage/AppData/config/jellyfin/data/plugins"; } - { mp = "/var/lib/jellyfin/root"; volume = "/srv/storage/AppData/config/jellyfin/data/root"; } - { mp = "/var/cache/jellyfin"; volume = "/srv/storage/AppData/config/jellyfin/cache"; } - ]; + mountpoints = + let + cfgPath = config.my.storage.getConfigPath "jellyfin"; + in + [ + { mp = "/data"; volume = config.my.storage.main; } + { mp = "/config"; volume = cfgPath; } + { mp = "/var/lib/jellyfin/data"; volume = "${cfgPath}/data/data"; } + { mp = "/var/lib/jellyfin/metadata"; volume = "${cfgPath}/data/metadata"; } + { mp = "/var/lib/jellyfin/plugins"; volume = "${cfgPath}/data/plugins"; } + { mp = "/var/lib/jellyfin/root"; volume = "${cfgPath}/data/root"; } + { mp = "/var/cache/jellyfin"; volume = "${cfgPath}/cache"; } + ]; nix.modules = makeDefaultModules "jellyfin"; }; }; diff --git a/terraform-nix/modules/config/default.nix b/terraform-nix/modules/config/default.nix index eb7b2bc..845590d 100644 --- a/terraform-nix/modules/config/default.nix +++ b/terraform-nix/modules/config/default.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, ... }@args: +{ lib, ... }@args: with lib; let @@ -8,17 +8,8 @@ in options.my = { domain = mkReadonlyOption "lpcha.im"; email = mkReadonlyOption "lpchaim@gmail.com"; - networking = mkReadonlyOption { - defaultGateway = "10.0.0.1"; - defaultPrefixLength = "8"; - cidrs = rec { - trusted = private ++ cloudflare; - private = [ "10.0.0.0/8" "fe80::/10" ]; - cloudflare = builtins.filter - (x: stringLength(x) > 0) - (splitString "\n" (builtins.readFile pkgs.cloudflare-ips)); - }; - }; + networking = mkReadonlyOption (import ./networking.nix); + storage = mkReadonlyOption (import ./storage.nix); lxcs = mkReadonlyOption (import ../../lxcs args); }; } diff --git a/terraform-nix/modules/config/networking.nix b/terraform-nix/modules/config/networking.nix new file mode 100644 index 0000000..494e91d --- /dev/null +++ b/terraform-nix/modules/config/networking.nix @@ -0,0 +1,11 @@ +{ + defaultGateway = "10.0.0.1"; + defaultPrefixLength = "8"; + cidrs = rec { + trusted = private ++ cloudflare; + private = [ "10.0.0.0/8" "fe80::/10" ]; + cloudflare = builtins.filter + (x: stringLength (x) > 0) + (splitString "\n" (builtins.readFile pkgs.cloudflare-ips)); + }; +} diff --git a/terraform-nix/modules/config/storage.nix b/terraform-nix/modules/config/storage.nix new file mode 100644 index 0000000..fe31b6e --- /dev/null +++ b/terraform-nix/modules/config/storage.nix @@ -0,0 +1,6 @@ +rec { + main = "/srv/storage"; + appData = "${main}/AppData"; + getConfigPath = name: "${appData}/config/${name}"; + getDataPath = name: "${appData}/data/${name}"; +} From abeff9022f0cae2b1c8bbff2265670053b400e9b Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Mon, 8 Jan 2024 11:11:06 -0300 Subject: [PATCH 30/43] feat(terraform-nix): Initial docker implementation Went for a KISS setup to start with, converted my existing compose to a nix expression which is then operated on as desired then remarshaled to compose.yaml. After that, an activation script just runs `docker compose up -d` --- terraform-nix/.sops.yaml | 9 +- terraform-nix/flake.nix | 12 +- terraform-nix/lxcs/default.nix | 14 +- terraform-nix/main.tf | 6 - terraform-nix/modules/config/default.nix | 5 +- terraform-nix/modules/config/networking.nix | 6 +- terraform-nix/modules/config/storage.nix | 1 + terraform-nix/modules/containers/default.nix | 5 + .../containers/docker/compose/default.nix | 549 ++++++++++++++++++ .../modules/containers/docker/default.nix | 117 ++++ terraform-nix/modules/default.nix | 1 + terraform-nix/modules/secrets/default.nix | 5 +- terraform-nix/secrets/default.env | 52 ++ terraform-nix/secrets/default.yaml | 0 terraform-nix/secrets/docker/default.env | 24 + terraform-nix/variables.tf | 2 +- 16 files changed, 787 insertions(+), 21 deletions(-) create mode 100644 terraform-nix/modules/containers/default.nix create mode 100644 terraform-nix/modules/containers/docker/compose/default.nix create mode 100644 terraform-nix/modules/containers/docker/default.nix create mode 100644 terraform-nix/secrets/default.env delete mode 100644 terraform-nix/secrets/default.yaml create mode 100644 terraform-nix/secrets/docker/default.env diff --git a/terraform-nix/.sops.yaml b/terraform-nix/.sops.yaml index 1549371..bbde19d 100644 --- a/terraform-nix/.sops.yaml +++ b/terraform-nix/.sops.yaml @@ -1,11 +1,18 @@ keys: - - &primary age1dsuede07h7akeaucp53uyxu6wa5y3yapxy3c0nh34vcdhxnanacqr6q3tc + - &primary age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h + - &docker age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 - &traefik age1k53pxzjtln8ds72ys5crlqz48q3flr8kawjhfmu34w4306mahscqgdamrx creation_rules: - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ key_groups: - age: - *primary + - *docker + - path_regex: secrets/docker/[^/]+\.(yaml|json|env|ini)$ + key_groups: + - age: + - *primary + - *docker - path_regex: secrets/traefik/[^/]+\.(yaml|json|env|ini)$ key_groups: - age: diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index 2bddfae..da6b640 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -24,7 +24,7 @@ allowUnfree = true; allowUnfreePredicate = _: true; }; - overlays = (import ./overlays { inherit inputs pkgs; }); + overlays = import ./overlays { inherit inputs pkgs; }; }; pkgs = makePkgs nixpkgs; @@ -50,8 +50,7 @@ makeProxmoxLxcTarball = { pkgs, modules ? [ ] }: nixos-generators.nixosGenerate ({ format = "proxmox-lxc"; - pkgs = nixpkgs.legacyPackages.${system}; - lib = nixpkgs.legacyPackages.${system}.lib; + inherit (pkgs) lib; } // makeCommonConfig { inherit pkgs; modules = modules; @@ -64,7 +63,7 @@ nixosConfigurations = mapAttrs (_: lxc: makeProxmoxLxcConfig { inherit pkgs; - modules = lxc.nix.modules or []; + modules = lxc.nix.modules or [ ]; }) lxcs.byName; @@ -118,7 +117,7 @@ program = toString (pkgs.writers.writeBash "deploy" '' ${enableBuild.program} ${generateTerraformVars.program} - ${pkgs.terraform}/bin/terraform apply "$([ "$1" ] && echo "-target=module.nixos["1010"]" || echo "")" + ${pkgs.terraform}/bin/terraform apply ''); }; ageFromSsh = { @@ -130,7 +129,7 @@ buildOsConfig = { type = "app"; program = toString (pkgs.writers.writeBash "buildosconfig" '' - nix build ".#legacyPackages.x86_64-linux.nixosConfigurations.$1.config.system.build.toplevel" --show-trace + nix build ".#nixosConfigurations.${system}.$1.config.system.build.toplevel" --show-trace ''); }; } // genAttrs [ "init" "plan" "apply" "destroy" ] (cmd: { @@ -151,6 +150,7 @@ b.null proxmox ])) + nil nixd nixpkgs-fmt rnix-lsp diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 72e407e..e4055e3 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -22,7 +22,6 @@ let "300" = { name = "traefik"; enable = false; - remotebuild = false; ip = "10.10.3.0"; tags = [ "networking" ]; cores = 2; @@ -62,6 +61,19 @@ let ]; nix.modules = makeDefaultModules "jellyfin"; }; + "1000" = { + name = "docker"; + privileged = true; + ip = "10.10.10.0"; + tags = [ "docker" ]; + memory = 4096; + cores = 6; + rootfs_size = "30G"; + mountpoints = [ + { mp = "/srv/storage"; volume = "/srv/storage"; } + ]; + nix.modules = makeDefaultModules "docker"; + }; }; in rec { diff --git a/terraform-nix/main.tf b/terraform-nix/main.tf index fcb9d5a..cba12c0 100644 --- a/terraform-nix/main.tf +++ b/terraform-nix/main.tf @@ -81,12 +81,6 @@ resource "proxmox_lxc" "nixos" { nesting = true } - lifecycle { - ignore_changes = [ - rootfs, - ] - } - connection { type = "ssh" user = var.pm_user diff --git a/terraform-nix/modules/config/default.nix b/terraform-nix/modules/config/default.nix index 845590d..33d9e2e 100644 --- a/terraform-nix/modules/config/default.nix +++ b/terraform-nix/modules/config/default.nix @@ -8,8 +8,9 @@ in options.my = { domain = mkReadonlyOption "lpcha.im"; email = mkReadonlyOption "lpchaim@gmail.com"; - networking = mkReadonlyOption (import ./networking.nix); - storage = mkReadonlyOption (import ./storage.nix); lxcs = mkReadonlyOption (import ../../lxcs args); + networking = mkReadonlyOption (import ./networking.nix args); + storage = mkReadonlyOption (import ./storage.nix); + timezone = mkReadonlyOption "America/Sao_Paulo"; }; } diff --git a/terraform-nix/modules/config/networking.nix b/terraform-nix/modules/config/networking.nix index 494e91d..18ed054 100644 --- a/terraform-nix/modules/config/networking.nix +++ b/terraform-nix/modules/config/networking.nix @@ -1,3 +1,5 @@ +{ lib ? pkgs.lib, pkgs, ... }: + { defaultGateway = "10.0.0.1"; defaultPrefixLength = "8"; @@ -5,7 +7,7 @@ trusted = private ++ cloudflare; private = [ "10.0.0.0/8" "fe80::/10" ]; cloudflare = builtins.filter - (x: stringLength (x) > 0) - (splitString "\n" (builtins.readFile pkgs.cloudflare-ips)); + (x: builtins.stringLength (x) > 0) + (lib.splitString "\n" (builtins.readFile pkgs.cloudflare-ips)); }; } diff --git a/terraform-nix/modules/config/storage.nix b/terraform-nix/modules/config/storage.nix index fe31b6e..f6aafad 100644 --- a/terraform-nix/modules/config/storage.nix +++ b/terraform-nix/modules/config/storage.nix @@ -3,4 +3,5 @@ rec { appData = "${main}/AppData"; getConfigPath = name: "${appData}/config/${name}"; getDataPath = name: "${appData}/data/${name}"; + getLogPath = name: "${appData}/log/${name}"; } diff --git a/terraform-nix/modules/containers/default.nix b/terraform-nix/modules/containers/default.nix new file mode 100644 index 0000000..092ab95 --- /dev/null +++ b/terraform-nix/modules/containers/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./docker + ]; +} diff --git a/terraform-nix/modules/containers/docker/compose/default.nix b/terraform-nix/modules/containers/docker/compose/default.nix new file mode 100644 index 0000000..ba3a7be --- /dev/null +++ b/terraform-nix/modules/containers/docker/compose/default.nix @@ -0,0 +1,549 @@ +{ config, ports, ... }: + +let + makeDefault = dockerConfig: + (dockerConfig // { + environment = { + TZ = config.my.timezone; + PUID = 1000; + GUID = 1000; + } // (dockerConfig.environment or {}); + volumes = [ + "/dev/rtc:/dev/rtc:ro" + "/etc/localtime:/etc/localtime:ro" + "/etc/timezone:/etc/timezone:ro" + ] ++ (dockerConfig.volumes or []); + restart = dockerConfig.restart or "unless-stopped"; + }); +in +{ + name = "homelab"; + version = "3.6"; + + services = { + cloudflare-ddns = makeDefault { + image = "oznu/cloudflare-ddns"; + environment = { + API_KEY = "\${secret_cloudflare_api_token}"; + ZONE = config.my.domain; + INTERFACE = "eth0"; + PROXIED = "true"; + RRTYPE = "AAAA"; + }; + network_mode = "host"; + }; + + crowdsec = makeDefault { + image = "crowdsecurity/crowdsec"; + networks = [ "default" "external" ]; + volumes = [ + "${config.my.storage.getConfigPath "crowdsec"}:/etc/crowdsec" + "${config.my.storage.getLogPath "crowdsec"}:/var/log/nginx" + "${config.my.storage.getDataPath "crowdsec"}:/var/lib/crowdsec/data" + ]; + }; + + themepark = makeDefault { + image = "ghcr.io/themepark-dev/theme.park"; + ports = [ "8084:80" "8444:443" ]; + volumes = [ "${config.my.storage.getConfigPath "themepark"}:/config" ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.themepark.entrypoints" = "websecure"; + "traefik.http.routers.themepark.rule" = "Host(`themepark.${config.my.domain}`)"; + "traefik.http.routers.themepark.middlewares" = "default@file"; + "traefik.http.services.themepark.loadbalancer.server.port" = "80"; + }; + }; + + traefik = makeDefault { + image = "traefik:v2.9"; + environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; + networks = [ "default" "external" ]; + ports = [ + "${builtins.toString ports.internal.http}:80" + "${builtins.toString ports.internal.https}:443" + "8080:8080" + ]; + volumes = [ + "${config.my.storage.getConfigPath "traefik"}:/etc/traefik" + "${config.my.storage.getConfigPath "traefik"}/acme:/etc/traefik/acme" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + }; + + audiobookshelf = makeDefault { + image = "ghcr.io/advplyr/audiobookshelf:2.7.0"; + ports = [ "13378:80" ]; + volumes = [ + "${config.my.storage.getConfigPath "audiobookshelf"}:/config" + "${config.my.storage.getDataPath "audiobookshelf"}:/metadata" + "${config.my.storage.main}:/storage" + ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Audiobookshelf"; + "homepage.icon" = "audiobookshelf.png"; + "homepage.href" = "https://audiobookshelf.${config.my.domain}"; + "homepage.description" = "Audiobook and podcast manager"; + "traefik.enable" = "true"; + "traefik.http.routers.audiobookshelf.entrypoints" = "websecure"; + "traefik.http.routers.audiobookshelf.rule" = + "Host(`audiobookshelf.${config.my.domain}`)"; + "traefik.http.routers.audiobookshelf.middlewares" = "default@file"; + }; + }; + + bazarr = makeDefault { + image = "lscr.io/linuxserver/bazarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:bazarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "6767:6767" ]; + volumes = + [ "${config.my.storage.getConfigPath "bazarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Bazarr"; + "homepage.icon" = "bazarr.png"; + "homepage.href" = "https://bazarr.${config.my.domain}"; + "homepage.description" = "Subtitle manager"; + "homepage.widget.type" = "bazarr"; + "homepage.widget.url" = "https://bazarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_bazarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.bazarr.entrypoints" = "websecure"; + "traefik.http.routers.bazarr.rule" = "Host(`bazarr.${config.my.domain}`)"; + "traefik.http.routers.bazarr.middlewares" = "default@file"; + }; + }; + + lazylibrarian = makeDefault { + image = "lscr.io/linuxserver/lazylibrarian:latest"; + environment = { + DOCKER_MODS = "linuxserver/mods:universal-calibre|linuxserver/mods:lazylibrarian-ffmpeg"; + }; + ports = [ "5299:5299" ]; + volumes = [ + "${config.my.storage.getConfigPath "lazylibrarian"}:/config" + "${config.my.storage.main}:/storage" + ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "LazyLibrarian"; + "homepage.icon" = "lazylibrarian.png"; + "homepage.href" = "https://lazylibrarian.${config.my.domain}"; + "homepage.description" = "Book manager"; + "traefik.enable" = "true"; + "traefik.http.routers.lazylibrarian.entrypoints" = "websecure"; + "traefik.http.routers.lazylibrarian.rule" = + "Host(`lazylibrarian.${config.my.domain}`)"; + "traefik.http.routers.lazylibrarian.middlewares" = "default@file"; + }; + }; + + lidarr = makeDefault { + image = "lscr.io/linuxserver/lidarr:2.0.7.3849-ls148"; + ports = [ "8686:8686" ]; + volumes = + [ "${config.my.storage.getConfigPath "lidarr"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Lidarr"; + "homepage.icon" = "lidarr.png"; + "homepage.href" = "https://lidarr.${config.my.domain}"; + "homepage.description" = "Music manager"; + "traefik.enable" = "true"; + "traefik.http.routers.lidarr.entrypoints" = "websecure"; + "traefik.http.routers.lidarr.rule" = "Host(`lidarr.${config.my.domain}`)"; + "traefik.http.routers.lidarr.middlewares" = "default@file"; + }; + }; + + mylar = makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/mylar3:latest"; + ports = [ "8090:8090" ]; + volumes = + [ "${config.my.storage.getConfigPath "mylar"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Mylar"; + "homepage.icon" = "mylar.png"; + "homepage.href" = "https://mylar.${config.my.domain}"; + "homepage.description" = "Comics/Manga manager"; + "traefik.enable" = "true"; + "traefik.http.routers.mylar.entrypoints" = "websecure"; + "traefik.http.routers.mylar.rule" = "Host(`mylar.${config.my.domain}`)"; + "traefik.http.routers.mylar.middlewares" = "default@file"; + }; + }; + + radarr = makeDefault { + image = "lscr.io/linuxserver/radarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:radarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "7878:7878" ]; + volumes = + [ "${config.my.storage.getConfigPath "radarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Radarr"; + "homepage.icon" = "radarr.png"; + "homepage.href" = "https://radarr.${config.my.domain}"; + "homepage.description" = "Movie manager"; + "homepage.weight" = -60000; + "homepage.widget.type" = "radarr"; + "homepage.widget.url" = "https://radarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_radarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.radarr.entrypoints" = "websecure"; + "traefik.http.routers.radarr.rule" = "Host(`radarr.${config.my.domain}`)"; + "traefik.http.routers.radarr.middlewares" = "default@file"; + }; + }; + + readarr = makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/readarr:nightly-0.3.14.2348-ls257"; + ports = [ "8787:8787" ]; + volumes = + [ "${config.my.storage.getConfigPath "readarr"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Readarr"; + "homepage.icon" = "readarr.png"; + "homepage.href" = "https://readarr.${config.my.domain}"; + "homepage.description" = "Book manager"; + "traefik.enable" = "false"; + "traefik.http.routers.readarr.entrypoints" = "websecure"; + "traefik.http.routers.readarr.rule" = "Host(`readarr.${config.my.domain}`)"; + "traefik.http.routers.readarr.middlewares" = "default@file"; + }; + }; + + sonarr = makeDefault { + image = "lscr.io/linuxserver/sonarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:sonarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "8989:8989" ]; + volumes = + [ "${config.my.storage.getConfigPath "sonarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Sonarr"; + "homepage.icon" = "sonarr.png"; + "homepage.href" = "https://sonarr.${config.my.domain}"; + "homepage.description" = "Series manager"; + "homepage.weight" = -70000; + "homepage.widget.type" = "sonarr"; + "homepage.widget.url" = "https://sonarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_sonarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.sonarr.entrypoints" = "websecure"; + "traefik.http.routers.sonarr.rule" = "Host(`sonarr.${config.my.domain}`)"; + "traefik.http.routers.sonarr.middlewares" = "default@file"; + }; + }; + + jellyseerr = makeDefault { + image = "fallenbagel/jellyseerr:latest"; + networks = [ "default" "external" ]; + ports = [ "5055:5055" ]; + volumes = [ "${config.my.storage.getConfigPath "jellyseerr"}:/app/config" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Jellyseerr"; + "homepage.icon" = "jellyseerr.png"; + "homepage.href" = "https://jellyseerr.${config.my.domain}"; + "homepage.description" = "Media requester"; + "homepage.weight" = -80000; + "homepage.widget.type" = "jellyseerr"; + "homepage.widget.url" = "https://jellyseerr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_jellyseerr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.jellyseerr.entrypoints" = "websecure"; + "traefik.http.routers.jellyseerr.rule" = + "Host(`jellyseerr.${config.my.domain}`)"; + "traefik.http.routers.jellyseerr.middlewares" = "default@file"; + }; + }; + + prowlarr = makeDefault { + image = "lscr.io/linuxserver/prowlarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:prowlarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "9696:9696" ]; + volumes = [ "${config.my.storage.getConfigPath "prowlarr"}:/config" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Prowlarr"; + "homepage.icon" = "prowlarr.png"; + "homepage.href" = "https://prowlarr.${config.my.domain}"; + "homepage.description" = "Arr index manager"; + "homepage.widget.type" = "prowlarr"; + "homepage.widget.url" = "https://prowlarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_prowlarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.prowlarr.entrypoints" = "websecure"; + "traefik.http.routers.prowlarr.rule" = "Host(`prowlarr.${config.my.domain}`)"; + "traefik.http.routers.prowlarr.middlewares" = "default@file"; + }; + }; + + actual-server = makeDefault { + image = "actualbudget/actual-server:latest"; + networks = [ "default" "external" ]; + ports = [ "5006:5006" ]; + volumes = [ "${config.my.storage.getDataPath "actual-server"}:/data" ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.actual.entrypoints" = "websecure"; + "traefik.http.routers.actual.rule" = "Host(`actual.${config.my.domain}`)"; + "traefik.http.routers.actual.middlewares" = "default@file"; + }; + }; + + adguardhome-sync = makeDefault { + image = "lscr.io/linuxserver/adguardhome-sync:latest"; + ports = [ "8082:8082/tcp" ]; + volumes = [ "${config.my.storage.getConfigPath "adguardhome-sync"}:/config" ]; + }; + + homepage = makeDefault { + image = "ghcr.io/gethomepage/homepage:v0.8.0"; + networks = [ "default" "external" ]; + ports = [ "3000:3000" ]; + volumes = [ + "${config.my.storage.getConfigPath "homepage"}:/app/config" + "/var/run/docker.sock:/var/run/docker.sock" + "${config.my.storage.main}:/storage:ro" + ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.home.entrypoints" = "websecure"; + "traefik.http.routers.home.rule" = "Host(`home.${config.my.domain}`)"; + "traefik.http.routers.home.middlewares" = "default@file"; + }; + }; + + forgejo-server = makeDefault { + image = "codeberg.org/forgejo/forgejo:1.20"; + environment = { + USER_UID = 1000; + USER_GID = 1000; + FORGEJO__actions__ENABLED = "true"; + FORGEJO__database__DB_TYPE = "postgres"; + FORGEJO__database__HOST = "forgejo-db:5432"; + FORGEJO__database__NAME = "forgejo"; + FORGEJO__database__USER = "forgejo"; + FORGEJO__database__PASSWD = "\${secret_forgejo_db_password}"; + FORGEJO__mailer__ENABLED = "true"; + FORGEJO__mailer__FROM = "forgejo@${config.my.domain}"; + FORGEJO__mailer__PROTOCOL = "starttls"; + FORGEJO__mailer__SMTP_ADDR = "\${secret_smtp_host}"; + FORGEJO__mailer__SMTP_PORT = "\${secret_smtp_port}"; + FORGEJO__mailer__USER = "\${secret_smtp_user}"; + FORGEJO__mailer__PASSWD = "\${secret_smtp_password}"; + FORGEJO__service__DISABLE_REGISTRATION = "true"; + FORGEJO__service__REGISTER_MANUAL_CONFIRM = "true"; + }; + networks = [ "default" "external" ]; + ports = [ "3001:3000" "222:22" ]; + volumes = [ "${config.my.storage.getDataPath "forgejo-server"}:/data" ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "Forgejo"; + "homepage.icon" = "forgejo.png"; + "homepage.href" = "https://git.${config.my.domain}"; + "homepage.description" = "Code forge"; + "traefik.enable" = "true"; + "traefik.http.routers.git.entrypoints" = "websecure"; + "traefik.http.routers.git.rule" = "Host(`git.${config.my.domain}`)"; + "traefik.http.routers.git.middlewares" = "default@file"; + "traefik.http.services.git.loadbalancer.server.port" = "3000"; + }; + depends_on = [ "forgejo-db" ]; + }; + + forgejo-db = makeDefault { + image = "postgres:14"; + environment = { + POSTGRES_USER = "forgejo"; + POSTGRES_PASSWORD = "\${secret_forgejo_db_password}"; + POSTGRES_DB = "forgejo"; + }; + networks = [ "default" ]; + volumes = + [ "${config.my.storage.getDataPath "forgejo-postgres"}:/var/lib/postgresql/data" ]; + }; + + influxdb = makeDefault { + image = "influxdb:latest"; + networks = [ "default" "external" ]; + ports = [ "8086:8086" ]; + volumes = [ + "${config.my.storage.getConfigPath "influxdb"}:/etc/influxdb2" + "${config.my.storage.getDataPath "influxdb"}:/var/lib/influxdb2" + ]; + }; + + jackett = makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/jackett"; + networks = [ "default" "external" ]; + ports = [ "9117:9117" ]; + volumes = + [ "${config.my.storage.getConfigPath "jackett"}:/config" "${config.my.storage.main}:/storage" ]; + }; + + jellyfin = makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/jellyfin"; + environment = { DOCKER_MODS = "linuxserver/mods:jellyfin-opencl-intel"; }; + networks = [ "default" "external" ]; + ports = [ "8096:8096" "7359:7359" "1900:1900" ]; + volumes = [ "${config.my.storage.getConfigPath "jellyfin"}:/config" "${config.my.storage.main}:/data" ]; + devices = [ "/dev/dri:/dev/dri" ]; + }; + + qbittorrent = makeDefault { + image = "lscr.io/linuxserver/qbittorrent:latest"; + environment = { + WEBUI_PORT = "8081"; + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:qbittorrent"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "8081:8081" "6881:6881" "6881:6881/udp" ]; + volumes = [ + "${config.my.storage.getConfigPath "qbittorrent"}:/config" + "${config.my.storage.main}/Downloads/Torrents:/downloads" + ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "qBittorrent"; + "homepage.icon" = "qbittorrent.png"; + "homepage.href" = "https://qbittorrent.${config.my.domain}"; + "homepage.description" = "Torrent downloader"; + "homepage.widget.type" = "qbittorrent"; + "homepage.widget.url" = "https://qbittorrent.${config.my.domain}"; + "homepage.widget.username" = "\${secret_qbittorrent_user}"; + "homepage.widget.password" = "\${secret_qbittorrent_password}"; + "traefik.enable" = "true"; + "traefik.http.routers.qbittorrent.entrypoints" = "websecure"; + "traefik.http.routers.qbittorrent.rule" = + "Host(`qbittorrent.${config.my.domain}`)"; + "traefik.http.routers.qbittorrent.middlewares" = "default@file"; + "traefik.http.services.qbittorrent.loadbalancer.server.port" = "8081"; + }; + }; + + syncthing = makeDefault { + image = "lscr.io/linuxserver/syncthing:latest"; + networks = [ "default" "external" ]; + ports = + [ "8384:8384" "22000:22000/tcp" "22000:22000/udp" "21027:21027/udp" ]; + volumes = [ + "${config.my.storage.getConfigPath "syncthing"}:/config" + "${config.my.storage.getDataPath "syncthing"}:/data" + ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "Syncthing"; + "homepage.icon" = "syncthing.png"; + "homepage.href" = "https://syncthing.${config.my.domain}"; + "homepage.description" = "P2P file syncing"; + "traefik.enable" = "true"; + "traefik.http.routers.syncthing.entrypoints" = "websecure"; + "traefik.http.routers.syncthing.rule" = "Host(`syncthing.${config.my.domain}`)"; + "traefik.http.routers.syncthing.middlewares" = "default@file"; + "traefik.http.services.syncthing.loadbalancer.server.port" = "8384"; + }; + }; + + vscode = makeDefault { + image = "lscr.io/linuxserver/code-server:latest"; + environment = { + PASSWORD = "\${secret_vscode_password}"; + PROXY_DOMAIN = "code.${config.my.domain}"; + }; + networks = [ "default" "external" ]; + ports = [ "8443:8443/tcp" ]; + volumes = [ "${config.my.storage.getConfigPath "vscode"}:/config" ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "Visual Studio Code"; + "homepage.icon" = "vscode.png"; + "homepage.href" = "https://code.${config.my.domain}"; + "homepage.description" = "Cloud IDE"; + "traefik.enable" = "true"; + "traefik.http.routers.code.entrypoints" = "websecure"; + "traefik.http.routers.code.rule" = "Host(`code.${config.my.domain}`)"; + "traefik.http.routers.code.middlewares" = "default@file"; + }; + }; + + portainer = makeDefault { + image = "portainer/portainer-ce:latest"; + network_mode = "bridge"; + ports = [ "8001:8000" "9443:9443" ]; + restart = "always"; + volumes = [ + "${config.my.storage.getDataPath "portainer"}:/data" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + }; + + watchtower = makeDefault { + image = "containrrr/watchtower"; + networks = [ "default" "external" ]; + environment = { + WATCHTOWER_SCHEDULE = "0 2 * * *"; + WATCHTOWER_CLEANUP = "true"; + }; + restart = "unless-stopped"; + volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ]; + }; + + yacht = makeDefault { + image = "selfhostedpro/yacht"; + networks = [ "default" "external" ]; + ports = [ "8000:8000" ]; + volumes = [ + "${config.my.storage.getConfigPath "yacht"}:/config" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + }; + }; + + networks = { + default = { + internal = true; + ipam.config = [{ subnet = "172.16.80.0/24"; }]; + }; + + external.ipam.config = [{ subnet = "10.10.250.0/24"; }]; + }; +} diff --git a/terraform-nix/modules/containers/docker/default.nix b/terraform-nix/modules/containers/docker/default.nix new file mode 100644 index 0000000..ed3d4a4 --- /dev/null +++ b/terraform-nix/modules/containers/docker/default.nix @@ -0,0 +1,117 @@ +# Documentation: https://nixos.wiki/wiki/Docker + +{ config, lib, pkgs, ... }@args: + +with lib; +let + cfg = config.my.services.docker; + + ports = { + internal.http = 8099; + internal.https = 44399; + }; + user = config.users.users.root; + + composeNix = import ./compose (args // { inherit ports; }); + composeFile = pkgs.runCommand "compose-nix-to-yaml" + { + buildInputs = [ pkgs.remarshal ]; + preferLocalBuild = true; + } '' + remarshal -if json -of yaml \ + < ${ + pkgs.writeText "compose.nix" + (builtins.toJSON composeNix) + } \ + > $out + ''; +in +with lib; +{ + options.my.services.docker = { + enable = mkEnableOption "docker"; + }; + + config = mkIf cfg.enable { + sops = { + defaultSopsFile = ../../../secrets/docker/default.env; + secrets."docker.env" = { + owner = user.name; + path = "${user.home}/.env"; + }; + }; + + networking.enableIPv6 = true; + networking.firewall = + let + allPorts = builtins.foldl' + (acc: curr: acc ++ (curr.ports or [ ])) + [ ] + (builtins.attrValues composeNix.services); + rawUdpPorts = filter (port: hasInfix "udp" (toLower port)) allPorts; + rawTcpPorts = filter (port: ! builtins.elem port rawUdpPorts) allPorts; + sanitizePorts = ports: lib.unique (map + (port: pipe port [ + (port: splitString ":" (builtins.toString port)) + builtins.head + strings.toInt + ]) + ports); + in + { + enable = true; + allowedTCPPorts = sanitizePorts ([ 80 443 ] ++ rawTcpPorts); + allowedUDPPorts = sanitizePorts rawUdpPorts; + }; + + virtualisation.docker.enable = true; + + environment.etc."timezone".text = config.my.timezone; + + system.activationScripts = { + docker-compose-cp.text = toString (pkgs.writers.writeBash "docker-compose-cp" '' + cp -r ${composeFile} ${user.home}/compose.yaml + ''); + docker-compose-up.text = toString (pkgs.writers.writeBash "docker-compose-up" '' + cd ${user.home} && ${pkgs.docker}/bin/docker compose up -d + ''); + }; + + # Services to tunnel ipv6 traffic to ipv4 + systemd.services = + let + make6to4Service = sourcePort: destPort: { + enable = true; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + startLimitIntervalSec = 86400; + startLimitBurst = 5; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.socat}/bin/socat TCP6-LISTEN:${builtins.toString sourcePort},fork TCP4:127.0.0.1:${builtins.toString destPort}"; + Restart = "on-failure"; + User = "socat"; + Group = "socat"; + AmbientCapabilities = "cap_net_bind_service"; + CapabilityBoundingSet = "cap_net_bind_service"; + }; + }; + in + { + "6to4-http" = make6to4Service 80 ports.internal.http; + "6to4-https" = make6to4Service 443 ports.internal.https; + }; + + users.users.socat = { + group = "socat"; + isSystemUser = true; + }; + users.groups.socat = { }; + + # Cron job to clean up dangling images + services.cron = { + enable = true; + systemCronJobs = [ "0 2 * * 0 docker image prune --force --all" ]; + }; + }; +} diff --git a/terraform-nix/modules/default.nix b/terraform-nix/modules/default.nix index 3ac8bb6..51f3319 100644 --- a/terraform-nix/modules/default.nix +++ b/terraform-nix/modules/default.nix @@ -4,6 +4,7 @@ imports = [ ./base.nix ./config + ./containers ./platforms ./secrets ./services diff --git a/terraform-nix/modules/secrets/default.nix b/terraform-nix/modules/secrets/default.nix index 5fb1a63..b730c6c 100644 --- a/terraform-nix/modules/secrets/default.nix +++ b/terraform-nix/modules/secrets/default.nix @@ -1,11 +1,12 @@ # Documentation: https://github.com/Mic92/sops-nix -{ config, lib, options, ... }: +{ lib, ... }: with lib; { sops = { - defaultSopsFile = mkDefault ../../secrets/default.yaml; + defaultSopsFile = mkDefault ../../secrets/default.env; + defaultSopsFormat = "dotenv"; age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; }; } diff --git a/terraform-nix/secrets/default.env b/terraform-nix/secrets/default.env new file mode 100644 index 0000000..c22ecb7 --- /dev/null +++ b/terraform-nix/secrets/default.env @@ -0,0 +1,52 @@ +secret_adguardhome_user=ENC[AES256_GCM,data:VeY6c5ZClj1q,iv:i5Q6OD+jfJjyYTW91kDW1HZ2Pm2dByqzd+UkfMLtff0=,tag:zv5KlVHQHncmIWGixkWK7g==,type:str] +secret_adguardhome_password=ENC[AES256_GCM,data:c2JoE7dyf9vl37nigcjs4QeAPRlNbOd/3hWt3EypJEqvgfewXdoXDKcKscRxEv35WO0Rbuw51iZdYMnZ3qc=,iv:MViJ/s0VK3R9C8ArougSj0sldsOwxtEMtdiP90lU6OU=,tag:+XJcqqZWXAx6BOZsN3ZzRA==,type:str] +secret_bazarr_api_key=ENC[AES256_GCM,data:iyuBnqa7Fgsb/MeiVrve0B3lYK7ttx1TG9PdFAGMZa2SvQ==,iv:rLoT8v87pCEMnMHCktq1TXMMWZRDXDTSRa4VbX1MNc0=,tag:+B22Gzb8jxzL18d5GqYYZg==,type:str] +secret_borg_passphrase_main=ENC[AES256_GCM,data:YdWWn8gUxjCl0NQkeemqC4UU4w==,iv:oDB9B3aaTLNJ3iBA9oLNzCvn7X2p4oBQf9xWXQQ9zhw=,tag:04qazgjcLieA5rfA9lKvFA==,type:str] +secret_borg_passphrase_appdata=ENC[AES256_GCM,data:eTxwvxcFjVybXn2Q8Bzz4cqH0Q==,iv:c96SsmuGxOx7vc04ziiRqJWhf1D23jwFCIM5yjUPnj4=,tag:LMzGeGFdnXwKDn7VmcUjgg==,type:str] +secret_borg_passphrase_personal=ENC[AES256_GCM,data:khN7YIZ2rBh9Ke3OskPs4sYMAH6uWA==,iv:YjbBRD2nAWhSMt6rKrfAK56r1rbxrj5eYa9SFOVYlCw=,tag:myNTj5y216BtbKZBj11L7g==,type:str] +secret_cloudflare_api_token=ENC[AES256_GCM,data:pZSNJv8dU6OO+ZTYbPMnT3tT0ra6IhjZi3cQ7R/D2FUU1lBxKniAhzmk,iv:P7dB/Gd21ttuzq0ZpJlj2DWyY/4qnE1BGOt7bBMo88o=,tag:5v5fSY0XO8vLpPe2dXCJBQ==,type:str] +secret_cloudflare_zone_id=ENC[AES256_GCM,data:KLaeXT3hlIM6BzfUfXJv1qVhVSa0p9K/Z2cDlIltBLOKsw==,iv:txzpxuH11oGrsqPHdE0Wq0DZ5fkda+k5n6SIb0WNiIs=,tag:5oVXC6FLeCH4pg/X6j9kkg==,type:str] +secret_crowdsec_bouncer_caddy_name=ENC[AES256_GCM,data:TT3pnFnKFw==,iv:PxIgd07fmLqPoS3MrbBSk6ifC/f+gk6qqgUhGHGZtqU=,tag:t2BeQ/82eC5EJMdcwp31NA==,type:str] +secret_crowdsec_bouncer_caddy_api_key=ENC[AES256_GCM,data:J0fkM7VswZocmBzqdRlFigeY1VdGQlFy1t6tiGFSv3E5Cg==,iv:Z1EIMgiR/Q6jIxiQgbBz8nL+JtmZgphcIJgSCaYScf4=,tag:Wg23VCNmudqlRYxlsADLfQ==,type:str] +secret_crowdsec_bouncer_name=ENC[AES256_GCM,data:9tdfqBz48YwADaAp/8Blswk=,iv:2rhDh6hw2SYxIm1i7c4izyVTi883Vyn0MGev9z4pNKs=,tag:LprSOTzkKfJ0UH1idBkGdA==,type:str] +secret_crowdsec_bouncer_api_key=ENC[AES256_GCM,data:dNOYQbNCFJQ1TfA3+2vrjYHxM50okqmfW/ga1aajJ+1qkA==,iv:wjiijWxvZRm60mqtLFNdgN6H7tOehAndXfwZkJm4TqE=,tag:IF5Ki+jesgTuG2gOUZaUCg==,type:str] +secret_crowdsec_bouncer_api_key=ENC[AES256_GCM,data:dNOYQbNCFJQ1TfA3+2vrjYHxM50okqmfW/ga1aajJ+1qkA==,iv:wjiijWxvZRm60mqtLFNdgN6H7tOehAndXfwZkJm4TqE=,tag:IF5Ki+jesgTuG2gOUZaUCg==,type:str] +secret_forgejo_db_password=ENC[AES256_GCM,data:HNDQUCHg/xzpVGZ2rf9Dgaj1tYBZyUfIKgv3uYG9WpftMkVwYJbgOx1mPv3xbMc=,iv:upkGHBCHvNZo7n60EpPW6O6bQ35V/r/0ZmKwh4ULahA=,tag:WyQVjhXAN3kpT10HAzU8wQ==,type:str] +secret_healthcheck_io_uuid_snapraid_scrub=ENC[AES256_GCM,data:Trddb1KzJFHjOPr7vDhlTu8XNQJRGKTtWpSdNJNSPjiZxk84otA=,iv:AONSOY1SJqdj8gF4xPvwo8dlXdXdmfcA9QCuo7RTWH8=,tag:jiFddHJnFK7IFew/oZRrvg==,type:str] +secret_healthcheck_io_uuid_snapraid_sync=ENC[AES256_GCM,data:eAswlqLj04YlhvXH3+utj2GtSRBvAEstMqUHEXfCsfVodzfu7zs=,iv:j+3Qx/F5vHWr/B/WEx4yEHp0keFrfIxTKjYbCJLo3f4=,tag:bYK/oWd8ynu5WpIzQPsuUA==,type:str] +secret_healthcheck_io_uuid_borg_appdata=ENC[AES256_GCM,data:TwYfypHNFzxUhQZlAy5mzUXmv+pas9efdwKjWQt8boaZtA08I9A=,iv:iLdfO1sVPIFhcAFfs/mSPUWxm1EroW+O/QfONnA6AFU=,tag:BkQohHFSyN/RaafCopxphA==,type:str] +secret_healthcheck_io_uuid_borg_personal=ENC[AES256_GCM,data:iWVHJBJWUTaEoqZ6EVVOKJR1f4Nw1jsDZKaj+CsnV+kbqMuiu40=,iv:LW0QOQKtzo/IPXWleWowvFU3sL72yaXbiWZOOdF4w5E=,tag:rpmabv1BoWw9pgLYRa0cqA==,type:str] +secret_healthcheck_io_uuid_borg_main=ENC[AES256_GCM,data:Szy/QGs5jrJ9uIiO8+2hktkC0grFiXL5RzeKHIKzQ2XtBX8Z3VU=,iv:1E1Hp7tAbuncDjwl+Vkte+Y41IiPZ4J2Y3J0OoaTxHM=,tag:QsT3rcgopM68bgryfw22Rg==,type:str] +secret_healthcheck_api_key_ro=ENC[AES256_GCM,data:fOuMjRhelg8/mJV8uPiHH/T45aucx04+vjkAehKOImEzvA==,iv:bT9qmAHu0cIuI8ehYFU84WNHPof8+9zvB4Xl0VrTkRQ=,tag:OHEG2bCGhlae/eoxs2IWbQ==,type:str] +secret_hetzner_user=ENC[AES256_GCM,data:/hyhp7M6Uiut,iv:BwThqS0XqZ7ztpcWQBAV497Mv0jf6L95zCuMhx+7b2g=,tag:10dD9jEg8qRBIng5kCiOcw==,type:str] +secret_hetzner_pass=ENC[AES256_GCM,data:UNIB77cI7bT8JBdq4HgPUHTt,iv:p5FuXeMlYnV2sDgr6ez0RAmB98PSBYbBwTRfEtBUXuw=,tag:AFmjv8nQnMMVWvYesQTnHA==,type:str] +secret_homeassistant_user=ENC[AES256_GCM,data:ymnhdISStAES,iv:VzGOt8ApNorUTwRcnP4pKRdMmbraCnwSSKulNOk0VqY=,tag:actIfCGD2OAeJAYonZ/4ig==,type:str] +secret_homeassistant_password=ENC[AES256_GCM,data:ZaPZ982VHBn4MkNN/oWc/oBUgw==,iv:zv8NAqu9uDu1wpvfiLeenpfIRWRZPvIgSSGNo0gDrmw=,tag:xq4WR9f5HhNH8AAwjzldAQ==,type:str] +secret_homeassistant_token=ENC[AES256_GCM,data:BLXcJphvWlsMbIfYY+zhtRLLHlvBtAcyYe0WCiS+1zZcX38pX2ot+ti7qtqyxcxjczhzhaMbZN+JRAHDVOs0ZEeTAkzCTR/birVZ0kMj2U4vHvZZE0haICOqUINyx13yBJC9ZUyx6SKLFXK3nXM1KRWbJxxwueOSXKdzc2LsHJ/oOfNUAv4e0OBxNBpyoZbJ/msxKGh+XrDSQGvxdkluHSvuHt+zIffvDYholEKh83pzyPQSKoiK0XI=,iv:0FNeBFDtvQGx2KiG1KrUDJiy0oXY8qR0wq1LM8JGUxE=,tag:5dtouEfnOqHmqRMvqVwfzA==,type:str] +secret_jellyfin_api_key=ENC[AES256_GCM,data:IxocyPWwNn9cpsuxdwfpZEyUlrdKbVjlbZaIsoQxadnbxw==,iv:4q/7DVagQybBzmuTHRItGWCtsLQgp6JP53bUPLi9A/o=,tag:nZkuZhbpw1iM1VL2wQja2w==,type:str] +secret_jellyseerr_api_key=ENC[AES256_GCM,data:2yU94ymZ1kkfz/CilV1YMa5dQYSVMZkTEfPmQFIDFU/kzdXfh7xENu9NCVl8d1klpmVPd5Ku5f+ILqqJNt3sa2bRF8JcCA==,iv:HGpyh95mng5P6c6gGA5U96wZ7nR/ut2nOPKKqnyTTz0=,tag:alLq1miW8AEn3oA/XoBKAw==,type:str] +secret_mariadb_root_password=ENC[AES256_GCM,data:0ZvZIvPaMyEb03QKGv9OwzbSW/yxNgZLBzGDu3t/UDfPyUK2YnquqCk1o+MRZ3cHDNq6zi7KYSbF2rh2GnvGEisQdROsvTW2yRZenbBK7R+5K9/a3TsQecN9n6SAPqdl3TW9U9ZW9NsSlP6vtcWANscGlhHpCXYjyFvzIg==,iv:SmvEHZiTNbd5iuO2kH+1vEOuW9XOPjzwmB0lUre2zDQ=,tag:1BAZ6R4RzmvY4QGN2ecdSQ==,type:str] +secret_nextcloud_serverinfo_token=ENC[AES256_GCM,data:QuiRti8A6ZcZzjp1mj6eSd9UVwYorDla43/ni25rYQ==,iv:Jjf3J9ZI38BvKMbdVao+gzWPhcA45g44wGN9ScVVOKc=,tag:lMCag+UE/CWmFCdeMj0ZVw==,type:str] +secret_prowlarr_api_key=ENC[AES256_GCM,data:dzQ7v/85m6nEavMJmir7nP1gLYpyjXQacoyWiN45Tln9wg==,iv:EcWjpKn90XfISLSO1Enqdj++L1hpGx2aMSty/9R46i4=,tag:iorVqu8WSt4j4I92fV5NIA==,type:str] +secret_proxmox_user_ro=ENC[AES256_GCM,data:MW/KsGycu+UZofuqM4AkXbKIYq2FaVnPGQucX6S1Hg==,iv:LQLS4zpnMwgC8YafTSQfT3jtAbGmpiFqni9otYCG99Q=,tag:YYtYfu6yFrUpRjuZnQfSiw==,type:str] +secret_proxmox_password_ro=ENC[AES256_GCM,data:nR58dYUD5Mwmk1WSFwIdOtapVhJsLSlxT/lCtToFu/Xkey622Is=,iv:4S0mU8DGhFj7/1j0HgsBawZvYWsUqrWb/VcKI0JH2MQ=,tag:Z1fxZwLmzpkqnRUzgYX5Cw==,type:str] +secret_qbittorrent_user=ENC[AES256_GCM,data:+HfgXFMpYJ03,iv:MBGN2qKkd8FjoRBy6o+rES129BPgTh9e3D8CAHmF2UQ=,tag:hm4gdsCPKCKJ9nATJyYvUg==,type:str] +secret_qbittorrent_password=ENC[AES256_GCM,data:YJGQXhcwWpP0cX/9R1hW2L2B3cxn6Xysk/ucBzaj/Kt3JRMBjulH2EgetlRdTbfyMxQbBEhZ++WvNKNX1yGungdVF3DblK7E3qQBuvOIqI87oMUNdKLECeR+xF277V5MpJMS1oLqaYkvB57v8dJ9Kogr/eXmxNN8lJw=,iv:Aue+jYbxY98/vgOoU1BADLdEeuI4Hr42dkeXK0YXr5M=,tag:Pdo4sIukjzDLltcM5AnF+w==,type:str] +secret_sonarr_api_key=ENC[AES256_GCM,data:mhWGsgrSifZXVz1a2725+PL1EE7kl6PoJvUCaX4j1E4ATA==,iv:zlDZrptmeT21o12g1QL+3NdDrEB7vNVdU5W+NnuSiSc=,tag:RoB1O2uGmmdsDMTKVaxNJA==,type:str] +secret_radarr_api_key=ENC[AES256_GCM,data:+YGvyohPer8t/cTrDMyUIFCzco69oMrd/Vg4MeELoBoIeA==,iv:uFtYFCozTwgQpwd9Lmk40rAQKLzmmc7Cf247nkdFHq4=,tag:8gF8tdE20rxuUp5UAFvuBg==,type:str] +secret_redis_password=ENC[AES256_GCM,data:r865EroFVJdW05VeP0L2GISBRUiq0haolh/Ekefj0iQq2M28BMGYADNSt+Uh4ar+AQVAzQ9GoIjNU1wrGvU/VXMiiRMTZBcE9OzthQfurlJk8SsiZOvxOiRcHHjNay488qFj01qsRNLaAX2FWSGqxQ7q86DR0FIMdSwKoQdnoaloi81n0g==,iv:vqTqSMErMVxsfoSyB6vBQRWWJEMhShTZKXd5HtqG5dw=,tag:XEATQomqh5m8E+Ltm8EQlQ==,type:str] +secret_smtp_user=ENC[AES256_GCM,data:jvaOqX5Th+J1lA==,iv:MhqJCJSTsz9RgS+2AbGhMwUP5cx6I2jH7ID7J+4JQpI=,tag:zZ5rQnQLaOF3j9WtUY77Eg==,type:str] +secret_smtp_password=ENC[AES256_GCM,data:3MxHdTpkKLepG2950ZCdvjH4,iv:EJGWYnm5KumuUHCCFbYHL/ZHu35lwqOLMlfdGC45Y2k=,tag:GQFTe6nBEW8ilVYzqOq8qA==,type:str] +secret_smtp_host=ENC[AES256_GCM,data:/j/8gESJ/Is0YEM8QRwXO3dF,iv:l/6vGBkaiRx5Dy7l8BIoAYQ8YlwrdAhSPToba4yTb4Y=,tag:8fPf/1JGfJXZ9iZ5WZScdw==,type:str] +secret_smtp_port=ENC[AES256_GCM,data:Kf5fzAM=,iv:v0466BM2hdAqCIR+n+uDAoipUgQmeaQXfJU+x7ptUgc=,tag:LUjbdsN5RULhxEIsvvPDiw==,type:str] +secret_vscode_password=ENC[AES256_GCM,data:5LxnJgm9IkjMETkP8nbuNunkubxORhVZj7mEztOgJJWyb13gq+Wd/w==,iv:X6tqdLg0jbxRUDUL8vykaTNa3kaxCIBvhvsZUHg6gkA=,tag:Kt+2k0RcsC+mhUwKjFjsQg==,type:str] +secret_traefik_password=ENC[AES256_GCM,data:N3QqEg46UinGJTjZzXNR+6R9kFrMO//GgKcnkXfFXKR0WxNjbFGESjSQmbfY50NgN23YG+p1vL9D1OpwDrsa5CTxYX1fDnQFEqxY6vhYn19/imu88pMTyz1/ZaIgbSDteIbnox+tyL4X47pfbF00QBHgvFrNvFAn2NU=,iv:Bs0aTVkhVq7ly+CZhIIQeeYaV3DYoJum8ooV2mHkK58=,tag:AIMONfuyyv4zztVPO5VaWw==,type:str] +secret_traefik_password_hashed=ENC[AES256_GCM,data:JMlpqKAehrQowOXzs7BkP2Qk+78phFqr2j+12qbfSIDq/8l+1hxnYEQm4MvQ,iv:mt1jaArv229385vXAJJOuvvl6llM/sUKWiuq8XqJAto=,tag:Dyxyv2Y5yNgqA8wDjcly+A==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUlpEUHdDQjdFY2xzNi9O\nS3lPdXRkVnhtcGR0YkdDaThLOEFWb1hIQ0hjClRvTkZyT3R3d2xkQ1JNZDQ0WEV5\nUkUrSmV2RU5ZL1BSR2R3L25iYStiTTQKLS0tIExLT2ZrWm5LZmR4MnQ5R3h0S21U\nUlZGUkpOdUU2ZzQ1M0cxUURFK3VCUWcKUu0uUJW5gRa+iDMCqpdePvrlugw19foK\nzFi5yXTTevUOZwWd6BL93JKLXcUFJG+DRxvccLw2vW8oR9DBa38muA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRHd3WDlLL2VMTms4RUZp\nbUVsQzBPc3p0ODF1WENJR01wL3QvOVgxTWhBClJQU0ZpajJEcU1SMllKQVMycVhj\nM2ZFY3QyWk8rSnFBcndsOUZMTmhabk0KLS0tIG1MU3dBZVhpZkpvNWNiQWFBSExP\ncVArQi85VGxKVUZUcUxZL3dOeXdaaEkK47ArdnYhWhbAV/3CBdF4fzmRO9hQS6Bd\n4DaS/yk//oG+Aiy3mKgdUrn1mztj41mLVPf2Y/Ay6T2iiwHWCVy+vA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 +sops_lastmodified=2024-01-08T00:46:48Z +sops_mac=ENC[AES256_GCM,data:56aPg1EeY6AZ3Bh0f1R+IX8FLVc193m3ErCDHeo1MPLKzlZJ3AQcX8SGXQwn55MOPhhfMI1sSJ8FwRh3P3YoztMotwlRgLVqd3XG2rSqPBy/ECO4ieCAN4y6/cs+DIsjNyD1urVqYqdZeppNMw2/X8gVHKr8ToXBHb8DLMnbeVw=,iv:OdZFSnsUbVBbX8nKEBj56ZOcTi+b44YlZBvPJC0AVHA=,tag:GCvPz0ANoKOH3QYJjCiq8g==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.8.1 diff --git a/terraform-nix/secrets/default.yaml b/terraform-nix/secrets/default.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/terraform-nix/secrets/docker/default.env b/terraform-nix/secrets/docker/default.env new file mode 100644 index 0000000..91d97cd --- /dev/null +++ b/terraform-nix/secrets/docker/default.env @@ -0,0 +1,24 @@ +secret_adguardhome_user=ENC[AES256_GCM,data:VeY6c5ZClj1q,iv:i5Q6OD+jfJjyYTW91kDW1HZ2Pm2dByqzd+UkfMLtff0=,tag:zv5KlVHQHncmIWGixkWK7g==,type:str] +secret_adguardhome_password=ENC[AES256_GCM,data:c2JoE7dyf9vl37nigcjs4QeAPRlNbOd/3hWt3EypJEqvgfewXdoXDKcKscRxEv35WO0Rbuw51iZdYMnZ3qc=,iv:MViJ/s0VK3R9C8ArougSj0sldsOwxtEMtdiP90lU6OU=,tag:+XJcqqZWXAx6BOZsN3ZzRA==,type:str] +secret_bazarr_api_key=ENC[AES256_GCM,data:iyuBnqa7Fgsb/MeiVrve0B3lYK7ttx1TG9PdFAGMZa2SvQ==,iv:rLoT8v87pCEMnMHCktq1TXMMWZRDXDTSRa4VbX1MNc0=,tag:+B22Gzb8jxzL18d5GqYYZg==,type:str] +secret_cloudflare_api_token=ENC[AES256_GCM,data:pZSNJv8dU6OO+ZTYbPMnT3tT0ra6IhjZi3cQ7R/D2FUU1lBxKniAhzmk,iv:P7dB/Gd21ttuzq0ZpJlj2DWyY/4qnE1BGOt7bBMo88o=,tag:5v5fSY0XO8vLpPe2dXCJBQ==,type:str] +secret_forgejo_db_password=ENC[AES256_GCM,data:HNDQUCHg/xzpVGZ2rf9Dgaj1tYBZyUfIKgv3uYG9WpftMkVwYJbgOx1mPv3xbMc=,iv:upkGHBCHvNZo7n60EpPW6O6bQ35V/r/0ZmKwh4ULahA=,tag:WyQVjhXAN3kpT10HAzU8wQ==,type:str] +secret_jellyseerr_api_key=ENC[AES256_GCM,data:2yU94ymZ1kkfz/CilV1YMa5dQYSVMZkTEfPmQFIDFU/kzdXfh7xENu9NCVl8d1klpmVPd5Ku5f+ILqqJNt3sa2bRF8JcCA==,iv:HGpyh95mng5P6c6gGA5U96wZ7nR/ut2nOPKKqnyTTz0=,tag:alLq1miW8AEn3oA/XoBKAw==,type:str] +secret_prowlarr_api_key=ENC[AES256_GCM,data:dzQ7v/85m6nEavMJmir7nP1gLYpyjXQacoyWiN45Tln9wg==,iv:EcWjpKn90XfISLSO1Enqdj++L1hpGx2aMSty/9R46i4=,tag:iorVqu8WSt4j4I92fV5NIA==,type:str] +secret_qbittorrent_user=ENC[AES256_GCM,data:+HfgXFMpYJ03,iv:MBGN2qKkd8FjoRBy6o+rES129BPgTh9e3D8CAHmF2UQ=,tag:hm4gdsCPKCKJ9nATJyYvUg==,type:str] +secret_qbittorrent_password=ENC[AES256_GCM,data:YJGQXhcwWpP0cX/9R1hW2L2B3cxn6Xysk/ucBzaj/Kt3JRMBjulH2EgetlRdTbfyMxQbBEhZ++WvNKNX1yGungdVF3DblK7E3qQBuvOIqI87oMUNdKLECeR+xF277V5MpJMS1oLqaYkvB57v8dJ9Kogr/eXmxNN8lJw=,iv:Aue+jYbxY98/vgOoU1BADLdEeuI4Hr42dkeXK0YXr5M=,tag:Pdo4sIukjzDLltcM5AnF+w==,type:str] +secret_radarr_api_key=ENC[AES256_GCM,data:+YGvyohPer8t/cTrDMyUIFCzco69oMrd/Vg4MeELoBoIeA==,iv:uFtYFCozTwgQpwd9Lmk40rAQKLzmmc7Cf247nkdFHq4=,tag:8gF8tdE20rxuUp5UAFvuBg==,type:str] +secret_smtp_user=ENC[AES256_GCM,data:jvaOqX5Th+J1lA==,iv:MhqJCJSTsz9RgS+2AbGhMwUP5cx6I2jH7ID7J+4JQpI=,tag:zZ5rQnQLaOF3j9WtUY77Eg==,type:str] +secret_smtp_password=ENC[AES256_GCM,data:3MxHdTpkKLepG2950ZCdvjH4,iv:EJGWYnm5KumuUHCCFbYHL/ZHu35lwqOLMlfdGC45Y2k=,tag:GQFTe6nBEW8ilVYzqOq8qA==,type:str] +secret_smtp_host=ENC[AES256_GCM,data:/j/8gESJ/Is0YEM8QRwXO3dF,iv:l/6vGBkaiRx5Dy7l8BIoAYQ8YlwrdAhSPToba4yTb4Y=,tag:8fPf/1JGfJXZ9iZ5WZScdw==,type:str] +secret_smtp_port=ENC[AES256_GCM,data:Kf5fzAM=,iv:v0466BM2hdAqCIR+n+uDAoipUgQmeaQXfJU+x7ptUgc=,tag:LUjbdsN5RULhxEIsvvPDiw==,type:str] +secret_sonarr_api_key=ENC[AES256_GCM,data:mhWGsgrSifZXVz1a2725+PL1EE7kl6PoJvUCaX4j1E4ATA==,iv:zlDZrptmeT21o12g1QL+3NdDrEB7vNVdU5W+NnuSiSc=,tag:RoB1O2uGmmdsDMTKVaxNJA==,type:str] +secret_vscode_password=ENC[AES256_GCM,data:5LxnJgm9IkjMETkP8nbuNunkubxORhVZj7mEztOgJJWyb13gq+Wd/w==,iv:X6tqdLg0jbxRUDUL8vykaTNa3kaxCIBvhvsZUHg6gkA=,tag:Kt+2k0RcsC+mhUwKjFjsQg==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUlpEUHdDQjdFY2xzNi9O\nS3lPdXRkVnhtcGR0YkdDaThLOEFWb1hIQ0hjClRvTkZyT3R3d2xkQ1JNZDQ0WEV5\nUkUrSmV2RU5ZL1BSR2R3L25iYStiTTQKLS0tIExLT2ZrWm5LZmR4MnQ5R3h0S21U\nUlZGUkpOdUU2ZzQ1M0cxUURFK3VCUWcKUu0uUJW5gRa+iDMCqpdePvrlugw19foK\nzFi5yXTTevUOZwWd6BL93JKLXcUFJG+DRxvccLw2vW8oR9DBa38muA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRHd3WDlLL2VMTms4RUZp\nbUVsQzBPc3p0ODF1WENJR01wL3QvOVgxTWhBClJQU0ZpajJEcU1SMllKQVMycVhj\nM2ZFY3QyWk8rSnFBcndsOUZMTmhabk0KLS0tIG1MU3dBZVhpZkpvNWNiQWFBSExP\ncVArQi85VGxKVUZUcUxZL3dOeXdaaEkK47ArdnYhWhbAV/3CBdF4fzmRO9hQS6Bd\n4DaS/yk//oG+Aiy3mKgdUrn1mztj41mLVPf2Y/Ay6T2iiwHWCVy+vA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 +sops_lastmodified=2024-01-08T13:46:42Z +sops_mac=ENC[AES256_GCM,data:+tKedM8vTVUuzyAXKJIXLB12gDyni4argL5MwTr1ATVpzEXCgpPblMJ6xwVnjGoQJ98jlyuhnuet5MpKamOuUtTmj9AzQPue/F2+aN9/+J+vFqeZv6UpQOnUnkbZhvgo1DLiPMqaNYcEjv4BifiZhgANo87oT0GNjXv5MTLdzQA=,iv:Kj8AajEtFGCytvn24zwu0353TC8rE7SX2h6hNkccVlw=,tag:OBaXOsetDhlC9KQTTaPahQ==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.8.1 diff --git a/terraform-nix/variables.tf b/terraform-nix/variables.tf index 605313d..6043e7d 100644 --- a/terraform-nix/variables.tf +++ b/terraform-nix/variables.tf @@ -47,7 +47,7 @@ variable "lxcs" { name = string ip = string enable = optional(bool, true) - remotebuild = optional(bool, true) + remotebuild = optional(bool, false) onboot = optional(bool, true) rootfs_size = optional(string, "8G") privileged = optional(bool, false) From dd9e7389d8322613b5db7b0630b35e8243efa8fb Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Mon, 8 Jan 2024 11:11:29 -0300 Subject: [PATCH 31/43] chore(terraform-nix): Change default LSP --- terraform-nix/.vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform-nix/.vscode/settings.json b/terraform-nix/.vscode/settings.json index 7a7d94d..f63f23c 100644 --- a/terraform-nix/.vscode/settings.json +++ b/terraform-nix/.vscode/settings.json @@ -1,6 +1,6 @@ { "nix.enableLanguageServer": true, - "nix.serverPath": "rnix-lsp", + "nix.serverPath": "nil", "nix.formatterPath": "nixpkgs-fmt", "[nix]": { "editor.insertSpaces": true, From 2cdd48d3124976a6eb181c0f486deffba23895d8 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 9 Jan 2024 16:08:13 -0300 Subject: [PATCH 32/43] fix(terraform-nix): Have NixOS manage IPs by default For whatever reason the LXCs would sometimes refuse to maintain a connection even though everything looked right. Making it so NixOS manages fixed IPs by default --- terraform-nix/lxcs/default.nix | 40 +++++++++++++------ terraform-nix/modules/config/default.nix | 2 +- terraform-nix/modules/config/networking.nix | 8 ++-- terraform-nix/modules/config/storage.nix | 2 + .../modules/platforms/proxmox-lxc/default.nix | 2 +- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index e4055e3..4fad11a 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -1,25 +1,44 @@ -{ lib, ... }: +{ lib, ... }@args: with lib; let config.my = { - networking = import ../modules/config/networking.nix; - storage = import ../modules/config/storage.nix; + networking = import ../modules/config/networking.nix args; + storage = import ../modules/config/storage.nix args; }; - makeDefaultModules = name: [{ config.my.services.${name}.enable = true; }]; + makeDefault = lxc: lxc // { nix.modules = (lxc.nix.modules or []) ++ [ + (enableServiceByName lxc.name) + (manageNetwork lxc.ip) + ]; }; + enableServiceByName = name: { config.my.services.${name}.enable = true; }; + manageNetwork = ip: { + config.proxmoxLXC.manageNetwork = true; + config.networking = { + interfaces = { + eth0.ipv4.addresses = [{ + address = ip; + prefixLength = config.my.networking.ipv4.prefixLength; + }]; + }; + defaultGateway = { + address = config.my.networking.ipv4.gateway; + interface = "eth0"; + }; + nameservers = [ "10.0.0.2" ]; + }; + }; byId = { - "241" = { + "241" = makeDefault { name = "caddy"; enable = false; ip = "10.10.2.41"; tags = [ "networking" ]; cores = 2; memory = 256; - nix.modules = makeDefaultModules "caddy"; }; - "300" = { + "300" = makeDefault { name = "traefik"; enable = false; ip = "10.10.3.0"; @@ -30,9 +49,8 @@ let { mp = "/etc/crowdsec"; volume = "/srv/storage/AppData/config/crowdsec"; } { mp = "/var/lib/crowdsec"; volume = "/srv/storage/AppData/data/crowdsec"; } ]; - nix.modules = makeDefaultModules "traefik"; }; - "810" = rec { + "810" = makeDefault rec { name = "jellyfin"; privileged = true; ip = "10.10.8.10"; @@ -59,9 +77,8 @@ let { mp = "/var/lib/jellyfin/root"; volume = "${cfgPath}/data/root"; } { mp = "/var/cache/jellyfin"; volume = "${cfgPath}/cache"; } ]; - nix.modules = makeDefaultModules "jellyfin"; }; - "1000" = { + "1000" = makeDefault { name = "docker"; privileged = true; ip = "10.10.10.0"; @@ -72,7 +89,6 @@ let mountpoints = [ { mp = "/srv/storage"; volume = "/srv/storage"; } ]; - nix.modules = makeDefaultModules "docker"; }; }; in diff --git a/terraform-nix/modules/config/default.nix b/terraform-nix/modules/config/default.nix index 33d9e2e..a547240 100644 --- a/terraform-nix/modules/config/default.nix +++ b/terraform-nix/modules/config/default.nix @@ -10,7 +10,7 @@ in email = mkReadonlyOption "lpchaim@gmail.com"; lxcs = mkReadonlyOption (import ../../lxcs args); networking = mkReadonlyOption (import ./networking.nix args); - storage = mkReadonlyOption (import ./storage.nix); + storage = mkReadonlyOption (import ./storage.nix args); timezone = mkReadonlyOption "America/Sao_Paulo"; }; } diff --git a/terraform-nix/modules/config/networking.nix b/terraform-nix/modules/config/networking.nix index 18ed054..34323f3 100644 --- a/terraform-nix/modules/config/networking.nix +++ b/terraform-nix/modules/config/networking.nix @@ -1,8 +1,10 @@ -{ lib ? pkgs.lib, pkgs, ... }: +{ lib ? pkgs.lib, pkgs ? import , ... }: { - defaultGateway = "10.0.0.1"; - defaultPrefixLength = "8"; + ipv4 = { + gateway = "10.0.0.1"; + prefixLength = 8; + }; cidrs = rec { trusted = private ++ cloudflare; private = [ "10.0.0.0/8" "fe80::/10" ]; diff --git a/terraform-nix/modules/config/storage.nix b/terraform-nix/modules/config/storage.nix index f6aafad..93bdd7b 100644 --- a/terraform-nix/modules/config/storage.nix +++ b/terraform-nix/modules/config/storage.nix @@ -1,3 +1,5 @@ +_: + rec { main = "/srv/storage"; appData = "${main}/AppData"; diff --git a/terraform-nix/modules/platforms/proxmox-lxc/default.nix b/terraform-nix/modules/platforms/proxmox-lxc/default.nix index c3526e5..8a4e6ad 100644 --- a/terraform-nix/modules/platforms/proxmox-lxc/default.nix +++ b/terraform-nix/modules/platforms/proxmox-lxc/default.nix @@ -17,7 +17,7 @@ in { }; proxmoxLXC = { - manageNetwork = false; + manageNetwork = mkDefault false; manageHostName = false; }; }; From 1170ca454724b90a82856ab3b9cc2ca76615630c Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 9 Jan 2024 17:25:55 -0300 Subject: [PATCH 33/43] fix(terraform-nix): Misc service fixes --- terraform-nix/modules/default.nix | 2 -- terraform-nix/modules/services/caddy.nix | 2 +- terraform-nix/modules/services/traefik/default.nix | 5 ++--- terraform-nix/secrets/traefik/default.env | 11 +++++++++++ terraform-nix/secrets/traefik/traefik.env | 11 ----------- 5 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 terraform-nix/secrets/traefik/default.env delete mode 100644 terraform-nix/secrets/traefik/traefik.env diff --git a/terraform-nix/modules/default.nix b/terraform-nix/modules/default.nix index 51f3319..9838036 100644 --- a/terraform-nix/modules/default.nix +++ b/terraform-nix/modules/default.nix @@ -1,5 +1,3 @@ -{ pkgs, ... }: - { imports = [ ./base.nix diff --git a/terraform-nix/modules/services/caddy.nix b/terraform-nix/modules/services/caddy.nix index 864c0ab..7fb3d78 100644 --- a/terraform-nix/modules/services/caddy.nix +++ b/terraform-nix/modules/services/caddy.nix @@ -1,6 +1,6 @@ # Documentation: https://nixos.wiki/wiki/Caddy -{ config, lib, options, ... }: +{ config, lib, ... }: let cfg = config.my.services.caddy; diff --git a/terraform-nix/modules/services/traefik/default.nix b/terraform-nix/modules/services/traefik/default.nix index 19e0138..27c6925 100644 --- a/terraform-nix/modules/services/traefik/default.nix +++ b/terraform-nix/modules/services/traefik/default.nix @@ -1,6 +1,6 @@ # Documentation: https://nixos.wiki/wiki/Traefik -{ config, inputs, lib, options, pkgs, system, ... }: +{ config, lib, pkgs, ... }: with lib; let @@ -22,8 +22,7 @@ with lib; config = mkIf cfg.enable { sops = { - defaultSopsFile = ../../../secrets/traefik/traefik.env; - defaultSopsFormat = "dotenv"; + defaultSopsFile = ../../../secrets/traefik/default.env; secrets."traefik.env" = { }; }; diff --git a/terraform-nix/secrets/traefik/default.env b/terraform-nix/secrets/traefik/default.env new file mode 100644 index 0000000..2647a11 --- /dev/null +++ b/terraform-nix/secrets/traefik/default.env @@ -0,0 +1,11 @@ +CF_DNS_API_TOKEN=ENC[AES256_GCM,data:XCn00CYFfVf9VO6yhApbAiZFrAOHh/IgqF2hF6gdwxTCfrU38rebBcAk,iv:CesK9FpEwiDjNQEKO5SIU1rTINxkn8UJ+HbvDhbG12Y=,tag:JZkwPzmFOtCHYrcfG17dgQ==,type:str] +TRAEFIK_PASSWORD_HASHED=ENC[AES256_GCM,data:57l0b2SzNTE+kn7QAH2dBbLCcA/5Lq8UiRl2YEMzdJjtUQSQZHYw3k0ulMgK,iv:fis3Ec+jH7/uQmQsn1ffhO318/5LdzMWknY1J4DtCbE=,tag:ZYngRrp+1QlDkTCqCeSUKg==,type:str] +CROWDSEC_BOUNCER_API_KEY=ENC[AES256_GCM,data:oWbAyBUORGCrBmOAj1XS43rAW1KxvZ0CixFMarFpjzmvlg==,iv:keQqROb1QOPrNUNHAjh++Ia3qcZHra2w5wTK2V4I0l0=,tag:9eQtF4frX1iOxPCP6Fomeg==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAza3M2NDNUVThLYmFsRmpG\ncExCZHJyV2xEK2FRUHI2WXhvOUNlL0lqUDBjCnEzQVdKYnBob25RTWY4aC9vUWlJ\nYzR1dCtGUmlYbndvcldCWTJBNnV3bFUKLS0tIE5ZWm9oR1hXU3VDeHNkYytya3Vs\nb0t2NkNWRFB3MWs0bzlpd3RkeXFBNVkK30+2IaKOb7L9YCF6WXzdyp/5H0+SMRo4\n2rzMMesm4tLsUXpZ2qS/FgJ5LK60odjzPHt1Nd9FhGtPPCy51s13/A==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmVVFuY0l5c2lBWkw2aURv\na3lZc0FMSzZwTndRYXBLV1pBQkthRkJiUGlFClZsOHJiSDlaTGYwaWNUbDFxTkp4\nRjJzbGtlampxTGZ3NnEyNDhhS0JtTmsKLS0tIGVWRjc3eTlVU1VnLy9UL013azNJ\ndkpxaHRvWXFKeG9BVDFRS0xNbjlSaWsKRj7H6EbcV+GLBKJZ8mBIJXsxK1cz6aQf\nv85jDLatYEtxtprcg7thdXfojV7aRQEg8G0Rgc0+AzHg1eakhvkkGg==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1k53pxzjtln8ds72ys5crlqz48q3flr8kawjhfmu34w4306mahscqgdamrx +sops_lastmodified=2024-01-09T19:54:19Z +sops_mac=ENC[AES256_GCM,data:fuATbQoHhDIzUNo4AQLH7FSI2WYtDNNlVxbZeDAznr2izYZ+wdRWxTpycXbWZ5p+Mey8kWOXEn8SsMyYFSwkuJTIRQK+MwgxXX1qrLxjqZ4z3zrIPLhflt8PzTS43pIFgJpY+XPO9WkB7YtzPgPH+SHQzTxbMI+cOvpT2bFgzP4=,iv:7DLD/QfAb3X6kv7SV1ln44R89KDZaBB0zuqZR2i61WE=,tag:fnzVt6Ytefz/t2XjYdDEvw==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.8.1 diff --git a/terraform-nix/secrets/traefik/traefik.env b/terraform-nix/secrets/traefik/traefik.env deleted file mode 100644 index bea9125..0000000 --- a/terraform-nix/secrets/traefik/traefik.env +++ /dev/null @@ -1,11 +0,0 @@ -CF_DNS_API_TOKEN=ENC[AES256_GCM,data:4iuMYMJUyfKG0Ty0CIr2EgNliPbcIXm7hY7JFD5iSsavWuxGkTjBIZkN,iv:5QhM2LbnNT9yy7I6+oWxQMk0GeCT1fQBCCkfrdzT1UM=,tag:CnHV138H+37rfsS07niCBA==,type:str] -TRAEFIK_PASSWORD_HASHED=ENC[AES256_GCM,data:sFaOgg9U5eRYMKRNGiYGf7gUMxDhw31zc7e8EqvyrMNfacUgzll/XyVGal/8,iv:+mwtWw1CLeoTXd7j8f8BNAsJg7/5enEBsN21N1kD0rE=,tag:j8bya01Huye30vL9BNrFSQ==,type:str] -CROWDSEC_BOUNCER_API_KEY=ENC[AES256_GCM,data:oBAdDjQJ38aOcxIWtKAwtBIxgBToyHt5jGAkgj7D2mUwzw==,iv:/anjeCiChvkAhtcjHhLAD7MFsn6tDXKhjuYCXwpEmi0=,tag:Y5g9h538/BBFj++fmZbFzw==,type:str] -sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWGl2QzFQekVqOFVIdzhl\nT0lSaUZHckJyTC9tc1VFNEdxRERQZnkvQ1M4CjJQbDFkZVo0aStJYk4vYVNzQzNK\nNUVLT25NRFR1Q2taWmdUMzAxNlU4dFEKLS0tIE5FRGlDYkdmcTNqWU1SY1pDRFRu\naFdoYnJBSmkyWGp1cUhnalFCbFp3cDgKgzxTVC5WdCkpCbTAEZpHBxBuV1NfC3pW\n8VZNaA8EPBtRjMSwqqTjix7fli0z0h3F9SPIcVay4/RzM1McxeH81g==\n-----END AGE ENCRYPTED FILE-----\n -sops_age__list_0__map_recipient=age1dsuede07h7akeaucp53uyxu6wa5y3yapxy3c0nh34vcdhxnanacqr6q3tc -sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwV3Z1UWp1bnZ1c01BWGxt\nejVnd0hIVWRPZXFydDgyaldaRWJ6dHozRWprCjIrbVpVTkQvNTVBSXJDbkcvK0ts\nZlp5ZUVGM1plc0ZadGg3UkJraStZUjgKLS0tIExGUldBSy9JMWFYSThDR1FrUmJk\nS3U2UkNtbEdaTjBaMVN5cXgwem95b3cK4vcAnlT0L/wQ05S5NeX15ckrgtt6+X3T\nSDsqhoCz1inU+uMnAk/PKY02A/BP4nc0ObsWvwFWcHBbjQKl/FRQOw==\n-----END AGE ENCRYPTED FILE-----\n -sops_age__list_1__map_recipient=age1k53pxzjtln8ds72ys5crlqz48q3flr8kawjhfmu34w4306mahscqgdamrx -sops_lastmodified=2024-01-06T15:25:39Z -sops_mac=ENC[AES256_GCM,data:30bnM4XgerD09gT8BkbADWdQOJnUu3lGoxZfCefKZh+km2i2oWEyUknsXlrikMUHuNbaQ99VSVz+6YD7QBoua68yNFwsqN/ZdwLxTwyNROWCj3klp5viP5tCGPy6WnNP/9li2Pmr2/sBcy2eUWhXhFmISey8fEjLszPqN9PApWc=,iv:KmmzB6IiRLCeqWyS2xWUWTdww9Gl3tcCEhTr7hMIHKY=,tag:5cTgx2slFs/jclBgC+7EWQ==,type:str] -sops_unencrypted_suffix=_unencrypted -sops_version=3.8.1 From 306b5c3617ede39ec2192027a8d814194d9b57cb Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 9 Jan 2024 17:27:10 -0300 Subject: [PATCH 34/43] fix(terraform-nix): Docker fixes --- .../containers/docker/compose/default.nix | 38 +++++++++++-------- .../modules/containers/docker/default.nix | 21 ++++++---- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/terraform-nix/modules/containers/docker/compose/default.nix b/terraform-nix/modules/containers/docker/compose/default.nix index ba3a7be..1c2959d 100644 --- a/terraform-nix/modules/containers/docker/compose/default.nix +++ b/terraform-nix/modules/containers/docker/compose/default.nix @@ -22,7 +22,7 @@ in services = { cloudflare-ddns = makeDefault { - image = "oznu/cloudflare-ddns"; + image = "oznu/cloudflare-ddns:latest"; environment = { API_KEY = "\${secret_cloudflare_api_token}"; ZONE = config.my.domain; @@ -34,7 +34,7 @@ in }; crowdsec = makeDefault { - image = "crowdsecurity/crowdsec"; + image = "crowdsecurity/crowdsec:latest"; networks = [ "default" "external" ]; volumes = [ "${config.my.storage.getConfigPath "crowdsec"}:/etc/crowdsec" @@ -44,7 +44,7 @@ in }; themepark = makeDefault { - image = "ghcr.io/themepark-dev/theme.park"; + image = "ghcr.io/themepark-dev/theme.park:latest"; ports = [ "8084:80" "8444:443" ]; volumes = [ "${config.my.storage.getConfigPath "themepark"}:/config" ]; labels = { @@ -57,7 +57,7 @@ in }; traefik = makeDefault { - image = "traefik:v2.9"; + image = "traefik:v2.10"; environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; networks = [ "default" "external" ]; ports = [ @@ -70,10 +70,13 @@ in "${config.my.storage.getConfigPath "traefik"}/acme:/etc/traefik/acme" "/var/run/docker.sock:/var/run/docker.sock" ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + }; }; audiobookshelf = makeDefault { - image = "ghcr.io/advplyr/audiobookshelf:2.7.0"; + image = "ghcr.io/advplyr/audiobookshelf:latest"; ports = [ "13378:80" ]; volumes = [ "${config.my.storage.getConfigPath "audiobookshelf"}:/config" @@ -148,7 +151,7 @@ in }; lidarr = makeDefault { - image = "lscr.io/linuxserver/lidarr:2.0.7.3849-ls148"; + image = "lscr.io/linuxserver/lidarr:latest"; ports = [ "8686:8686" ]; volumes = [ "${config.my.storage.getConfigPath "lidarr"}:/config" "${config.my.storage.main}:/storage" ]; @@ -216,7 +219,7 @@ in readarr = makeDefault { profiles = [ "disable" ]; - image = "lscr.io/linuxserver/readarr:nightly-0.3.14.2348-ls257"; + image = "lscr.io/linuxserver/readarr:latest"; ports = [ "8787:8787" ]; volumes = [ "${config.my.storage.getConfigPath "readarr"}:/config" "${config.my.storage.main}:/storage" ]; @@ -331,13 +334,13 @@ in }; homepage = makeDefault { - image = "ghcr.io/gethomepage/homepage:v0.8.0"; + image = "ghcr.io/gethomepage/homepage:latest"; networks = [ "default" "external" ]; ports = [ "3000:3000" ]; volumes = [ "${config.my.storage.getConfigPath "homepage"}:/app/config" - "/var/run/docker.sock:/var/run/docker.sock" "${config.my.storage.main}:/storage:ro" + "/var/run/docker.sock:/var/run/docker.sock" ]; labels = { "traefik.enable" = "true"; @@ -372,6 +375,7 @@ in ports = [ "3001:3000" "222:22" ]; volumes = [ "${config.my.storage.getDataPath "forgejo-server"}:/data" ]; labels = { + "com.centurylinklabs.watchtower.enable" = "false"; "homepage.group" = "Other"; "homepage.name" = "Forgejo"; "homepage.icon" = "forgejo.png"; @@ -394,8 +398,10 @@ in POSTGRES_DB = "forgejo"; }; networks = [ "default" ]; - volumes = - [ "${config.my.storage.getDataPath "forgejo-postgres"}:/var/lib/postgresql/data" ]; + volumes = [ "${config.my.storage.getDataPath "forgejo-postgres"}:/var/lib/postgresql/data" ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + }; }; influxdb = makeDefault { @@ -431,9 +437,9 @@ in image = "lscr.io/linuxserver/qbittorrent:latest"; environment = { WEBUI_PORT = "8081"; - DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:qbittorrent"; - TP_DOMAIN = "themepark.${config.my.domain}"; - TP_THEME = "frappe"; + # DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:qbittorrent"; + # TP_DOMAIN = "themepark.${config.my.domain}"; + # TP_THEME = "frappe"; }; networks = [ "default" "external" ]; ports = [ "8081:8081" "6881:6881" "6881:6881/udp" ]; @@ -517,7 +523,7 @@ in }; watchtower = makeDefault { - image = "containrrr/watchtower"; + image = "containrrr/watchtower:latest"; networks = [ "default" "external" ]; environment = { WATCHTOWER_SCHEDULE = "0 2 * * *"; @@ -528,7 +534,7 @@ in }; yacht = makeDefault { - image = "selfhostedpro/yacht"; + image = "selfhostedpro/yacht:latest"; networks = [ "default" "external" ]; ports = [ "8000:8000" ]; volumes = [ diff --git a/terraform-nix/modules/containers/docker/default.nix b/terraform-nix/modules/containers/docker/default.nix index ed3d4a4..006c223 100644 --- a/terraform-nix/modules/containers/docker/default.nix +++ b/terraform-nix/modules/containers/docker/default.nix @@ -64,14 +64,22 @@ with lib; allowedUDPPorts = sanitizePorts rawUdpPorts; }; - virtualisation.docker.enable = true; - - environment.etc."timezone".text = config.my.timezone; + virtualisation.docker = { + enable = true; + autoPrune = { + enable = true; + flags = [ "--all" "--force" ]; + dates = "weekly"; + }; + }; system.activationScripts = { docker-compose-cp.text = toString (pkgs.writers.writeBash "docker-compose-cp" '' cp -r ${composeFile} ${user.home}/compose.yaml ''); + docker-socket-perms.text = toString (pkgs.writers.writeBash "docker-socket-perms" '' + chmod o+rw /var/run/docker.sock + ''); docker-compose-up.text = toString (pkgs.writers.writeBash "docker-compose-up" '' cd ${user.home} && ${pkgs.docker}/bin/docker compose up -d ''); @@ -108,10 +116,7 @@ with lib; }; users.groups.socat = { }; - # Cron job to clean up dangling images - services.cron = { - enable = true; - systemCronJobs = [ "0 2 * * 0 docker image prune --force --all" ]; - }; + # Misc adjustments + environment.etc."timezone".text = config.my.timezone; }; } From 6edc5f72a35435e53d9d728f5fe9b1ab37802dd0 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 9 Jan 2024 17:32:38 -0300 Subject: [PATCH 35/43] refactor(terraform-nix): Move docker to backend dir --- terraform-nix/modules/containers/backends/default.nix | 5 +++++ .../containers/{ => backends}/docker/compose/default.nix | 0 .../modules/containers/{ => backends}/docker/default.nix | 2 +- terraform-nix/modules/containers/default.nix | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 terraform-nix/modules/containers/backends/default.nix rename terraform-nix/modules/containers/{ => backends}/docker/compose/default.nix (100%) rename terraform-nix/modules/containers/{ => backends}/docker/default.nix (98%) diff --git a/terraform-nix/modules/containers/backends/default.nix b/terraform-nix/modules/containers/backends/default.nix new file mode 100644 index 0000000..092ab95 --- /dev/null +++ b/terraform-nix/modules/containers/backends/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./docker + ]; +} diff --git a/terraform-nix/modules/containers/docker/compose/default.nix b/terraform-nix/modules/containers/backends/docker/compose/default.nix similarity index 100% rename from terraform-nix/modules/containers/docker/compose/default.nix rename to terraform-nix/modules/containers/backends/docker/compose/default.nix diff --git a/terraform-nix/modules/containers/docker/default.nix b/terraform-nix/modules/containers/backends/docker/default.nix similarity index 98% rename from terraform-nix/modules/containers/docker/default.nix rename to terraform-nix/modules/containers/backends/docker/default.nix index 006c223..edb538f 100644 --- a/terraform-nix/modules/containers/docker/default.nix +++ b/terraform-nix/modules/containers/backends/docker/default.nix @@ -34,7 +34,7 @@ with lib; config = mkIf cfg.enable { sops = { - defaultSopsFile = ../../../secrets/docker/default.env; + defaultSopsFile = ../../../../secrets/docker/default.env; secrets."docker.env" = { owner = user.name; path = "${user.home}/.env"; diff --git a/terraform-nix/modules/containers/default.nix b/terraform-nix/modules/containers/default.nix index 092ab95..767e0d1 100644 --- a/terraform-nix/modules/containers/default.nix +++ b/terraform-nix/modules/containers/default.nix @@ -1,5 +1,5 @@ { imports = [ - ./docker + ./backends ]; } From fe1bd8ac36a49b3d8863af4243547781c0fde90b Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Tue, 9 Jan 2024 19:32:11 -0300 Subject: [PATCH 36/43] chore(terraform-nix): Refactor container modules Split the monolythic config into backends (docker, podman), instrumentation (docker compose, arion) and services (backend/instrumentation-agnostic container configuration) --- terraform-nix/lxcs/default.nix | 13 +++++--- terraform-nix/modules/config/networking.nix | 4 +++ .../modules/containers/backends/default.nix | 2 +- .../modules/containers/backends/docker.nix | 31 +++++++++++++++++++ terraform-nix/modules/containers/default.nix | 2 ++ .../containers/instrumentation/default.nix | 5 +++ .../docker-compose/compose.nix | 17 ++++++++++ .../docker-compose}/default.nix | 30 ++++-------------- .../docker/compose => services}/default.nix | 23 ++++---------- 9 files changed, 81 insertions(+), 46 deletions(-) create mode 100644 terraform-nix/modules/containers/backends/docker.nix create mode 100644 terraform-nix/modules/containers/instrumentation/default.nix create mode 100644 terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix rename terraform-nix/modules/containers/{backends/docker => instrumentation/docker-compose}/default.nix (78%) rename terraform-nix/modules/containers/{backends/docker/compose => services}/default.nix (98%) diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 4fad11a..71cab67 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -7,10 +7,12 @@ let storage = import ../modules/config/storage.nix args; }; - makeDefault = lxc: lxc // { nix.modules = (lxc.nix.modules or []) ++ [ - (enableServiceByName lxc.name) - (manageNetwork lxc.ip) - ]; }; + makeDefault = lxc: lxc // { + nix.modules = (lxc.nix.modules or []) ++ [ + (enableServiceByName lxc.name) + (manageNetwork lxc.ip) + ]; + }; enableServiceByName = name: { config.my.services.${name}.enable = true; }; manageNetwork = ip: { config.proxmoxLXC.manageNetwork = true; @@ -89,6 +91,9 @@ let mountpoints = [ { mp = "/srv/storage"; volume = "/srv/storage"; } ]; + nix.modules = [ + { config.my.services.docker.compose.enable = true; } + ]; }; }; in diff --git a/terraform-nix/modules/config/networking.nix b/terraform-nix/modules/config/networking.nix index 34323f3..c2a917d 100644 --- a/terraform-nix/modules/config/networking.nix +++ b/terraform-nix/modules/config/networking.nix @@ -5,6 +5,10 @@ gateway = "10.0.0.1"; prefixLength = 8; }; + ports.internal = { + http = 8099; + https = 44399; + }; cidrs = rec { trusted = private ++ cloudflare; private = [ "10.0.0.0/8" "fe80::/10" ]; diff --git a/terraform-nix/modules/containers/backends/default.nix b/terraform-nix/modules/containers/backends/default.nix index 092ab95..9045632 100644 --- a/terraform-nix/modules/containers/backends/default.nix +++ b/terraform-nix/modules/containers/backends/default.nix @@ -1,5 +1,5 @@ { imports = [ - ./docker + ./docker.nix ]; } diff --git a/terraform-nix/modules/containers/backends/docker.nix b/terraform-nix/modules/containers/backends/docker.nix new file mode 100644 index 0000000..b41f438 --- /dev/null +++ b/terraform-nix/modules/containers/backends/docker.nix @@ -0,0 +1,31 @@ +# Documentation: https://nixos.wiki/wiki/Docker + +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.my.services.docker; +in +with lib; +{ + options.my.services.docker = { + enable = mkEnableOption "docker"; + }; + + config = mkIf cfg.enable { + virtualisation.docker = { + enable = true; + autoPrune = { + enable = true; + flags = [ "--all" "--force" ]; + dates = "weekly"; + }; + }; + + system.activationScripts = { + docker-socket-perms.text = toString (pkgs.writers.writeBash "docker-socket-perms" '' + chmod o+rw /var/run/docker.sock + ''); + }; + }; +} diff --git a/terraform-nix/modules/containers/default.nix b/terraform-nix/modules/containers/default.nix index 767e0d1..5d7acc2 100644 --- a/terraform-nix/modules/containers/default.nix +++ b/terraform-nix/modules/containers/default.nix @@ -1,5 +1,7 @@ { imports = [ ./backends + ./instrumentation + ./services ]; } diff --git a/terraform-nix/modules/containers/instrumentation/default.nix b/terraform-nix/modules/containers/instrumentation/default.nix new file mode 100644 index 0000000..8ce8f5b --- /dev/null +++ b/terraform-nix/modules/containers/instrumentation/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./docker-compose + ]; +} diff --git a/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix new file mode 100644 index 0000000..1480847 --- /dev/null +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix @@ -0,0 +1,17 @@ +{ config, ... }: + +{ + name = "homelab"; + version = "3.6"; + + services = config.my.containers.services; + + networks = { + default = { + internal = true; + ipam.config = [{ subnet = "172.16.80.0/24"; }]; + }; + + external.ipam.config = [{ subnet = "10.10.250.0/24"; }]; + }; +} diff --git a/terraform-nix/modules/containers/backends/docker/default.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix similarity index 78% rename from terraform-nix/modules/containers/backends/docker/default.nix rename to terraform-nix/modules/containers/instrumentation/docker-compose/default.nix index edb538f..a5bf065 100644 --- a/terraform-nix/modules/containers/backends/docker/default.nix +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix @@ -1,18 +1,12 @@ -# Documentation: https://nixos.wiki/wiki/Docker - { config, lib, pkgs, ... }@args: with lib; let - cfg = config.my.services.docker; + cfg = config.my.services.docker.compose; - ports = { - internal.http = 8099; - internal.https = 44399; - }; user = config.users.users.root; - composeNix = import ./compose (args // { inherit ports; }); + composeNix = import ./compose.nix args; composeFile = pkgs.runCommand "compose-nix-to-yaml" { buildInputs = [ pkgs.remarshal ]; @@ -28,8 +22,8 @@ let in with lib; { - options.my.services.docker = { - enable = mkEnableOption "docker"; + options.my.services.docker.compose = { + enable = mkEnableOption "docker compose"; }; config = mkIf cfg.enable { @@ -64,22 +58,10 @@ with lib; allowedUDPPorts = sanitizePorts rawUdpPorts; }; - virtualisation.docker = { - enable = true; - autoPrune = { - enable = true; - flags = [ "--all" "--force" ]; - dates = "weekly"; - }; - }; - system.activationScripts = { docker-compose-cp.text = toString (pkgs.writers.writeBash "docker-compose-cp" '' cp -r ${composeFile} ${user.home}/compose.yaml ''); - docker-socket-perms.text = toString (pkgs.writers.writeBash "docker-socket-perms" '' - chmod o+rw /var/run/docker.sock - ''); docker-compose-up.text = toString (pkgs.writers.writeBash "docker-compose-up" '' cd ${user.home} && ${pkgs.docker}/bin/docker compose up -d ''); @@ -106,8 +88,8 @@ with lib; }; in { - "6to4-http" = make6to4Service 80 ports.internal.http; - "6to4-https" = make6to4Service 443 ports.internal.https; + "6to4-http" = make6to4Service 80 config.my.networking.ports.internal.http; + "6to4-https" = make6to4Service 443 config.my.networking.ports.internal.https; }; users.users.socat = { diff --git a/terraform-nix/modules/containers/backends/docker/compose/default.nix b/terraform-nix/modules/containers/services/default.nix similarity index 98% rename from terraform-nix/modules/containers/backends/docker/compose/default.nix rename to terraform-nix/modules/containers/services/default.nix index 1c2959d..bfd12a0 100644 --- a/terraform-nix/modules/containers/backends/docker/compose/default.nix +++ b/terraform-nix/modules/containers/services/default.nix @@ -1,5 +1,6 @@ -{ config, ports, ... }: +{ config, lib, ... }: +with lib; let makeDefault = dockerConfig: (dockerConfig // { @@ -17,10 +18,7 @@ let }); in { - name = "homelab"; - version = "3.6"; - - services = { + options.my.containers.services = mkOption { default = { cloudflare-ddns = makeDefault { image = "oznu/cloudflare-ddns:latest"; environment = { @@ -61,8 +59,8 @@ in environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; networks = [ "default" "external" ]; ports = [ - "${builtins.toString ports.internal.http}:80" - "${builtins.toString ports.internal.https}:443" + "${builtins.toString config.my.networking.ports.internal.http}:80" + "${builtins.toString config.my.networking.ports.internal.https}:443" "8080:8080" ]; volumes = [ @@ -542,14 +540,5 @@ in "/var/run/docker.sock:/var/run/docker.sock" ]; }; - }; - - networks = { - default = { - internal = true; - ipam.config = [{ subnet = "172.16.80.0/24"; }]; - }; - - external.ipam.config = [{ subnet = "10.10.250.0/24"; }]; - }; + }; }; } From 0db50d11f11e8e2e674741070232dc3704450610 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Wed, 10 Jan 2024 10:32:07 -0300 Subject: [PATCH 37/43] feat(terraform-nix): Split docker configuration into categories --- terraform-nix/lxcs/default.nix | 4 +- .../docker-compose/compose.nix | 2 +- .../docker-compose/default.nix | 4 +- .../modules/containers/services/default.nix | 557 +----------------- .../services/management/default.nix | 48 ++ .../containers/services/media/default.nix | 283 +++++++++ .../containers/services/misc/default.nix | 144 +++++ .../services/networking/default.nix | 57 ++ .../containers/services/storage/default.nix | 82 +++ .../modules/containers/services/utils.nix | 20 + 10 files changed, 658 insertions(+), 543 deletions(-) create mode 100644 terraform-nix/modules/containers/services/management/default.nix create mode 100644 terraform-nix/modules/containers/services/media/default.nix create mode 100644 terraform-nix/modules/containers/services/misc/default.nix create mode 100644 terraform-nix/modules/containers/services/networking/default.nix create mode 100644 terraform-nix/modules/containers/services/storage/default.nix create mode 100644 terraform-nix/modules/containers/services/utils.nix diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 71cab67..a44004d 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -8,7 +8,7 @@ let }; makeDefault = lxc: lxc // { - nix.modules = (lxc.nix.modules or []) ++ [ + nix.modules = (lxc.nix.modules or [ ]) ++ [ (enableServiceByName lxc.name) (manageNetwork lxc.ip) ]; @@ -92,7 +92,7 @@ let { mp = "/srv/storage"; volume = "/srv/storage"; } ]; nix.modules = [ - { config.my.services.docker.compose.enable = true; } + { config.my.services.containers.instrumentation.compose.enable = true; } ]; }; }; diff --git a/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix index 1480847..a0e792c 100644 --- a/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix @@ -4,7 +4,7 @@ name = "homelab"; version = "3.6"; - services = config.my.containers.services; + services = config.my.containers.services.out; networks = { default = { diff --git a/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix index a5bf065..3fa0bcd 100644 --- a/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix @@ -2,7 +2,7 @@ with lib; let - cfg = config.my.services.docker.compose; + cfg = config.my.services.containers.instrumentation.compose; user = config.users.users.root; @@ -22,7 +22,7 @@ let in with lib; { - options.my.services.docker.compose = { + options.my.services.containers.instrumentation.compose = { enable = mkEnableOption "docker compose"; }; diff --git a/terraform-nix/modules/containers/services/default.nix b/terraform-nix/modules/containers/services/default.nix index bfd12a0..1373fc9 100644 --- a/terraform-nix/modules/containers/services/default.nix +++ b/terraform-nix/modules/containers/services/default.nix @@ -1,544 +1,25 @@ -{ config, lib, ... }: +{ config, lib, pkgs, ... }@args: with lib; let - makeDefault = dockerConfig: - (dockerConfig // { - environment = { - TZ = config.my.timezone; - PUID = 1000; - GUID = 1000; - } // (dockerConfig.environment or {}); - volumes = [ - "/dev/rtc:/dev/rtc:ro" - "/etc/localtime:/etc/localtime:ro" - "/etc/timezone:/etc/timezone:ro" - ] ++ (dockerConfig.volumes or []); - restart = dockerConfig.restart or "unless-stopped"; - }); + cfg = config.my.containers.services; + utils = pkgs.callPackage ./utils.nix args; + inherit (utils) makeEnableOptionDefaultTrue; in { - options.my.containers.services = mkOption { default = { - cloudflare-ddns = makeDefault { - image = "oznu/cloudflare-ddns:latest"; - environment = { - API_KEY = "\${secret_cloudflare_api_token}"; - ZONE = config.my.domain; - INTERFACE = "eth0"; - PROXIED = "true"; - RRTYPE = "AAAA"; - }; - network_mode = "host"; - }; - - crowdsec = makeDefault { - image = "crowdsecurity/crowdsec:latest"; - networks = [ "default" "external" ]; - volumes = [ - "${config.my.storage.getConfigPath "crowdsec"}:/etc/crowdsec" - "${config.my.storage.getLogPath "crowdsec"}:/var/log/nginx" - "${config.my.storage.getDataPath "crowdsec"}:/var/lib/crowdsec/data" - ]; - }; - - themepark = makeDefault { - image = "ghcr.io/themepark-dev/theme.park:latest"; - ports = [ "8084:80" "8444:443" ]; - volumes = [ "${config.my.storage.getConfigPath "themepark"}:/config" ]; - labels = { - "traefik.enable" = "true"; - "traefik.http.routers.themepark.entrypoints" = "websecure"; - "traefik.http.routers.themepark.rule" = "Host(`themepark.${config.my.domain}`)"; - "traefik.http.routers.themepark.middlewares" = "default@file"; - "traefik.http.services.themepark.loadbalancer.server.port" = "80"; - }; - }; - - traefik = makeDefault { - image = "traefik:v2.10"; - environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; - networks = [ "default" "external" ]; - ports = [ - "${builtins.toString config.my.networking.ports.internal.http}:80" - "${builtins.toString config.my.networking.ports.internal.https}:443" - "8080:8080" - ]; - volumes = [ - "${config.my.storage.getConfigPath "traefik"}:/etc/traefik" - "${config.my.storage.getConfigPath "traefik"}/acme:/etc/traefik/acme" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - labels = { - "com.centurylinklabs.watchtower.enable" = "false"; - }; - }; - - audiobookshelf = makeDefault { - image = "ghcr.io/advplyr/audiobookshelf:latest"; - ports = [ "13378:80" ]; - volumes = [ - "${config.my.storage.getConfigPath "audiobookshelf"}:/config" - "${config.my.storage.getDataPath "audiobookshelf"}:/metadata" - "${config.my.storage.main}:/storage" - ]; - networks = [ "default" "external" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Audiobookshelf"; - "homepage.icon" = "audiobookshelf.png"; - "homepage.href" = "https://audiobookshelf.${config.my.domain}"; - "homepage.description" = "Audiobook and podcast manager"; - "traefik.enable" = "true"; - "traefik.http.routers.audiobookshelf.entrypoints" = "websecure"; - "traefik.http.routers.audiobookshelf.rule" = - "Host(`audiobookshelf.${config.my.domain}`)"; - "traefik.http.routers.audiobookshelf.middlewares" = "default@file"; - }; - }; - - bazarr = makeDefault { - image = "lscr.io/linuxserver/bazarr:latest"; - environment = { - DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:bazarr"; - TP_DOMAIN = "themepark.${config.my.domain}"; - TP_THEME = "frappe"; - }; - networks = [ "default" "external" ]; - ports = [ "6767:6767" ]; - volumes = - [ "${config.my.storage.getConfigPath "bazarr"}:/config" "${config.my.storage.main}:/storage" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Bazarr"; - "homepage.icon" = "bazarr.png"; - "homepage.href" = "https://bazarr.${config.my.domain}"; - "homepage.description" = "Subtitle manager"; - "homepage.widget.type" = "bazarr"; - "homepage.widget.url" = "https://bazarr.${config.my.domain}"; - "homepage.widget.key" = "\${secret_bazarr_api_key}"; - "traefik.enable" = "true"; - "traefik.http.routers.bazarr.entrypoints" = "websecure"; - "traefik.http.routers.bazarr.rule" = "Host(`bazarr.${config.my.domain}`)"; - "traefik.http.routers.bazarr.middlewares" = "default@file"; - }; - }; - - lazylibrarian = makeDefault { - image = "lscr.io/linuxserver/lazylibrarian:latest"; - environment = { - DOCKER_MODS = "linuxserver/mods:universal-calibre|linuxserver/mods:lazylibrarian-ffmpeg"; - }; - ports = [ "5299:5299" ]; - volumes = [ - "${config.my.storage.getConfigPath "lazylibrarian"}:/config" - "${config.my.storage.main}:/storage" - ]; - networks = [ "default" "external" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "LazyLibrarian"; - "homepage.icon" = "lazylibrarian.png"; - "homepage.href" = "https://lazylibrarian.${config.my.domain}"; - "homepage.description" = "Book manager"; - "traefik.enable" = "true"; - "traefik.http.routers.lazylibrarian.entrypoints" = "websecure"; - "traefik.http.routers.lazylibrarian.rule" = - "Host(`lazylibrarian.${config.my.domain}`)"; - "traefik.http.routers.lazylibrarian.middlewares" = "default@file"; - }; - }; - - lidarr = makeDefault { - image = "lscr.io/linuxserver/lidarr:latest"; - ports = [ "8686:8686" ]; - volumes = - [ "${config.my.storage.getConfigPath "lidarr"}:/config" "${config.my.storage.main}:/storage" ]; - networks = [ "default" "external" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Lidarr"; - "homepage.icon" = "lidarr.png"; - "homepage.href" = "https://lidarr.${config.my.domain}"; - "homepage.description" = "Music manager"; - "traefik.enable" = "true"; - "traefik.http.routers.lidarr.entrypoints" = "websecure"; - "traefik.http.routers.lidarr.rule" = "Host(`lidarr.${config.my.domain}`)"; - "traefik.http.routers.lidarr.middlewares" = "default@file"; - }; - }; - - mylar = makeDefault { - profiles = [ "disable" ]; - image = "lscr.io/linuxserver/mylar3:latest"; - ports = [ "8090:8090" ]; - volumes = - [ "${config.my.storage.getConfigPath "mylar"}:/config" "${config.my.storage.main}:/storage" ]; - networks = [ "default" "external" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Mylar"; - "homepage.icon" = "mylar.png"; - "homepage.href" = "https://mylar.${config.my.domain}"; - "homepage.description" = "Comics/Manga manager"; - "traefik.enable" = "true"; - "traefik.http.routers.mylar.entrypoints" = "websecure"; - "traefik.http.routers.mylar.rule" = "Host(`mylar.${config.my.domain}`)"; - "traefik.http.routers.mylar.middlewares" = "default@file"; - }; - }; - - radarr = makeDefault { - image = "lscr.io/linuxserver/radarr:latest"; - environment = { - DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:radarr"; - TP_DOMAIN = "themepark.${config.my.domain}"; - TP_THEME = "frappe"; - }; - networks = [ "default" "external" ]; - ports = [ "7878:7878" ]; - volumes = - [ "${config.my.storage.getConfigPath "radarr"}:/config" "${config.my.storage.main}:/storage" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Radarr"; - "homepage.icon" = "radarr.png"; - "homepage.href" = "https://radarr.${config.my.domain}"; - "homepage.description" = "Movie manager"; - "homepage.weight" = -60000; - "homepage.widget.type" = "radarr"; - "homepage.widget.url" = "https://radarr.${config.my.domain}"; - "homepage.widget.key" = "\${secret_radarr_api_key}"; - "traefik.enable" = "true"; - "traefik.http.routers.radarr.entrypoints" = "websecure"; - "traefik.http.routers.radarr.rule" = "Host(`radarr.${config.my.domain}`)"; - "traefik.http.routers.radarr.middlewares" = "default@file"; - }; - }; - - readarr = makeDefault { - profiles = [ "disable" ]; - image = "lscr.io/linuxserver/readarr:latest"; - ports = [ "8787:8787" ]; - volumes = - [ "${config.my.storage.getConfigPath "readarr"}:/config" "${config.my.storage.main}:/storage" ]; - networks = [ "default" "external" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Readarr"; - "homepage.icon" = "readarr.png"; - "homepage.href" = "https://readarr.${config.my.domain}"; - "homepage.description" = "Book manager"; - "traefik.enable" = "false"; - "traefik.http.routers.readarr.entrypoints" = "websecure"; - "traefik.http.routers.readarr.rule" = "Host(`readarr.${config.my.domain}`)"; - "traefik.http.routers.readarr.middlewares" = "default@file"; - }; - }; - - sonarr = makeDefault { - image = "lscr.io/linuxserver/sonarr:latest"; - environment = { - DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:sonarr"; - TP_DOMAIN = "themepark.${config.my.domain}"; - TP_THEME = "frappe"; - }; - networks = [ "default" "external" ]; - ports = [ "8989:8989" ]; - volumes = - [ "${config.my.storage.getConfigPath "sonarr"}:/config" "${config.my.storage.main}:/storage" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Sonarr"; - "homepage.icon" = "sonarr.png"; - "homepage.href" = "https://sonarr.${config.my.domain}"; - "homepage.description" = "Series manager"; - "homepage.weight" = -70000; - "homepage.widget.type" = "sonarr"; - "homepage.widget.url" = "https://sonarr.${config.my.domain}"; - "homepage.widget.key" = "\${secret_sonarr_api_key}"; - "traefik.enable" = "true"; - "traefik.http.routers.sonarr.entrypoints" = "websecure"; - "traefik.http.routers.sonarr.rule" = "Host(`sonarr.${config.my.domain}`)"; - "traefik.http.routers.sonarr.middlewares" = "default@file"; - }; - }; - - jellyseerr = makeDefault { - image = "fallenbagel/jellyseerr:latest"; - networks = [ "default" "external" ]; - ports = [ "5055:5055" ]; - volumes = [ "${config.my.storage.getConfigPath "jellyseerr"}:/app/config" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Jellyseerr"; - "homepage.icon" = "jellyseerr.png"; - "homepage.href" = "https://jellyseerr.${config.my.domain}"; - "homepage.description" = "Media requester"; - "homepage.weight" = -80000; - "homepage.widget.type" = "jellyseerr"; - "homepage.widget.url" = "https://jellyseerr.${config.my.domain}"; - "homepage.widget.key" = "\${secret_jellyseerr_api_key}"; - "traefik.enable" = "true"; - "traefik.http.routers.jellyseerr.entrypoints" = "websecure"; - "traefik.http.routers.jellyseerr.rule" = - "Host(`jellyseerr.${config.my.domain}`)"; - "traefik.http.routers.jellyseerr.middlewares" = "default@file"; - }; - }; - - prowlarr = makeDefault { - image = "lscr.io/linuxserver/prowlarr:latest"; - environment = { - DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:prowlarr"; - TP_DOMAIN = "themepark.${config.my.domain}"; - TP_THEME = "frappe"; - }; - networks = [ "default" "external" ]; - ports = [ "9696:9696" ]; - volumes = [ "${config.my.storage.getConfigPath "prowlarr"}:/config" ]; - labels = { - "homepage.group" = "Media"; - "homepage.name" = "Prowlarr"; - "homepage.icon" = "prowlarr.png"; - "homepage.href" = "https://prowlarr.${config.my.domain}"; - "homepage.description" = "Arr index manager"; - "homepage.widget.type" = "prowlarr"; - "homepage.widget.url" = "https://prowlarr.${config.my.domain}"; - "homepage.widget.key" = "\${secret_prowlarr_api_key}"; - "traefik.enable" = "true"; - "traefik.http.routers.prowlarr.entrypoints" = "websecure"; - "traefik.http.routers.prowlarr.rule" = "Host(`prowlarr.${config.my.domain}`)"; - "traefik.http.routers.prowlarr.middlewares" = "default@file"; - }; - }; - - actual-server = makeDefault { - image = "actualbudget/actual-server:latest"; - networks = [ "default" "external" ]; - ports = [ "5006:5006" ]; - volumes = [ "${config.my.storage.getDataPath "actual-server"}:/data" ]; - labels = { - "traefik.enable" = "true"; - "traefik.http.routers.actual.entrypoints" = "websecure"; - "traefik.http.routers.actual.rule" = "Host(`actual.${config.my.domain}`)"; - "traefik.http.routers.actual.middlewares" = "default@file"; - }; - }; - - adguardhome-sync = makeDefault { - image = "lscr.io/linuxserver/adguardhome-sync:latest"; - ports = [ "8082:8082/tcp" ]; - volumes = [ "${config.my.storage.getConfigPath "adguardhome-sync"}:/config" ]; - }; - - homepage = makeDefault { - image = "ghcr.io/gethomepage/homepage:latest"; - networks = [ "default" "external" ]; - ports = [ "3000:3000" ]; - volumes = [ - "${config.my.storage.getConfigPath "homepage"}:/app/config" - "${config.my.storage.main}:/storage:ro" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - labels = { - "traefik.enable" = "true"; - "traefik.http.routers.home.entrypoints" = "websecure"; - "traefik.http.routers.home.rule" = "Host(`home.${config.my.domain}`)"; - "traefik.http.routers.home.middlewares" = "default@file"; - }; - }; - - forgejo-server = makeDefault { - image = "codeberg.org/forgejo/forgejo:1.20"; - environment = { - USER_UID = 1000; - USER_GID = 1000; - FORGEJO__actions__ENABLED = "true"; - FORGEJO__database__DB_TYPE = "postgres"; - FORGEJO__database__HOST = "forgejo-db:5432"; - FORGEJO__database__NAME = "forgejo"; - FORGEJO__database__USER = "forgejo"; - FORGEJO__database__PASSWD = "\${secret_forgejo_db_password}"; - FORGEJO__mailer__ENABLED = "true"; - FORGEJO__mailer__FROM = "forgejo@${config.my.domain}"; - FORGEJO__mailer__PROTOCOL = "starttls"; - FORGEJO__mailer__SMTP_ADDR = "\${secret_smtp_host}"; - FORGEJO__mailer__SMTP_PORT = "\${secret_smtp_port}"; - FORGEJO__mailer__USER = "\${secret_smtp_user}"; - FORGEJO__mailer__PASSWD = "\${secret_smtp_password}"; - FORGEJO__service__DISABLE_REGISTRATION = "true"; - FORGEJO__service__REGISTER_MANUAL_CONFIRM = "true"; - }; - networks = [ "default" "external" ]; - ports = [ "3001:3000" "222:22" ]; - volumes = [ "${config.my.storage.getDataPath "forgejo-server"}:/data" ]; - labels = { - "com.centurylinklabs.watchtower.enable" = "false"; - "homepage.group" = "Other"; - "homepage.name" = "Forgejo"; - "homepage.icon" = "forgejo.png"; - "homepage.href" = "https://git.${config.my.domain}"; - "homepage.description" = "Code forge"; - "traefik.enable" = "true"; - "traefik.http.routers.git.entrypoints" = "websecure"; - "traefik.http.routers.git.rule" = "Host(`git.${config.my.domain}`)"; - "traefik.http.routers.git.middlewares" = "default@file"; - "traefik.http.services.git.loadbalancer.server.port" = "3000"; - }; - depends_on = [ "forgejo-db" ]; - }; - - forgejo-db = makeDefault { - image = "postgres:14"; - environment = { - POSTGRES_USER = "forgejo"; - POSTGRES_PASSWORD = "\${secret_forgejo_db_password}"; - POSTGRES_DB = "forgejo"; - }; - networks = [ "default" ]; - volumes = [ "${config.my.storage.getDataPath "forgejo-postgres"}:/var/lib/postgresql/data" ]; - labels = { - "com.centurylinklabs.watchtower.enable" = "false"; - }; - }; - - influxdb = makeDefault { - image = "influxdb:latest"; - networks = [ "default" "external" ]; - ports = [ "8086:8086" ]; - volumes = [ - "${config.my.storage.getConfigPath "influxdb"}:/etc/influxdb2" - "${config.my.storage.getDataPath "influxdb"}:/var/lib/influxdb2" - ]; - }; - - jackett = makeDefault { - profiles = [ "disable" ]; - image = "lscr.io/linuxserver/jackett"; - networks = [ "default" "external" ]; - ports = [ "9117:9117" ]; - volumes = - [ "${config.my.storage.getConfigPath "jackett"}:/config" "${config.my.storage.main}:/storage" ]; - }; - - jellyfin = makeDefault { - profiles = [ "disable" ]; - image = "lscr.io/linuxserver/jellyfin"; - environment = { DOCKER_MODS = "linuxserver/mods:jellyfin-opencl-intel"; }; - networks = [ "default" "external" ]; - ports = [ "8096:8096" "7359:7359" "1900:1900" ]; - volumes = [ "${config.my.storage.getConfigPath "jellyfin"}:/config" "${config.my.storage.main}:/data" ]; - devices = [ "/dev/dri:/dev/dri" ]; - }; - - qbittorrent = makeDefault { - image = "lscr.io/linuxserver/qbittorrent:latest"; - environment = { - WEBUI_PORT = "8081"; - # DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:qbittorrent"; - # TP_DOMAIN = "themepark.${config.my.domain}"; - # TP_THEME = "frappe"; - }; - networks = [ "default" "external" ]; - ports = [ "8081:8081" "6881:6881" "6881:6881/udp" ]; - volumes = [ - "${config.my.storage.getConfigPath "qbittorrent"}:/config" - "${config.my.storage.main}/Downloads/Torrents:/downloads" - ]; - labels = { - "homepage.group" = "Other"; - "homepage.name" = "qBittorrent"; - "homepage.icon" = "qbittorrent.png"; - "homepage.href" = "https://qbittorrent.${config.my.domain}"; - "homepage.description" = "Torrent downloader"; - "homepage.widget.type" = "qbittorrent"; - "homepage.widget.url" = "https://qbittorrent.${config.my.domain}"; - "homepage.widget.username" = "\${secret_qbittorrent_user}"; - "homepage.widget.password" = "\${secret_qbittorrent_password}"; - "traefik.enable" = "true"; - "traefik.http.routers.qbittorrent.entrypoints" = "websecure"; - "traefik.http.routers.qbittorrent.rule" = - "Host(`qbittorrent.${config.my.domain}`)"; - "traefik.http.routers.qbittorrent.middlewares" = "default@file"; - "traefik.http.services.qbittorrent.loadbalancer.server.port" = "8081"; - }; - }; - - syncthing = makeDefault { - image = "lscr.io/linuxserver/syncthing:latest"; - networks = [ "default" "external" ]; - ports = - [ "8384:8384" "22000:22000/tcp" "22000:22000/udp" "21027:21027/udp" ]; - volumes = [ - "${config.my.storage.getConfigPath "syncthing"}:/config" - "${config.my.storage.getDataPath "syncthing"}:/data" - ]; - labels = { - "homepage.group" = "Other"; - "homepage.name" = "Syncthing"; - "homepage.icon" = "syncthing.png"; - "homepage.href" = "https://syncthing.${config.my.domain}"; - "homepage.description" = "P2P file syncing"; - "traefik.enable" = "true"; - "traefik.http.routers.syncthing.entrypoints" = "websecure"; - "traefik.http.routers.syncthing.rule" = "Host(`syncthing.${config.my.domain}`)"; - "traefik.http.routers.syncthing.middlewares" = "default@file"; - "traefik.http.services.syncthing.loadbalancer.server.port" = "8384"; - }; - }; - - vscode = makeDefault { - image = "lscr.io/linuxserver/code-server:latest"; - environment = { - PASSWORD = "\${secret_vscode_password}"; - PROXY_DOMAIN = "code.${config.my.domain}"; - }; - networks = [ "default" "external" ]; - ports = [ "8443:8443/tcp" ]; - volumes = [ "${config.my.storage.getConfigPath "vscode"}:/config" ]; - labels = { - "homepage.group" = "Other"; - "homepage.name" = "Visual Studio Code"; - "homepage.icon" = "vscode.png"; - "homepage.href" = "https://code.${config.my.domain}"; - "homepage.description" = "Cloud IDE"; - "traefik.enable" = "true"; - "traefik.http.routers.code.entrypoints" = "websecure"; - "traefik.http.routers.code.rule" = "Host(`code.${config.my.domain}`)"; - "traefik.http.routers.code.middlewares" = "default@file"; - }; - }; - - portainer = makeDefault { - image = "portainer/portainer-ce:latest"; - network_mode = "bridge"; - ports = [ "8001:8000" "9443:9443" ]; - restart = "always"; - volumes = [ - "${config.my.storage.getDataPath "portainer"}:/data" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - }; - - watchtower = makeDefault { - image = "containrrr/watchtower:latest"; - networks = [ "default" "external" ]; - environment = { - WATCHTOWER_SCHEDULE = "0 2 * * *"; - WATCHTOWER_CLEANUP = "true"; - }; - restart = "unless-stopped"; - volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ]; - }; - - yacht = makeDefault { - image = "selfhostedpro/yacht:latest"; - networks = [ "default" "external" ]; - ports = [ "8000:8000" ]; - volumes = [ - "${config.my.storage.getConfigPath "yacht"}:/config" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - }; - }; }; + imports = + let importArgs = args // { inherit utils; }; + in [ + (import ./management importArgs) + (import ./media importArgs) + (import ./misc importArgs) + (import ./storage importArgs) + (import ./networking importArgs) + ]; + + options.my.containers.services = { + enable = makeEnableOptionDefaultTrue "containerized services"; + contents = mkOption { default = { }; }; + out = mkOption { default = mapAttrs (_: service: service.content) cfg.contents; }; + }; } diff --git a/terraform-nix/modules/containers/services/management/default.nix b/terraform-nix/modules/containers/services/management/default.nix new file mode 100644 index 0000000..141f653 --- /dev/null +++ b/terraform-nix/modules/containers/services/management/default.nix @@ -0,0 +1,48 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault; +in +{ + options.my.containers.services = { + portainer.enable = makeEnableOptionDefaultTrue "portainer"; + watchtower.enable = makeEnableOptionDefaultTrue "watchtower"; + yacht.enable = makeEnableOptionDefaultTrue "yacht"; + }; + + config.my.containers.services.contents = mkIf cfg.enable { + portainer = mkIf cfg.portainer.enable (makeDefault { + image = "portainer/portainer-ce:latest"; + network_mode = "bridge"; + ports = [ "8001:8000" "9443:9443" ]; + restart = "always"; + volumes = [ + "${config.my.storage.getDataPath "portainer"}:/data" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + }); + + watchtower = mkIf cfg.watchtower.enable (makeDefault { + image = "containrrr/watchtower:latest"; + networks = [ "default" "external" ]; + environment = { + WATCHTOWER_SCHEDULE = "0 2 * * *"; + WATCHTOWER_CLEANUP = "true"; + }; + restart = "unless-stopped"; + volumes = [ "/var/run/docker.sock:/var/run/docker.sock" ]; + }); + + yacht = mkIf cfg.yacht.enable (makeDefault { + image = "selfhostedpro/yacht:latest"; + networks = [ "default" "external" ]; + ports = [ "8000:8000" ]; + volumes = [ + "${config.my.storage.getConfigPath "yacht"}:/config" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + }); + }; +} diff --git a/terraform-nix/modules/containers/services/media/default.nix b/terraform-nix/modules/containers/services/media/default.nix new file mode 100644 index 0000000..d362ef6 --- /dev/null +++ b/terraform-nix/modules/containers/services/media/default.nix @@ -0,0 +1,283 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault; +in +{ + options.my.containers.services = { + audiobookshelf.enable = makeEnableOptionDefaultTrue "audiobookshelf"; + bazarr.enable = makeEnableOptionDefaultTrue "bazarr"; + jackett.enable = makeEnableOptionDefaultTrue "jackett"; + jellyfin.enable = makeEnableOptionDefaultTrue "jellyfin"; + jellyseerr.enable = makeEnableOptionDefaultTrue "jellyseerr"; + lazylibrarian.enable = makeEnableOptionDefaultTrue "lazylibrarian"; + lidarr.enable = makeEnableOptionDefaultTrue "lidarr"; + mylar.enable = makeEnableOptionDefaultTrue "mylar"; + prowlarr.enable = makeEnableOptionDefaultTrue "prowlarr"; + radarr.enable = makeEnableOptionDefaultTrue "radarr"; + readarr.enable = makeEnableOptionDefaultTrue "readarr"; + sonarr.enable = makeEnableOptionDefaultTrue "sonarr"; + }; + + config.my.containers.services.contents = mkIf cfg.enable { + audiobookshelf = mkIf cfg.audiobookshelf.enable (makeDefault { + image = "ghcr.io/advplyr/audiobookshelf:latest"; + ports = [ "13378:80" ]; + volumes = [ + "${config.my.storage.getConfigPath "audiobookshelf"}:/config" + "${config.my.storage.getDataPath "audiobookshelf"}:/metadata" + "${config.my.storage.main}:/storage" + ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Audiobookshelf"; + "homepage.icon" = "audiobookshelf.png"; + "homepage.href" = "https://audiobookshelf.${config.my.domain}"; + "homepage.description" = "Audiobook and podcast manager"; + "traefik.enable" = "true"; + "traefik.http.routers.audiobookshelf.entrypoints" = "websecure"; + "traefik.http.routers.audiobookshelf.rule" = + "Host(`audiobookshelf.${config.my.domain}`)"; + "traefik.http.routers.audiobookshelf.middlewares" = "default@file"; + }; + }); + + bazarr = mkIf cfg.bazarr.enable (makeDefault { + image = "lscr.io/linuxserver/bazarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:bazarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "6767:6767" ]; + volumes = + [ "${config.my.storage.getConfigPath "bazarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Bazarr"; + "homepage.icon" = "bazarr.png"; + "homepage.href" = "https://bazarr.${config.my.domain}"; + "homepage.description" = "Subtitle manager"; + "homepage.widget.type" = "bazarr"; + "homepage.widget.url" = "https://bazarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_bazarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.bazarr.entrypoints" = "websecure"; + "traefik.http.routers.bazarr.rule" = "Host(`bazarr.${config.my.domain}`)"; + "traefik.http.routers.bazarr.middlewares" = "default@file"; + }; + }); + + jackett = mkIf cfg.jackett.enable (makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/jackett"; + networks = [ "default" "external" ]; + ports = [ "9117:9117" ]; + volumes = + [ "${config.my.storage.getConfigPath "jackett"}:/config" "${config.my.storage.main}:/storage" ]; + }); + + jellyfin = mkIf cfg.jellyfin.enable (makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/jellyfin"; + environment = { DOCKER_MODS = "linuxserver/mods:jellyfin-opencl-intel"; }; + networks = [ "default" "external" ]; + ports = [ "8096:8096" "7359:7359" "1900:1900" ]; + volumes = [ "${config.my.storage.getConfigPath "jellyfin"}:/config" "${config.my.storage.main}:/data" ]; + devices = [ "/dev/dri:/dev/dri" ]; + }); + + jellyseerr = mkIf cfg.jellyseerr.enable (makeDefault { + image = "fallenbagel/jellyseerr:latest"; + networks = [ "default" "external" ]; + ports = [ "5055:5055" ]; + volumes = [ "${config.my.storage.getConfigPath "jellyseerr"}:/app/config" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Jellyseerr"; + "homepage.icon" = "jellyseerr.png"; + "homepage.href" = "https://jellyseerr.${config.my.domain}"; + "homepage.description" = "Media requester"; + "homepage.weight" = -80000; + "homepage.widget.type" = "jellyseerr"; + "homepage.widget.url" = "https://jellyseerr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_jellyseerr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.jellyseerr.entrypoints" = "websecure"; + "traefik.http.routers.jellyseerr.rule" = + "Host(`jellyseerr.${config.my.domain}`)"; + "traefik.http.routers.jellyseerr.middlewares" = "default@file"; + }; + }); + + lazylibrarian = mkIf cfg.lazylibrarian.enable (makeDefault { + image = "lscr.io/linuxserver/lazylibrarian:latest"; + environment = { + DOCKER_MODS = "linuxserver/mods:universal-calibre|linuxserver/mods:lazylibrarian-ffmpeg"; + }; + ports = [ "5299:5299" ]; + volumes = [ + "${config.my.storage.getConfigPath "lazylibrarian"}:/config" + "${config.my.storage.main}:/storage" + ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "LazyLibrarian"; + "homepage.icon" = "lazylibrarian.png"; + "homepage.href" = "https://lazylibrarian.${config.my.domain}"; + "homepage.description" = "Book manager"; + "traefik.enable" = "true"; + "traefik.http.routers.lazylibrarian.entrypoints" = "websecure"; + "traefik.http.routers.lazylibrarian.rule" = + "Host(`lazylibrarian.${config.my.domain}`)"; + "traefik.http.routers.lazylibrarian.middlewares" = "default@file"; + }; + }); + + lidarr = mkIf cfg.lidarr.enable (makeDefault { + image = "lscr.io/linuxserver/lidarr:latest"; + ports = [ "8686:8686" ]; + volumes = + [ "${config.my.storage.getConfigPath "lidarr"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Lidarr"; + "homepage.icon" = "lidarr.png"; + "homepage.href" = "https://lidarr.${config.my.domain}"; + "homepage.description" = "Music manager"; + "traefik.enable" = "true"; + "traefik.http.routers.lidarr.entrypoints" = "websecure"; + "traefik.http.routers.lidarr.rule" = "Host(`lidarr.${config.my.domain}`)"; + "traefik.http.routers.lidarr.middlewares" = "default@file"; + }; + }); + + mylar = mkIf cfg.mylar.enable (makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/mylar3:latest"; + ports = [ "8090:8090" ]; + volumes = + [ "${config.my.storage.getConfigPath "mylar"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Mylar"; + "homepage.icon" = "mylar.png"; + "homepage.href" = "https://mylar.${config.my.domain}"; + "homepage.description" = "Comics/Manga manager"; + "traefik.enable" = "true"; + "traefik.http.routers.mylar.entrypoints" = "websecure"; + "traefik.http.routers.mylar.rule" = "Host(`mylar.${config.my.domain}`)"; + "traefik.http.routers.mylar.middlewares" = "default@file"; + }; + }); + + prowlarr = mkIf cfg.prowlarr.enable (makeDefault { + image = "lscr.io/linuxserver/prowlarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:prowlarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "9696:9696" ]; + volumes = [ "${config.my.storage.getConfigPath "prowlarr"}:/config" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Prowlarr"; + "homepage.icon" = "prowlarr.png"; + "homepage.href" = "https://prowlarr.${config.my.domain}"; + "homepage.description" = "Arr index manager"; + "homepage.widget.type" = "prowlarr"; + "homepage.widget.url" = "https://prowlarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_prowlarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.prowlarr.entrypoints" = "websecure"; + "traefik.http.routers.prowlarr.rule" = "Host(`prowlarr.${config.my.domain}`)"; + "traefik.http.routers.prowlarr.middlewares" = "default@file"; + }; + }); + + radarr = mkIf cfg.radarr.enable (makeDefault { + image = "lscr.io/linuxserver/radarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:radarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "7878:7878" ]; + volumes = + [ "${config.my.storage.getConfigPath "radarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Radarr"; + "homepage.icon" = "radarr.png"; + "homepage.href" = "https://radarr.${config.my.domain}"; + "homepage.description" = "Movie manager"; + "homepage.weight" = -60000; + "homepage.widget.type" = "radarr"; + "homepage.widget.url" = "https://radarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_radarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.radarr.entrypoints" = "websecure"; + "traefik.http.routers.radarr.rule" = "Host(`radarr.${config.my.domain}`)"; + "traefik.http.routers.radarr.middlewares" = "default@file"; + }; + }); + + readarr = mkIf cfg.readarr.enable (makeDefault { + profiles = [ "disable" ]; + image = "lscr.io/linuxserver/readarr:latest"; + ports = [ "8787:8787" ]; + volumes = + [ "${config.my.storage.getConfigPath "readarr"}:/config" "${config.my.storage.main}:/storage" ]; + networks = [ "default" "external" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Readarr"; + "homepage.icon" = "readarr.png"; + "homepage.href" = "https://readarr.${config.my.domain}"; + "homepage.description" = "Book manager"; + "traefik.enable" = "false"; + "traefik.http.routers.readarr.entrypoints" = "websecure"; + "traefik.http.routers.readarr.rule" = "Host(`readarr.${config.my.domain}`)"; + "traefik.http.routers.readarr.middlewares" = "default@file"; + }; + }); + + sonarr = mkIf cfg.sonarr.enable (makeDefault { + image = "lscr.io/linuxserver/sonarr:latest"; + environment = { + DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:sonarr"; + TP_DOMAIN = "themepark.${config.my.domain}"; + TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "8989:8989" ]; + volumes = + [ "${config.my.storage.getConfigPath "sonarr"}:/config" "${config.my.storage.main}:/storage" ]; + labels = { + "homepage.group" = "Media"; + "homepage.name" = "Sonarr"; + "homepage.icon" = "sonarr.png"; + "homepage.href" = "https://sonarr.${config.my.domain}"; + "homepage.description" = "Series manager"; + "homepage.weight" = -70000; + "homepage.widget.type" = "sonarr"; + "homepage.widget.url" = "https://sonarr.${config.my.domain}"; + "homepage.widget.key" = "\${secret_sonarr_api_key}"; + "traefik.enable" = "true"; + "traefik.http.routers.sonarr.entrypoints" = "websecure"; + "traefik.http.routers.sonarr.rule" = "Host(`sonarr.${config.my.domain}`)"; + "traefik.http.routers.sonarr.middlewares" = "default@file"; + }; + }); + }; +} diff --git a/terraform-nix/modules/containers/services/misc/default.nix b/terraform-nix/modules/containers/services/misc/default.nix new file mode 100644 index 0000000..14e8284 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/default.nix @@ -0,0 +1,144 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault; +in +{ + options.my.containers.services = { + actual-server.enable = makeEnableOptionDefaultTrue "actual-server"; + adguardhome-sync.enable = makeEnableOptionDefaultTrue "adguardhome-sync"; + forgejo.enable = makeEnableOptionDefaultTrue "forgejo"; + homepage.enable = makeEnableOptionDefaultTrue "homepage"; + themepark.enable = makeEnableOptionDefaultTrue "themepark"; + vscode.enable = makeEnableOptionDefaultTrue "vscode"; + }; + + config.my.containers.services.contents = mkIf cfg.enable { + actual-server = mkIf cfg.actual-server.enable (makeDefault { + image = "actualbudget/actual-server:latest"; + networks = [ "default" "external" ]; + ports = [ "5006:5006" ]; + volumes = [ "${config.my.storage.getDataPath "actual-server"}:/data" ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.actual.entrypoints" = "websecure"; + "traefik.http.routers.actual.rule" = "Host(`actual.${config.my.domain}`)"; + "traefik.http.routers.actual.middlewares" = "default@file"; + }; + }); + + adguardhome-sync = mkIf cfg.adguardhome-sync.enable (makeDefault { + image = "lscr.io/linuxserver/adguardhome-sync:latest"; + ports = [ "8082:8082/tcp" ]; + volumes = [ "${config.my.storage.getConfigPath "adguardhome-sync"}:/config" ]; + }); + + forgejo-server = mkIf cfg.forgejo.enable (makeDefault { + image = "codeberg.org/forgejo/forgejo:1.20"; + environment = { + USER_UID = 1000; + USER_GID = 1000; + FORGEJO__actions__ENABLED = "true"; + FORGEJO__database__DB_TYPE = "postgres"; + FORGEJO__database__HOST = "forgejo-db:5432"; + FORGEJO__database__NAME = "forgejo"; + FORGEJO__database__USER = "forgejo"; + FORGEJO__database__PASSWD = "\${secret_forgejo_db_password}"; + FORGEJO__mailer__ENABLED = "true"; + FORGEJO__mailer__FROM = "forgejo@${config.my.domain}"; + FORGEJO__mailer__PROTOCOL = "starttls"; + FORGEJO__mailer__SMTP_ADDR = "\${secret_smtp_host}"; + FORGEJO__mailer__SMTP_PORT = "\${secret_smtp_port}"; + FORGEJO__mailer__USER = "\${secret_smtp_user}"; + FORGEJO__mailer__PASSWD = "\${secret_smtp_password}"; + FORGEJO__service__DISABLE_REGISTRATION = "true"; + FORGEJO__service__REGISTER_MANUAL_CONFIRM = "true"; + }; + networks = [ "default" "external" ]; + ports = [ "3001:3000" "222:22" ]; + volumes = [ "${config.my.storage.getDataPath "forgejo-server"}:/data" ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + "homepage.group" = "Other"; + "homepage.name" = "Forgejo"; + "homepage.icon" = "forgejo.png"; + "homepage.href" = "https://git.${config.my.domain}"; + "homepage.description" = "Code forge"; + "traefik.enable" = "true"; + "traefik.http.routers.git.entrypoints" = "websecure"; + "traefik.http.routers.git.rule" = "Host(`git.${config.my.domain}`)"; + "traefik.http.routers.git.middlewares" = "default@file"; + "traefik.http.services.git.loadbalancer.server.port" = "3000"; + }; + depends_on = [ "forgejo-db" ]; + }); + + forgejo-db = mkIf cfg.forgejo.enable (makeDefault { + image = "postgres:14"; + environment = { + POSTGRES_USER = "forgejo"; + POSTGRES_PASSWORD = "\${secret_forgejo_db_password}"; + POSTGRES_DB = "forgejo"; + }; + networks = [ "default" ]; + volumes = [ "${config.my.storage.getDataPath "forgejo-postgres"}:/var/lib/postgresql/data" ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + }; + }); + + homepage = mkIf cfg.homepage.enable (makeDefault { + image = "ghcr.io/gethomepage/homepage:latest"; + networks = [ "default" "external" ]; + ports = [ "3000:3000" ]; + volumes = [ + "${config.my.storage.getConfigPath "homepage"}:/app/config" + "${config.my.storage.main}:/storage:ro" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.home.entrypoints" = "websecure"; + "traefik.http.routers.home.rule" = "Host(`home.${config.my.domain}`)"; + "traefik.http.routers.home.middlewares" = "default@file"; + }; + }); + + themepark = mkIf cfg.themepark.enable (makeDefault { + image = "ghcr.io/themepark-dev/theme.park:latest"; + ports = [ "8084:80" "8444:443" ]; + volumes = [ "${config.my.storage.getConfigPath "themepark"}:/config" ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.themepark.entrypoints" = "websecure"; + "traefik.http.routers.themepark.rule" = "Host(`themepark.${config.my.domain}`)"; + "traefik.http.routers.themepark.middlewares" = "default@file"; + "traefik.http.services.themepark.loadbalancer.server.port" = "80"; + }; + }); + + vscode = mkIf cfg.vscode.enable (makeDefault { + image = "lscr.io/linuxserver/code-server:latest"; + environment = { + PASSWORD = "\${secret_vscode_password}"; + PROXY_DOMAIN = "code.${config.my.domain}"; + }; + networks = [ "default" "external" ]; + ports = [ "8443:8443/tcp" ]; + volumes = [ "${config.my.storage.getConfigPath "vscode"}:/config" ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "Visual Studio Code"; + "homepage.icon" = "vscode.png"; + "homepage.href" = "https://code.${config.my.domain}"; + "homepage.description" = "Cloud IDE"; + "traefik.enable" = "true"; + "traefik.http.routers.code.entrypoints" = "websecure"; + "traefik.http.routers.code.rule" = "Host(`code.${config.my.domain}`)"; + "traefik.http.routers.code.middlewares" = "default@file"; + }; + }); + }; +} diff --git a/terraform-nix/modules/containers/services/networking/default.nix b/terraform-nix/modules/containers/services/networking/default.nix new file mode 100644 index 0000000..78d4de6 --- /dev/null +++ b/terraform-nix/modules/containers/services/networking/default.nix @@ -0,0 +1,57 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault; +in +{ + options.my.containers.services = { + traefik.enable = makeEnableOptionDefaultTrue "traefik"; + crowdsec.enable = makeEnableOptionDefaultTrue "crowdsec"; + cloudflare-ddns.enable = makeEnableOptionDefaultTrue "cloudflare-ddns"; + }; + + config.my.containers.services.contents = mkIf cfg.enable { + traefik = mkIf cfg.traefik.enable (makeDefault { + image = "traefik:v2.10"; + environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; + networks = [ "default" "external" ]; + ports = [ + "${builtins.toString config.my.networking.ports.internal.http}:80" + "${builtins.toString config.my.networking.ports.internal.https}:443" + "8080:8080" + ]; + volumes = [ + "${config.my.storage.getConfigPath "traefik"}:/etc/traefik" + "${config.my.storage.getConfigPath "traefik"}/acme:/etc/traefik/acme" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + }; + }); + + crowdsec = mkIf cfg.crowdsec.enable (makeDefault { + image = "crowdsecurity/crowdsec:latest"; + networks = [ "default" "external" ]; + volumes = [ + "${config.my.storage.getConfigPath "crowdsec"}:/etc/crowdsec" + "${config.my.storage.getLogPath "crowdsec"}:/var/log/nginx" + "${config.my.storage.getDataPath "crowdsec"}:/var/lib/crowdsec/data" + ]; + }); + + cloudflare-ddns = mkIf cfg.cloudflare-ddns.enable (makeDefault { + image = "oznu/cloudflare-ddns:latest"; + environment = { + API_KEY = "\${secret_cloudflare_api_token}"; + ZONE = config.my.domain; + INTERFACE = "eth0"; + PROXIED = "true"; + RRTYPE = "AAAA"; + }; + network_mode = "host"; + }); + }; +} diff --git a/terraform-nix/modules/containers/services/storage/default.nix b/terraform-nix/modules/containers/services/storage/default.nix new file mode 100644 index 0000000..70d6e37 --- /dev/null +++ b/terraform-nix/modules/containers/services/storage/default.nix @@ -0,0 +1,82 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault; +in +{ + options.my.containers.services = { + influxdb.enable = makeEnableOptionDefaultTrue "influxdb"; + qbittorrent.enable = makeEnableOptionDefaultTrue "qbittorrent"; + syncthing.enable = makeEnableOptionDefaultTrue "syncthing"; + }; + + config.my.containers.services.contents = mkIf cfg.enable { + influxdb = mkIf cfg.influxdb.enable (makeDefault { + image = "influxdb:latest"; + networks = [ "default" "external" ]; + ports = [ "8086:8086" ]; + volumes = [ + "${config.my.storage.getConfigPath "influxdb"}:/etc/influxdb2" + "${config.my.storage.getDataPath "influxdb"}:/var/lib/influxdb2" + ]; + }); + + qbittorrent = mkIf cfg.qbittorrent.enable (makeDefault { + image = "lscr.io/linuxserver/qbittorrent:latest"; + environment = { + WEBUI_PORT = "8081"; + # DOCKER_MODS = "ghcr.io/themepark-dev/theme.park:qbittorrent"; + # TP_DOMAIN = "themepark.${config.my.domain}"; + # TP_THEME = "frappe"; + }; + networks = [ "default" "external" ]; + ports = [ "8081:8081" "6881:6881" "6881:6881/udp" ]; + volumes = [ + "${config.my.storage.getConfigPath "qbittorrent"}:/config" + "${config.my.storage.main}/Downloads/Torrents:/downloads" + ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "qBittorrent"; + "homepage.icon" = "qbittorrent.png"; + "homepage.href" = "https://qbittorrent.${config.my.domain}"; + "homepage.description" = "Torrent downloader"; + "homepage.widget.type" = "qbittorrent"; + "homepage.widget.url" = "https://qbittorrent.${config.my.domain}"; + "homepage.widget.username" = "\${secret_qbittorrent_user}"; + "homepage.widget.password" = "\${secret_qbittorrent_password}"; + "traefik.enable" = "true"; + "traefik.http.routers.qbittorrent.entrypoints" = "websecure"; + "traefik.http.routers.qbittorrent.rule" = + "Host(`qbittorrent.${config.my.domain}`)"; + "traefik.http.routers.qbittorrent.middlewares" = "default@file"; + "traefik.http.services.qbittorrent.loadbalancer.server.port" = "8081"; + }; + }); + + syncthing = mkIf cfg.syncthing.enable (makeDefault { + image = "lscr.io/linuxserver/syncthing:latest"; + networks = [ "default" "external" ]; + ports = + [ "8384:8384" "22000:22000/tcp" "22000:22000/udp" "21027:21027/udp" ]; + volumes = [ + "${config.my.storage.getConfigPath "syncthing"}:/config" + "${config.my.storage.getDataPath "syncthing"}:/data" + ]; + labels = { + "homepage.group" = "Other"; + "homepage.name" = "Syncthing"; + "homepage.icon" = "syncthing.png"; + "homepage.href" = "https://syncthing.${config.my.domain}"; + "homepage.description" = "P2P file syncing"; + "traefik.enable" = "true"; + "traefik.http.routers.syncthing.entrypoints" = "websecure"; + "traefik.http.routers.syncthing.rule" = "Host(`syncthing.${config.my.domain}`)"; + "traefik.http.routers.syncthing.middlewares" = "default@file"; + "traefik.http.services.syncthing.loadbalancer.server.port" = "8384"; + }; + }); + }; +} diff --git a/terraform-nix/modules/containers/services/utils.nix b/terraform-nix/modules/containers/services/utils.nix new file mode 100644 index 0000000..8031266 --- /dev/null +++ b/terraform-nix/modules/containers/services/utils.nix @@ -0,0 +1,20 @@ +{ config, lib, ... }: + +with lib; +{ + makeEnableOptionDefaultTrue = name: mkEnableOption name // { default = true; }; + makeDefault = dockerConfig: + (dockerConfig // { + environment = { + TZ = config.my.timezone; + PUID = 1000; + GUID = 1000; + } // (dockerConfig.environment or {}); + volumes = [ + "/dev/rtc:/dev/rtc:ro" + "/etc/localtime:/etc/localtime:ro" + "/etc/timezone:/etc/timezone:ro" + ] ++ (dockerConfig.volumes or []); + restart = dockerConfig.restart or "unless-stopped"; + }); +} From 277ac88a383b1ca9b91b36fe3d64470f620f2e7e Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Fri, 12 Jan 2024 18:04:13 -0300 Subject: [PATCH 38/43] feat(terraform-nix): Implement basic docker config file templating utils Initial implementation of helpers to copy configuration files and optionally apply envsubst templating --- terraform-nix/lxcs/default.nix | 11 ++- .../modules/containers/services/default.nix | 15 ++-- .../assets/adguardhome-sync.yaml | 45 ++++++++++++ .../misc/adguardhome-sync/default.nix | 35 ++++++++++ .../containers/services/misc/default.nix | 13 ++-- .../modules/containers/services/utils.nix | 68 +++++++++++++++++-- terraform-nix/secrets/docker/default.env | 6 +- 7 files changed, 171 insertions(+), 22 deletions(-) create mode 100644 terraform-nix/modules/containers/services/misc/adguardhome-sync/assets/adguardhome-sync.yaml create mode 100644 terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index a44004d..41128af 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -91,9 +91,14 @@ let mountpoints = [ { mp = "/srv/storage"; volume = "/srv/storage"; } ]; - nix.modules = [ - { config.my.services.containers.instrumentation.compose.enable = true; } - ]; + nix.modules = [{ + config.my.containers = { + instrumentation.compose.enable = true; + services = { + adguardhome-sync.enable = false; + }; + }; + }]; }; }; in diff --git a/terraform-nix/modules/containers/services/default.nix b/terraform-nix/modules/containers/services/default.nix index 1373fc9..0d43756 100644 --- a/terraform-nix/modules/containers/services/default.nix +++ b/terraform-nix/modules/containers/services/default.nix @@ -17,9 +17,14 @@ in (import ./networking importArgs) ]; - options.my.containers.services = { - enable = makeEnableOptionDefaultTrue "containerized services"; - contents = mkOption { default = { }; }; - out = mkOption { default = mapAttrs (_: service: service.content) cfg.contents; }; - }; + options.my.containers.services = + let + enabledServices = filterAttrs (_: service: service.condition) cfg.contents; + rawServices = mapAttrs (_: service: service.content) enabledServices; + in + { + enable = makeEnableOptionDefaultTrue "containerized services"; + contents = mkOption { default = { }; }; + out = mkOption { default = rawServices; }; + }; } diff --git a/terraform-nix/modules/containers/services/misc/adguardhome-sync/assets/adguardhome-sync.yaml b/terraform-nix/modules/containers/services/misc/adguardhome-sync/assets/adguardhome-sync.yaml new file mode 100644 index 0000000..47626c1 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/adguardhome-sync/assets/adguardhome-sync.yaml @@ -0,0 +1,45 @@ +# cron expression to run in daemon mode. (default; "" = runs only once) +cron: "*/10 * * * *" + +# runs the synchronisation on startup +runOnStart: true + +origin: + # url of the origin instance + url: http://10.0.0.2:8082 + # apiPath: define an api path if other than "/control" + # insecureSkipVerify: true # disable tls check + username: $secret_homeassistant_user + password: $secret_homeassistant_password + +# replicas instances (optional, if more than one) +replicas: + # url of the replica instance + - url: http://10.10.2.20:80 + username: $secret_adguardhome_user + password: $secret_adguardhome_password + # autoSetup: true # if true, AdGuardHome is automatically initialized. + +# Configure the sync API server, disabled if api port is 0 +api: + # Port, default 8080 + port: 8080 + # if username and password are defined, basic auth is applied to the sync API + # username: username + # password: password + +# Configure sync features; by default all features are enabled. +features: + generalSettings: true + queryLogConfig: true + statsConfig: true + clientSettings: true + services: true + filters: true + dhcp: + serverConfig: true + staticLeases: true + dns: + serverConfig: true + accessLists: true + rewrites: true diff --git a/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix b/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix new file mode 100644 index 0000000..06c34c5 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix @@ -0,0 +1,35 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeAssetsDerivation makeEnableOptionDefaultTrue makeDefault makeRuntimeAssetsDir; + + name = "adguardhome-sync"; +in +{ + options.my.containers.services.adguardhome-sync.enable = makeEnableOptionDefaultTrue name; + + config = mkIf cfg.adguardhome-sync.enable (mkMerge [ + { + my.containers.services.contents = mkIf cfg.enable { + adguardhome-sync = mkIf cfg.adguardhome-sync.enable (makeDefault { + image = "lscr.io/linuxserver/adguardhome-sync:latest"; + ports = [ "8082:8082/tcp" ]; + volumes = [ + "/run/${name}/adguardhome-sync.yaml:/config/adguardhome-sync.yaml" + ]; + }); + }; + } + (makeRuntimeAssetsDir { + inherit name; + assets = makeAssetsDerivation name ./assets; + envPath = ../../../../../secrets/docker/default.env; + filesToTemplate = [ "adguardhome-sync.yaml" ]; + postExec = optionalString config.my.containers.instrumentation.compose.enable '' + docker compose --file ~/compose.yaml restart ${name} + ''; + }) + ]); +} diff --git a/terraform-nix/modules/containers/services/misc/default.nix b/terraform-nix/modules/containers/services/misc/default.nix index 14e8284..0b5a8b2 100644 --- a/terraform-nix/modules/containers/services/misc/default.nix +++ b/terraform-nix/modules/containers/services/misc/default.nix @@ -1,4 +1,4 @@ -{ config, lib, utils, ... }: +{ config, lib, utils, ... }@args: with lib; let @@ -6,9 +6,12 @@ let inherit (utils) makeEnableOptionDefaultTrue makeDefault; in { + imports = [ + (import ./adguardhome-sync args) + ]; + options.my.containers.services = { actual-server.enable = makeEnableOptionDefaultTrue "actual-server"; - adguardhome-sync.enable = makeEnableOptionDefaultTrue "adguardhome-sync"; forgejo.enable = makeEnableOptionDefaultTrue "forgejo"; homepage.enable = makeEnableOptionDefaultTrue "homepage"; themepark.enable = makeEnableOptionDefaultTrue "themepark"; @@ -29,12 +32,6 @@ in }; }); - adguardhome-sync = mkIf cfg.adguardhome-sync.enable (makeDefault { - image = "lscr.io/linuxserver/adguardhome-sync:latest"; - ports = [ "8082:8082/tcp" ]; - volumes = [ "${config.my.storage.getConfigPath "adguardhome-sync"}:/config" ]; - }); - forgejo-server = mkIf cfg.forgejo.enable (makeDefault { image = "codeberg.org/forgejo/forgejo:1.20"; environment = { diff --git a/terraform-nix/modules/containers/services/utils.nix b/terraform-nix/modules/containers/services/utils.nix index 8031266..e9c4c00 100644 --- a/terraform-nix/modules/containers/services/utils.nix +++ b/terraform-nix/modules/containers/services/utils.nix @@ -1,20 +1,80 @@ -{ config, lib, ... }: +{ config, lib, pkgs, ... }: with lib; { - makeEnableOptionDefaultTrue = name: mkEnableOption name // { default = true; }; + makeEnableOptionDefaultTrue = name: mkEnableOption name // { default = config.my.services.docker.enable; }; + makeDefault = dockerConfig: (dockerConfig // { environment = { TZ = config.my.timezone; PUID = 1000; GUID = 1000; - } // (dockerConfig.environment or {}); + } // (dockerConfig.environment or { }); volumes = [ "/dev/rtc:/dev/rtc:ro" "/etc/localtime:/etc/localtime:ro" "/etc/timezone:/etc/timezone:ro" - ] ++ (dockerConfig.volumes or []); + ] ++ (dockerConfig.volumes or [ ]); restart = dockerConfig.restart or "unless-stopped"; }); + + makeAssetsDerivation = name: path: pkgs.stdenvNoCC.mkDerivation { + name = "${name}-assets"; + src = path; + phases = [ "unpackPhase" "installPhase" ]; + installPhase = '' + mkdir -p $out + cp -r $src/* $out/ + ''; + }; + + /** + * Creates a directory at /run/${name} and optionally templates select files with envsubst + */ + makeRuntimeAssetsDir = + { name + , assets + , filesToTemplate ? [ ] + , envPath ? null + , envFile ? "docker.env" + , postExec ? "" + }: + let + configDir = "/run/${name}"; + hasTemplates = (envPath != null) && ((lib.length filesToTemplate) > 0); + in { + sops.secrets."${name}.env".sopsFile = lib.mkIf hasTemplates envPath; + + systemd.services = { + "create-runtime-${name}" = { + requiredBy = [ "multi-user.target" "docker.service" ]; + restartIfChanged = true; + serviceConfig = { + Type = "simple"; + ExecStart = + let + makeTemplateLogic = map (path: '' + ${pkgs.envsubst}/bin/envsubst -fail-fast -no-empty -no-unset \ + < ${assets}/${path} \ + > ${configDir}/${path} + ''); + in + pkgs.writers.writeBash "template-${name}" ('' + mkdir -p ${configDir} + cp -r \ + ${assets}/* \ + ${configDir}/ + '' + (lib.optionalString hasTemplates '' + while IFS= read -r line; do + export $line + done < /run/secrets/${envFile} + ${concatStringsSep "\n" (makeTemplateLogic filesToTemplate)} + '') + '' + ${postExec} + ''); + }; + }; + }; + }; } diff --git a/terraform-nix/secrets/docker/default.env b/terraform-nix/secrets/docker/default.env index 91d97cd..6f3ef31 100644 --- a/terraform-nix/secrets/docker/default.env +++ b/terraform-nix/secrets/docker/default.env @@ -3,6 +3,8 @@ secret_adguardhome_password=ENC[AES256_GCM,data:c2JoE7dyf9vl37nigcjs4QeAPRlNbOd/ secret_bazarr_api_key=ENC[AES256_GCM,data:iyuBnqa7Fgsb/MeiVrve0B3lYK7ttx1TG9PdFAGMZa2SvQ==,iv:rLoT8v87pCEMnMHCktq1TXMMWZRDXDTSRa4VbX1MNc0=,tag:+B22Gzb8jxzL18d5GqYYZg==,type:str] secret_cloudflare_api_token=ENC[AES256_GCM,data:pZSNJv8dU6OO+ZTYbPMnT3tT0ra6IhjZi3cQ7R/D2FUU1lBxKniAhzmk,iv:P7dB/Gd21ttuzq0ZpJlj2DWyY/4qnE1BGOt7bBMo88o=,tag:5v5fSY0XO8vLpPe2dXCJBQ==,type:str] secret_forgejo_db_password=ENC[AES256_GCM,data:HNDQUCHg/xzpVGZ2rf9Dgaj1tYBZyUfIKgv3uYG9WpftMkVwYJbgOx1mPv3xbMc=,iv:upkGHBCHvNZo7n60EpPW6O6bQ35V/r/0ZmKwh4ULahA=,tag:WyQVjhXAN3kpT10HAzU8wQ==,type:str] +secret_homeassistant_user=ENC[AES256_GCM,data:BA67SCNj5Lso,iv:GrQpOh4Q5BPQziYhhwywNFOCCmYL6cVnHqpnWkzO0q8=,tag:RFQEDiXms3HX+kyYBa+rtg==,type:str] +secret_homeassistant_password=ENC[AES256_GCM,data:RRUmsi6ZBRRzCO/69LaGTBC3rA==,iv:QHU1xsk5JzS+ogCzpJ9pTn1B6GISFss9Rw4marpQof4=,tag:bfboxPXtfHSZ7zosHiTX7g==,type:str] secret_jellyseerr_api_key=ENC[AES256_GCM,data:2yU94ymZ1kkfz/CilV1YMa5dQYSVMZkTEfPmQFIDFU/kzdXfh7xENu9NCVl8d1klpmVPd5Ku5f+ILqqJNt3sa2bRF8JcCA==,iv:HGpyh95mng5P6c6gGA5U96wZ7nR/ut2nOPKKqnyTTz0=,tag:alLq1miW8AEn3oA/XoBKAw==,type:str] secret_prowlarr_api_key=ENC[AES256_GCM,data:dzQ7v/85m6nEavMJmir7nP1gLYpyjXQacoyWiN45Tln9wg==,iv:EcWjpKn90XfISLSO1Enqdj++L1hpGx2aMSty/9R46i4=,tag:iorVqu8WSt4j4I92fV5NIA==,type:str] secret_qbittorrent_user=ENC[AES256_GCM,data:+HfgXFMpYJ03,iv:MBGN2qKkd8FjoRBy6o+rES129BPgTh9e3D8CAHmF2UQ=,tag:hm4gdsCPKCKJ9nATJyYvUg==,type:str] @@ -18,7 +20,7 @@ sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb2 sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRHd3WDlLL2VMTms4RUZp\nbUVsQzBPc3p0ODF1WENJR01wL3QvOVgxTWhBClJQU0ZpajJEcU1SMllKQVMycVhj\nM2ZFY3QyWk8rSnFBcndsOUZMTmhabk0KLS0tIG1MU3dBZVhpZkpvNWNiQWFBSExP\ncVArQi85VGxKVUZUcUxZL3dOeXdaaEkK47ArdnYhWhbAV/3CBdF4fzmRO9hQS6Bd\n4DaS/yk//oG+Aiy3mKgdUrn1mztj41mLVPf2Y/Ay6T2iiwHWCVy+vA==\n-----END AGE ENCRYPTED FILE-----\n sops_age__list_1__map_recipient=age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 -sops_lastmodified=2024-01-08T13:46:42Z -sops_mac=ENC[AES256_GCM,data:+tKedM8vTVUuzyAXKJIXLB12gDyni4argL5MwTr1ATVpzEXCgpPblMJ6xwVnjGoQJ98jlyuhnuet5MpKamOuUtTmj9AzQPue/F2+aN9/+J+vFqeZv6UpQOnUnkbZhvgo1DLiPMqaNYcEjv4BifiZhgANo87oT0GNjXv5MTLdzQA=,iv:Kj8AajEtFGCytvn24zwu0353TC8rE7SX2h6hNkccVlw=,tag:OBaXOsetDhlC9KQTTaPahQ==,type:str] +sops_lastmodified=2024-01-11T19:10:28Z +sops_mac=ENC[AES256_GCM,data:6VbDBjwDXbDFJfhABk4Fm1PBmfYWSapreeAVMF7Rx/fVwvV+43tf7JnlWq1Jp7cBH3qDVOWBqG3w6Q3vI+WA5Z2XowKGpiO500zEE3Dd7hwUHbnjBlaXKL5vreTlnuE7z6IHp/hTlG2wypOPH79GkfUmudOz4pXRozksl7qn95E=,iv:0uyqBjVPCm5wiYDckLrORrbxU6XHQKVxonudg92LAWQ=,tag:/NgorcUlKe/sQBMp8obxmg==,type:str] sops_unencrypted_suffix=_unencrypted sops_version=3.8.1 From ccdf8b3557c87cb7e312b4c5215438540ec6c85e Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Fri, 12 Jan 2024 18:05:59 -0300 Subject: [PATCH 39/43] fix(terraform-nix): Update docker compose disabling strategy Disable docker services by removing them from the compose file, as opposed to the existing workaround using profiles --- terraform-nix/lxcs/default.nix | 4 ++++ terraform-nix/modules/containers/services/media/default.nix | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/terraform-nix/lxcs/default.nix b/terraform-nix/lxcs/default.nix index 41128af..cdbafab 100644 --- a/terraform-nix/lxcs/default.nix +++ b/terraform-nix/lxcs/default.nix @@ -96,6 +96,10 @@ let instrumentation.compose.enable = true; services = { adguardhome-sync.enable = false; + jackett.enable = false; + jellyfin.enable = false; + mylar.enable = false; + readarr.enable = false; }; }; }]; diff --git a/terraform-nix/modules/containers/services/media/default.nix b/terraform-nix/modules/containers/services/media/default.nix index d362ef6..72c9570 100644 --- a/terraform-nix/modules/containers/services/media/default.nix +++ b/terraform-nix/modules/containers/services/media/default.nix @@ -73,7 +73,6 @@ in }); jackett = mkIf cfg.jackett.enable (makeDefault { - profiles = [ "disable" ]; image = "lscr.io/linuxserver/jackett"; networks = [ "default" "external" ]; ports = [ "9117:9117" ]; @@ -82,7 +81,6 @@ in }); jellyfin = mkIf cfg.jellyfin.enable (makeDefault { - profiles = [ "disable" ]; image = "lscr.io/linuxserver/jellyfin"; environment = { DOCKER_MODS = "linuxserver/mods:jellyfin-opencl-intel"; }; networks = [ "default" "external" ]; @@ -159,7 +157,6 @@ in }); mylar = mkIf cfg.mylar.enable (makeDefault { - profiles = [ "disable" ]; image = "lscr.io/linuxserver/mylar3:latest"; ports = [ "8090:8090" ]; volumes = @@ -233,7 +230,6 @@ in }); readarr = mkIf cfg.readarr.enable (makeDefault { - profiles = [ "disable" ]; image = "lscr.io/linuxserver/readarr:latest"; ports = [ "8787:8787" ]; volumes = From f77dea27fb46f7e986141c17f32f4f80b8b27c19 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Fri, 12 Jan 2024 18:06:42 -0300 Subject: [PATCH 40/43] fix(terraform-nix): Update docker compose command Add --remove-orphans arguments so that removed services are properly disabled --- .../instrumentation/docker-compose/default.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix index 3fa0bcd..e7aed48 100644 --- a/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/default.nix @@ -2,7 +2,7 @@ with lib; let - cfg = config.my.services.containers.instrumentation.compose; + cfg = config.my.containers.instrumentation.compose; user = config.users.users.root; @@ -22,14 +22,14 @@ let in with lib; { - options.my.services.containers.instrumentation.compose = { + options.my.containers.instrumentation.compose = { enable = mkEnableOption "docker compose"; }; config = mkIf cfg.enable { sops = { - defaultSopsFile = ../../../../secrets/docker/default.env; secrets."docker.env" = { + sopsFile = ../../../../secrets/docker/default.env; owner = user.name; path = "${user.home}/.env"; }; @@ -63,7 +63,9 @@ with lib; cp -r ${composeFile} ${user.home}/compose.yaml ''); docker-compose-up.text = toString (pkgs.writers.writeBash "docker-compose-up" '' - cd ${user.home} && ${pkgs.docker}/bin/docker compose up -d + cd ${user.home} && ${pkgs.docker}/bin/docker compose up \ + --detach \ + --remove-orphans ''); }; From 24c352f719867a80f876e82b88d07d7bad877f57 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sat, 13 Jan 2024 09:35:19 -0300 Subject: [PATCH 41/43] feat(terraform-nix): Proper themepark docker service Grabs themes from flake input pointing to the official repo, auto mounts it to themepark's container --- terraform-nix/flake.lock | 37 ++++++++------- terraform-nix/flake.nix | 14 ++++-- .../misc/adguardhome-sync/default.nix | 3 -- .../containers/services/misc/default.nix | 15 +----- .../services/misc/themepark/default.nix | 47 +++++++++++++++++++ 5 files changed, 77 insertions(+), 39 deletions(-) create mode 100644 terraform-nix/modules/containers/services/misc/themepark/default.nix diff --git a/terraform-nix/flake.lock b/terraform-nix/flake.lock index 812d918..7f86a42 100644 --- a/terraform-nix/flake.lock +++ b/terraform-nix/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "catppuccin-theme-park": { + "flake": false, + "locked": { + "lastModified": 1702845443, + "narHash": "sha256-6C4awzW7b5TG26lnJTKMlZfMmwmbJQiEKhy6i76kiUk=", + "owner": "catppuccin", + "repo": "theme.park", + "rev": "5d462d10fef50feb3f6cae447d64e55826cb52df", + "type": "github" + }, + "original": { + "owner": "catppuccin", + "repo": "theme.park", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -102,24 +118,9 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1703499205, - "narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "root": { "inputs": { + "catppuccin-theme-park": "catppuccin-theme-park", "flake-utils": "flake-utils", "mmproxy": "mmproxy", "nixos-generators": "nixos-generators", @@ -129,7 +130,9 @@ }, "sops-nix": { "inputs": { - "nixpkgs": "nixpkgs_2", + "nixpkgs": [ + "nixpkgs" + ], "nixpkgs-stable": "nixpkgs-stable" }, "locked": { diff --git a/terraform-nix/flake.nix b/terraform-nix/flake.nix index da6b640..47bd612 100644 --- a/terraform-nix/flake.nix +++ b/terraform-nix/flake.nix @@ -2,16 +2,20 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/23.11"; + # Misc flake-utils.url = "github:numtide/flake-utils"; - mmproxy = { - flake = false; - url = "github:cloudflare/mmproxy"; - }; + mmproxy = { url = "github:cloudflare/mmproxy"; flake = false; }; nixos-generators = { url = "github:nix-community/nixos-generators"; inputs.nixpkgs.follows = "nixpkgs"; }; - sops-nix.url = "github:Mic92/sops-nix"; + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Docker inputs + catppuccin-theme-park = { url = "github:catppuccin/theme.park"; flake = false; }; }; outputs = { self, flake-utils, nixpkgs, nixos-generators, ... }@inputs: diff --git a/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix b/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix index 06c34c5..53aae9a 100644 --- a/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix +++ b/terraform-nix/modules/containers/services/misc/adguardhome-sync/default.nix @@ -27,9 +27,6 @@ in assets = makeAssetsDerivation name ./assets; envPath = ../../../../../secrets/docker/default.env; filesToTemplate = [ "adguardhome-sync.yaml" ]; - postExec = optionalString config.my.containers.instrumentation.compose.enable '' - docker compose --file ~/compose.yaml restart ${name} - ''; }) ]); } diff --git a/terraform-nix/modules/containers/services/misc/default.nix b/terraform-nix/modules/containers/services/misc/default.nix index 0b5a8b2..ccd3fcb 100644 --- a/terraform-nix/modules/containers/services/misc/default.nix +++ b/terraform-nix/modules/containers/services/misc/default.nix @@ -8,13 +8,13 @@ in { imports = [ (import ./adguardhome-sync args) + (import ./themepark args) ]; options.my.containers.services = { actual-server.enable = makeEnableOptionDefaultTrue "actual-server"; forgejo.enable = makeEnableOptionDefaultTrue "forgejo"; homepage.enable = makeEnableOptionDefaultTrue "homepage"; - themepark.enable = makeEnableOptionDefaultTrue "themepark"; vscode.enable = makeEnableOptionDefaultTrue "vscode"; }; @@ -103,19 +103,6 @@ in }; }); - themepark = mkIf cfg.themepark.enable (makeDefault { - image = "ghcr.io/themepark-dev/theme.park:latest"; - ports = [ "8084:80" "8444:443" ]; - volumes = [ "${config.my.storage.getConfigPath "themepark"}:/config" ]; - labels = { - "traefik.enable" = "true"; - "traefik.http.routers.themepark.entrypoints" = "websecure"; - "traefik.http.routers.themepark.rule" = "Host(`themepark.${config.my.domain}`)"; - "traefik.http.routers.themepark.middlewares" = "default@file"; - "traefik.http.services.themepark.loadbalancer.server.port" = "80"; - }; - }); - vscode = mkIf cfg.vscode.enable (makeDefault { image = "lscr.io/linuxserver/code-server:latest"; environment = { diff --git a/terraform-nix/modules/containers/services/misc/themepark/default.nix b/terraform-nix/modules/containers/services/misc/themepark/default.nix new file mode 100644 index 0000000..18db49f --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/themepark/default.nix @@ -0,0 +1,47 @@ +{ config, inputs, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeEnableOptionDefaultTrue makeDefault makeRuntimeAssetsDir; + + name = "themepark"; + configPath = config.my.storage.getConfigPath name; +in +{ + options.my.containers.services.themepark = { + enable = makeEnableOptionDefaultTrue name; + catppuccin = { + enable = mkEnableOption name // { default = true; }; + }; + }; + + config = mkIf cfg.themepark.enable (mkMerge [ + { + my.containers.services.contents = mkIf cfg.enable { + themepark = mkIf cfg.themepark.enable (makeDefault { + image = "ghcr.io/themepark-dev/theme.park:latest"; + ports = [ "8084:80" "8444:443" ]; + volumes = [ + "${configPath}:/config" + ] ++ optionals cfg.themepark.catppuccin.enable ( + map + (file: "/run/${name}/flavors/${file}:/config/www/css/theme-options/${file}") + (attrNames (builtins.readDir "${inputs.catppuccin-theme-park}/flavors")) + ); + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.themepark.entrypoints" = "websecure"; + "traefik.http.routers.themepark.rule" = "Host(`themepark.${config.my.domain}`)"; + "traefik.http.routers.themepark.middlewares" = "default@file"; + "traefik.http.services.themepark.loadbalancer.server.port" = "80"; + }; + }); + }; + } + (makeRuntimeAssetsDir { + inherit name; + assets = "${inputs.catppuccin-theme-park}"; + }) + ]); +} From 4c0eff88bee1136faa4500b6c0981df2b81369d5 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sat, 13 Jan 2024 10:05:22 -0300 Subject: [PATCH 42/43] feat(terraform-nix): Add homepage config files --- .../containers/services/misc/default.nix | 19 +--- .../misc/homepage/assets/bookmarks.yaml | 24 +++++ .../services/misc/homepage/assets/custom.css | 8 ++ .../services/misc/homepage/assets/custom.js | 0 .../services/misc/homepage/assets/docker.yaml | 6 ++ .../misc/homepage/assets/kubernetes.yaml | 2 + .../misc/homepage/assets/services.yaml | 98 +++++++++++++++++++ .../misc/homepage/assets/settings.yaml | 25 +++++ .../misc/homepage/assets/widgets.yaml | 10 ++ .../services/misc/homepage/default.nix | 47 +++++++++ .../modules/containers/services/utils.nix | 11 ++- terraform-nix/secrets/docker/default.env | 15 ++- 12 files changed, 240 insertions(+), 25 deletions(-) create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/bookmarks.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/custom.css create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/custom.js create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/docker.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/kubernetes.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/services.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/settings.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/assets/widgets.yaml create mode 100644 terraform-nix/modules/containers/services/misc/homepage/default.nix diff --git a/terraform-nix/modules/containers/services/misc/default.nix b/terraform-nix/modules/containers/services/misc/default.nix index ccd3fcb..271c79f 100644 --- a/terraform-nix/modules/containers/services/misc/default.nix +++ b/terraform-nix/modules/containers/services/misc/default.nix @@ -8,13 +8,13 @@ in { imports = [ (import ./adguardhome-sync args) + (import ./homepage args) (import ./themepark args) ]; options.my.containers.services = { actual-server.enable = makeEnableOptionDefaultTrue "actual-server"; forgejo.enable = makeEnableOptionDefaultTrue "forgejo"; - homepage.enable = makeEnableOptionDefaultTrue "homepage"; vscode.enable = makeEnableOptionDefaultTrue "vscode"; }; @@ -86,23 +86,6 @@ in }; }); - homepage = mkIf cfg.homepage.enable (makeDefault { - image = "ghcr.io/gethomepage/homepage:latest"; - networks = [ "default" "external" ]; - ports = [ "3000:3000" ]; - volumes = [ - "${config.my.storage.getConfigPath "homepage"}:/app/config" - "${config.my.storage.main}:/storage:ro" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - labels = { - "traefik.enable" = "true"; - "traefik.http.routers.home.entrypoints" = "websecure"; - "traefik.http.routers.home.rule" = "Host(`home.${config.my.domain}`)"; - "traefik.http.routers.home.middlewares" = "default@file"; - }; - }); - vscode = mkIf cfg.vscode.enable (makeDefault { image = "lscr.io/linuxserver/code-server:latest"; environment = { diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/bookmarks.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/bookmarks.yaml new file mode 100644 index 0000000..df0197e --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/bookmarks.yaml @@ -0,0 +1,24 @@ +--- +# For configuration options and examples, please see: +# https://gethomepage.dev/latest/configs/bookmarks + +- Development: + - GitHub: + - abbr: GH + href: https://github.com/ + +- Social: + - Lemmy: + - abbr: LM + href: https://lemm.ee/ + - Mastodon: + - abbr: MT + href: https://mastodon.online/ + +- Entertainment: + - YouTube: + - abbr: YT + href: https://youtube.com/ + - Steam: + - abbr: ST + href: https://store.steampowered.com/ diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/custom.css b/terraform-nix/modules/containers/services/misc/homepage/assets/custom.css new file mode 100644 index 0000000..4b8e8c0 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/custom.css @@ -0,0 +1,8 @@ +[id*=inlinewidget] > .service-card { + display: flex; +} + +[id*=inlinewidget] > .service-card > .service-title { + width: 50%; + display: inline-flex; +} diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/custom.js b/terraform-nix/modules/containers/services/misc/homepage/assets/custom.js new file mode 100644 index 0000000..e69de29 diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/docker.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/docker.yaml new file mode 100644 index 0000000..566bb04 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/docker.yaml @@ -0,0 +1,6 @@ +--- +# For configuration options and examples, please see: +# https://gethomepage.dev/latest/configs/docker/ + +my-docker: + socket: /var/run/docker.sock diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/kubernetes.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/kubernetes.yaml new file mode 100644 index 0000000..aca6e82 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/kubernetes.yaml @@ -0,0 +1,2 @@ +--- +# sample kubernetes config diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/services.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/services.yaml new file mode 100644 index 0000000..9c4a667 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/services.yaml @@ -0,0 +1,98 @@ +--- +# For configuration options and examples, please see: +# https://gethomepage.dev/latest/configs/services + +- Main: + - Jellyfin: + href: https://jellyfin.$domain + ping: https://jellyfin.$domain + icon: jellyfin.svg + description: Media server + weight: -990000 + widget: + type: jellyfin + url: https://jellyfin.$domain + key: $secret_jellyfin_api_key + enableNowPlaying: true + - Home Assistant: + href: https://hass.$domain + ping: https://hass.$domain + icon: home-assistant.svg + description: Home automation + weight: -980000 + widget: + type: homeassistant + url: https://hass.$domain + key: $secret_homeassistant_token + - Nextcloud: + href: https://nextcloud.$domain + ping: https://nextcloud.$domain + icon: nextcloud.svg + description: Personal cloud + weight: -970000 + widget: + type: nextcloud + url: https://nextcloud.$domain + key: $secret_nextcloud_serverinfo_token + fields: + - activeusers + - numfiles + - numshares + - Proxmox: + href: https://pve01.$domain + ping: https://pve01.$domain + icon: proxmox.svg + description: Virtualization platform + weight: -960000 + widget: + type: proxmox + url: https://10.10.0.1:8006 + username: $secret_proxmox_user_ro + password: $secret_proxmox_password_ro + +- Health Checks: + - SnapRAID Sync: + href: https://healthchecks.io/checks/$secret_healthcheck_io_uuid_snapraid_sync/details/ + icon: healthchecks.svg + description: Syncs new data to SnapRAID array + widget: + type: healthchecks + url: https://healthchecks.io + key: $secret_healthcheck_api_key_ro + uuid: $secret_healthcheck_io_uuid_snapraid_sync + - SnapRAID Scrub: + href: https://healthchecks.io/checks/$secret_healthcheck_io_uuid_snapraid_scrub/details/ + icon: healthchecks.svg + description: Checks SnapRAID array for errors + widget: + type: healthchecks + url: https://healthchecks.io + key: $secret_healthcheck_api_key_ro + uuid: $secret_healthcheck_io_uuid_snapraid_scrub + - Borg Backup Main: + href: https://healthchecks.io/checks/$secret_healthcheck_io_uuid_borg_main/details/ + icon: healthchecks.svg + description: Backs up PVE data + widget: + type: healthchecks + url: https://healthchecks.io + key: $secret_healthcheck_api_key_ro + uuid: $secret_healthcheck_io_uuid_borg_main + - Borg Backup App Data: + href: https://healthchecks.io/checks/$secret_healthcheck_io_uuid_borg_appdata/details/ + icon: healthchecks.svg + description: Backs up application data + widget: + type: healthchecks + url: https://healthchecks.io + key: $secret_healthcheck_api_key_ro + uuid: $secret_healthcheck_io_uuid_borg_appdata + - Borg Backup Personal: + href: https://healthchecks.io/checks/$secret_healthcheck_io_uuid_borg_personal/details/ + icon: healthchecks.svg + description: Backs up personal data + widget: + type: healthchecks + url: https://healthchecks.io + key: $secret_healthcheck_api_key_ro + uuid: $secret_healthcheck_io_uuid_borg_personal diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/settings.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/settings.yaml new file mode 100644 index 0000000..6aac50b --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/settings.yaml @@ -0,0 +1,25 @@ +--- +# For configuration options and examples, please see: +# https://gethomepage.dev/latest/configs/settings + +headerStyle: clean +quickLaunch: + searchDescriptions: true + hideInternetSearch: true + hideVisitURL: true +statusStyle: dot +language: en +layout: + Main: + style: row + columns: 4 + header: false + Media: + style: row + columns: 4 + Other: + style: row + columns: 4 + Health Checks: + style: row + columns: 6 diff --git a/terraform-nix/modules/containers/services/misc/homepage/assets/widgets.yaml b/terraform-nix/modules/containers/services/misc/homepage/assets/widgets.yaml new file mode 100644 index 0000000..960fcce --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/assets/widgets.yaml @@ -0,0 +1,10 @@ +--- +# For configuration options and examples, please see: +# https://gethomepage.dev/latest/configs/widgets + +- resources: + cpu: true + memory: true + disk: /storage + cputemp: true + uptime: true diff --git a/terraform-nix/modules/containers/services/misc/homepage/default.nix b/terraform-nix/modules/containers/services/misc/homepage/default.nix new file mode 100644 index 0000000..e1b5fb9 --- /dev/null +++ b/terraform-nix/modules/containers/services/misc/homepage/default.nix @@ -0,0 +1,47 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeAssetsDerivation makeEnableOptionDefaultTrue makeDefault makeRuntimeAssetsDir; + + name = "homepage"; +in +{ + options.my.containers.services.homepage = { + enable = makeEnableOptionDefaultTrue name; + catppuccin = { + enable = mkEnableOption name // { default = true; }; + }; + }; + + config = mkIf cfg.homepage.enable (mkMerge [ + { + my.containers.services.contents = mkIf cfg.enable { + homepage = mkIf cfg.homepage.enable (makeDefault { + image = "ghcr.io/gethomepage/homepage:latest"; + networks = [ "default" "external" ]; + ports = [ "3000:3000" ]; + volumes = [ + "/run/${name}:/app/config" + "${config.my.storage.main}:/storage:ro" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.home.entrypoints" = "websecure"; + "traefik.http.routers.home.rule" = "Host(`home.${config.my.domain}`)"; + "traefik.http.routers.home.middlewares" = "default@file"; + }; + }); + }; + } + (makeRuntimeAssetsDir { + inherit name; + assets = makeAssetsDerivation name ./assets; + envPath = ../../../../../secrets/docker/default.env; + extraEnv = { domain = config.my.domain; }; + filesToTemplate = [ "services.yaml" ]; + }) + ]); +} diff --git a/terraform-nix/modules/containers/services/utils.nix b/terraform-nix/modules/containers/services/utils.nix index e9c4c00..15c4579 100644 --- a/terraform-nix/modules/containers/services/utils.nix +++ b/terraform-nix/modules/containers/services/utils.nix @@ -22,7 +22,7 @@ with lib; makeAssetsDerivation = name: path: pkgs.stdenvNoCC.mkDerivation { name = "${name}-assets"; src = path; - phases = [ "unpackPhase" "installPhase" ]; + phases = [ "installPhase" ]; installPhase = '' mkdir -p $out cp -r $src/* $out/ @@ -37,8 +37,8 @@ with lib; , assets , filesToTemplate ? [ ] , envPath ? null + , extraEnv ? {} , envFile ? "docker.env" - , postExec ? "" }: let configDir = "/run/${name}"; @@ -69,10 +69,11 @@ with lib; while IFS= read -r line; do export $line done < /run/secrets/${envFile} + ${concatStringsSep "\n" (builtins.attrValues (builtins.mapAttrs (name: val: "export ${name}=${val}") extraEnv))} ${concatStringsSep "\n" (makeTemplateLogic filesToTemplate)} - '') + '' - ${postExec} - ''); + '') + (lib.optionalString config.my.containers.instrumentation.compose.enable '' + ${pkgs.docker}/bin/docker compose --file ~/compose.yaml restart ${name} + '')); }; }; }; diff --git a/terraform-nix/secrets/docker/default.env b/terraform-nix/secrets/docker/default.env index 6f3ef31..7638d73 100644 --- a/terraform-nix/secrets/docker/default.env +++ b/terraform-nix/secrets/docker/default.env @@ -3,10 +3,21 @@ secret_adguardhome_password=ENC[AES256_GCM,data:c2JoE7dyf9vl37nigcjs4QeAPRlNbOd/ secret_bazarr_api_key=ENC[AES256_GCM,data:iyuBnqa7Fgsb/MeiVrve0B3lYK7ttx1TG9PdFAGMZa2SvQ==,iv:rLoT8v87pCEMnMHCktq1TXMMWZRDXDTSRa4VbX1MNc0=,tag:+B22Gzb8jxzL18d5GqYYZg==,type:str] secret_cloudflare_api_token=ENC[AES256_GCM,data:pZSNJv8dU6OO+ZTYbPMnT3tT0ra6IhjZi3cQ7R/D2FUU1lBxKniAhzmk,iv:P7dB/Gd21ttuzq0ZpJlj2DWyY/4qnE1BGOt7bBMo88o=,tag:5v5fSY0XO8vLpPe2dXCJBQ==,type:str] secret_forgejo_db_password=ENC[AES256_GCM,data:HNDQUCHg/xzpVGZ2rf9Dgaj1tYBZyUfIKgv3uYG9WpftMkVwYJbgOx1mPv3xbMc=,iv:upkGHBCHvNZo7n60EpPW6O6bQ35V/r/0ZmKwh4ULahA=,tag:WyQVjhXAN3kpT10HAzU8wQ==,type:str] +secret_healthcheck_api_key_ro=ENC[AES256_GCM,data:o7yIyUglRaKrcfsqzfxGyyZaiqHZF3bZjaZBfeHUarU=,iv:keGDiDRfHE5yDAQD0c0fUYs5eCLEPg2Z42Ii7mGKGdo=,tag:DUwaFtg7+eFUFImCXGExuQ==,type:str] +secret_healthcheck_io_uuid_snapraid_scrub=ENC[AES256_GCM,data:CIeFoUuaIKuPQZZalcn9rKorOvMnwF346RVdsRhznd2+/r5R,iv:D4e5r+n4/j0dhzfDHUSuXPElFcfJVNrl1lEBbQ4JEOQ=,tag:xVNvx1VB8mWM69QbJ/lLhg==,type:str] +secret_healthcheck_io_uuid_snapraid_sync=ENC[AES256_GCM,data:IOo0GaNS3lcc2om5bohrGI5qaNWYfEIpOgmFM+2Vwu4kTqV/,iv:zPYO8YWXR/pluG6jBF4qvz6Y7WTMG6OflnmYe6CrEK8=,tag:qAwOAzpdfbXZAafWm/JlZg==,type:str] +secret_healthcheck_io_uuid_borg_appdata=ENC[AES256_GCM,data:Wkxag+rtH5f7jRPpErVKsSUA1PUJS3Trf+jL8ybmzQovYufg,iv:HOl6gcEOCFQDSn/3doTbdD8vDBrgx0uCk6XeBOZf9UU=,tag:P4RZzN9E43hA7DTTKEpSMQ==,type:str] +secret_healthcheck_io_uuid_borg_personal=ENC[AES256_GCM,data:Duj6+f3cC9vXBEJCmQmRJgmgCDH1DJGqpamFWxyyejtgr9pY,iv:IVIV918Q+TgTsy87TnIrDsa8OTMR3RYofR8geSI28p0=,tag:3id35Sn3v3bKZn67Sozdkw==,type:str] +secret_healthcheck_io_uuid_borg_main=ENC[AES256_GCM,data:ZaLYwFkDdjd5pg3pUCjRCm5R796LCAxzczG9+5A44oxM5EUu,iv:tQBHCT1CQrJIvSsZUvQ/vzYqtJEchXyc6wuJ5/PwXTI=,tag:S1BIbt+UuheOXlfQR+/JGA==,type:str] secret_homeassistant_user=ENC[AES256_GCM,data:BA67SCNj5Lso,iv:GrQpOh4Q5BPQziYhhwywNFOCCmYL6cVnHqpnWkzO0q8=,tag:RFQEDiXms3HX+kyYBa+rtg==,type:str] secret_homeassistant_password=ENC[AES256_GCM,data:RRUmsi6ZBRRzCO/69LaGTBC3rA==,iv:QHU1xsk5JzS+ogCzpJ9pTn1B6GISFss9Rw4marpQof4=,tag:bfboxPXtfHSZ7zosHiTX7g==,type:str] +secret_homeassistant_token=ENC[AES256_GCM,data:8+3ZefH8VowfWOSw3MLv56VT4jrqKSKpr/u2xSDNtwhJrK+hns7RNirrSip2GGx4gCu77kEoescoiHyPo78PYJwxjU1brXy8xtESntqbGFjFnL9IS7vsBQNvUwCIRPnFnJbR39E8cBf4aFsFL5qmqIgOq65T1kLAR0l/fgaEJdW6OfQxYdRsfcV7leQrSavPJj5dWEL0UFSvutxiQL0NxHk59GoqtPeQxJOV4z9f4XTIvBOjY812,iv:IIOBu1sZbzwTw35AxdlFSddJpHZ3hX/5Yx9JcFy9Nw0=,tag:n2LjinVYLduF7uejSEL+eg==,type:str] +secret_jellyfin_api_key=ENC[AES256_GCM,data:+lsBu6Ojc5Qj8ahmciM1Mq5K9fBl4sUQk6+iDurxXQ8=,iv:IGauk8rdSCKWZier8nZpqIalyxJQMi77b6xyFXVmOoM=,tag:BOvzYVu01w+IEzKWe93LJw==,type:str] secret_jellyseerr_api_key=ENC[AES256_GCM,data:2yU94ymZ1kkfz/CilV1YMa5dQYSVMZkTEfPmQFIDFU/kzdXfh7xENu9NCVl8d1klpmVPd5Ku5f+ILqqJNt3sa2bRF8JcCA==,iv:HGpyh95mng5P6c6gGA5U96wZ7nR/ut2nOPKKqnyTTz0=,tag:alLq1miW8AEn3oA/XoBKAw==,type:str] +secret_nextcloud_serverinfo_token=ENC[AES256_GCM,data:vp01u4jYB5cZaKotqk9M/uup3637jXN1J6CYS4c=,iv:SZLsjAG2Ivf2/ny8V77TdgXDzfTBch+0FA+waInZZWs=,tag:V6hfbPCTupZeVznERvXMxw==,type:str] secret_prowlarr_api_key=ENC[AES256_GCM,data:dzQ7v/85m6nEavMJmir7nP1gLYpyjXQacoyWiN45Tln9wg==,iv:EcWjpKn90XfISLSO1Enqdj++L1hpGx2aMSty/9R46i4=,tag:iorVqu8WSt4j4I92fV5NIA==,type:str] +secret_proxmox_user_ro=ENC[AES256_GCM,data:7ir/5FLZySvhRq62VmVU/przXiwFiJLacN47P84=,iv:Df5EHW+BsKsUfs1cl1hEjiGTK7KQMxDMXnfYWXO2uAI=,tag:7CB3JbZmUUMAq0c8bpl2/Q==,type:str] +secret_proxmox_password_ro=ENC[AES256_GCM,data:c1EsZHj5U8yesw3B3EHbg+IBYa96OXDmJwA4Hs5qtt87Izge,iv:veA5m1ZmdaYhWKHPEXHZ/5g9mgfGz9dVWmktscV0JQw=,tag:QllCdph3vwTPVHVJw1R3eg==,type:str] secret_qbittorrent_user=ENC[AES256_GCM,data:+HfgXFMpYJ03,iv:MBGN2qKkd8FjoRBy6o+rES129BPgTh9e3D8CAHmF2UQ=,tag:hm4gdsCPKCKJ9nATJyYvUg==,type:str] secret_qbittorrent_password=ENC[AES256_GCM,data:YJGQXhcwWpP0cX/9R1hW2L2B3cxn6Xysk/ucBzaj/Kt3JRMBjulH2EgetlRdTbfyMxQbBEhZ++WvNKNX1yGungdVF3DblK7E3qQBuvOIqI87oMUNdKLECeR+xF277V5MpJMS1oLqaYkvB57v8dJ9Kogr/eXmxNN8lJw=,iv:Aue+jYbxY98/vgOoU1BADLdEeuI4Hr42dkeXK0YXr5M=,tag:Pdo4sIukjzDLltcM5AnF+w==,type:str] secret_radarr_api_key=ENC[AES256_GCM,data:+YGvyohPer8t/cTrDMyUIFCzco69oMrd/Vg4MeELoBoIeA==,iv:uFtYFCozTwgQpwd9Lmk40rAQKLzmmc7Cf247nkdFHq4=,tag:8gF8tdE20rxuUp5UAFvuBg==,type:str] @@ -20,7 +31,7 @@ sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb2 sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRHd3WDlLL2VMTms4RUZp\nbUVsQzBPc3p0ODF1WENJR01wL3QvOVgxTWhBClJQU0ZpajJEcU1SMllKQVMycVhj\nM2ZFY3QyWk8rSnFBcndsOUZMTmhabk0KLS0tIG1MU3dBZVhpZkpvNWNiQWFBSExP\ncVArQi85VGxKVUZUcUxZL3dOeXdaaEkK47ArdnYhWhbAV/3CBdF4fzmRO9hQS6Bd\n4DaS/yk//oG+Aiy3mKgdUrn1mztj41mLVPf2Y/Ay6T2iiwHWCVy+vA==\n-----END AGE ENCRYPTED FILE-----\n sops_age__list_1__map_recipient=age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 -sops_lastmodified=2024-01-11T19:10:28Z -sops_mac=ENC[AES256_GCM,data:6VbDBjwDXbDFJfhABk4Fm1PBmfYWSapreeAVMF7Rx/fVwvV+43tf7JnlWq1Jp7cBH3qDVOWBqG3w6Q3vI+WA5Z2XowKGpiO500zEE3Dd7hwUHbnjBlaXKL5vreTlnuE7z6IHp/hTlG2wypOPH79GkfUmudOz4pXRozksl7qn95E=,iv:0uyqBjVPCm5wiYDckLrORrbxU6XHQKVxonudg92LAWQ=,tag:/NgorcUlKe/sQBMp8obxmg==,type:str] +sops_lastmodified=2024-01-13T12:51:46Z +sops_mac=ENC[AES256_GCM,data:Ucaq/E0cS1y8M250604EmMpmmnbtkZFHUPtMmMP5OhVJ+wFuCZKWi7PYs6hCpTIDiisyMfm7V/Enxg2C3UW8gfNcjq1vOHJdo/cCvA9lnsAGiMGBE4WIKyTNbpZbzWc/IT98WjJvGaobdICmhEtOhW7goVIV0z7t6+9m6ueG8fs=,iv:9S48tIABSP5+6h0I7enm0sQSkbzuurI8/bw2HhAWos4=,tag:NkrX5fGOJgCtALMOjPDYBw==,type:str] sops_unencrypted_suffix=_unencrypted sops_version=3.8.1 From e5943d53901a3a118644d35013d62852ec810ed9 Mon Sep 17 00:00:00 2001 From: Lucas Chaim Date: Sat, 13 Jan 2024 11:20:57 -0300 Subject: [PATCH 43/43] feat(terraform-nix): Add traefik config files --- group_vars/all/vault.yml | 357 +++++++++--------- .../containers/instrumentation/arion.nix | 33 ++ .../docker-compose/compose.nix | 9 +- .../modules/containers/services/default.nix | 15 +- .../services/misc/homepage/default.nix | 3 - .../services/networking/default.nix | 46 +-- .../networking/traefik/assets/dynamic.yml | 147 ++++++++ .../networking/traefik/assets/traefik.yml | 59 +++ .../services/networking/traefik/default.nix | 48 +++ .../modules/containers/services/utils.nix | 2 +- terraform-nix/secrets/docker/default.env | 6 +- 11 files changed, 496 insertions(+), 229 deletions(-) create mode 100644 terraform-nix/modules/containers/instrumentation/arion.nix create mode 100755 terraform-nix/modules/containers/services/networking/traefik/assets/dynamic.yml create mode 100755 terraform-nix/modules/containers/services/networking/traefik/assets/traefik.yml create mode 100644 terraform-nix/modules/containers/services/networking/traefik/default.nix diff --git a/group_vars/all/vault.yml b/group_vars/all/vault.yml index 2934c15..6fe245b 100644 --- a/group_vars/all/vault.yml +++ b/group_vars/all/vault.yml @@ -1,181 +1,178 @@ $ANSIBLE_VAULT;1.1;AES256 -30353065643734333062656564643064613663356237616630373035333838396461363838393164 -6632346535626263646535663562643765656138633865310a396166323563626264646230346631 -35313363653136376432653839626134663965356632393462643233636264346239373133313036 -3639313834383737300a616431336530383462353333356237323061623837366534373334326634 -35306433353034303239653730303164356432353762633936333632393365373335353164373439 -66653164383533623738623032353331333437306364353436333561353931623232656231353532 -65326238636262366663393131333533373633396237373762643362643262633632653064323632 -39333364393261343661656339353762396531663161313965646539346234303063373864373438 -32313132393730326233643466616630396239336461623337326265313131656264393338363332 -63636466663137643763666466353463613263613763623431383038326637303938343536373031 -30303936613139633863656265336430383132393530323638303131316231653461623130386431 -36646462666534643833336636386233326663323134346164653434656365353065343532666635 -38633932653830333861303836383836653433383863613561366661383665363961643238626337 -61623661346464393162666665393330326132663565383536313532383638666439336262353036 -33386261636230383130393763373436376363393737623734313631626536653738323030393835 -32616536633135303734356631616562616335383835646535386362313438653831616333663130 -36653733393736343361623462313864386133623534323164613063666566313562643632373662 -64306133653838306137646663336562636335643638646533623461373461666439623162623537 -37353534376630663433386438616537313362366332666236383766353437663238613332346137 -30366138366537363638316432623462313431353837316238613935396136396336353631303734 -63316138386464373265303166636536383436643562656133353033636231333132616237356536 -37386434643330346338373932616461353938303133666435393862373530373866313538396466 -30356134643333353336623834333537306565373233323663376638336139383739316361396263 -33626465666230303264306532343061636335386263653061613335376161396637626165373431 -34373064366365356163316431623638633364613262326432636131313039636366356336663638 -61663539386431373662663433373039623563383434373338336363386630623664363533333766 -63383333353533383563386561386231306531666130633531396430373437363661373761333739 -39386337363632616234343034646262386162653262303133353164303264363963613539353363 -33653263316138393834373432346465363839376563383463303566653265376166343237336430 -30366133323134616164323634353037346638373633653863373064326464633966376461356139 -63643362623838643963316365323063376535643330376636646331613831383438643464333634 -62386130633236303733623832613032653864393038366262663336376433353964643138303630 -36656630616532626430623366356538623038303938656162373532643531396562613536336535 -32656262303730333031386430353262616239393761393364376562303262616161643735616332 -65366463383066663064663963383135313232663963333035623965333830653064323561643334 -37653862663164616437323562356231343532613637626365336334353865313931356437646134 -30303363326435626330316334633162353539343463336234663730313632303138616438366438 -66373439336563326265303764626639643935656330656537373935663436343136366236356535 -61633931303066613061636662633937346265623835623661613036376338346535313935666361 -64313365353132306563616539666566646530306332663236623262333031616365393765643331 -38353466343834386566373063643531366539316231386434633237623537376464336338323061 -65646264613666363436626238373136353738323834616263666431623265663037343932306335 -65656334326363333432363339363463386133646533373638353930626639383064623538353639 -65333661623731376662396463656562626539393033636232383436633766383634356437373762 -32326563393234396234386338636432623861666235616332643763323832663564333839303766 -35663933643530613731663266633537323263653963393464356161346634333364633232333761 -33333962323030363162343363366439323031363836356435313136653135363936636431376263 -39323237366532316335326531656363613435393639353332383463623039363536613833636631 -38663230376463623839333737376635613965613239643437373532303335636130376237623463 -36636133383862306264393331393436616639356638343132326539643138656431356337333364 -35643631383938343735653965323333646333366639383564643434643933306135313137376130 -31313235346232366336643063353732316632343931393331383637336637633862366664306531 -33303265623262353435653035393930346537653639386431313931323964663435313162336237 -39666238643130643964633032383434653266303862353665343634303461306338643437663561 -30343262636337386333623362633634393236366534666265313631323030646165386236303234 -31303639636266326339353935306633663762383735366334653531333464653332333564386239 -35356366303561333235636563663464386562316137626632323037356230353130313862643237 -34336539303435626562633539353831393462303061373830653336326330663139346339393032 -65616138346532366165323230393565653832643337376135316531613161353832613865376163 -66633832623064616162383936346631333835343565353139643937333637313766366661666137 -39616431333031633463643036366334326538306135313537366532313331666637303831333432 -62643766623061303630653530353732613733313366316139323765393935616339393034326237 -33396630616233633139643761343534353839336161636137646134623739623336623565326539 -63643334343366343435326536313231343534326632633434343337623034336434656538326266 -31343666346639353339343135316637646164636231313164623930303164656635653931313432 -37653832383165363031393163613234633836666538623033636333623336343638313839326163 -35396666626530306635393765303038653734396462336163373365366162323637646533386464 -66643839323734343138653634626535303163356436616638313964616462626265653637316331 -37623164636632383236633738623638366332626265383262353736323661303736353965383130 -33333161343234343232373733363430363866613565396538626233353935313266393131643132 -62323333313337633966376164343461366639323530313239346537626662353939303161313837 -61323766316332653939643962363832303466636339643962376339663534616638393836346631 -65343334633462376433303465313364383666613736326264623430633235373137653362353565 -30633164653837326233346430633739366135653464623638313133646362616631383034353863 -66353163353666613963613438333365393338623861653937343062303934323237343134623837 -30396665326236656536623835313934613536373561396438343531613734346133333036616533 -66643366626361643836303761376634336630313061666233346665636332663764376637633533 -32633035663762636265666436323037653732303239383632333563623136326462336139303739 -37636533326364623765336533333563636163346234346461376530323234393866383161383734 -35306238353965383534363033383264383736306136313938623738363634616361663430626238 -64633165363834366530303437323261333036343436333436393765366137353966363934396633 -38343562396563646137396163386236343131646561643164643365666234646434663265353263 -31623865613838373037623035623766626139333431616434616363643830363237363861303538 -65613564376638376636303238343836323161336466333765336262613739326462623062376666 -66653566323731346231663566313438343630366237643035636439306137636439306435656162 -39316165333636366530653837353961663161363036646663626565396261313562666464656436 -34386362623534643964613661363966626231393634623131303330326334323732616235326634 -34326533316438396535353330303566366234616437633738326331623435356232373836666537 -36326332393163306564383935646161633130383130393461346538356137303466643930393239 -62626365636438336461303933333064653664313637346436643563656165323036313030663164 -63373363346661326334306436646438396332643434313636323961356561613837616363646332 -33343837363361373235666463363237616538386466643233663939393035623935653631383165 -39326264666637303634356638373663623036623432616564356231323464653761343666323836 -62633330303864346531343534363439626365643461363430363139383430313034663761343064 -37336335386633353937653162383131376664666630636632613166653930666233663265396530 -62633538616131373335666331636566336637613038616562663734616138396139326134323365 -31643837626665653632633637666538633262643561306664333631383638343636343639653136 -63653962646365323132336637336436386133313836343865303264373639366430346338363536 -33306364663232393536323866616137313531616635666236323063656534643934386534346664 -34636238376566366164396162306631383164303830386636356331633032356637663135373334 -35363334376463353562316539656638643461376164666434353263363933343364326265656530 -63643663656262353461386638383565386462356136633135313262646336666365663138373662 -64316563353064323063333161346232613366343766323066366465386431666330306232386331 -32623434313433646630303066303362383130656439393562313231616535323938376333626236 -39316264613361626139303166373462323364376461323738353134376330326233333637613436 -63376163343433663539393837333564616366356166366164613434613135323036633136653830 -61343666333861653138616432396464626331303261373837653231386237313133383530646633 -33353564623862396463616162313239326665376538306431306539626337643731613530643838 -35633232643834316636663431303063316235363532323434646133363739303430613564636661 -37656563616665366230383435326333343538386262353266626531356637383536626638323736 -65393731393435316230303736346561626362366166643331316539633937623964336336343766 -38326363313539613963323565306139643133333732663361353964363063363234323962373933 -38363466623033656333663566363330353766653864356132316530386333306139363139336438 -39353466383536643633663431663064396633663465663439306630396234303031383235663739 -38623636303139346365636538313362356635616561383461653739613465353461363138363131 -66653933303634313231386634393234393165356632323332346234633739343134393534356430 -65373566306233303865313165363566353135613532656464633730346139333138346138666562 -61316635396365656630366237373062326233353430626331363062343961383264393238373339 -36336438393832333763363262313334636137373237383362303934383065356366353162323863 -61356237613863316439336130343164636339303035393832663638633530643236343365383966 -64626664323530343465303666306133643438326262653465356134666135386539353537623163 -63376633313639303231343633333037306235626537326239383562356230393265396339313865 -33356234653064643936393335646461383831333361386665393133343465373634623633616333 -38663732663235323638333533633738663263653732373139366334326638646232316562383965 -61666432633962643032393563663465316563353366623032636430373665383866663164313163 -33396635623239313933393162663764636637643037333831313666663438323237663931633061 -32636261343833303233383031313765386231643862643233663961666264623532633462346164 -63303638343838333831616434626161316231626235333136303063396539633136643932326334 -64333962376661386539306430633931393132396238333532653339623433363830313630393338 -37653338373037336562653562623565326664346636656138396538626262633464326662663163 -34353363613061623835313461623733303364643763333237316161303163373634356438396530 -34333365646665386265373430616366623030386366653862356433653062653061623163393134 -61303334613537633334663637333537353732653137626632613061663531663935396336323536 -30636534613061333736383637376264613463316164343830396631336635336534353661386461 -36663262663938353934383065626465396638393230623965656262303135346435653034353538 -32643865616635373735363862656431373162333164326362396261353564653334303439373939 -61386466303135306231333033623230343262343566343636666465313061663734613337336233 -30643332323065316263303531346439663133333030313633643735383030383662623363623261 -39343734383761393030396134313761633333393163353036393164316434646630313333626566 -64633233653537373230626634623337303266623161383364373066353465313034656634396136 -65656637363162646535383262393563323935626539316633323165346361396532373338383638 -33666638646531323437633966663565333738386266663139366662386434633635353531656264 -38353337306439366137366433363333646133313932313632666666613431346663323233663063 -39383234663262626438393938316431346439656539626338336533643339643464666363613762 -33303838336635323166336163356334633735323139373064316630343439303738656666383837 -34303938663734653261356632633230303566383863646366663132326161646262613633623135 -31303232386631323338656530336262356561353064313839373464316336653036396137643137 -64626435323831396235616362356564386135353464663262323661633061343061373061373962 -30313665623462376338363565626531316137633563303430626530366362336664373631646539 -36303066353533613035323435373131653438643266656538623032396232646433383261653865 -39356339626635306464656339663033613161343831666230653963636132363362303730613037 -62333738366139663939343739376637633732366364303763363037363366393961643765303738 -33653732346665666463386330656665386464333133326237643136396165616230346136313461 -33313535626234346536313130313839646235346665373161646161643365633565653963393839 -65666532643266636638323663316361303430613033613137626335333339656262396365353264 -38626536653935383363386162386362623539343136376534663638393366393531653661623834 -32646236313637393533306230396637326532343237666333633231666532616432613037313730 -61383865613633363234393634656131366336633136373339393435613032663434653865313735 -32343335653738353437613230643039646464663836643833333536333039386233663237663130 -38376462353633366163393334333239633337646466626564373838313765313964393735626532 -30653135376561373437393631636136316361376636653934653461323365656565323463666535 -31303931326561386365643630326165316637323661303361393464383139333936366165356136 -36353233623531343736383562313666373134613930326432306266663832646462646330306638 -63373534313533303961343763353866636163346335666630343634336462316136636463326163 -38363066336534666566653866333632333662353339393632363635346333363831646165373163 -35396430393238393062616136333864666264333833316263303465363962643066393562323266 -30653132373633663633333162643436653262623332346239343733396539643463663838666665 -64626533363262383864326465373332366163323836366235386330393230316637386663313933 -65646633313638633366303263656364393633313035346534643035663030336262393162336330 -37333937643433633639396361313531613463373634376664636562346334633839613665613331 -30323166313433353833396365653165356261666430393136386431393939383764373666333266 -35316638376464396666383764636261303832633833383634643065653831356563323431356664 -34643865313366613534366534376261653736353566663864636337313132653031323931396366 -35393034303832663839376161613166323537653430313338306364626638326632313834623364 -34356365313630636438613237396164363139383764356334393938313039633839633030396132 -30396263383566613538393035656237663766663837666231383133663762323432363762633461 -65653466643438366232643236376262356364646230363539313763306361626131386266353364 -61386631666537333734323335626632356133343961666132666134376337376632366135353464 -62626465633431303633613630643738383439303364666331633134396437373133393231373733 -33663338646432383161 +65663830313764343561343266343233303735616161346362343665383162316234316338663534 +6532326338616335323165643339633463623763613262300a393966303039363938633437663365 +38313864346566333634386330333132613936656436353537643930613861373366313366663563 +3834383466313363650a636564633134373533653530616434653761363238633766373438313961 +62633738313661663130376461623839383434613632343339636165396161323939663931373966 +65373339633736613564396534653031666264636536643862356361373736363862366461336137 +35396166383737333034616566623930376633393134643737323961353866343133653362316439 +37343830623638646364653164623034653862343635633731343937633130363065616263386464 +39626230373239383733623630663466633731653731636165663639643637393531393237393266 +64303364386139343731353566376364376535333439313166623636383231646461306534313237 +39633035646565623365373062366632373063303738323030333832623137346332643037663437 +62616232646438623639393761333566386230636663376330633033373138353937636539323962 +39306536343630343332633538666131383435376165323966333632383063633730353537376330 +65336135663161313731623435643366376566646363313462396339666239343437336431613239 +62623034333566333836336464666632646566303063333638356438613635643931323537353465 +33343632323234653066633930336539643831613332303662353334366231653562653262386161 +64653336653430623033656637396561663934343563656535613161353230613238316431303337 +32376330643731366330376665663261326536646132643064633264336263316639373461613439 +37306165616364396461383463363062326662633833626431366136663932396262353636383364 +35373737663366353066313964643861396333393964616637396461656462376539363932633239 +65383963313061396563306533326432386564363561396431396366623637363165653330336333 +62336564616233393436656330343863376162663865306264613566313931376231323762656264 +31616333656538326532373665353465663836323732313964313137393439343234333330373634 +62333764303066366533396665666666363131306466613065346462373533373866313137336434 +63333233343737303438643535396264663963663661363934303531666334393861623761323030 +38366137663061303866306331313363396435636437623461346662613764363733616431363139 +36626239353139306263616362316633666132623965646335383562653039626432383866373035 +64303235343534663961396465653935346336306239373965333230306366663535623930386161 +66303633643139643336623238663132383332346131643934323838366438653636373266303533 +30303036663138353965353165663831623038396532626465336530373037386463323131623133 +39623533666530323135643662646234656332646136386130373561343966346461653934633862 +31663332643564333634636233303533333361633261656130616438323438626661663132646665 +61333937623539353766623832346163643333626134646433613631306633363862356563353433 +36373836333661626362633032666565383566643139633731636261386334343265336538323565 +30666564343038303036323739373761373239306465306566613331653866376565326132343832 +35656436363864376234333165323332643063336437656366346635316132393762306361333335 +37306433633563646138653734326235333637623436656235363132343962656365393465633161 +64663961366364653763316365653139363465323562326434353032633830376364336464353361 +31343837356262333938376632653036306530363137363036616535376663363561633737313366 +31366565393335363237306439353930656538313237613234333431373263643137643963623133 +66386262366531316530336338343664666439306232613639623838663765633566616539353639 +64386239363861623864623637343032646232366265356534623162353266633134303132333332 +65383730323231616363623330333637643935383639353263393562363664363235346431303535 +66663936633963653962373133386463623035663132373531623833396237313134313461303961 +31643265323835336537623263323935333032343835383936303262326531353136346434366366 +65336339633735356266396535333139306561373965623237323066626631316630646431313065 +32346131373762363466633137386564663234623735396466326534613133333830636530373663 +36623534646464356234663132663838303537306232636632336461373865333338636266316362 +66376362386262343364613434623763663431623332663132386133346166633161356263326637 +65383831653737386661303838323431323363363064666132333465363962313738343765666234 +35366539353062393235616264316664353634666431636238386435616166653037303234363031 +65653361383765376238376633653031613336313630326335386435313436656532323364303738 +36336262336538346634333061653664346365353537626238346638623232656564303934623936 +30343966383833313435393835626561343935373437643032633738323538306261643336303564 +61356434633838333639656631353133613734386465646261653635306161323361376332333461 +36336662613866626662656430643864653438653463316562373534643631366264653930363165 +62656361343863306664613332343736376533326632643061633838333263343466653632383335 +61393461393463346339326532366331333031393332333664353832396533373564333136396533 +65636261666434656465306666376233326136653532396165613437666263373333623138653639 +63336139646165313637383765383163656132373933353463616261326162653439313162323531 +32326164653530323738353439373630633565323135396463623032636436323139646434393537 +38393438666534316634373665326334633766383432363038663531653732313730643763656661 +32386332366166323430383231326561353032613135356238623637653430653837663465353164 +32396439643639633933613937343139333639383434376434343865356566313363626630323433 +32376531613132646530633764386338396133663139376634306666346138333764623865306363 +33333237333736346431653764626266316336393164633934303765353632363230613536366638 +32643735653231313266366331366438326637626230316130333861663133666334323434333665 +30366664353361613738303334363833376233343631396637636538623636623866616665646166 +34623836343535636266386630646331393130663266656561313231666131643463373766323166 +62353365333633626333393532636334613836623237326561643938363265396235346331303431 +39306537353764663438363131343335646639313732356336653763323735386430383033376230 +34663131613530656263656361383836626430336165633264623935396164613038626662336436 +39376161646334323662623463653430363761643165613464383635356534356266616335343066 +30346634343165393834643030323531336631323439653966666333613031363734373236326565 +33303431303334356263346438336635373764393030343735383438386162313835343632653933 +32303036313031353038653439306265613832323736313339613937376137646339386534346365 +31626330666263306435306636313935363637366333333534633238366134396130616239643764 +63663337383935653932376138626131636537383664323361366639663238663633373738316634 +31393834333564653439623563663737646264336666366134343537636163343966663135373639 +34323062313862336436656362396231393336366465633532356633316165323532303261353032 +66656531343333323637663632393766316235663231656134333862623334386133663666643239 +36313433303331373237313130303038636532613132303734306361373662383935363563396131 +30323230373663386638393237376635623264613461626130373539616435613966383763643463 +34633065613938336634663537393134633537666138366633633465663963393461333830643265 +62386437653538396265363361623565383933623565373962616465653236663034336364323034 +37363266306634383937316235313236306138616234666162643162333166663233323138393162 +63306135393730333165343238633065386330373331323034623032666665383837663236626565 +32373536633563613766343135613862346666653263303865663335613862343061363435333662 +64666131656666313062396237346632363130323230363661393133383932386362623763353834 +36646434656337386231636238373962343739333162373863646463626565646263313564376665 +36626230666439396536313637666339396562363034663132376262346464613738643364306437 +38633861363661663463343461333266323231343339386364613464356631303664373164383838 +62333535323930353065613738333166333338346131326264383933633032343135326463373834 +32366138643536336230373437363333393339353032306166356233323134373830356538356539 +61636464653430383465383731383839616162646436653232333065633035313163333063343066 +37353761306535636335613066376164393537363361313263326463353266306662643032363662 +66613662623763343737663534333033343934376134613437626565333661666666323939366366 +31623261303466383836666239316561643165376232373639373166666664336263306563323434 +30643439363432313838653135313164646439383464363635303433373937663536316137316333 +64313133303434613863333833663264396133336533653635633064656432393131373232323833 +36616336353761373539666564326539393464373935373736316237636334353265326236623439 +33643562613963346431663761366366303666353036323336373465663239363236366434373137 +39336437653830303965636563343237656437386365336462346435653639306239373535353432 +62613134313431323964376662643732656438306261303431343466383666646431386535356639 +33643964643961356162656534613938633737306163653837373138336330396631336166326266 +34666233313663326537653533663666393036663131343764616438343738656166303731333436 +38653039653739343366656335646561643738633564393664643635383462383037313032653437 +64383236376161643737653566313462303264376538663364653334323431376139343534643934 +31343066376338363566363336333333623133646466646662653230396334373731386531333562 +35343031333536316665383663393963633763653030313439653565326335636163363462636363 +65326436356138633236613432336533313237386634313062646431333032346564323435613530 +30336164393332343562383339653863346461653336303237396236303736313064313532396432 +35383434653466346137366162343866346262313338363137653364393834363234653063326230 +65616366316463396434343534373137633835656238646262393839656462356362323832323666 +37643237633532353633363636363261666639373433663536373464313766306435656665323732 +35373535336430643866633932343130626364623965643034663137653031363464363134343934 +31303962363261323066653438313736633634633761373537353037323536613963306334363164 +62653337386434333731666436383061383866303235663130656431636439336434356438306139 +32326662633062386134303932396439616537383430303065306135313662323033636465363135 +33393963633232356461666530343339393866633765383163626466323531653637356535303839 +38336562313935646564353565343461343539656366643266626436653431653031613435356338 +35633064633965646532333866383532636438323066666431616137626562656362313838323031 +30633033303732343836343862376132306436336239633336656134373638316638343439306530 +39643830333034336562663734383237386230623461616264323530386236636533623661336263 +32333335303062656336396131663734323961626461633664663537333334333136373033636531 +31373964653035616135333334303637636234323639333562646664663137373966333438393638 +66393336663561323164623566313663653262656636663832383464343839343364623563373837 +34313435623630653735643961323336383566636135646636386433353133643637666432643536 +30666165653665396138643230663565623063393233383563623061666336663634373763393063 +62616233613565653030343861363964616562356435323963363262333331396463326539356566 +66383766626635333566626634323261666430633439333736343436613563386134643637666235 +30333330626232383834343961373237393561303638373863313035643831313162353963343637 +39376435626461343734363538336333383934626334643565363730343437653364636562316336 +37663965383030323061336462393938363334643236653239316666373761653262333734376364 +30633933393030376563383765636331386362376264666464386462616330346536613436646438 +65366230356231336636643230663732633932646164303839613037383336313035356162323862 +63363439313261373038633439326163333736616539383734663437366136333531643563396131 +34356237613630653430316361383434636262323033306636353761396434383532636234656262 +61363733643630373234363662393130626633353732666536346365623561336339363765396639 +65343366346561316138366330633933663764656237626132663337363536303961373337643932 +65623364663065336264396661646638393034333165326334346137396637383831353765396133 +37333330313366653965636665646636396365316437626437393333336137633137666465353261 +37383666326366373435633430313834396333386464333134373532643437663137633066333036 +30383766343530346632393739383463336335313630386163626438366533613737393038663134 +38353232613962626361386339383031646664363866636361653263636636613862616136663035 +63326464316362636437383635326631316637346362323461626430666235353130333463663734 +64353733383166626361616665623136623135666430663633633461386462356139386463326637 +35336565666436313830323665666361356633363061366232396632633433343066646430623838 +62653437353066393632306461323431396532653863626263383366383066333731353137613039 +65613138376336376235326664643230336132316432643363353937616234613037353034383530 +62376238333737366162323937613331326530396565653034623862656438393064306639383235 +39643766363337643031633136616539393233623239306334313936373064663264666430623337 +39616262303335323937333339333062313139626437333138626431393031396332333535643632 +35646565383038653538383039616537626433393563613764373533376562373461356333353665 +36386636306237663332333238636139393637653765623631353737353639353861373936393366 +62623737383362333461366538343739613035336533383439366639386566383362653861653237 +34626464396161646335666336326566663631633236306435363862653461663762396634363034 +38323430626666623130363232393330313961366661653166373939636133333538333633363532 +38346536663161313337343036383662323739613533613461663030643431366332613732316536 +36626563666331333465626531393665363739366430323631346562656665383836663137353463 +63393239646466626337633638613239343433666562326265353835656339303933623462333265 +34303836343763346335373238303235333138663031366462333033656665666236336232373530 +64303762346533376433623635356564613734366137653736653461326462383036333863623763 +31383161323030383664386665333864356636336465376437623635323831393038393466393137 +30316263666438613965336663386337363433353966653033386438633835663064623432303332 +34316338356666313831313631666664393861303332376532326261373965383665353363313365 +63666264376331363061643661346331386133656537373363306530663936343533333962386531 +37623539666366623361343331393364613566396435396432303335613764613435663030373765 +31316131643439316162396462393263383536393234653036373961393837333138333064313434 +37303139636436393066353565343665616364636665316531656163336436346263666261386338 +62333536636162386330336661616531613331363631656566333762646666343663613061636461 +61666634646166333337383032323738353136346534323564316134633865653163656638333231 +31346266623764613366663737656164383431653537323433666339663131356266656431396364 +36383139636338376434633039633431396234316465366531663937383932346661636636666133 +66376465656166643563386530316531633931633832346665356462333135383036306235333661 +36333561306631383365376237656433376362663939383063343836336238616230663762623036 +3832 diff --git a/terraform-nix/modules/containers/instrumentation/arion.nix b/terraform-nix/modules/containers/instrumentation/arion.nix new file mode 100644 index 0000000..6bad91d --- /dev/null +++ b/terraform-nix/modules/containers/instrumentation/arion.nix @@ -0,0 +1,33 @@ +{ lib, ... }: + +with lib; +{ + imports = [ + inputs.arion.nixosModules.arion + ]; + + virtualisation.arion = { + backend = "docker"; + projects."homelab".settings = { + services = { + "whoami".service = { + image = "traefik/whoami:latest"; + restart = "unless-stopped"; + ports = [ "80:80" ]; + networks = [ "default" "external" ]; + }; + }; + networks = { + default = { + name = mkForce "default"; + internal = true; + ipam.config.subnet = [ "172.16.80.0/24" ]; + }; + external = { + name = "external"; + ipam.config.subnet = [ "10.10.250.0/24" ]; + }; + }; + }; + }; +} diff --git a/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix index a0e792c..974ba39 100644 --- a/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix +++ b/terraform-nix/modules/containers/instrumentation/docker-compose/compose.nix @@ -1,10 +1,15 @@ -{ config, ... }: +{ config, lib, ... }: +with lib; { name = "homelab"; version = "3.6"; - services = config.my.containers.services.out; + services = + let + enabledServices = filterAttrs (_: service: if service ? _type then service.condition else true) config.my.containers.services.contents; + serviceContents = mapAttrs (_: service: if service ? _type then service.content else service) enabledServices; + in serviceContents; networks = { default = { diff --git a/terraform-nix/modules/containers/services/default.nix b/terraform-nix/modules/containers/services/default.nix index 0d43756..4c3f5b7 100644 --- a/terraform-nix/modules/containers/services/default.nix +++ b/terraform-nix/modules/containers/services/default.nix @@ -17,14 +17,9 @@ in (import ./networking importArgs) ]; - options.my.containers.services = - let - enabledServices = filterAttrs (_: service: service.condition) cfg.contents; - rawServices = mapAttrs (_: service: service.content) enabledServices; - in - { - enable = makeEnableOptionDefaultTrue "containerized services"; - contents = mkOption { default = { }; }; - out = mkOption { default = rawServices; }; - }; + options.my.containers.services = { + enable = makeEnableOptionDefaultTrue "containerized services"; + contents = mkOption { default = { }; }; + out = mkOption { default = {}; }; + }; } diff --git a/terraform-nix/modules/containers/services/misc/homepage/default.nix b/terraform-nix/modules/containers/services/misc/homepage/default.nix index e1b5fb9..c84635d 100644 --- a/terraform-nix/modules/containers/services/misc/homepage/default.nix +++ b/terraform-nix/modules/containers/services/misc/homepage/default.nix @@ -10,9 +10,6 @@ in { options.my.containers.services.homepage = { enable = makeEnableOptionDefaultTrue name; - catppuccin = { - enable = mkEnableOption name // { default = true; }; - }; }; config = mkIf cfg.homepage.enable (mkMerge [ diff --git a/terraform-nix/modules/containers/services/networking/default.nix b/terraform-nix/modules/containers/services/networking/default.nix index 78d4de6..936e224 100644 --- a/terraform-nix/modules/containers/services/networking/default.nix +++ b/terraform-nix/modules/containers/services/networking/default.nix @@ -1,4 +1,4 @@ -{ config, lib, utils, ... }: +{ config, lib, utils, ... }@args: with lib; let @@ -6,30 +6,26 @@ let inherit (utils) makeEnableOptionDefaultTrue makeDefault; in { + imports = [ + (import ./traefik args) + ]; + options.my.containers.services = { - traefik.enable = makeEnableOptionDefaultTrue "traefik"; - crowdsec.enable = makeEnableOptionDefaultTrue "crowdsec"; cloudflare-ddns.enable = makeEnableOptionDefaultTrue "cloudflare-ddns"; + crowdsec.enable = makeEnableOptionDefaultTrue "crowdsec"; }; config.my.containers.services.contents = mkIf cfg.enable { - traefik = mkIf cfg.traefik.enable (makeDefault { - image = "traefik:v2.10"; - environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; - networks = [ "default" "external" ]; - ports = [ - "${builtins.toString config.my.networking.ports.internal.http}:80" - "${builtins.toString config.my.networking.ports.internal.https}:443" - "8080:8080" - ]; - volumes = [ - "${config.my.storage.getConfigPath "traefik"}:/etc/traefik" - "${config.my.storage.getConfigPath "traefik"}/acme:/etc/traefik/acme" - "/var/run/docker.sock:/var/run/docker.sock" - ]; - labels = { - "com.centurylinklabs.watchtower.enable" = "false"; + cloudflare-ddns = mkIf cfg.cloudflare-ddns.enable (makeDefault { + image = "oznu/cloudflare-ddns:latest"; + environment = { + API_KEY = "\${secret_cloudflare_api_token}"; + ZONE = config.my.domain; + INTERFACE = "eth0"; + PROXIED = "true"; + RRTYPE = "AAAA"; }; + network_mode = "host"; }); crowdsec = mkIf cfg.crowdsec.enable (makeDefault { @@ -41,17 +37,5 @@ in "${config.my.storage.getDataPath "crowdsec"}:/var/lib/crowdsec/data" ]; }); - - cloudflare-ddns = mkIf cfg.cloudflare-ddns.enable (makeDefault { - image = "oznu/cloudflare-ddns:latest"; - environment = { - API_KEY = "\${secret_cloudflare_api_token}"; - ZONE = config.my.domain; - INTERFACE = "eth0"; - PROXIED = "true"; - RRTYPE = "AAAA"; - }; - network_mode = "host"; - }); }; } diff --git a/terraform-nix/modules/containers/services/networking/traefik/assets/dynamic.yml b/terraform-nix/modules/containers/services/networking/traefik/assets/dynamic.yml new file mode 100755 index 0000000..018d7e3 --- /dev/null +++ b/terraform-nix/modules/containers/services/networking/traefik/assets/dynamic.yml @@ -0,0 +1,147 @@ +# Traefik dynamic configuration file +# See https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-dynamic-configuration + +http: + routers: + api: + rule: Host(`traefik.$domain`) + entrypoints: + - websecure + service: api@internal + middlewares: + - auth@file + - default@file + tls: + certResolver: letsEncrypt + + middlewares: + # A basic authentication middleware, to protect the Traefik dashboard to anyone except myself + # Use with traefik.http.routers.myRouter.middlewares: "auth@file" + auth: + basicAuth: + users: + - $secret_traefik_password_hashed + + # Recommended default middleware for most of the services + # Use with traefik.http.routers.myRouter.middlewares: "default@file" + default: + chain: + middlewares: + - cloudflarewarp + - crowdsec + - default-security-headers + - gzip + + # Add automatically some security headers + # Use with traefik.http.routers.myRouter.middlewares: "default-security-headers@file" + default-security-headers: + headers: + browserXssFilter: true # X-XSS-Protection=1; mode=block + contentTypeNosniff: true # X-Content-Type-Options=nosniff + forceSTSHeader: true # Add the Strict-Transport-Security header even when the connection is HTTP + frameDeny: false # X-Frame-Options=deny + customFrameOptionsValue: SAMEORIGIN + referrerPolicy: "no-referrer-when-downgrade" + sslRedirect: true # Allow only https requests + stsIncludeSubdomains: true # Add includeSubdomains to the Strict-Transport-Security header + stsPreload: true # Add preload flag appended to the Strict-Transport-Security header + stsSeconds: 31536000 # Set the max-age of the Strict-Transport-Security header (63072000 = 2 years) + customResponseHeaders: + X-Robots-Tags: "noindex, nofollow" + Server: "" + + # Enables GZIP compression (https://docs.traefik.io/middlewares/compress/) + # if the response body is larger than 1400 bytes + # if the Accept-Encoding request header contains gzip + # if the response is not already compressed (Content-Encoding is not set) + # Use with traefik.http.routers.myRouter.middlewares: "gzip@file" + gzip: + compress: {} + + crowdsec: + plugin: + bouncer: + enabled: true + logLevel: INFO + crowdsecMode: live # live | stream | alone + updateIntervalSeconds: 60 # For stream mode + defaultDecisionSeconds: 60 # For live mode + crowdsecLapiKey: $secret_crowdsec_bouncer_api_key + crowdsecLapiHost: crowdsec:8080 + crowdsecLapiScheme: http + crowdsecLapiTLSInsecureVerify: false + forwardedHeadersTrustedIPs: [$trustedIpsCloudflare, $trustedIpsPrivate] + clientTrustedIPs: [$trustedIpsPrivate] + forwardedHeadersCustomName: X-Forwarded-For + # redisCacheEnabled: false + # redisCacheHost: "redis:6379" + # redisCachePassword: password + # redisCacheDatabase: "5" + + cloudflarewarp: + plugin: + cloudflarewarp: + disableDefault: false + trustip: [$trustedIpsCloudflare, $trustedIpsPrivate] + + nextcloudSecureHeaders: + headers: + hostsProxyHeaders: + - X-Forwarded-Host + referrerPolicy: same-origin + + httpsRedirect: + redirectScheme: + scheme: https + + serversTransports: + ignoreCert: + insecureSkipVerify: true + +# See https://doc.traefik.io/traefik/https/tls/ +tls: + options: + # To use with the label "traefik.http.routers.myrouter.tls.options=modern@file" + modern: + minVersion: "VersionTLS13" # Minimum TLS Version + sniStrict: true # Strict SNI Checking + + # To use with the label "traefik.http.routers.myrouter.tls.options=intermediate@file" + intermediate: + cipherSuites: + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" + - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" + minVersion: "VersionTLS12" # Minimum TLS Version + sniStrict: true # Strict SNI Checking + + # To use with the label "traefik.http.routers.myrouter.tls.options=old@file" + old: + cipherSuites: + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" + - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" + - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" + - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" + - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" + - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" + - "TLS_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_RSA_WITH_AES_128_CBC_SHA256" + - "TLS_RSA_WITH_AES_128_CBC_SHA" + - "TLS_RSA_WITH_AES_256_CBC_SHA" + - "TLS_RSA_WITH_3DES_EDE_CBC_SHA" + minVersion: "TLSv1" # Minimum TLS Version + sniStrict: true # Strict SNI Checking + +# # Generated 2021-08-12, Mozilla Guideline v5.6, Traefik 2.4.8 +# # https://ssl-config.mozilla.org/#server=traefik&version=2.4.8&config=old&guideline=5.6 +# # https://ssl-config.mozilla.org/#server=traefik&version=2.4.8&config=intermediate&guideline=5.6 diff --git a/terraform-nix/modules/containers/services/networking/traefik/assets/traefik.yml b/terraform-nix/modules/containers/services/networking/traefik/assets/traefik.yml new file mode 100755 index 0000000..f953d69 --- /dev/null +++ b/terraform-nix/modules/containers/services/networking/traefik/assets/traefik.yml @@ -0,0 +1,59 @@ +# Traefik static configuration file (/etc/traefik/traefik.yml) +# See https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-static-configuration +# and https://doc.traefik.io/traefik/reference/static-configuration/cli/ + +api: + dashboard: true # Enable the dashboard + insecure: false + +# Certificate Resolvers are responsible for retrieving certificates from an ACME server +# See https://doc.traefik.io/traefik/https/acme/#certificate-resolvers +certificatesResolvers: + letsEncrypt: + acme: + dnsChallenge: + provider: cloudflare + storage: "/etc/traefik/acme/acme.json" + +entryPoints: + web: + address: ":80" # Create the HTTP entrypoint on port 80 + http: + redirections: # HTTPS redirection (80 to 443) + entryPoint: + to: "websecure" # The target element + scheme: "https" # The redirection target scheme + websecure: + address: ":443" # Create the HTTPS entrypoint on port 443 + http: + tls: + certResolver: letsEncrypt + forwardedHeaders: + trustedIps: [$trustedIpsCloudflare, $trustedIpsPrivate] + +global: + checknewversion: true # Periodically check if a new version has been released. + sendanonymoususage: true # Periodically send anonymous usage statistics. + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" # Listen to the UNIX Docker socket + exposedByDefault: false # Only expose container that are explicitly enabled (using label traefik.enabled) + network: "external" # Default network to use for connections to all containers. + watch: true # Watch Docker Swarm events + file: + filename: "/etc/traefik/dynamic.yml" # Link to the dynamic configuration + watch: true # Watch for modifications + providersThrottleDuration: 10 # Configuration reload frequency + +experimental: + plugins: + bouncer: + moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin + version: v1.1.12 + cloudflarewarp: + modulename: github.com/BetterCorp/cloudflarewarp + version: v1.3.0 + +log: {} +accessLog: {} diff --git a/terraform-nix/modules/containers/services/networking/traefik/default.nix b/terraform-nix/modules/containers/services/networking/traefik/default.nix new file mode 100644 index 0000000..9a317af --- /dev/null +++ b/terraform-nix/modules/containers/services/networking/traefik/default.nix @@ -0,0 +1,48 @@ +{ config, lib, utils, ... }: + +with lib; +let + cfg = config.my.containers.services; + inherit (utils) makeAssetsDerivation makeEnableOptionDefaultTrue makeDefault makeRuntimeAssetsDir; + + name = "traefik"; +in +{ + options.my.containers.services.traefik = { + enable = makeEnableOptionDefaultTrue name; + }; + + config = mkIf cfg.traefik.enable (mkMerge [ + { + my.containers.services.contents.traefik = makeDefault { + image = "traefik:v2.10"; + environment = { CF_DNS_API_TOKEN = "\${secret_cloudflare_api_token}"; }; + networks = [ "default" "external" ]; + ports = [ + "${builtins.toString config.my.networking.ports.internal.http}:80" + "${builtins.toString config.my.networking.ports.internal.https}:443" + "8080:8080" + ]; + volumes = [ + "/run/${name}:/etc/traefik" + "${config.my.storage.getConfigPath "traefik"}/acme/acme.json:/etc/traefik/acme/acme.json" + "/var/run/docker.sock:/var/run/docker.sock" + ]; + labels = { + "com.centurylinklabs.watchtower.enable" = "false"; + }; + }; + } + (makeRuntimeAssetsDir { + inherit name; + assets = makeAssetsDerivation name ./assets; + envPath = ../../../../../secrets/docker/default.env; + extraEnv = { + domain = config.my.domain; + trustedIpsCloudflare = concatStringsSep ", " (map (ip: "'${ip}'") config.my.networking.cidrs.cloudflare); + trustedIpsPrivate = concatStringsSep ", " (map (ip: "'${ip}'") config.my.networking.cidrs.private); + }; + filesToTemplate = [ "dynamic.yml" "traefik.yml" ]; + }) + ]); +} diff --git a/terraform-nix/modules/containers/services/utils.nix b/terraform-nix/modules/containers/services/utils.nix index 15c4579..10a642f 100644 --- a/terraform-nix/modules/containers/services/utils.nix +++ b/terraform-nix/modules/containers/services/utils.nix @@ -69,7 +69,7 @@ with lib; while IFS= read -r line; do export $line done < /run/secrets/${envFile} - ${concatStringsSep "\n" (builtins.attrValues (builtins.mapAttrs (name: val: "export ${name}=${val}") extraEnv))} + ${concatStringsSep "\n" (builtins.attrValues (builtins.mapAttrs (name: val: "export ${name}=\"${val}\"") extraEnv))} ${concatStringsSep "\n" (makeTemplateLogic filesToTemplate)} '') + (lib.optionalString config.my.containers.instrumentation.compose.enable '' ${pkgs.docker}/bin/docker compose --file ~/compose.yaml restart ${name} diff --git a/terraform-nix/secrets/docker/default.env b/terraform-nix/secrets/docker/default.env index 7638d73..c87505c 100644 --- a/terraform-nix/secrets/docker/default.env +++ b/terraform-nix/secrets/docker/default.env @@ -2,6 +2,7 @@ secret_adguardhome_user=ENC[AES256_GCM,data:VeY6c5ZClj1q,iv:i5Q6OD+jfJjyYTW91kDW secret_adguardhome_password=ENC[AES256_GCM,data:c2JoE7dyf9vl37nigcjs4QeAPRlNbOd/3hWt3EypJEqvgfewXdoXDKcKscRxEv35WO0Rbuw51iZdYMnZ3qc=,iv:MViJ/s0VK3R9C8ArougSj0sldsOwxtEMtdiP90lU6OU=,tag:+XJcqqZWXAx6BOZsN3ZzRA==,type:str] secret_bazarr_api_key=ENC[AES256_GCM,data:iyuBnqa7Fgsb/MeiVrve0B3lYK7ttx1TG9PdFAGMZa2SvQ==,iv:rLoT8v87pCEMnMHCktq1TXMMWZRDXDTSRa4VbX1MNc0=,tag:+B22Gzb8jxzL18d5GqYYZg==,type:str] secret_cloudflare_api_token=ENC[AES256_GCM,data:pZSNJv8dU6OO+ZTYbPMnT3tT0ra6IhjZi3cQ7R/D2FUU1lBxKniAhzmk,iv:P7dB/Gd21ttuzq0ZpJlj2DWyY/4qnE1BGOt7bBMo88o=,tag:5v5fSY0XO8vLpPe2dXCJBQ==,type:str] +secret_crowdsec_bouncer_api_key=ENC[AES256_GCM,data:ME339kobEXO/aif2/5IW2yYcYmG3tir0lR7vpJ5vvss=,iv:3qT2eDMY4q8Vk7tKFogncJ24GLicVfD6gum/cU/6ep8=,tag:6+owGXD4Xb7CGbLJPQU9SA==,type:str] secret_forgejo_db_password=ENC[AES256_GCM,data:HNDQUCHg/xzpVGZ2rf9Dgaj1tYBZyUfIKgv3uYG9WpftMkVwYJbgOx1mPv3xbMc=,iv:upkGHBCHvNZo7n60EpPW6O6bQ35V/r/0ZmKwh4ULahA=,tag:WyQVjhXAN3kpT10HAzU8wQ==,type:str] secret_healthcheck_api_key_ro=ENC[AES256_GCM,data:o7yIyUglRaKrcfsqzfxGyyZaiqHZF3bZjaZBfeHUarU=,iv:keGDiDRfHE5yDAQD0c0fUYs5eCLEPg2Z42Ii7mGKGdo=,tag:DUwaFtg7+eFUFImCXGExuQ==,type:str] secret_healthcheck_io_uuid_snapraid_scrub=ENC[AES256_GCM,data:CIeFoUuaIKuPQZZalcn9rKorOvMnwF346RVdsRhznd2+/r5R,iv:D4e5r+n4/j0dhzfDHUSuXPElFcfJVNrl1lEBbQ4JEOQ=,tag:xVNvx1VB8mWM69QbJ/lLhg==,type:str] @@ -26,12 +27,13 @@ secret_smtp_password=ENC[AES256_GCM,data:3MxHdTpkKLepG2950ZCdvjH4,iv:EJGWYnm5Kum secret_smtp_host=ENC[AES256_GCM,data:/j/8gESJ/Is0YEM8QRwXO3dF,iv:l/6vGBkaiRx5Dy7l8BIoAYQ8YlwrdAhSPToba4yTb4Y=,tag:8fPf/1JGfJXZ9iZ5WZScdw==,type:str] secret_smtp_port=ENC[AES256_GCM,data:Kf5fzAM=,iv:v0466BM2hdAqCIR+n+uDAoipUgQmeaQXfJU+x7ptUgc=,tag:LUjbdsN5RULhxEIsvvPDiw==,type:str] secret_sonarr_api_key=ENC[AES256_GCM,data:mhWGsgrSifZXVz1a2725+PL1EE7kl6PoJvUCaX4j1E4ATA==,iv:zlDZrptmeT21o12g1QL+3NdDrEB7vNVdU5W+NnuSiSc=,tag:RoB1O2uGmmdsDMTKVaxNJA==,type:str] +secret_traefik_password_hashed=ENC[AES256_GCM,data:nJq6Mc1RvX5Q4O5H2q9K1gjrCaq7Yg7yLPnXT1NFQ4iOMons31Wta2fK4A==,iv:rEAX6h/MyxTq/9gcuxyEq/9oPdV6ghpDB4GK4FhCpXg=,tag:oyVT3tBZNw1qmn3e9R6IYg==,type:str] secret_vscode_password=ENC[AES256_GCM,data:5LxnJgm9IkjMETkP8nbuNunkubxORhVZj7mEztOgJJWyb13gq+Wd/w==,iv:X6tqdLg0jbxRUDUL8vykaTNa3kaxCIBvhvsZUHg6gkA=,tag:Kt+2k0RcsC+mhUwKjFjsQg==,type:str] sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHUlpEUHdDQjdFY2xzNi9O\nS3lPdXRkVnhtcGR0YkdDaThLOEFWb1hIQ0hjClRvTkZyT3R3d2xkQ1JNZDQ0WEV5\nUkUrSmV2RU5ZL1BSR2R3L25iYStiTTQKLS0tIExLT2ZrWm5LZmR4MnQ5R3h0S21U\nUlZGUkpOdUU2ZzQ1M0cxUURFK3VCUWcKUu0uUJW5gRa+iDMCqpdePvrlugw19foK\nzFi5yXTTevUOZwWd6BL93JKLXcUFJG+DRxvccLw2vW8oR9DBa38muA==\n-----END AGE ENCRYPTED FILE-----\n sops_age__list_0__map_recipient=age10fa7ce7w6q0ppk5l2gvg6d02g9cmj26rpt00ct54d4latqsnwajs90a43h sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRHd3WDlLL2VMTms4RUZp\nbUVsQzBPc3p0ODF1WENJR01wL3QvOVgxTWhBClJQU0ZpajJEcU1SMllKQVMycVhj\nM2ZFY3QyWk8rSnFBcndsOUZMTmhabk0KLS0tIG1MU3dBZVhpZkpvNWNiQWFBSExP\ncVArQi85VGxKVUZUcUxZL3dOeXdaaEkK47ArdnYhWhbAV/3CBdF4fzmRO9hQS6Bd\n4DaS/yk//oG+Aiy3mKgdUrn1mztj41mLVPf2Y/Ay6T2iiwHWCVy+vA==\n-----END AGE ENCRYPTED FILE-----\n sops_age__list_1__map_recipient=age1q256fq2ef0qm7a9yvp80ttnmk0xuusuwtduvrp7x7d6pz63lnqssjw3473 -sops_lastmodified=2024-01-13T12:51:46Z -sops_mac=ENC[AES256_GCM,data:Ucaq/E0cS1y8M250604EmMpmmnbtkZFHUPtMmMP5OhVJ+wFuCZKWi7PYs6hCpTIDiisyMfm7V/Enxg2C3UW8gfNcjq1vOHJdo/cCvA9lnsAGiMGBE4WIKyTNbpZbzWc/IT98WjJvGaobdICmhEtOhW7goVIV0z7t6+9m6ueG8fs=,iv:9S48tIABSP5+6h0I7enm0sQSkbzuurI8/bw2HhAWos4=,tag:NkrX5fGOJgCtALMOjPDYBw==,type:str] +sops_lastmodified=2024-01-13T14:16:43Z +sops_mac=ENC[AES256_GCM,data:LZg2TrmM3K89rdHfo82FvhqTWdB/I30mUw/VG5w6eYJhoKPDkq7Qw6CmWXIpr8u49NiVDaF98oKNsJumc+jQJTpeqrtu+udwj2zEcMaUQerJhPpJynVrMDz+Qw3TQr60O6vikE1M8crsv+x2sD+htrO76OQJd8wQf9ddpG4v9E4=,iv:Lj7xS44ihymhmL4hxgJ2Z/n0koN5OL5fR1CUU8hPjpg=,tag:M5IVx65PrfwqqaqJ904vGA==,type:str] sops_unencrypted_suffix=_unencrypted sops_version=3.8.1