From ae636eb2679ac0d9cc4f1726cdfa1a297f787f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 13:00:59 +0200 Subject: [PATCH 01/12] re-add matrix server --- flake.nix | 2 +- modules/web01/default.nix | 2 +- modules/web01/matrix-synapse.nix | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index dbf7833..fa72c4e 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ # Use the version of nixpkgs that has been tested to work with SrvOS srvos.inputs.nixpkgs.follows = "nixpkgs"; - clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz"; + clan-core.url = "https://git.clan.lol/clan/clan-core/archive/synapse.tar.gz"; clan-core.inputs.flake-parts.follows = "flake-parts"; clan-core.inputs.nixpkgs.follows = "nixpkgs"; clan-core.inputs.treefmt-nix.follows = "treefmt-nix"; diff --git a/modules/web01/default.nix b/modules/web01/default.nix index b4b7fba..d1d7b3a 100644 --- a/modules/web01/default.nix +++ b/modules/web01/default.nix @@ -8,7 +8,7 @@ ./homepage.nix ./postfix.nix ./jobs.nix - #./matrix-synapse.nix + ./matrix-synapse.nix ../dev.nix self.inputs.clan-core.clanModules.zt-tcp-relay ]; diff --git a/modules/web01/matrix-synapse.nix b/modules/web01/matrix-synapse.nix index a32db76..9730588 100644 --- a/modules/web01/matrix-synapse.nix +++ b/modules/web01/matrix-synapse.nix @@ -1,6 +1,5 @@ { self, ... }: { imports = [ self.inputs.clan-core.clanModules.matrix-synapse ]; - clan.matrix-synapse.enable = true; clan.matrix-synapse.domain = "clan.lol"; } From ab6c39c77e35ce7a65f8c6bdefa1e36893560c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 14:46:47 +0200 Subject: [PATCH 02/12] add dns wildcard for clan.lol --- terraform/web01/dns.tf | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/terraform/web01/dns.tf b/terraform/web01/dns.tf index ec8d5d9..3b716da 100644 --- a/terraform/web01/dns.tf +++ b/terraform/web01/dns.tf @@ -1,34 +1,32 @@ -locals { - hostnames = [ - "@", - "git", - "mail", - "cache", - "matrix", - "www", - "docs", - "metrics", - "buildbot" - ] -} - resource "hetznerdns_zone" "server" { name = var.dns_zone ttl = 3600 } -resource "hetznerdns_record" "server_a" { - for_each = toset(local.hostnames) +resource "hetznerdns_record" "root_a" { zone_id = hetznerdns_zone.server.id - name = each.value + name = "@" type = "A" value = var.ipv4_address } -resource "hetznerdns_record" "server_aaaa" { - for_each = toset(local.hostnames) +resource "hetznerdns_record" "root_aaaa" { zone_id = hetznerdns_zone.server.id - name = each.value + name = "@" + type = "AAAA" + value = var.ipv6_address +} + +resource "hetznerdns_record" "wildcard_a" { + zone_id = hetznerdns_zone.server.id + name = "*" + type = "A" + value = var.ipv4_address +} + +resource "hetznerdns_record" "wildcard_aaaa" { + zone_id = hetznerdns_zone.server.id + name = "*" type = "AAAA" value = var.ipv6_address } From 4e182bec1de024b32985bd8cd427434570605723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 17:52:20 +0200 Subject: [PATCH 03/12] reformat with nixfmt --- devShells/flake-module.nix | 37 +- flake.nix | 86 +- modules/admins.nix | 5 +- modules/flake-module.nix | 3 +- modules/initrd-networking.nix | 27 +- modules/web01/borgbackup.nix | 7 +- modules/web01/clan-merge.nix | 12 +- modules/web01/default.nix | 3 +- modules/web01/gitea/actions-runner.nix | 379 ++++---- modules/web01/gitea/default.nix | 22 +- modules/web01/gitea/postgresql.nix | 3 +- modules/web01/goaccess.nix | 14 +- modules/web01/harmonia.nix | 11 +- modules/web01/homepage.nix | 18 +- modules/web01/jobs.nix | 20 +- modules/zfs-crypto-raid.nix | 11 +- pkgs/action-create-pr/default.nix | 27 +- pkgs/action-ensure-tea-login/default.nix | 21 +- pkgs/action-flake-update-pr-clan/default.nix | 41 +- pkgs/action-flake-update/default.nix | 24 +- pkgs/clan-merge/clan_merge/__init__.py | 4 +- pkgs/clan-merge/default.nix | 24 +- pkgs/clan-merge/flake-module.nix | 3 +- pkgs/clan-merge/shell.nix | 15 +- pkgs/clan-merge/tests/test_cli.py | 4 +- pkgs/flake-module.nix | 63 +- pkgs/job-flake-updates/default.nix | 16 +- pkgs/renovate/composition.nix | 25 +- pkgs/renovate/default.nix | 7 +- pkgs/renovate/node-env.nix | 495 ++++++---- pkgs/renovate/node-packages.nix | 925 +++++++++---------- pkgs/writers.nix | 30 +- targets/flake-module.nix | 8 +- 33 files changed, 1269 insertions(+), 1121 deletions(-) diff --git a/devShells/flake-module.nix b/devShells/flake-module.nix index c77c9a3..044b75e 100644 --- a/devShells/flake-module.nix +++ b/devShells/flake-module.nix @@ -1,14 +1,14 @@ { perSystem = - { inputs' - , pkgs - , lib - , ... - }: + { inputs', pkgs, ... }: let - convert2Tofu = provider: provider.override (prev: { - homepage = builtins.replaceStrings [ "registry.terraform.io/providers" ] [ "registry.opentofu.org" ] prev.homepage; - }); + convert2Tofu = + provider: + provider.override (prev: { + homepage = builtins.replaceStrings [ "registry.terraform.io/providers" ] [ + "registry.opentofu.org" + ] prev.homepage; + }); in { devShells.default = pkgs.mkShellNoCC { @@ -18,17 +18,18 @@ inputs'.clan-core.packages.clan-cli - (pkgs.opentofu.withPlugins (p: builtins.map convert2Tofu [ - p.hetznerdns - p.hcloud - p.null - p.external - p.local - ])) - ]; - inputsFrom = [ - inputs'.clan-core.devShells.default + (pkgs.opentofu.withPlugins ( + p: + builtins.map convert2Tofu [ + p.hetznerdns + p.hcloud + p.null + p.external + p.local + ] + )) ]; + inputsFrom = [ inputs'.clan-core.devShells.default ]; }; }; } diff --git a/flake.nix b/flake.nix index fa72c4e..71db00b 100644 --- a/flake.nix +++ b/flake.nix @@ -37,39 +37,55 @@ buildbot-nix.inputs.treefmt-nix.follows = "treefmt-nix"; }; - outputs = inputs@{ flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } ({ self, ... }: { - systems = [ - "x86_64-linux" - "aarch64-linux" - ]; - imports = [ - inputs.treefmt-nix.flakeModule - ./devShells/flake-module.nix - ./targets/flake-module.nix - ./modules/flake-module.nix - ./pkgs/flake-module.nix - ]; - perSystem = ({ lib, self', system, ... }: { - treefmt = { - projectRootFile = ".git/config"; - programs.hclfmt.enable = true; - programs.nixpkgs-fmt.enable = true; - settings.formatter.nixpkgs-fmt.excludes = [ - # generated files - "node-env.nix" - "node-packages.nix" - "composition.nix" - ]; - }; - checks = - let - nixosMachines = lib.mapAttrs' (name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel) ((lib.filterAttrs (_: config: config.pkgs.system == system)) self.nixosConfigurations); - packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages; - devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells; - homeConfigurations = lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (self'.legacyPackages.homeConfigurations or { }); - in - nixosMachines // packages // devShells // homeConfigurations; - }); - }); + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } ( + { self, ... }: + { + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + imports = [ + inputs.treefmt-nix.flakeModule + ./devShells/flake-module.nix + ./targets/flake-module.nix + ./modules/flake-module.nix + ./pkgs/flake-module.nix + ]; + perSystem = ( + { + lib, + self', + system, + ... + }: + { + treefmt = { + projectRootFile = ".git/config"; + programs.hclfmt.enable = true; + programs.nixfmt-rfc-style.enable = true; + settings.formatter.nixfmt-rfc-style.excludes = [ + # generated files + "node-env.nix" + "node-packages.nix" + "composition.nix" + ]; + }; + checks = + let + nixosMachines = lib.mapAttrs' ( + name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel + ) ((lib.filterAttrs (_: config: config.pkgs.system == system)) self.nixosConfigurations); + packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages; + devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells; + homeConfigurations = lib.mapAttrs' ( + name: config: lib.nameValuePair "home-manager-${name}" config.activation-script + ) (self'.legacyPackages.homeConfigurations or { }); + in + nixosMachines // packages // devShells // homeConfigurations; + } + ); + } + ); } diff --git a/modules/admins.nix b/modules/admins.nix index df3bb04..e35e312 100644 --- a/modules/admins.nix +++ b/modules/admins.nix @@ -41,7 +41,10 @@ in extraGroups = [ "wheel" ]; shell = "/run/current-system/sw/bin/zsh"; uid = 1004; - openssh.authorizedKeys.keys = [ admins.kenji admins.kenji-remote ]; + openssh.authorizedKeys.keys = [ + admins.kenji + admins.kenji-remote + ]; }; johannes = { isNormalUser = true; diff --git a/modules/flake-module.nix b/modules/flake-module.nix index 9c57836..2310522 100644 --- a/modules/flake-module.nix +++ b/modules/flake-module.nix @@ -1,4 +1,5 @@ -{ self, inputs, ... }: { +{ self, inputs, ... }: +{ flake.nixosModules = { server.imports = [ inputs.srvos.nixosModules.server diff --git a/modules/initrd-networking.nix b/modules/initrd-networking.nix index 2c4d05f..3c5a8d3 100644 --- a/modules/initrd-networking.nix +++ b/modules/initrd-networking.nix @@ -1,31 +1,22 @@ -{ config -, lib -, ... -}: -with lib; let +{ config, lib, ... }: +let cfg = config.clan.networking; in { options = { - clan.networking.ipv4.address = mkOption { - type = types.str; - }; + clan.networking.ipv4.address = lib.mkOption { type = lib.types.str; }; - clan.networking.ipv4.cidr = mkOption { - type = types.str; + clan.networking.ipv4.cidr = lib.mkOption { + type = lib.types.str; default = "26"; }; - clan.networking.ipv4.gateway = mkOption { - type = types.str; - }; + clan.networking.ipv4.gateway = lib.mkOption { type = lib.types.str; }; - clan.networking.ipv6.address = mkOption { - type = types.str; - }; + clan.networking.ipv6.address = lib.mkOption { type = lib.types.str; }; - clan.networking.ipv6.cidr = mkOption { - type = types.str; + clan.networking.ipv6.cidr = lib.mkOption { + type = lib.types.str; default = "64"; }; }; diff --git a/modules/web01/borgbackup.nix b/modules/web01/borgbackup.nix index 150ae13..856a597 100644 --- a/modules/web01/borgbackup.nix +++ b/modules/web01/borgbackup.nix @@ -1,9 +1,8 @@ -{ config, ... }: { +{ config, ... }: +{ # 100GB storagebox is under the nix-community hetzner account - systemd.services.borgbackup-job-clan-lol.serviceConfig.ReadWritePaths = [ - "/var/log/telegraf" - ]; + systemd.services.borgbackup-job-clan-lol.serviceConfig.ReadWritePaths = [ "/var/log/telegraf" ]; # Run this from the hetzner network: # ssh-keyscan -p 23 u359378.your-storagebox.de diff --git a/modules/web01/clan-merge.nix b/modules/web01/clan-merge.nix index 9efe37b..3b3000b 100644 --- a/modules/web01/clan-merge.nix +++ b/modules/web01/clan-merge.nix @@ -1,10 +1,18 @@ -{ config, self, pkgs, ... }: { +{ + config, + self, + pkgs, + ... +}: +{ # service to for automatic merge bot systemd.services.clan-merge = { description = "Merge clan.lol PRs automatically"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - environment = { GITEA_TOKEN_FILE = "%d/GITEA_TOKEN_FILE"; }; + environment = { + GITEA_TOKEN_FILE = "%d/GITEA_TOKEN_FILE"; + }; serviceConfig = { LoadCredential = [ "GITEA_TOKEN_FILE:${config.sops.secrets.merge-bot-gitea-token.path}" ]; Restart = "on-failure"; diff --git a/modules/web01/default.nix b/modules/web01/default.nix index d1d7b3a..5bdf337 100644 --- a/modules/web01/default.nix +++ b/modules/web01/default.nix @@ -1,4 +1,5 @@ -{ self, ... }: { +{ self, ... }: +{ imports = [ ./borgbackup.nix ./clan-merge.nix diff --git a/modules/web01/gitea/actions-runner.nix b/modules/web01/gitea/actions-runner.nix index cf7d5b3..a85d032 100644 --- a/modules/web01/gitea/actions-runner.nix +++ b/modules/web01/gitea/actions-runner.nix @@ -1,8 +1,26 @@ -{ config, self, pkgs, lib, ... }: +{ + config, + self, + pkgs, + lib, + ... +}: let storeDeps = pkgs.runCommand "store-deps" { } '' mkdir -p $out/bin - for dir in ${toString [ pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gawk pkgs.git pkgs.nix pkgs.bash pkgs.jq pkgs.nodejs ]}; do + for dir in ${ + toString [ + pkgs.coreutils + pkgs.findutils + pkgs.gnugrep + pkgs.gawk + pkgs.git + pkgs.nix + pkgs.bash + pkgs.jq + pkgs.nodejs + ] + }; do for bin in "$dir"/bin/*; do ln -s "$bin" "$out/bin/$(basename "$bin")" done @@ -14,87 +32,95 @@ let ''; numInstances = 2; in -lib.mkMerge [{ - # everything here has no dependencies on the store - systemd.services.gitea-runner-nix-image = { - wantedBy = [ "multi-user.target" ]; - after = [ "podman.service" ]; - requires = [ "podman.service" ]; - path = [ config.virtualisation.podman.package pkgs.gnutar pkgs.shadow pkgs.getent ]; - # we also include etc here because the cleanup job also wants the nixuser to be present - script = '' - set -eux -o pipefail - mkdir -p etc/nix - - # Create an unpriveleged user that we can use also without the run-as-user.sh script - touch etc/passwd etc/group - groupid=$(cut -d: -f3 < <(getent group nixuser)) - userid=$(cut -d: -f3 < <(getent passwd nixuser)) - groupadd --prefix $(pwd) --gid "$groupid" nixuser - emptypassword='$6$1ero.LwbisiU.h3D$GGmnmECbPotJoPQ5eoSTD6tTjKnSWZcjHoVTkxFLZP17W9hRi/XkmCiAMOfWruUwy8gMjINrBMNODc7cYEo4K.' - useradd --prefix $(pwd) -p "$emptypassword" -m -d /tmp -u "$userid" -g "$groupid" -G nixuser nixuser - - cat < etc/nix/nix.conf - accept-flake-config = true - experimental-features = nix-command flakes - NIX_CONFIG - - cat < etc/nsswitch.conf - passwd: files mymachines systemd - group: files mymachines systemd - shadow: files - - hosts: files mymachines dns myhostname - networks: files - - ethers: files - services: files - protocols: files - rpc: files - NSSWITCH - - # list the content as it will be imported into the container - tar -cv . | tar -tvf - - tar -cv . | podman import - gitea-runner-nix - ''; - serviceConfig = { - RuntimeDirectory = "gitea-runner-nix-image"; - WorkingDirectory = "/run/gitea-runner-nix-image"; - Type = "oneshot"; - RemainAfterExit = true; - }; - }; - - users.users.nixuser = { - group = "nixuser"; - description = "Used for running nix ci jobs"; - home = "/var/empty"; - isSystemUser = true; - }; - users.groups.nixuser = { }; -} +lib.mkMerge [ { - systemd.services = lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}-token") numInstances) (name: { + # everything here has no dependencies on the store + systemd.services.gitea-runner-nix-image = { wantedBy = [ "multi-user.target" ]; - after = [ "gitea.service" ]; - environment = { - GITEA_CUSTOM = "/var/lib/gitea/custom"; - GITEA_WORK_DIR = "/var/lib/gitea"; - }; + after = [ "podman.service" ]; + requires = [ "podman.service" ]; + path = [ + config.virtualisation.podman.package + pkgs.gnutar + pkgs.shadow + pkgs.getent + ]; + # we also include etc here because the cleanup job also wants the nixuser to be present script = '' - set -euo pipefail - token=$(${lib.getExe self.packages.${pkgs.hostPlatform.system}.gitea} actions generate-runner-token) - echo "TOKEN=$token" > /var/lib/gitea-registration/${name} + set -eux -o pipefail + mkdir -p etc/nix + + # Create an unpriveleged user that we can use also without the run-as-user.sh script + touch etc/passwd etc/group + groupid=$(cut -d: -f3 < <(getent group nixuser)) + userid=$(cut -d: -f3 < <(getent passwd nixuser)) + groupadd --prefix $(pwd) --gid "$groupid" nixuser + emptypassword='$6$1ero.LwbisiU.h3D$GGmnmECbPotJoPQ5eoSTD6tTjKnSWZcjHoVTkxFLZP17W9hRi/XkmCiAMOfWruUwy8gMjINrBMNODc7cYEo4K.' + useradd --prefix $(pwd) -p "$emptypassword" -m -d /tmp -u "$userid" -g "$groupid" -G nixuser nixuser + + cat < etc/nix/nix.conf + accept-flake-config = true + experimental-features = nix-command flakes + NIX_CONFIG + + cat < etc/nsswitch.conf + passwd: files mymachines systemd + group: files mymachines systemd + shadow: files + + hosts: files mymachines dns myhostname + networks: files + + ethers: files + services: files + protocols: files + rpc: files + NSSWITCH + + # list the content as it will be imported into the container + tar -cv . | tar -tvf - + tar -cv . | podman import - gitea-runner-nix ''; - unitConfig.ConditionPathExists = [ "!/var/lib/gitea-registration/${name}" ]; serviceConfig = { - User = "gitea"; - Group = "gitea"; - StateDirectory = "gitea-registration"; + RuntimeDirectory = "gitea-runner-nix-image"; + WorkingDirectory = "/run/gitea-runner-nix-image"; Type = "oneshot"; RemainAfterExit = true; }; - }); + }; + + users.users.nixuser = { + group = "nixuser"; + description = "Used for running nix ci jobs"; + home = "/var/empty"; + isSystemUser = true; + }; + users.groups.nixuser = { }; + } + { + systemd.services = + lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}-token") numInstances) + (name: { + wantedBy = [ "multi-user.target" ]; + after = [ "gitea.service" ]; + environment = { + GITEA_CUSTOM = "/var/lib/gitea/custom"; + GITEA_WORK_DIR = "/var/lib/gitea"; + }; + script = '' + set -euo pipefail + token=$(${lib.getExe self.packages.${pkgs.hostPlatform.system}.gitea} actions generate-runner-token) + echo "TOKEN=$token" > /var/lib/gitea-registration/${name} + ''; + unitConfig.ConditionPathExists = [ "!/var/lib/gitea-registration/${name}" ]; + serviceConfig = { + User = "gitea"; + Group = "gitea"; + StateDirectory = "gitea-registration"; + Type = "oneshot"; + RemainAfterExit = true; + }; + }); # Format of the token file: virtualisation = { @@ -111,106 +137,119 @@ lib.mkMerge [{ virtualisation.containers.containersConf.settings = { # podman seems to not work with systemd-resolved - containers.dns_servers = [ "8.8.8.8" "8.8.4.4" ]; + containers.dns_servers = [ + "8.8.8.8" + "8.8.4.4" + ]; }; } { - systemd.services = lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}") numInstances) (name: { - after = [ - "${name}-token.service" - "gitea-runner-nix-image.service" - ]; - requires = [ - "${name}-token.service" - "gitea-runner-nix-image.service" - ]; + systemd.services = + lib.genAttrs (builtins.genList (n: "gitea-runner-nix${builtins.toString n}") numInstances) + (name: { + after = [ + "${name}-token.service" + "gitea-runner-nix-image.service" + ]; + requires = [ + "${name}-token.service" + "gitea-runner-nix-image.service" + ]; - # TODO: systemd confinment - serviceConfig = { - # Hardening (may overlap with DynamicUser=) - # The following options are only for optimizing output of systemd-analyze - AmbientCapabilities = ""; - CapabilityBoundingSet = ""; - # ProtectClock= adds DeviceAllow=char-rtc r - DeviceAllow = ""; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateMounts = true; - PrivateTmp = true; - PrivateUsers = true; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectSystem = "strict"; - RemoveIPC = true; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - UMask = "0066"; - ProtectProc = "invisible"; - SystemCallFilter = [ - "~@clock" - "~@cpu-emulation" - "~@module" - "~@mount" - "~@obsolete" - "~@raw-io" - "~@reboot" - "~@swap" - # needed by go? - #"~@resources" - "~@privileged" - "~capset" - "~setdomainname" - "~sethostname" - ]; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ]; + # TODO: systemd confinment + serviceConfig = { + # Hardening (may overlap with DynamicUser=) + # The following options are only for optimizing output of systemd-analyze + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + UMask = "0066"; + ProtectProc = "invisible"; + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@module" + "~@mount" + "~@obsolete" + "~@raw-io" + "~@reboot" + "~@swap" + # needed by go? + #"~@resources" + "~@privileged" + "~capset" + "~setdomainname" + "~sethostname" + ]; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + "AF_NETLINK" + ]; - # Needs network access - PrivateNetwork = false; - # Cannot be true due to Node - MemoryDenyWriteExecute = false; + # Needs network access + PrivateNetwork = false; + # Cannot be true due to Node + MemoryDenyWriteExecute = false; - # The more restrictive "pid" option makes `nix` commands in CI emit - # "GC Warning: Couldn't read /proc/stat" - # You may want to set this to "pid" if not using `nix` commands - ProcSubset = "all"; - # Coverage programs for compiled code such as `cargo-tarpaulin` disable - # ASLR (address space layout randomization) which requires the - # `personality` syscall - # You may want to set this to `true` if not using coverage tooling on - # compiled code - LockPersonality = false; + # The more restrictive "pid" option makes `nix` commands in CI emit + # "GC Warning: Couldn't read /proc/stat" + # You may want to set this to "pid" if not using `nix` commands + ProcSubset = "all"; + # Coverage programs for compiled code such as `cargo-tarpaulin` disable + # ASLR (address space layout randomization) which requires the + # `personality` syscall + # You may want to set this to `true` if not using coverage tooling on + # compiled code + LockPersonality = false; - # Note that this has some interactions with the User setting; so you may - # want to consult the systemd docs if using both. - DynamicUser = true; - }; - }); + # Note that this has some interactions with the User setting; so you may + # want to consult the systemd docs if using both. + DynamicUser = true; + }; + }); - services.gitea-actions-runner.instances = lib.genAttrs (builtins.genList (n: "nix${builtins.toString n}") numInstances) (name: { - enable = true; - name = "nix-runner"; - # take the git root url from the gitea config - # only possible if you've also configured your gitea though the same nix config - # otherwise you need to set it manually - url = config.services.gitea.settings.server.ROOT_URL; - # use your favourite nix secret manager to get a path for this - tokenFile = "/var/lib/gitea-registration/gitea-runner-${name}-token"; - labels = [ "nix:docker://gitea-runner-nix" ]; - settings = { - container.options = "-e NIX_BUILD_SHELL=/bin/bash -e PAGER=cat -e PATH=/bin -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt --device /dev/kvm -v /nix:/nix -v ${storeDeps}/bin:/bin -v ${storeDeps}/etc/ssl:/etc/ssl --user nixuser --device=/dev/kvm"; - # the default network that also respects our dns server settings - container.network = "host"; - container.valid_volumes = [ - "/nix" - "${storeDeps}/bin" - "${storeDeps}/etc/ssl" - ]; - }; - }); - }] + services.gitea-actions-runner.instances = + lib.genAttrs (builtins.genList (n: "nix${builtins.toString n}") numInstances) + (name: { + enable = true; + name = "nix-runner"; + # take the git root url from the gitea config + # only possible if you've also configured your gitea though the same nix config + # otherwise you need to set it manually + url = config.services.gitea.settings.server.ROOT_URL; + # use your favourite nix secret manager to get a path for this + tokenFile = "/var/lib/gitea-registration/gitea-runner-${name}-token"; + labels = [ "nix:docker://gitea-runner-nix" ]; + settings = { + container.options = "-e NIX_BUILD_SHELL=/bin/bash -e PAGER=cat -e PATH=/bin -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt --device /dev/kvm -v /nix:/nix -v ${storeDeps}/bin:/bin -v ${storeDeps}/etc/ssl:/etc/ssl --user nixuser --device=/dev/kvm"; + # the default network that also respects our dns server settings + container.network = "host"; + container.valid_volumes = [ + "/nix" + "${storeDeps}/bin" + "${storeDeps}/etc/ssl" + ]; + }; + }); + } +] diff --git a/modules/web01/gitea/default.nix b/modules/web01/gitea/default.nix index cd41223..080745c 100644 --- a/modules/web01/gitea/default.nix +++ b/modules/web01/gitea/default.nix @@ -1,12 +1,22 @@ -{ config, pkgs, lib, self, ... }: +{ + pkgs, + lib, + self, + config, + ... +}: let # make the logs for this host "public" so that they show up in e.g. metrics - publog = vhost: lib.attrsets.unionOfDisjoint vhost { - extraConfig = (vhost.extraConfig or "") + '' - access_log /var/log/nginx/public.log vcombined; - ''; - }; + publog = + vhost: + lib.attrsets.unionOfDisjoint vhost { + extraConfig = + (vhost.extraConfig or "") + + '' + access_log /var/log/nginx/public.log vcombined; + ''; + }; in { diff --git a/modules/web01/gitea/postgresql.nix b/modules/web01/gitea/postgresql.nix index 67a7486..c5dde57 100644 --- a/modules/web01/gitea/postgresql.nix +++ b/modules/web01/gitea/postgresql.nix @@ -1,4 +1,5 @@ -{ pkgs, ... }: { +{ pkgs, ... }: +{ services.postgresql.enable = true; services.postgresql.package = pkgs.postgresql_14; services.postgresql.settings = { diff --git a/modules/web01/goaccess.nix b/modules/web01/goaccess.nix index e243df5..231921f 100644 --- a/modules/web01/goaccess.nix +++ b/modules/web01/goaccess.nix @@ -1,4 +1,4 @@ -{ stdenv, lib, pkgs, ... }: +{ pkgs, ... }: let domain = "metrics.clan.lol"; @@ -38,14 +38,13 @@ in "d ${pub_goaccess} 0755 goaccess nginx -" ]; - # --browsers-file=/etc/goaccess/browsers.list # https://raw.githubusercontent.com/allinurl/goaccess/master/config/browsers.list systemd.services.goaccess = { description = "GoAccess server monitoring"; preStart = '' - rm -f ${pub_goaccess}/index.html - ''; + rm -f ${pub_goaccess}/index.html + ''; serviceConfig = { User = "goaccess"; Group = "nginx"; @@ -83,7 +82,11 @@ in ProtectSystem = "strict"; SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io"; ReadOnlyPaths = "/"; - ReadWritePaths = [ "/proc/self" "${pub_goaccess}" "${priv_goaccess}" ]; + ReadWritePaths = [ + "/proc/self" + "${pub_goaccess}" + "${priv_goaccess}" + ]; PrivateDevices = "yes"; ProtectKernelModules = "yes"; ProtectKernelTunables = "yes"; @@ -92,7 +95,6 @@ in wantedBy = [ "multi-user.target" ]; }; - services.nginx.virtualHosts."${domain}" = { addSSL = true; enableACME = true; diff --git a/modules/web01/harmonia.nix b/modules/web01/harmonia.nix index 7eda587..d5ca8d8 100644 --- a/modules/web01/harmonia.nix +++ b/modules/web01/harmonia.nix @@ -1,17 +1,18 @@ -{ config, pkgs, ... }: { +{ config, pkgs, ... }: +{ services.harmonia.enable = true; # $ nix-store --generate-binary-cache-key cache.yourdomain.tld-1 harmonia.secret harmonia.pub services.harmonia.signKeyPath = config.sops.secrets.harmonia-secret.path; services.nginx = { - package = pkgs.nginxStable.override { - modules = [ pkgs.nginxModules.zstd ]; - }; + package = pkgs.nginxStable.override { modules = [ pkgs.nginxModules.zstd ]; }; }; # trust our own cache nix.settings.trusted-substituters = [ "https://cache.clan.lol" ]; - nix.settings.trusted-public-keys = [ "cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=" ]; + nix.settings.trusted-public-keys = [ + "cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=" + ]; services.nginx.virtualHosts."cache.clan.lol" = { forceSSL = true; diff --git a/modules/web01/homepage.nix b/modules/web01/homepage.nix index 56623b2..32601d6 100644 --- a/modules/web01/homepage.nix +++ b/modules/web01/homepage.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, self, ... }: +{ config, ... }: { security.acme.defaults.email = "admins@clan.lol"; @@ -6,13 +6,11 @@ # www user to push website artifacts via ssh users.users.www = { - openssh.authorizedKeys.keys = - config.users.users.root.openssh.authorizedKeys.keys - ++ [ - # ssh-homepage-key - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxZ3Av30M6Sh6NU1mnCskB16bYtNP8vskc/+ud0AU1C ssh-homepage-key" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBuYyfSuETSrwqCsWHeeClqjcsFlMEmiJN6Rr8/DwrU0 gitea-ci" - ]; + openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys ++ [ + # ssh-homepage-key + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxZ3Av30M6Sh6NU1mnCskB16bYtNP8vskc/+ud0AU1C ssh-homepage-key" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBuYyfSuETSrwqCsWHeeClqjcsFlMEmiJN6Rr8/DwrU0 gitea-ci" + ]; isSystemUser = true; shell = "/run/current-system/sw/bin/bash"; group = "www"; @@ -20,9 +18,7 @@ users.groups.www = { }; # ensure /var/www can be accessed by nginx and www user - systemd.tmpfiles.rules = [ - "d /var/www 0755 www nginx" - ]; + systemd.tmpfiles.rules = [ "d /var/www 0755 www nginx" ]; services.nginx = { diff --git a/modules/web01/jobs.nix b/modules/web01/jobs.nix index 9925787..f0c98a8 100644 --- a/modules/web01/jobs.nix +++ b/modules/web01/jobs.nix @@ -1,4 +1,10 @@ -{ config, self, pkgs, lib, ... }: +{ + config, + self, + pkgs, + lib, + ... +}: let configForJob = name: { systemd.timers.${name} = { @@ -46,9 +52,11 @@ let }; in { - config = lib.mkMerge (map configForJob [ - "job-flake-update-clan-core" - "job-flake-update-clan-homepage" - "job-flake-update-clan-infra" - ]); + config = lib.mkMerge ( + map configForJob [ + "job-flake-update-clan-core" + "job-flake-update-clan-homepage" + "job-flake-update-clan-infra" + ] + ); } diff --git a/modules/zfs-crypto-raid.nix b/modules/zfs-crypto-raid.nix index 7a3bd3c..0497350 100644 --- a/modules/zfs-crypto-raid.nix +++ b/modules/zfs-crypto-raid.nix @@ -1,4 +1,3 @@ -{ self, ... }: let mirrorBoot = idx: { type = "disk"; @@ -41,8 +40,14 @@ in efiSupport = true; efiInstallAsRemovable = true; mirroredBoots = [ - { path = "/boot0"; devices = [ "nodev" ]; } - { path = "/boot1"; devices = [ "nodev" ]; } + { + path = "/boot0"; + devices = [ "nodev" ]; + } + { + path = "/boot1"; + devices = [ "nodev" ]; + } ]; }; diff --git a/pkgs/action-create-pr/default.nix b/pkgs/action-create-pr/default.nix index 05e4f40..9040bc6 100644 --- a/pkgs/action-create-pr/default.nix +++ b/pkgs/action-create-pr/default.nix @@ -1,10 +1,19 @@ -{ bash -, coreutils -, git -, tea -, openssh -, writePureShellScriptBin +{ + bash, + coreutils, + git, + tea, + openssh, + writePureShellScriptBin, }: -writePureShellScriptBin "action-create-pr" [ bash coreutils git tea openssh ] '' - bash ${./script.sh} "$@" -'' +writePureShellScriptBin "action-create-pr" + [ + bash + coreutils + git + tea + openssh + ] + '' + bash ${./script.sh} "$@" + '' diff --git a/pkgs/action-ensure-tea-login/default.nix b/pkgs/action-ensure-tea-login/default.nix index 0857ef1..5542896 100644 --- a/pkgs/action-ensure-tea-login/default.nix +++ b/pkgs/action-ensure-tea-login/default.nix @@ -1,8 +1,15 @@ -{ bash -, coreutils -, tea -, writePureShellScriptBin +{ + bash, + coreutils, + tea, + writePureShellScriptBin, }: -writePureShellScriptBin "action-ensure-tea-login" [ bash coreutils tea ] '' - bash ${./script.sh} -'' +writePureShellScriptBin "action-ensure-tea-login" + [ + bash + coreutils + tea + ] + '' + bash ${./script.sh} + '' diff --git a/pkgs/action-flake-update-pr-clan/default.nix b/pkgs/action-flake-update-pr-clan/default.nix index 34b1c0b..25b30de 100644 --- a/pkgs/action-flake-update-pr-clan/default.nix +++ b/pkgs/action-flake-update-pr-clan/default.nix @@ -1,20 +1,23 @@ -{ bash -, coreutils -, git -, openssh -, action-ensure-tea-login -, action-create-pr -, action-flake-update -, writePureShellScriptBin +{ + bash, + coreutils, + git, + openssh, + action-ensure-tea-login, + action-create-pr, + action-flake-update, + writePureShellScriptBin, }: -writePureShellScriptBin "action-flake-update-pr-clan" [ - bash - coreutils - git - openssh - action-ensure-tea-login - action-create-pr - action-flake-update -] '' - bash ${./script.sh} -'' +writePureShellScriptBin "action-flake-update-pr-clan" + [ + bash + coreutils + git + openssh + action-ensure-tea-login + action-create-pr + action-flake-update + ] + '' + bash ${./script.sh} + '' diff --git a/pkgs/action-flake-update/default.nix b/pkgs/action-flake-update/default.nix index 7d2560f..2bf0889 100644 --- a/pkgs/action-flake-update/default.nix +++ b/pkgs/action-flake-update/default.nix @@ -1,9 +1,17 @@ -{ bash -, coreutils -, git -, nix -, writePureShellScriptBin +{ + bash, + coreutils, + git, + nix, + writePureShellScriptBin, }: -writePureShellScriptBin "action-flake-update" [ bash coreutils git nix ] '' - bash ${./script.sh} -'' +writePureShellScriptBin "action-flake-update" + [ + bash + coreutils + git + nix + ] + '' + bash ${./script.sh} + '' diff --git a/pkgs/clan-merge/clan_merge/__init__.py b/pkgs/clan-merge/clan_merge/__init__.py index 6f765aa..cf9077b 100644 --- a/pkgs/clan-merge/clan_merge/__init__.py +++ b/pkgs/clan-merge/clan_merge/__init__.py @@ -1,7 +1,7 @@ import argparse import json -import urllib.request import urllib.error +import urllib.request from os import environ from typing import Optional @@ -38,6 +38,7 @@ def is_ci_green(pr: dict) -> bool: return False return True + def is_org_member(user: str, token: str) -> bool: url = "https://git.clan.lol/api/v1/orgs/clan/members/" + user + f"?token={token}" try: @@ -50,7 +51,6 @@ def is_org_member(user: str, token: str) -> bool: raise - def merge_allowed(pr: dict, bot_name: str, token: str) -> bool: assignees = pr["assignees"] if pr["assignees"] else [] if ( diff --git a/pkgs/clan-merge/default.nix b/pkgs/clan-merge/default.nix index a9ae5f7..11107fb 100644 --- a/pkgs/clan-merge/default.nix +++ b/pkgs/clan-merge/default.nix @@ -1,9 +1,9 @@ -{ pkgs ? import { } -, lib ? pkgs.lib -, python3 ? pkgs.python3 -, ruff ? pkgs.ruff -, runCommand ? pkgs.runCommand -, +{ + pkgs ? import { }, + lib ? pkgs.lib, + python3 ? pkgs.python3, + ruff ? pkgs.ruff, + runCommand ? pkgs.runCommand, }: let pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml); @@ -32,13 +32,11 @@ let package = python3.pkgs.buildPythonPackage { inherit name src; format = "pyproject"; - nativeBuildInputs = [ - python3.pkgs.setuptools - ]; - propagatedBuildInputs = - dependencies - ++ [ ]; - passthru.tests = { inherit check; }; + nativeBuildInputs = [ python3.pkgs.setuptools ]; + propagatedBuildInputs = dependencies ++ [ ]; + passthru.tests = { + inherit check; + }; passthru.devDependencies = devDependencies; }; diff --git a/pkgs/clan-merge/flake-module.nix b/pkgs/clan-merge/flake-module.nix index 8623e16..a104914 100644 --- a/pkgs/clan-merge/flake-module.nix +++ b/pkgs/clan-merge/flake-module.nix @@ -1,5 +1,6 @@ { - perSystem = { pkgs, ... }: + perSystem = + { pkgs, ... }: let package = pkgs.callPackage ./default.nix { inherit pkgs; }; in diff --git a/pkgs/clan-merge/shell.nix b/pkgs/clan-merge/shell.nix index e506aaf..eb097bc 100644 --- a/pkgs/clan-merge/shell.nix +++ b/pkgs/clan-merge/shell.nix @@ -1,16 +1,11 @@ -{ pkgs ? import { } }: +{ + pkgs ? import { }, +}: let inherit (pkgs) lib python3; - package = import ./default.nix { - inherit lib pkgs python3; - }; + package = import ./default.nix { inherit lib pkgs python3; }; pythonWithDeps = python3.withPackages ( - ps: - package.propagatedBuildInputs - ++ package.devDependencies - ++ [ - ps.pip - ] + ps: package.propagatedBuildInputs ++ package.devDependencies ++ [ ps.pip ] ); checkScript = pkgs.writeScriptBin "check" '' nix build -f . tests -L "$@" diff --git a/pkgs/clan-merge/tests/test_cli.py b/pkgs/clan-merge/tests/test_cli.py index 74e3ab9..26f02c4 100644 --- a/pkgs/clan-merge/tests/test_cli.py +++ b/pkgs/clan-merge/tests/test_cli.py @@ -112,4 +112,6 @@ def test_list_prs_to_merge(monkeypatch: pytest.MonkeyPatch) -> None: assignees=[dict(login=bot_name)], ), ] - assert clan_merge.list_prs_to_merge(prs, bot_name=bot_name, gitea_token="test") == [prs[0]] + assert clan_merge.list_prs_to_merge(prs, bot_name=bot_name, gitea_token="test") == [ + prs[0] + ] diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix index 9329022..d58fac3 100644 --- a/pkgs/flake-module.nix +++ b/pkgs/flake-module.nix @@ -1,33 +1,38 @@ { - imports = [ - ./clan-merge/flake-module.nix - ]; - perSystem = { pkgs, config, ... }: { - packages = - let - writers = pkgs.callPackage ./writers.nix { }; - in - { - inherit (pkgs.callPackage ./renovate { }) renovate; - gitea = pkgs.callPackage ./gitea { }; + imports = [ ./clan-merge/flake-module.nix ]; + perSystem = + { pkgs, config, ... }: + { + packages = + let + writers = pkgs.callPackage ./writers.nix { }; + in + { + inherit (pkgs.callPackage ./renovate { }) renovate; + gitea = pkgs.callPackage ./gitea { }; - action-create-pr = pkgs.callPackage ./action-create-pr { - inherit (writers) writePureShellScriptBin; + action-create-pr = pkgs.callPackage ./action-create-pr { + inherit (writers) writePureShellScriptBin; + }; + action-ensure-tea-login = pkgs.callPackage ./action-ensure-tea-login { + inherit (writers) writePureShellScriptBin; + }; + action-flake-update = pkgs.callPackage ./action-flake-update { + inherit (writers) writePureShellScriptBin; + }; + action-flake-update-pr-clan = pkgs.callPackage ./action-flake-update-pr-clan { + inherit (writers) writePureShellScriptBin; + inherit (config.packages) action-ensure-tea-login action-create-pr action-flake-update; + }; + inherit + (pkgs.callPackages ./job-flake-updates { + inherit (writers) writePureShellScriptBin; + inherit (config.packages) action-flake-update-pr-clan; + }) + job-flake-update-clan-core + job-flake-update-clan-homepage + job-flake-update-clan-infra + ; }; - action-ensure-tea-login = pkgs.callPackage ./action-ensure-tea-login { - inherit (writers) writePureShellScriptBin; - }; - action-flake-update = pkgs.callPackage ./action-flake-update { - inherit (writers) writePureShellScriptBin; - }; - action-flake-update-pr-clan = pkgs.callPackage ./action-flake-update-pr-clan { - inherit (writers) writePureShellScriptBin; - inherit (config.packages) action-ensure-tea-login action-create-pr action-flake-update; - }; - inherit (pkgs.callPackages ./job-flake-updates { - inherit (writers) writePureShellScriptBin; - inherit (config.packages) action-flake-update-pr-clan; - }) job-flake-update-clan-core job-flake-update-clan-homepage job-flake-update-clan-infra; - }; - }; + }; } diff --git a/pkgs/job-flake-updates/default.nix b/pkgs/job-flake-updates/default.nix index cc5fcb7..4e0afdd 100644 --- a/pkgs/job-flake-updates/default.nix +++ b/pkgs/job-flake-updates/default.nix @@ -1,13 +1,13 @@ -{ action-flake-update-pr-clan -, writePureShellScriptBin -}: +{ action-flake-update-pr-clan, writePureShellScriptBin }: let - job-flake-update = repo: writePureShellScriptBin "job-flake-update-${repo}" [ action-flake-update-pr-clan ] '' - export REPO="gitea@git.clan.lol:clan/${repo}.git" - export KEEP_VARS="REPO''${KEEP_VARS:+ $KEEP_VARS}" + job-flake-update = + repo: + writePureShellScriptBin "job-flake-update-${repo}" [ action-flake-update-pr-clan ] '' + export REPO="gitea@git.clan.lol:clan/${repo}.git" + export KEEP_VARS="REPO''${KEEP_VARS:+ $KEEP_VARS}" - action-flake-update-pr-clan - ''; + action-flake-update-pr-clan + ''; in { job-flake-update-clan-core = job-flake-update "clan-core"; diff --git a/pkgs/renovate/composition.nix b/pkgs/renovate/composition.nix index d9c0dae..c4d6395 100644 --- a/pkgs/renovate/composition.nix +++ b/pkgs/renovate/composition.nix @@ -1,17 +1,32 @@ # This file has been generated by node2nix 1.11.1. Do not edit! -{pkgs ? import { - inherit system; - }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs_18"}: +{ + pkgs ? import { inherit system; }, + system ? builtins.currentSystem, + nodejs ? pkgs."nodejs_18", +}: let nodeEnv = import ./node-env.nix { - inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript; + inherit (pkgs) + stdenv + lib + python2 + runCommand + writeTextFile + writeShellScript + ; inherit pkgs nodejs; libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; }; in import ./node-packages.nix { - inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit; + inherit (pkgs) + fetchurl + nix-gitignore + stdenv + lib + fetchgit + ; inherit nodeEnv; } diff --git a/pkgs/renovate/default.nix b/pkgs/renovate/default.nix index bc18db9..c3f442b 100644 --- a/pkgs/renovate/default.nix +++ b/pkgs/renovate/default.nix @@ -1,4 +1,9 @@ -{ pkgs, system, nodejs-18_x, makeWrapper }: +{ + pkgs, + system, + nodejs-18_x, + makeWrapper, +}: let nodePackages = import ./composition.nix { inherit pkgs system; diff --git a/pkgs/renovate/node-env.nix b/pkgs/renovate/node-env.nix index bc1e366..68f8c82 100644 --- a/pkgs/renovate/node-env.nix +++ b/pkgs/renovate/node-env.nix @@ -1,6 +1,16 @@ # This file originates from node2nix -{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}: +{ + lib, + stdenv, + nodejs, + python2, + pkgs, + libtool, + runCommand, + writeTextFile, + writeShellScript, +}: let # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master @@ -9,7 +19,7 @@ let python = if nodejs ? python then nodejs.python else python2; # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise - tarWrapper = runCommand "tarWrapper" {} '' + tarWrapper = runCommand "tarWrapper" { } '' mkdir -p $out/bin cat > $out/bin/tar < $out/bin/shell < $out/bin/shell < Date: Thu, 6 Jun 2024 18:09:35 +0200 Subject: [PATCH 04/12] Update secret web01-borgbackup.repokey --- .../web01-borgbackup.repokey/machines/web01 | 1 + sops/secrets/web01-borgbackup.repokey/secret | 24 +++++++++++++++++++ .../web01-borgbackup.repokey/users/joerg | 1 + 3 files changed, 26 insertions(+) create mode 120000 sops/secrets/web01-borgbackup.repokey/machines/web01 create mode 100644 sops/secrets/web01-borgbackup.repokey/secret create mode 120000 sops/secrets/web01-borgbackup.repokey/users/joerg diff --git a/sops/secrets/web01-borgbackup.repokey/machines/web01 b/sops/secrets/web01-borgbackup.repokey/machines/web01 new file mode 120000 index 0000000..a3c776b --- /dev/null +++ b/sops/secrets/web01-borgbackup.repokey/machines/web01 @@ -0,0 +1 @@ +../../../machines/web01 \ No newline at end of file diff --git a/sops/secrets/web01-borgbackup.repokey/secret b/sops/secrets/web01-borgbackup.repokey/secret new file mode 100644 index 0000000..282756f --- /dev/null +++ b/sops/secrets/web01-borgbackup.repokey/secret @@ -0,0 +1,24 @@ +{ + "data": "ENC[AES256_GCM,data:WW0RmSs3k81jSgYLt8dHEiJOxlncPWl3QWvRtmNgtIxvup7h,iv:nw7SP15EVWfS78dJE37msnxAZ/goYb7rGqAKNzhXFP4=,tag:yxVyGUMFczq8cGuU4V/FzA==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiM0FBVkhPc2luMjlpSW1R\nN0NlUU9ZQkxIOVAwa3hlMVg3bFluTjRlRUdRCmMxSkMvZjg2ckUyUThhSC9VOW1H\nZExFY2owcHQ5NzJtUW5pbDFjd2oyaEUKLS0tIG1Fd25acHdYWEdlQkMxajhRQXNw\nTGxJUDdPMlRrQ0t3SkVSaWdZZXJGT0EK7WfQ+6jVzOBToqO9wJby/qaF6kM00hMh\n+Y4A08X/ItLzyfCc5LQ97GQ2VlwXK5+HoD7jNnn//3xeH6YC1VBdkg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age17xuvz0fqtynzdmf8rfh4g3e46tx8w3mc6zgytrmuj5v9dhnldgxs7ue7ct", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhNlpOQ1QydEJuM3pCbEpK\nSDUzVlppNkFnSDJLSU1ITEdWWCtaUEE0THcwCmljSUl0amx2OTBVZXBPMFNGbjJP\nakNQcWlad1R3cDZYWWZpQkJkQmEvUEEKLS0tIFpJOU1GUnNaTnlaL25GRkdxZnhs\nUEhIVEpNWjNOV2FTSmVnRkVCWm90MDgKMvz6QdPRoYb2bPjS9oSOVA5gTfwrgn4q\nIyboQIMV3oAaAs9LSUcUMBvERzQ31JXnHRzrnqtdiNX0NLbIrN47yg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2024-06-06T16:09:34Z", + "mac": "ENC[AES256_GCM,data:7iKDT5577mLLeNyi46JHa4AUumqbQm65V3DXqNdNyLWccpIcML8n7jgFNxuK9gTqV2LM6bG18qS1orBJtPdawKnvxJwUaFb3Mo06C2+LVnWG4fT6MV+5eF8y6SM3IngT9BPk7IhTTGWe8lGJ6HTlg+9/f4/cq5NSKfeRgTkDEcE=,iv:T8wjeq2D1J8krhWeQJbVCOPY5sr05z/wMJqvr9onQK8=,tag:XgDTOTa2zv4NiBFN0b3rqA==,type:str]", + "pgp": null, + "unencrypted_suffix": "_unencrypted", + "version": "3.8.1" + } +} \ No newline at end of file diff --git a/sops/secrets/web01-borgbackup.repokey/users/joerg b/sops/secrets/web01-borgbackup.repokey/users/joerg new file mode 120000 index 0000000..4c1fba9 --- /dev/null +++ b/sops/secrets/web01-borgbackup.repokey/users/joerg @@ -0,0 +1 @@ +../../../users/joerg \ No newline at end of file From b2e55511afa01503f0b22b72e18901ee0f4c09ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 18:09:35 +0200 Subject: [PATCH 05/12] Update secret web01-borgbackup.ssh --- .../web01-borgbackup.ssh/machines/web01 | 1 + sops/secrets/web01-borgbackup.ssh/secret | 24 +++++++++++++++++++ sops/secrets/web01-borgbackup.ssh/users/joerg | 1 + 3 files changed, 26 insertions(+) create mode 120000 sops/secrets/web01-borgbackup.ssh/machines/web01 create mode 100644 sops/secrets/web01-borgbackup.ssh/secret create mode 120000 sops/secrets/web01-borgbackup.ssh/users/joerg diff --git a/sops/secrets/web01-borgbackup.ssh/machines/web01 b/sops/secrets/web01-borgbackup.ssh/machines/web01 new file mode 120000 index 0000000..a3c776b --- /dev/null +++ b/sops/secrets/web01-borgbackup.ssh/machines/web01 @@ -0,0 +1 @@ +../../../machines/web01 \ No newline at end of file diff --git a/sops/secrets/web01-borgbackup.ssh/secret b/sops/secrets/web01-borgbackup.ssh/secret new file mode 100644 index 0000000..22f8554 --- /dev/null +++ b/sops/secrets/web01-borgbackup.ssh/secret @@ -0,0 +1,24 @@ +{ + "data": "ENC[AES256_GCM,data:ybX1/Uc+LqfgUoZQqCURgPfsTyzlsO+Xn7z8/0H9v5kyfJYX7PI1VlXVFBgR3Xh+2iuTF+v98PQyYeJOYLk3NTWggZayQQ4ivt0DLdhgG+DRFbeN8GMiqV5NWNhnL2tgLBu9DZViBSpgcbg9aHfI2cagboJnCSqyS2w1i/anvKaEgKa5YucrS4jywVxhBbvON6Oa2v8Hb0f/R8Ldl9HSqMM6o3pQEaYOsTNieNy63h9C4ERP/jIhKSajggpeHENdnuQC7Kavz54faL9xaz0jwRHb1fd+IGTM7fxqbyB5702nKEGytDwKzH0fh6q1HJNHbhWeWyCmGFKOkqywaQjcpJsczP2FIwkZmoui0juTEluNk1KzugP0gxtsuwjUiJlZeJxtZEgsnifLPpHyCaN99jzPjhd1TknT7MWZMVJT/R14bdD+QdwvR8rHK8IMctMGrNsqu3+Crdwu3WSfDH9jEM1zAZQNvLUT13azRABz1rpJvNFnvhDTBwDbUJlLpIQcPOPtVKO0IQeM5EUnCr+oCfBrP/To3mqo82s3,iv:jbY6WK0BcyLlU3Sbo7qNOHfCGU4TjUqTiww546Tyq20=,tag:VklHST51z5XI9+UiASBO9g==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlenFrWW45UXcyd3pwaTE0\nY3ROWWFPdzIwK1JuU2h2NCsyanF1VzhoM1NjCndscHZpTUNmcWc0TDd4V2xHM0lh\ndVNZQW1jMGFNeDdxbDhwdFB1Z3AwdVUKLS0tIGNmbVNMRUZGb1lPcnlrdkhnZ3JX\nM2szRHVydldGN3haV2lhZlFMeUgzcU0KDDwWVSjsua4DKXlqqk2Ns2e1zkzJK2Y2\n8+r8bXkBLJyXqQCQteXBrc5U+0n1KfHVkkvPmuBI3BmcAiVVmr/RxQ==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age17xuvz0fqtynzdmf8rfh4g3e46tx8w3mc6zgytrmuj5v9dhnldgxs7ue7ct", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJdHEyT3U0TzJQUW9wM2Z3\nYVdTYmRSeUwrZy9iNTVpcmZJMnhOeHlGRGp3CnVYbk15NWxadk9waFdJVFBKa0Vx\nYUtkYWZuYmhabk1xREtDdzFGdUcyaW8KLS0tIGYxcDVuQXZwdk1rVHJOOHljTmVl\nMXNXTHdSam12djE3Z0UyU2dSZDcrRGcKaQnrYuUpSTjOYYHH0EsqnTLHkU5Md4Ro\nUpeJX1GmAoIAUGruB/8jPbMaDQQXbjNLDCCalStlbqMgbgz/Ty4ukQ==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2024-06-06T16:09:35Z", + "mac": "ENC[AES256_GCM,data:2RptHkE/k4JfqdybmnI3sbeEDaaD4bUtEPLuBcpltZjR5EHFYLsEB1Woxlzj2rLqq+8Wr6kWZtsG3uJSxsColUbazJd1CoVJxHpm6tAnM47Mv1YG5PdLwqpwJWji4AI5lAer4ZMfuGDpNbrwvbO3qB8R55r5SYay4b4Yc49wQXA=,iv:ESimFSybysRrgEj+27ECUi6kIklv1IunWVclTjX7C5g=,tag:LONP+cUm5NVcBvgVStZnwQ==,type:str]", + "pgp": null, + "unencrypted_suffix": "_unencrypted", + "version": "3.8.1" + } +} \ No newline at end of file diff --git a/sops/secrets/web01-borgbackup.ssh/users/joerg b/sops/secrets/web01-borgbackup.ssh/users/joerg new file mode 120000 index 0000000..4c1fba9 --- /dev/null +++ b/sops/secrets/web01-borgbackup.ssh/users/joerg @@ -0,0 +1 @@ +../../../users/joerg \ No newline at end of file From 9751dc648efd61afb55b0cedcfc235283ce0c994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 18:09:35 +0200 Subject: [PATCH 06/12] Update facts/secrets for service borgbackup in machine web01 --- machines/web01/facts/borgbackup.ssh.pub | 1 + 1 file changed, 1 insertion(+) create mode 100644 machines/web01/facts/borgbackup.ssh.pub diff --git a/machines/web01/facts/borgbackup.ssh.pub b/machines/web01/facts/borgbackup.ssh.pub new file mode 100644 index 0000000..66c2d02 --- /dev/null +++ b/machines/web01/facts/borgbackup.ssh.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHS2PvT2e04pqbt1EFFN2y1za9nNmr8rcfnXq9kG5RS2 nixbld@turingmachine From 1fbd237a91a936e3aa5718eacb9383fda4196738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 18:17:20 +0200 Subject: [PATCH 07/12] borgbackup: dogfood clan-core --- modules/web01/borgbackup.nix | 69 ++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/modules/web01/borgbackup.nix b/modules/web01/borgbackup.nix index 856a597..8c3d37d 100644 --- a/modules/web01/borgbackup.nix +++ b/modules/web01/borgbackup.nix @@ -1,25 +1,21 @@ -{ config, ... }: +{ config, self, ... }: { + imports = [ self.inputs.clan-core.clanModules.borgbackup ]; + # 100GB storagebox is under the nix-community hetzner account - - systemd.services.borgbackup-job-clan-lol.serviceConfig.ReadWritePaths = [ "/var/log/telegraf" ]; - - # Run this from the hetzner network: - # ssh-keyscan -p 23 u359378.your-storagebox.de - programs.ssh.knownHosts = { - storagebox-ecdsa.hostNames = [ "[u359378.your-storagebox.de]:23" ]; - storagebox-ecdsa.publicKey = "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGK0po6usux4Qv2d8zKZN1dDvbWjxKkGsx7XwFdSUCnF19Q8psHEUWR7C/LtSQ5crU/g+tQVRBtSgoUcE8T+FWp5wBxKvWG2X9gD+s9/4zRmDeSJR77W6gSA/+hpOZoSE+4KgNdnbYSNtbZH/dN74EG7GLb/gcIpbUUzPNXpfKl7mQitw=="; - - storagebox-rsa.hostNames = [ "[u359378.your-storagebox.de]:23" ]; - storagebox-rsa.publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5EB5p/5Hp3hGW1oHok+PIOH9Pbn7cnUiGmUEBrCVjnAw+HrKyN8bYVV0dIGllswYXwkG/+bgiBlE6IVIBAq+JwVWu1Sss3KarHY3OvFJUXZoZyRRg/Gc/+LRCE7lyKpwWQ70dbelGRyyJFH36eNv6ySXoUYtGkwlU5IVaHPApOxe4LHPZa/qhSRbPo2hwoh0orCtgejRebNtW5nlx00DNFgsvn8Svz2cIYLxsPVzKgUxs8Zxsxgn+Q/UvR7uq4AbAhyBMLxv7DjJ1pc7PJocuTno2Rw9uMZi1gkjbnmiOh6TTXIEWbnroyIhwc8555uto9melEUmWNQ+C+PwAK+MPw=="; + clan.borgbackup.destinations.${config.networking.hostName} = { + repo = "u366395@u366395.your-storagebox.de:/./borgbackup"; + rsh = "ssh -oPort=23 -i ${config.clanCore.facts.services.borgbackup.secret."borgbackup.ssh".path}"; }; - services.borgbackup.jobs.clan-lol = { - paths = [ - "/home" - "/var" - "/root" - ]; + clanCore.state.system.folders = [ + "/home" + "/etc" + "/var" + "/root" + ]; + + services.borgbackup.jobs.${config.networking.hostName} = { exclude = [ "*.pyc" "/home/*/.direnv" @@ -40,32 +36,20 @@ "/var/tmp" "/var/log" ]; - # $ ssh-keygen -y -f /run/secrets/hetzner-borgbackup-ssh > /tmp/hetzner-borgbackup-ssh.pub - # $ cat /tmp/hetzner-borgbackup-ssh.pub | ssh -p23 u366395@u366395.your-storagebox.de install-ssh-key - repo = "u366395@u366395.your-storagebox.de:/./borgbackup"; # Disaster recovery: # get the backup passphrase and ssh key from the sops and store them in /tmp # $ export BORG_PASSCOMMAND='cat /tmp/hetzner-borgbackup-passphrase' # $ export BORG_REPO='u359378@u359378.your-storagebox.de:/./borgbackup' - # $ export BORG_RSH='ssh -oPort=23 -i /tmp/hetzner-borgbackup-ssh' + # $ export BORG_RSH='ssh -oPort=23 -i /tmp/hetzner-borgbackup-ssh' # $ borg list # web01-clan-lol-2023-07-21T14:12:22 Fri, 2023-07-21 14:12:27 [539b1037669ffd0d3f50020f439bbe2881b7234910e405eafc333125383351bc] # $ borg mount u359378@u359378.your-storagebox.de:/./borgbackup::web01-clan-lol-2023-07-21T14:12:22 /tmp/backup - doInit = true; - encryption = { - mode = "repokey-blake2"; - # $ nix run nixpkgs#xkcdpass -- -d '-' -n 3 -C capitalize "$@" - passCommand = "cat ${config.sops.secrets.hetzner-borgbackup-passphrase.path}"; - }; - compression = "auto,zstd"; - startAt = "daily"; # Also enable ssh support in the storagebox web interface. # By default the storage box is only accessible from the hetzner network. - # $ ssh-keygen -t ed25519 -N "" -f /tmp/ssh_host_ed25519_key - # $ cat /tmp/ssh_host_ed25519_key.pub | ssh -p23 u359378@u359378.your-storagebox.de install-ssh-key - environment.BORG_RSH = "ssh -oPort=23 -i ${config.sops.secrets.hetzner-borgbackup-ssh.path}"; + # $ clan facts generate + # $ clan facts list web01 | jq .borgbackup.ssh.pub | ssh -p23 u359378@u359378.your-storagebox.de install-ssh-key preHook = '' set -x ''; @@ -75,12 +59,19 @@ task,frequency=daily last_run=$(date +%s)i,state="$([[ $exitStatus == 0 ]] && echo ok || echo fail)" EOF ''; + }; - prune.keep = { - within = "1d"; # Keep all archives from the last day - daily = 7; - weekly = 4; - monthly = 0; - }; + systemd.services."borgbackup-job-${config.networking.hostName}".serviceConfig.ReadWritePaths = [ + "/var/log/telegraf" + ]; + + # Run this from the hetzner network: + # ssh-keyscan -p 23 u359378.your-storagebox.de + programs.ssh.knownHosts = { + storagebox-ecdsa.hostNames = [ "[u359378.your-storagebox.de]:23" ]; + storagebox-ecdsa.publicKey = "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGK0po6usux4Qv2d8zKZN1dDvbWjxKkGsx7XwFdSUCnF19Q8psHEUWR7C/LtSQ5crU/g+tQVRBtSgoUcE8T+FWp5wBxKvWG2X9gD+s9/4zRmDeSJR77W6gSA/+hpOZoSE+4KgNdnbYSNtbZH/dN74EG7GLb/gcIpbUUzPNXpfKl7mQitw=="; + + storagebox-rsa.hostNames = [ "[u359378.your-storagebox.de]:23" ]; + storagebox-rsa.publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA5EB5p/5Hp3hGW1oHok+PIOH9Pbn7cnUiGmUEBrCVjnAw+HrKyN8bYVV0dIGllswYXwkG/+bgiBlE6IVIBAq+JwVWu1Sss3KarHY3OvFJUXZoZyRRg/Gc/+LRCE7lyKpwWQ70dbelGRyyJFH36eNv6ySXoUYtGkwlU5IVaHPApOxe4LHPZa/qhSRbPo2hwoh0orCtgejRebNtW5nlx00DNFgsvn8Svz2cIYLxsPVzKgUxs8Zxsxgn+Q/UvR7uq4AbAhyBMLxv7DjJ1pc7PJocuTno2Rw9uMZi1gkjbnmiOh6TTXIEWbnroyIhwc8555uto9melEUmWNQ+C+PwAK+MPw=="; }; } From 81351953fc778e59316bd77f89ec1275e24b2fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 10 Jun 2024 12:24:31 +0200 Subject: [PATCH 08/12] bump flake --- flake.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 8ffc153..059f9e4 100644 --- a/flake.lock +++ b/flake.lock @@ -59,14 +59,15 @@ ] }, "locked": { - "lastModified": 1717997057, - "narHash": "sha256-SQtmiLGFuZTuRT+IhOD8K38PHmkhof3mHM4aKIP6pW8=", + "lastModified": 1717687835, + "narHash": "sha256-CR60V+CNo/hdUnOTmN8IiorPN8dl7DtzZSiwDHAe+ss=", + "rev": "5c11a30b4685d992d955a2849129c4a7ea1a29ad", "type": "tarball", - "url": "https://git.clan.lol/clan/clan-core/archive/main.tar.gz" + "url": "https://git.clan.lol/api/v1/repos/clan/clan-core/archive/5c11a30b4685d992d955a2849129c4a7ea1a29ad.tar.gz" }, "original": { "type": "tarball", - "url": "https://git.clan.lol/clan/clan-core/archive/main.tar.gz" + "url": "https://git.clan.lol/clan/clan-core/archive/synapse.tar.gz" } }, "disko": { From 5962d1226de6413b452b462c896444f2d98a523b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 10 Jun 2024 12:32:16 +0200 Subject: [PATCH 09/12] mailserver: switch to acme-nginx --- modules/mailserver.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/mailserver.nix b/modules/mailserver.nix index 4902d1f..86d41ca 100644 --- a/modules/mailserver.nix +++ b/modules/mailserver.nix @@ -22,6 +22,7 @@ in fqdn = "mail.clan.lol"; domains = [ "clan.lol" ]; enablePop3 = true; + certificateScheme = "acme-nginx"; # kresd sucks unfortunally (fails when one NS server is not working, instead of trying other ones) localDnsResolver = false; From d3f9ea2cea79922a594c6ac28b51429c75264c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 10 Jun 2024 14:34:58 +0200 Subject: [PATCH 10/12] drop unused renovate --- pkgs/flake-module.nix | 1 - pkgs/renovate/composition.nix | 32 - pkgs/renovate/default.nix | 13 - pkgs/renovate/generate.sh | 5 - pkgs/renovate/node-env.nix | 784 --- pkgs/renovate/node-packages.json | 3 - pkgs/renovate/node-packages.nix | 8288 ------------------------------ 7 files changed, 9126 deletions(-) delete mode 100644 pkgs/renovate/composition.nix delete mode 100644 pkgs/renovate/default.nix delete mode 100755 pkgs/renovate/generate.sh delete mode 100644 pkgs/renovate/node-env.nix delete mode 100644 pkgs/renovate/node-packages.json delete mode 100644 pkgs/renovate/node-packages.nix diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix index d58fac3..3e7c297 100644 --- a/pkgs/flake-module.nix +++ b/pkgs/flake-module.nix @@ -8,7 +8,6 @@ writers = pkgs.callPackage ./writers.nix { }; in { - inherit (pkgs.callPackage ./renovate { }) renovate; gitea = pkgs.callPackage ./gitea { }; action-create-pr = pkgs.callPackage ./action-create-pr { diff --git a/pkgs/renovate/composition.nix b/pkgs/renovate/composition.nix deleted file mode 100644 index c4d6395..0000000 --- a/pkgs/renovate/composition.nix +++ /dev/null @@ -1,32 +0,0 @@ -# This file has been generated by node2nix 1.11.1. Do not edit! - -{ - pkgs ? import { inherit system; }, - system ? builtins.currentSystem, - nodejs ? pkgs."nodejs_18", -}: - -let - nodeEnv = import ./node-env.nix { - inherit (pkgs) - stdenv - lib - python2 - runCommand - writeTextFile - writeShellScript - ; - inherit pkgs nodejs; - libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; - }; -in -import ./node-packages.nix { - inherit (pkgs) - fetchurl - nix-gitignore - stdenv - lib - fetchgit - ; - inherit nodeEnv; -} diff --git a/pkgs/renovate/default.nix b/pkgs/renovate/default.nix deleted file mode 100644 index c3f442b..0000000 --- a/pkgs/renovate/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - pkgs, - system, - nodejs-18_x, - makeWrapper, -}: -let - nodePackages = import ./composition.nix { - inherit pkgs system; - nodejs = nodejs-18_x; - }; -in -nodePackages diff --git a/pkgs/renovate/generate.sh b/pkgs/renovate/generate.sh deleted file mode 100755 index 6c96c25..0000000 --- a/pkgs/renovate/generate.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env nix-shell -#! nix-shell -i bash -p nodePackages.node2nix - -rm -f node-env.nix -node2nix -18 -i node-packages.json -o node-packages.nix -c composition.nix diff --git a/pkgs/renovate/node-env.nix b/pkgs/renovate/node-env.nix deleted file mode 100644 index 68f8c82..0000000 --- a/pkgs/renovate/node-env.nix +++ /dev/null @@ -1,784 +0,0 @@ -# This file originates from node2nix - -{ - lib, - stdenv, - nodejs, - python2, - pkgs, - libtool, - runCommand, - writeTextFile, - writeShellScript, -}: - -let - # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master - utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux; - - python = if nodejs ? python then nodejs.python else python2; - - # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise - tarWrapper = runCommand "tarWrapper" { } '' - mkdir -p $out/bin - - cat > $out/bin/tar <> $out/nix-support/hydra-build-products - ''; - }; - - # Common shell logic - installPackage = writeShellScript "install-package" '' - installPackage() { - local packageName=$1 src=$2 - - local strippedName - - local DIR=$PWD - cd $TMPDIR - - unpackFile $src - - # Make the base dir in which the target dependency resides first - mkdir -p "$(dirname "$DIR/$packageName")" - - if [ -f "$src" ] - then - # Figure out what directory has been unpacked - packageDir="$(find . -maxdepth 1 -type d | tail -1)" - - # Restore write permissions to make building work - find "$packageDir" -type d -exec chmod u+x {} \; - chmod -R u+w "$packageDir" - - # Move the extracted tarball into the output folder - mv "$packageDir" "$DIR/$packageName" - elif [ -d "$src" ] - then - # Get a stripped name (without hash) of the source directory. - # On old nixpkgs it's already set internally. - if [ -z "$strippedName" ] - then - strippedName="$(stripHash $src)" - fi - - # Restore write permissions to make building work - chmod -R u+w "$strippedName" - - # Move the extracted directory into the output folder - mv "$strippedName" "$DIR/$packageName" - fi - - # Change to the package directory to install dependencies - cd "$DIR/$packageName" - } - ''; - - # Bundle the dependencies of the package - # - # Only include dependencies if they don't exist. They may also be bundled in the package. - includeDependencies = - { dependencies }: - lib.optionalString (dependencies != [ ]) ( - '' - mkdir -p node_modules - cd node_modules - '' - + (lib.concatMapStrings (dependency: '' - if [ ! -e "${dependency.packageName}" ]; then - ${composePackage dependency} - fi - '') dependencies) - + '' - cd .. - '' - ); - - # Recursively composes the dependencies of a package - composePackage = - { - packageName, - src, - dependencies ? [ ], - ... - }: - builtins.addErrorContext "while evaluating node package '${packageName}'" '' - installPackage "${packageName}" "${src}" - ${includeDependencies { inherit dependencies; }} - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - ''; - - pinpointDependencies = - { dependencies, production }: - let - pinpointDependenciesFromPackageJSON = writeTextFile { - name = "pinpointDependencies.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - function resolveDependencyVersion(location, name) { - if(location == process.env['NIX_STORE']) { - return null; - } else { - var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); - - if(fs.existsSync(dependencyPackageJSON)) { - var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); - - if(dependencyPackageObj.name == name) { - return dependencyPackageObj.version; - } - } else { - return resolveDependencyVersion(path.resolve(location, ".."), name); - } - } - } - - function replaceDependencies(dependencies) { - if(typeof dependencies == "object" && dependencies !== null) { - for(var dependency in dependencies) { - var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); - - if(resolvedVersion === null) { - process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); - } else { - dependencies[dependency] = resolvedVersion; - } - } - } - } - - /* Read the package.json configuration */ - var packageObj = JSON.parse(fs.readFileSync('./package.json')); - - /* Pinpoint all dependencies */ - replaceDependencies(packageObj.dependencies); - if(process.argv[2] == "development") { - replaceDependencies(packageObj.devDependencies); - } - else { - packageObj.devDependencies = {}; - } - replaceDependencies(packageObj.optionalDependencies); - replaceDependencies(packageObj.peerDependencies); - - /* Write the fixed package.json file */ - fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); - ''; - }; - in - '' - node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"} - - ${lib.optionalString (dependencies != [ ]) '' - if [ -d node_modules ] - then - cd node_modules - ${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies} - cd .. - fi - ''} - ''; - - # Recursively traverses all dependencies of a package and pinpoints all - # dependencies in the package.json file to the versions that are actually - # being used. - - pinpointDependenciesOfPackage = - { - packageName, - dependencies ? [ ], - production ? true, - ... - }: - '' - if [ -d "${packageName}" ] - then - cd "${packageName}" - ${pinpointDependencies { inherit dependencies production; }} - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - fi - ''; - - # Extract the Node.js source code which is used to compile packages with - # native bindings - nodeSources = runCommand "node-sources" { } '' - tar --no-same-owner --no-same-permissions -xf ${nodejs.src} - mv node-* $out - ''; - - # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) - addIntegrityFieldsScript = writeTextFile { - name = "addintegrityfields.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - function augmentDependencies(baseDir, dependencies) { - for(var dependencyName in dependencies) { - var dependency = dependencies[dependencyName]; - - // Open package.json and augment metadata fields - var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); - var packageJSONPath = path.join(packageJSONDir, "package.json"); - - if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored - console.log("Adding metadata fields to: "+packageJSONPath); - var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); - - if(dependency.integrity) { - packageObj["_integrity"] = dependency.integrity; - } else { - packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. - } - - if(dependency.resolved) { - packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided - } else { - packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. - } - - if(dependency.from !== undefined) { // Adopt from property if one has been provided - packageObj["_from"] = dependency.from; - } - - fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); - } - - // Augment transitive dependencies - if(dependency.dependencies !== undefined) { - augmentDependencies(packageJSONDir, dependency.dependencies); - } - } - } - - if(fs.existsSync("./package-lock.json")) { - var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); - - if(![1, 2].includes(packageLock.lockfileVersion)) { - process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n"); - process.exit(1); - } - - if(packageLock.dependencies !== undefined) { - augmentDependencies(".", packageLock.dependencies); - } - } - ''; - }; - - # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes - reconstructPackageLock = writeTextFile { - name = "reconstructpackagelock.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - var packageObj = JSON.parse(fs.readFileSync("package.json")); - - var lockObj = { - name: packageObj.name, - version: packageObj.version, - lockfileVersion: 2, - requires: true, - packages: { - "": { - name: packageObj.name, - version: packageObj.version, - license: packageObj.license, - bin: packageObj.bin, - dependencies: packageObj.dependencies, - engines: packageObj.engines, - optionalDependencies: packageObj.optionalDependencies - } - }, - dependencies: {} - }; - - function augmentPackageJSON(filePath, packages, dependencies) { - var packageJSON = path.join(filePath, "package.json"); - if(fs.existsSync(packageJSON)) { - var packageObj = JSON.parse(fs.readFileSync(packageJSON)); - packages[filePath] = { - version: packageObj.version, - integrity: "sha1-000000000000000000000000000=", - dependencies: packageObj.dependencies, - engines: packageObj.engines, - optionalDependencies: packageObj.optionalDependencies - }; - dependencies[packageObj.name] = { - version: packageObj.version, - integrity: "sha1-000000000000000000000000000=", - dependencies: {} - }; - processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies); - } - } - - function processDependencies(dir, packages, dependencies) { - if(fs.existsSync(dir)) { - var files = fs.readdirSync(dir); - - files.forEach(function(entry) { - var filePath = path.join(dir, entry); - var stats = fs.statSync(filePath); - - if(stats.isDirectory()) { - if(entry.substr(0, 1) == "@") { - // When we encounter a namespace folder, augment all packages belonging to the scope - var pkgFiles = fs.readdirSync(filePath); - - pkgFiles.forEach(function(entry) { - if(stats.isDirectory()) { - var pkgFilePath = path.join(filePath, entry); - augmentPackageJSON(pkgFilePath, packages, dependencies); - } - }); - } else { - augmentPackageJSON(filePath, packages, dependencies); - } - } - }); - } - } - - processDependencies("node_modules", lockObj.packages, lockObj.dependencies); - - fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); - ''; - }; - - # Script that links bins defined in package.json to the node_modules bin directory - # NPM does not do this for top-level packages itself anymore as of v7 - linkBinsScript = writeTextFile { - name = "linkbins.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - var packageObj = JSON.parse(fs.readFileSync("package.json")); - - var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep); - - if(packageObj.bin !== undefined) { - fs.mkdirSync(path.join(nodeModules, ".bin")) - - if(typeof packageObj.bin == "object") { - Object.keys(packageObj.bin).forEach(function(exe) { - if(fs.existsSync(packageObj.bin[exe])) { - console.log("linking bin '" + exe + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.bin[exe]), - path.join(nodeModules, ".bin", exe) - ); - } - else { - console.log("skipping non-existent bin '" + exe + "'"); - } - }) - } - else { - if(fs.existsSync(packageObj.bin)) { - console.log("linking bin '" + packageObj.bin + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.bin), - path.join(nodeModules, ".bin", packageObj.name.split("/").pop()) - ); - } - else { - console.log("skipping non-existent bin '" + packageObj.bin + "'"); - } - } - } - else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) { - fs.mkdirSync(path.join(nodeModules, ".bin")) - - fs.readdirSync(packageObj.directories.bin).forEach(function(exe) { - if(fs.existsSync(path.join(packageObj.directories.bin, exe))) { - console.log("linking bin '" + exe + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.directories.bin, exe), - path.join(nodeModules, ".bin", exe) - ); - } - else { - console.log("skipping non-existent bin '" + exe + "'"); - } - }) - } - ''; - }; - - prepareAndInvokeNPM = - { - packageName, - bypassCache, - reconstructLock, - npmFlags, - production, - }: - let - forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com"; - in - '' - # Pinpoint the versions of all dependencies to the ones that are actually being used - echo "pinpointing versions of dependencies..." - source $pinpointDependenciesScriptPath - - # Patch the shebangs of the bundled modules to prevent them from - # calling executables outside the Nix store as much as possible - patchShebangs . - - # Deploy the Node.js package by running npm install. Since the - # dependencies have been provided already by ourselves, it should not - # attempt to install them again, which is good, because we want to make - # it Nix's responsibility. If it needs to install any dependencies - # anyway (e.g. because the dependency parameters are - # incomplete/incorrect), it fails. - # - # The other responsibilities of NPM are kept -- version checks, build - # steps, postprocessing etc. - - export HOME=$TMPDIR - cd "${packageName}" - runHook preRebuild - - ${lib.optionalString bypassCache '' - ${lib.optionalString reconstructLock '' - if [ -f package-lock.json ] - then - echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" - echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" - rm package-lock.json - else - echo "No package-lock.json file found, reconstructing..." - fi - - node ${reconstructPackageLock} - ''} - - node ${addIntegrityFieldsScript} - ''} - - npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild - - runHook postRebuild - - if [ "''${dontNpmInstall-}" != "1" ] - then - # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. - rm -f npm-shrinkwrap.json - - npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install - fi - - # Link executables defined in package.json - node ${linkBinsScript} - ''; - - # Builds and composes an NPM package including all its dependencies - buildNodePackage = - { - name, - packageName, - version ? null, - buildInputs ? [ ], - production ? true, - npmFlags ? "", - dontNpmInstall ? false, - bypassCache ? false, - reconstructLock ? false, - preRebuild ? "", - dontStrip ? true, - unpackPhase ? "true", - buildPhase ? "true", - meta ? { }, - ... - }@args: - - let - extraArgs = removeAttrs args [ - "name" - "dependencies" - "buildInputs" - "dontStrip" - "dontNpmInstall" - "preRebuild" - "unpackPhase" - "buildPhase" - "meta" - ]; - in - stdenv.mkDerivation ( - { - name = "${name}${if version == null then "" else "-${version}"}"; - buildInputs = - [ - tarWrapper - python - nodejs - ] - ++ lib.optional (stdenv.isLinux) utillinux ++ lib.optional (stdenv.isDarwin) libtool ++ buildInputs; - - inherit nodejs; - - inherit dontStrip; # Stripping may fail a build for some package deployments - inherit - dontNpmInstall - preRebuild - unpackPhase - buildPhase - ; - - compositionScript = composePackage args; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; - - passAsFile = [ - "compositionScript" - "pinpointDependenciesScript" - ]; - - installPhase = '' - source ${installPackage} - - # Create and enter a root node_modules/ folder - mkdir -p $out/lib/node_modules - cd $out/lib/node_modules - - # Compose the package and all its dependencies - source $compositionScriptPath - - ${prepareAndInvokeNPM { - inherit - packageName - bypassCache - reconstructLock - npmFlags - production - ; - }} - - # Create symlink to the deployed executable folder, if applicable - if [ -d "$out/lib/node_modules/.bin" ] - then - ln -s $out/lib/node_modules/.bin $out/bin - - # Fixup all executables - ls $out/bin/* | while read i - do - file="$(readlink -f "$i")" - chmod u+rwx "$file" - if isScript "$file" - then - sed -i 's/\r$//' "$file" # convert crlf to lf - fi - done - fi - - # Create symlinks to the deployed manual page folders, if applicable - if [ -d "$out/lib/node_modules/${packageName}/man" ] - then - mkdir -p $out/share - for dir in "$out/lib/node_modules/${packageName}/man/"* - do - mkdir -p $out/share/man/$(basename "$dir") - for page in "$dir"/* - do - ln -s $page $out/share/man/$(basename "$dir") - done - done - fi - - # Run post install hook, if provided - runHook postInstall - ''; - - meta = { - # default to Node.js' platforms - platforms = nodejs.meta.platforms; - } // meta; - } - // extraArgs - ); - - # Builds a node environment (a node_modules folder and a set of binaries) - buildNodeDependencies = - { - name, - packageName, - version ? null, - src, - dependencies ? [ ], - buildInputs ? [ ], - production ? true, - npmFlags ? "", - dontNpmInstall ? false, - bypassCache ? false, - reconstructLock ? false, - dontStrip ? true, - unpackPhase ? "true", - buildPhase ? "true", - ... - }@args: - - let - extraArgs = removeAttrs args [ - "name" - "dependencies" - "buildInputs" - ]; - in - stdenv.mkDerivation ( - { - name = "node-dependencies-${name}${if version == null then "" else "-${version}"}"; - - buildInputs = - [ - tarWrapper - python - nodejs - ] - ++ lib.optional (stdenv.isLinux) utillinux ++ lib.optional (stdenv.isDarwin) libtool ++ buildInputs; - - inherit dontStrip; # Stripping may fail a build for some package deployments - inherit dontNpmInstall unpackPhase buildPhase; - - includeScript = includeDependencies { inherit dependencies; }; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; - - passAsFile = [ - "includeScript" - "pinpointDependenciesScript" - ]; - - installPhase = '' - source ${installPackage} - - mkdir -p $out/${packageName} - cd $out/${packageName} - - source $includeScriptPath - - # Create fake package.json to make the npm commands work properly - cp ${src}/package.json . - chmod 644 package.json - ${lib.optionalString bypassCache '' - if [ -f ${src}/package-lock.json ] - then - cp ${src}/package-lock.json . - chmod 644 package-lock.json - fi - ''} - - # Go to the parent folder to make sure that all packages are pinpointed - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - - ${prepareAndInvokeNPM { - inherit - packageName - bypassCache - reconstructLock - npmFlags - production - ; - }} - - # Expose the executables that were installed - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - - mv ${packageName} lib - ln -s $out/lib/node_modules/.bin $out/bin - ''; - } - // extraArgs - ); - - # Builds a development shell - buildNodeShell = - { - name, - version ? null, - dependencies ? [ ], - buildInputs ? [ ], - ... - }@args: - - let - nodeDependencies = buildNodeDependencies args; - extraArgs = removeAttrs args [ - "name" - "dependencies" - "buildInputs" - "dontStrip" - "dontNpmInstall" - "unpackPhase" - "buildPhase" - ]; - in - stdenv.mkDerivation ( - { - name = "node-shell-${name}${if version == null then "" else "-${version}"}"; - - buildInputs = [ - python - nodejs - ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs; - buildCommand = '' - mkdir -p $out/bin - cat > $out/bin/shell < Date: Mon, 10 Jun 2024 14:36:32 +0200 Subject: [PATCH 11/12] update terraform state --- targets/web01/terraform.tfstate | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/targets/web01/terraform.tfstate b/targets/web01/terraform.tfstate index a389b3a..8670f3b 100644 --- a/targets/web01/terraform.tfstate +++ b/targets/web01/terraform.tfstate @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data: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,iv:SgX7Xf2xRGpwoSAZwLUQgTB8lcjH52lIkpE9EB9IBls=,tag:Ku7hHB13/hgUf5LSbvBfVg==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:6VvKJ/L+HbX5HchHkBCzWjDB0nTXT2q5LKgP+wFIwVE=,tag:DRSEQM7Xz+oZF/3XKAdfLw==,type:str]", "sops": { "kms": null, "gcp_kms": null, @@ -8,19 +8,19 @@ "age": [ { "recipient": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzSzlodjNJaTFjWXlhZlRC\nM3RvRTBQS2dhQ2dDZ3ZabFgrQkg0UmFZR0hzCm1FZGU2T1RZU1JtZCs2ZWpsaTJ6\nRmNZUkpteTBnRWN6WERsckxzNHJBUTQKLS0tIDQ0alExbzdrd0ZNelRzMzRIRHBR\nZ1BNVE9MR2UwaDhyUmptcnRxV2pXcWsKfZB5Xofp3EOHpOQCji6xgmnRHE+DgNST\nw/hsfNRpHKj4zoR9hRt3VgP4SNatgoEPe+Djaovi0iLs1bfJiJKO0g==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1U2ZqdXVuUGFOMWJwU3dF\nZ1MrbytNaFdHUzJTNWFsV0tuaTFoSlVxNWtRCmswU09CNDRKSTNnWnVJcWtRTFR3\nVEJFY0FpRU9oWTlRb2NuNU90MndZUHMKLS0tIG9CVGEreEhPbmtQSU1XS3dXUmFx\nYmhkMnY5cGUwNE80bVZuTFJYMXVQblkKV/hEMBc65tiYvMgGwXNbc99p26wvpHP6\nkRkWM3xXykNqosxOqnUOSUf8gwRP/ARjWSobO24vrZgSmfCRAh8A5g==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1eq0e6uhjj2tja8v338tkdz8ema2aw5anpuyaq2uru7rt4lq7msyqqut6m2", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBa1dsSVI0bHNGK21zazI1\nYlR6WDFBVVZtT2VMalF3UXJIUU5XQlFNNVJzCllYYUtzbFZTc3hiZzlxVXdDRWlJ\nd1M1dDNlWEdhMy9JWDNFQ2hrcDFiUVEKLS0tIGlUaXRsRk1mL3pDRGZsWU4yNENu\nT1NJUkV4Rjh1NDN3N1B3SjMvNWJiclUKtToU5+0DtJ5LoieYwGOI1cId0pboaAsi\ntIaDZPXdiN0DkY15ovWMvcDJB8mrd1mgGUTAMYRbgGFjUBY6fVI09Q==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxNW5aWTlxdjBHc3NuOEI4\ncHFEd1VvT0JOeCtUbUx1NmM4SWs5Qm5nbFZnCkpQWUkzbkNPeG5LaVlEMHFJc3Vs\nNHovZ2JvU01WV3FZMTRuUUpUcXIwQkkKLS0tIE1xNEY2bmtpcVpBdkdpZ0d1YS8v\nMlVTQzcvRkFidWZUTUpjR2JsQWxpK3cKpJkt6nhatJOqCoTIFb9Wih7krVDlXMxB\nCYA03dA3QaZjYQfTpNUG5t4w2y3t1Xllg4NY5s76Q+nm7Y6P9XPPWw==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwb0JFUWFuTXlyU29GRGlk\naEFLb0V4Y3VNWTFCQVNmaG9DcnZmVFJwcVFzClpLNzBqOVl4VWduQWQwdENwR2ta\nUTJ2K3JPcHdDVE0vTWRMaVRUQ1I2ZTQKLS0tIGFiYThoZHA1YVdxYU5xM3ZYR1V4\nRXlZTnNVMlVlaUlKaW14Rnh4QXJKc3MKiTXWF5z5EOu9qiC+kpCuGXp6bAmMz8nO\npw16sssn2Kse4MQ8T3gMevoS3LdN5pb+2Uy8ZLOKwkd1cziyzX/9WQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZczB1TnErWjFMNGRCZDhw\ndndEbjZyekJ2NjhacmV4Qk9nWTJ0aHI2d2hFClRkeE9LZFpITkpPRnlDUmNDRkpV\neG5VWHNyYk9wSzhtdFRTVld5MHVhR2MKLS0tIFdNQVBiZlhrSElYWW40ODc5Vmd2\neEVqMlh5c0ZQVCtxbkJVTlk3RzNqNjAKvoTR3Jp8ek5xoWf5LPkkwhcVNU4v6BVf\nuN/LZua/xJtXqnAlQQwyPzdsdPtgZP6PrYuJUtx4deH/Wvlh+ig+RQ==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2024-06-10T10:03:52Z", - "mac": "ENC[AES256_GCM,data:9vUc+bzCMQyykgIJpI/FJipSzWBbRCW9+oX+mcP2xQQHiXYdSo2RW1gpjtpBLolkSF8MmrH4MhqdSGu6wIsypfIjPMo8nEvwWGZIkkQcf/ci9oOS3uDPz9k/g5Uw5LMtegjPuOJH6s4mEIa9vmDDH0wGJjOEhXZJgpYtHhN7hXc=,iv:9ONB2awn+klnUGax4i6clh+7F5xHrxVVIXfPrAlzmjE=,tag:XNxbXh2dCDafWt4IIHpiIg==,type:str]", + "lastmodified": "2024-06-10T10:40:11Z", + "mac": "ENC[AES256_GCM,data:O8Czc3p97bPhDzs8hB7Arx1zJmtgNCfo+RlOAA6o52ndzuAKVVLG2qxPReliOO2tIhub2jHIW0vifbHo/hNaewDz2pB7G3Y9l1GGZ5RDkHaQh6NCvZCaaGTt0SD+gvFvQaSFh1wspugOvnW3nLATDXnS+dBpSs9G9OzHo6tMXBw=,iv:6a4RdG3vXQ0fepQE674rw2uCyAdSe7N5WiE28RQ3UYc=,tag:eHNiP6c4fGC9vsJravA5WA==,type:str]", "pgp": null, "unencrypted_suffix": "_unencrypted", "version": "3.8.1" From e7613d611ac8489998827eec6e4f331454f45e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 10 Jun 2024 15:25:33 +0200 Subject: [PATCH 12/12] switch to clan-core main --- flake.lock | 62 +++++++++++++++++++++++++++--------------------------- flake.nix | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/flake.lock b/flake.lock index 059f9e4..8c9cc3c 100644 --- a/flake.lock +++ b/flake.lock @@ -59,15 +59,15 @@ ] }, "locked": { - "lastModified": 1717687835, - "narHash": "sha256-CR60V+CNo/hdUnOTmN8IiorPN8dl7DtzZSiwDHAe+ss=", - "rev": "5c11a30b4685d992d955a2849129c4a7ea1a29ad", + "lastModified": 1718025848, + "narHash": "sha256-la9fj0/QSJyEaw1LXMLke9JdiKRdue7sS1bg4uQ5A/4=", + "rev": "a1acf0b05d201e12dcbebd2396d71b93e14b8982", "type": "tarball", - "url": "https://git.clan.lol/api/v1/repos/clan/clan-core/archive/5c11a30b4685d992d955a2849129c4a7ea1a29ad.tar.gz" + "url": "https://git.clan.lol/api/v1/repos/clan/clan-core/archive/a1acf0b05d201e12dcbebd2396d71b93e14b8982.tar.gz" }, "original": { "type": "tarball", - "url": "https://git.clan.lol/clan/clan-core/archive/synapse.tar.gz" + "url": "https://git.clan.lol/clan/clan-core/archive/main.tar.gz" } }, "disko": { @@ -78,11 +78,11 @@ ] }, "locked": { - "lastModified": 1717915259, - "narHash": "sha256-VsGPboaleIlPELHY5cNTrXK4jHVmgUra8uC6h7KVC5c=", + "lastModified": 1717177033, + "narHash": "sha256-G3CZJafCO8WDy3dyA2EhpUJEmzd5gMJ2IdItAg0Hijw=", "owner": "nix-community", "repo": "disko", - "rev": "1bbdb06f14e2621290b250e631cf3d8948e4d19b", + "rev": "0274af4c92531ebfba4a5bd493251a143bc51f3c", "type": "github" }, "original": { @@ -159,6 +159,22 @@ "type": "github" } }, + "nixos-2311": { + "locked": { + "lastModified": 1717017538, + "narHash": "sha256-S5kltvDDfNQM3xx9XcvzKEOyN2qk8Sa+aSOLqZ+1Ujc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "64e468fd2652105710d86cd2ae3e65a5a6d58dec", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nixos-generators": { "inputs": { "nixlib": "nixlib", @@ -183,18 +199,18 @@ }, "nixos-images": { "inputs": { - "nixos-stable": "nixos-stable", + "nixos-2311": "nixos-2311", "nixos-unstable": [ "clan-core", "nixpkgs" ] }, "locked": { - "lastModified": 1717770332, - "narHash": "sha256-NQmFHj0hTCUgnMAsaNTu6sNTRyo0rFQEe+/lVgV5yxU=", + "lastModified": 1717040312, + "narHash": "sha256-yI/en4IxuCEClIUpIs3QTyYCCtmSPLOhwLJclfNwdeg=", "owner": "nix-community", "repo": "nixos-images", - "rev": "72771bd35f4e19e32d6f652528483b5e07fc317b", + "rev": "47bfb55316e105390dd761e0b6e8e0be09462b67", "type": "github" }, "original": { @@ -230,22 +246,6 @@ "type": "gitlab" } }, - "nixos-stable": { - "locked": { - "lastModified": 1717555607, - "narHash": "sha256-WZ1s48OODmRJ3DHC+I/DtM3tDRuRJlNqMvxvAPTD7ec=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "0b8e7a1ae5a94da2e1ee3f3030a32020f6254105", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1717868076, @@ -286,11 +286,11 @@ ] }, "locked": { - "lastModified": 1717902109, - "narHash": "sha256-OQTjaEZcByyVmHwJlKp/8SE9ikC4w+mFd3X0jJs6wiA=", + "lastModified": 1717297459, + "narHash": "sha256-cZC2f68w5UrJ1f+2NWGV9Gx0dEYmxwomWN2B0lx0QRA=", "owner": "Mic92", "repo": "sops-nix", - "rev": "f0922ad001829b400f0160ba85b47d252fa3d925", + "rev": "ab2a43b0d21d1d37d4d5726a892f714eaeb4b075", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 71db00b..6f556a8 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ # Use the version of nixpkgs that has been tested to work with SrvOS srvos.inputs.nixpkgs.follows = "nixpkgs"; - clan-core.url = "https://git.clan.lol/clan/clan-core/archive/synapse.tar.gz"; + clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz"; clan-core.inputs.flake-parts.follows = "flake-parts"; clan-core.inputs.nixpkgs.follows = "nixpkgs"; clan-core.inputs.treefmt-nix.follows = "treefmt-nix";