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.
This commit is contained in:
stuebinm 2024-04-19 19:11:03 +02:00
parent 3dc63acf52
commit f654b33a56
10 changed files with 134 additions and 135 deletions

View file

@ -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;
};
};

View file

@ -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";
}

95
modules/containers.nix Normal file
View file

@ -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.<name>.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/<name> at /persist into this container.";
};
bindSecrets = mkOption {
default = false;
type = types.bool;
description =
"Whether to mount /run/secrets/<name> 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));
}

View file

@ -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" = {

View file

@ -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;

View file

@ -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" = {

View file

@ -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" = {

View file

@ -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" = {

View file

@ -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" ];
});
};
};
}

View file

@ -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;