From f654b33a564d4d2259b6edcbc10503d64e5b52bf Mon Sep 17 00:00:00 2001 From: stuebinm Date: Fri, 19 Apr 2024 19:11:03 +0200 Subject: [PATCH] modules/containers: a hacc-specific containers module this started with emily pointing out to me that it's possible to generate IP addresses for containers in Nix (hence no need to worry about ever having collisions, as we had before), but then I thought, hey, while I'm at it, I can also write a little container module so we have a little less repetition in our configs in general (and a more reasonable place for our custom evalConfig than just keeping it around in flake.nix). See the option descriptions in modules/containers.nix for further details. Apart from giving all containers a new IP address (and also shiny new IPv6 addresses), this should be a no-op for the actual built system. --- flake.nix | 19 ++----- modules/container-profile.nix | 7 --- modules/containers.nix | 95 +++++++++++++++++++++++++++++++++++ parsons/forgejo.nix | 21 ++------ parsons/hedgedoc-hacc.nix | 23 +++------ parsons/hedgedoc-i4f.nix | 19 ++----- parsons/mattermost.nix | 22 ++------ parsons/nextcloud.nix | 19 ++----- parsons/tracktrain.nix | 25 +++------ parsons/uffd.nix | 19 ++----- 10 files changed, 134 insertions(+), 135 deletions(-) create mode 100644 modules/containers.nix diff --git a/flake.nix b/flake.nix index cb9501a..7e3747c 100644 --- a/flake.nix +++ b/flake.nix @@ -38,33 +38,20 @@ system = "x86_64-linux"; config.allowUnfree = true; }; - evalConfig = config: (nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config - { - nixpkgs.pkgs = pkgs.lib.mkForce pkgs; - imports = [ modules.nopersist profiles.container ]; - } - ]; - specialArgs = { - # some of our modules import each other, and evalConfig is used for containers - inherit modules evalConfig; - sources = inputs; - }; - }).config.system.build.toplevel; in { nixosConfigurations.parsons = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./parsons/configuration.nix ./modules/buildinfo.nix + ./modules/containers.nix sops-nix.nixosModules.sops { nixpkgs.pkgs = pkgs; } ]; specialArgs = { sources = inputs; - inherit modules evalConfig; + inherit modules profiles; + inherit (nixpkgs.lib) nixosSystem; }; }; diff --git a/modules/container-profile.nix b/modules/container-profile.nix index 53e051f..b50b6be 100644 --- a/modules/container-profile.nix +++ b/modules/container-profile.nix @@ -14,12 +14,5 @@ ''; }; - # I /suspect/ this is not actually needed. - # TODO: find spoons to deal with potential breakage, test removing this - networking.defaultGateway = { - address = "192.168.100.1"; - interface = "eth0"; - }; - system.stateVersion = lib.mkDefault "21.05"; } diff --git a/modules/containers.nix b/modules/containers.nix new file mode 100644 index 0000000..af46181 --- /dev/null +++ b/modules/containers.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, modules, profiles, sources, nixosSystem, ... }: + +let + mkIPv4 = index: local: + "192.168.${if local then "100" else "101"}.${toString index}"; + mkIPv6 = index: local: + "fd00::${if local then "100" else "101"}:${toString index}"; + + evalConfig = nixosConfig: (nixosSystem { + inherit (config.nixpkgs) system; + modules = [ + nixosConfig + modules.nopersist + profiles.container + { nixpkgs.pkgs = lib.mkForce pkgs; } + ]; + specialArgs = { + inherit modules sources; + }; + }).config.system.build.toplevel; + +in { + options.hacc.containers = with lib.options; + mkOption { + description = '' + hacc-specific containers. These are a thin wrapper around "normal" nixos containers: + - they automatically get an IPv4/IPv6 address assigned + (note that these are not guaranteed to be stable across config changes, + so please use {option}`containers..hostAddress` & friends to + reference them elsewhere) + - they set a couple default options (e.g. ephemeral, autoStart, privateNetwork) + - they are evaluated with our own version of {nix}`evalConfig`, which includes a + couple more modules by default, use our version of `nixpkgs`, and includes the + {nix}`profiles.containers` profile setting sane defaults for containers. + ''; + default = { }; + type = with lib.types; + types.attrsOf (types.submodule { + options = { + bindToPersist = mkOption { + default = true; + type = types.bool; + description = + "Wether to mount /persist/containers/ at /persist into this container."; + }; + + bindSecrets = mkOption { + default = false; + type = types.bool; + description = + "Whether to mount /run/secrets/ at /secrets into this container."; + }; + + config = mkOption { + type = types.unspecified; + description = + "The container's config, to be evaluated with our own {nix}`evalConfig`."; + }; + }; + }); + }; + + # wrapped into imap1, which enumerates the containers; IP addresses are then + # simply assigned based on the order the containers are in the list. + config.containers = lib.mkMerge (lib.imap1 + (index: { name, value }: let container = value; in { + ${name} = { + hostAddress = mkIPv4 index false; + localAddress = mkIPv4 index true; + hostAddress6 = mkIPv6 index false; + localAddress6 = mkIPv6 index true; + + privateNetwork = true; + autoStart = true; + ephemeral = true; + + bindMounts = lib.mkMerge [ + (lib.mkIf container.bindToPersist { + "/persist" = { + hostPath = "/persist/containers/${name}"; + isReadOnly = false; + }; + }) + (lib.mkIf container.bindSecrets { + "/secrets" = { + hostPath = "/run/secrets/${name}"; + isReadOnly = true; + }; + }) + ]; + + path = evalConfig container.config; + }; + }) (lib.attrsToList config.hacc.containers)); +} diff --git a/parsons/forgejo.nix b/parsons/forgejo.nix index 2832b22..6d71ab2 100644 --- a/parsons/forgejo.nix +++ b/parsons/forgejo.nix @@ -1,19 +1,8 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: { - containers.forgejo = { - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.10"; - autoStart = true; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/forgejo"; - isReadOnly = false; - }; - }; - path = evalConfig ({ config, lib, pkgs, ... }: { + hacc.containers.forgejo = { + config = { lib, pkgs, ... }: { system.stateVersion = "21.11"; environment.systemPackages = [ pkgs.forgejo ]; @@ -79,7 +68,7 @@ services.openssh = { enable = true; listenAddresses = [ { - addr = "192.168.100.10"; + addr = config.containers.forgejo.localAddress; port = 22; } ]; settings = { @@ -87,7 +76,7 @@ AcceptEnv = "GIT_PROTOCOL"; }; }; - }); + }; }; services.nginx.virtualHosts."git.infra4future.de" = { diff --git a/parsons/hedgedoc-hacc.nix b/parsons/hedgedoc-hacc.nix index 35462f2..05575c6 100644 --- a/parsons/hedgedoc-hacc.nix +++ b/parsons/hedgedoc-hacc.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: { @@ -6,20 +6,11 @@ "hedgedoc-hacc/env" = {}; }; - containers.pad-hacc = { - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.5"; - autoStart = true; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/pad-hacc"; - isReadOnly = false; - }; - "/secrets".hostPath = "/run/secrets/hedgedoc-hacc"; - }; - path = evalConfig ({ config, lib, ... }: { + containers.pad-hacc.bindMounts = { + "/secrets".hostPath = "/run/secrets/hedgedoc-hacc"; + }; + hacc.containers.pad-hacc = { + config = { config, lib, ... }: { services.hedgedoc = { enable = true; settings = { @@ -78,7 +69,7 @@ location = "/persist/backups/postgres"; }; hacc.bindToPersist = [ "/var/lib/hedgedoc" ]; - }); + }; }; services.nginx.virtualHosts."pad.hacc.earth" = { enableACME = true; diff --git a/parsons/hedgedoc-i4f.nix b/parsons/hedgedoc-i4f.nix index 97ca2ce..aa210d9 100644 --- a/parsons/hedgedoc-i4f.nix +++ b/parsons/hedgedoc-i4f.nix @@ -1,19 +1,8 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: { - containers.pad-i4f = { - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.6"; - autoStart = true; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/pad-i4f"; - isReadOnly = false; - }; - }; - path = evalConfig ({ config, lib, ... }: { + hacc.containers.pad-i4f = { + config = { config, lib, ... }: { services.hedgedoc = { enable = true; settings = { @@ -57,7 +46,7 @@ location = "/persist/backups/postgres"; }; hacc.bindToPersist = [ "/var/lib/hedgedoc" ]; - }); + }; }; services.nginx.virtualHosts."pad.infra4future.de" = { diff --git a/parsons/mattermost.nix b/parsons/mattermost.nix index 890d16a..7c20c5c 100644 --- a/parsons/mattermost.nix +++ b/parsons/mattermost.nix @@ -1,26 +1,14 @@ -{ config, pkgs, lib, evalConfig, ...}: +{ config, pkgs, lib, ...}: { sops.secrets = { "mattermost/env" = {}; }; - containers.mattermost = { - autoStart = true; - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.3"; - ephemeral = true; + hacc.containers.mattermost = { + bindSecrets = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/mattermost"; - isReadOnly = false; - }; - "/secrets".hostPath = "/run/secrets/mattermost"; - }; - - path = evalConfig ({ config, lib, pkgs, ... }: { + config = { config, lib, pkgs, ... }: { systemd.services.mattermost.serviceConfig.EnvironmentFile = lib.mkForce "/secrets/env"; @@ -218,7 +206,7 @@ startAt = "*-*-* 23:45:00"; location = "/persist/backups/postgres"; }; - }); + }; }; services.nginx.virtualHosts."mattermost.infra4future.de" = { diff --git a/parsons/nextcloud.nix b/parsons/nextcloud.nix index 85bfcb6..401c002 100644 --- a/parsons/nextcloud.nix +++ b/parsons/nextcloud.nix @@ -1,19 +1,8 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: { - containers.nextcloud = { - autoStart = true; - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.2"; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/nextcloud"; - isReadOnly = false; - }; - }; - path = evalConfig ({ config, lib, pkgs, ... }: { + hacc.containers.nextcloud = { + config = { config, lib, pkgs, ... }: { environment.systemPackages = [ pkgs.htop ]; services.nextcloud = { @@ -82,7 +71,7 @@ requires = ["postgresql.service"]; after = ["postgresql.service"]; }; - }); + }; }; services.nginx.virtualHosts."cloud.infra4future.de" = { diff --git a/parsons/tracktrain.nix b/parsons/tracktrain.nix index c1ae335..f4df212 100644 --- a/parsons/tracktrain.nix +++ b/parsons/tracktrain.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: let tracktrain-config = '' @@ -25,14 +25,14 @@ in enableACME = true; forceSSL = true; locations."/" = { - proxyPass = "http://192.168.42.41:4000"; + proxyPass = "http://${config.containers.tracktrain.localAddress}:4000"; proxyWebsockets = true; }; # note: this shadows the /metrics endpoint of tracktrain # in case you remove this, please consider putting something # else here to keep it from being publicly scrapable locations."/metrics/" = { - proxyPass = "http://192.168.42.41:2342"; + proxyPass = "http://${config.containers.tracktrain.localAddress}:2342"; proxyWebsockets = true; extraConfig = '' rewrite ^/metrics/(.*) /$1 break; @@ -40,21 +40,10 @@ in }; }; - containers.tracktrain = { - privateNetwork = true; - hostAddress = "192.168.42.40"; - localAddress = "192.168.42.41"; - autoStart = true; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/tracktrain"; - isReadOnly = false; - }; - "/secrets".hostPath = "/run/secrets/tracktrain"; - }; + hacc.containers.tracktrain = { + bindSecrets = true; - path = evalConfig ({ config, lib, pkgs, ... }: { + config = { config, lib, pkgs, ... }: { system.stateVersion = "21.11"; users.users.tracktrain = { @@ -151,7 +140,7 @@ in systemd.services.grafana.serviceConfig.EnvironmentFile = "/secrets/env"; hacc.bindToPersist = [ "/var/lib/grafana" ]; - }); + }; }; } diff --git a/parsons/uffd.nix b/parsons/uffd.nix index b2d2048..747ed76 100644 --- a/parsons/uffd.nix +++ b/parsons/uffd.nix @@ -1,19 +1,8 @@ -{ config, lib, pkgs, evalConfig, ... }: +{ config, lib, pkgs, ... }: { - containers.uffd = { - privateNetwork = true; - hostAddress = "192.168.100.1"; - localAddress = "192.168.100.9"; - autoStart = true; - ephemeral = true; - bindMounts = { - "/persist" = { - hostPath = "/persist/containers/uffd"; - isReadOnly = false; - }; - }; - path = evalConfig ({ config, lib, pkgs, ... }: { + hacc.containers.uffd = { + config = { config, lib, pkgs, ... }: { services.uwsgi = { enable = true; plugins = [ "python3" ]; @@ -29,7 +18,7 @@ hook-pre-app = "exec:FLASK_APP=${pkgs.uffd}/lib/python3.10/site-packages/uffd flask db upgrade"; }; }; - }); + }; }; services.nginx.virtualHosts."login.infra4future.de" = { enableACME = true;