Merge config/ into modules/

This commit is contained in:
Sandro - 2022-09-28 21:10:09 +02:00
parent 1f100e5f87
commit e9639581a3
Signed by: sandro
GPG Key ID: 3AF5A43A3EECC2E5
11 changed files with 403 additions and 424 deletions

View File

@ -1,282 +0,0 @@
# This module sets configuration for all NixOS machines defined in this flake
{ zentralwerk, hostRegistry, config, options, lib, pkgs, ... }:
let
hqPrefix64 = lib.removeSuffix "::" (builtins.head (
builtins.split "/" zentralwerk.lib.config.site.net.c3d2.subnets6.dn42
));
# Generate a deterministic IPv6 address for a 64 bit prefix
# and seed string. Prefix must not contain trailing ':'.
toIpv6Address = prefix64: seed:
with builtins;
let
digest = builtins.hashString "sha256" seed;
hextets = map (i: substring (4 * i) 4 digest) [ 0 1 2 3 ];
in concatStringsSep ":" ([ prefix64 ] ++ hextets);
# Generate a deterministic public IPv6 addresses
# for the HQ networking using a seed string.
toHqPrivateAddress = toIpv6Address hqPrefix64;
in {
imports = [
./stats.nix
./audio-server
./logging.nix
];
config = let
cfg = config.c3d2;
mkIfIsInHq = x: lib.mkIf cfg.isInHq (lib.mkDefault x);
in {
# Configuration specific to this machine
assertions = [
{
assertion = cfg.isInHq -> (config.users.users.root.password == null);
message = "Root passwords not allowed in HQ";
}
{
assertion = cfg.hq.enableBinaryCache -> cfg.mergeHostsFile;
message = "mergeHostsFile must be enabled for enableBinaryCache";
}
{
assertion = cfg.hq.enableMpdProxy -> cfg.mergeHostsFile;
message = "mergeHostsFile must be enabled for enableMpdProxy";
}
{
assertion = cfg.isInHq -> builtins.hasAttr config.networking.hostName cfg.hosts;
message = "${config.networking.hostName} is not registered in ${
toString ../host-registry.nix
}";
}
( # Check for host registry address collisions
let
getAddrHosts = key:
builtins.foldl' (result: host:
if cfg.hosts.${host}.${key} != null
then let
addr = cfg.hosts."${host}"."${key}";
in if result ? "${addr}"
then result // {
"${addr}" = result."${addr}" ++ [ host ];
}
else result // {
"${addr}" = [ host ];
}
else result
) {} (builtins.attrNames cfg.hosts);
dupHosts =
builtins.concatMap (hosts:
if builtins.length hosts == 1
then []
else hosts
) (
builtins.attrValues (
getAddrHosts "ip4" // getAddrHosts "ip6"
)
);
in {
assertion = dupHosts == [];
message = "Hosts have duplicate addresses: ${
lib.concatStringsSep " " dupHosts
}";
})
];
boot.cleanTmpDir = true;
c3d2.allUsersCanSshRoot = lib.mkDefault true;
i18n = {
defaultLocale = "en_US.UTF-8";
supportedLocales = [
"en_US.UTF-8/UTF-8"
"de_DE.UTF-8/UTF-8"
"C.UTF-8/UTF-8"
];
};
networking.defaultGateway = lib.mkIf (!config.networking.useNetworkd) (
mkIfIsInHq "172.22.99.4"
);
networking.domain = mkIfIsInHq "hq.c3d2.de";
systemd.network.networks =
if cfg.hq.interface != null && config.networking.useNetworkd
then {
"40-eth0".routes = [ {
routeConfig = {
Gateway = "172.22.99.4";
GatewayOnLink = true;
};
} ];
} else {};
networking.interfaces =
/* (if cfg.hq.externalInterface == null then
{ }
else {
"${cfg.hq.externalInterface}" = {
ipv6.addresses = [{
address = toHqPublicAddress config.networking.hostName;
prefixLength = 64;
}];
};
}) //
*/
if cfg.hq.interface == null then
{ }
else {
"${cfg.hq.interface}" = {
ipv6.addresses = [{
address = toHqPrivateAddress config.networking.hostName;
prefixLength = 64;
}];
};
};
networking.nameservers = with hostRegistry.hosts.dnscache; [
ip4
ip6
"9.9.9.9"
];
networking.useHostResolvConf = lib.mkIf (!config.services.resolved.enable) true;
environment.etc."resolv.conf" = lib.mkIf (!config.services.resolved.enable) {
text = lib.concatMapStrings (ns: ''
nameserver ${ns}
'') config.networking.nameservers;
};
nix = {
settings = {
auto-optimise-store = true;
trusted-public-keys = lib.mkIf (config.networking.hostName != "hydra") [
(builtins.readFile ../hosts/hydra/cache-pub.key)
];
substituters = lib.mkIf (config.networking.hostName != "hydra") (
lib.mkBefore [ "https://nix-serve.hq.c3d2.de" ]
);
};
gc = {
automatic = true;
dates = "06:00";
options = "--delete-older-than 21d";
randomizedDelaySec = "6h";
};
registry.c3d2 = {
from = {
id = "c3d2";
type = "indirect";
};
to = {
type = "git";
url = "https://gitea.c3d2.de/C3D2/nix-config.git";
};
};
extraOptions = ''
experimental-features = nix-command flakes
builders-use-substitutes = true
'';
};
services.openssh = {
# Required for deployment
enable = true;
permitRootLogin = "prohibit-password";
};
sops.age.sshKeyPaths = lib.mkDefault [ "/etc/ssh/ssh_host_ed25519_key" ];
environment = {
systemPackages = with pkgs; [
# Network fetchers
curl wget git
# System monitors
htop iotop bmon
ripgrep
# Terminal managers
tmux screen
# Editors
vim
# Pipeview
pv
# Network debugging
tcpdump ethtool mtr
];
variables = {
TERM = "xterm-256color";
};
# breaks various package builds
noXlibs = lib.mkForce false;
};
programs = {
ssh.knownHosts = with builtins;
let
intersectKeys = intersectAttrs {
publicKey = null;
publicKeyFile = null;
};
list = map (name:
let
host = getAttr name cfg.hosts;
sshAttrs = intersectKeys host;
in if sshAttrs == { } then
null
else {
inherit name;
value = let
ip6 = if host.ip6 != null then
host.ip6
else
toHqPrivateAddress name;
in {
publicKey = null;
publicKeyFile = null;
hostNames = [ ip6 "${name}.hq.c3d2.de" "${name}.hq" name ];
} // sshAttrs;
}) (builtins.attrNames cfg.hosts);
keyedHosts = filter (x: x.value.publicKey != null || x.value.publicKeyFile != null) list;
in listToAttrs keyedHosts;
vim.defaultEditor = true;
};
services.nginx = lib.mkIf config.services.nginx.enable {
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
};
time.timeZone = lib.mkDefault "Europe/Berlin";
# Reboot on hang
systemd.watchdog = lib.mkIf (!config.boot.isContainer) {
runtimeTime = "15s";
rebootTime = "15s";
};
# Defaults for LetsEncrypt
security.acme =
if options.security.acme ? defaults
then {
acceptTerms = true;
# NixOS>=22.05
defaults = {
email = cfg.acmeEmail;
# letsencrypt staging server with way higher rate limits
# server = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
}
else {
acceptTerms = true;
# TODO: NixOS<=21.05
email = cfg.acmeEmail;
};
zramSwap.enable = true;
};
}

View File

@ -520,9 +520,9 @@
})
self.nixosModules.c3d2
./config/audio-server
./config/c3d2.nix
./config/stats.nix
./modules/audio-server.nix
./modules/c3d2.nix
./modules/stats.nix
./modules/pi-sensors.nix
] ++ modules;
};
@ -962,7 +962,7 @@
];
c3d2.hosts = hostRegistry.hosts;
c3d2.users = import ./users.nix;
c3d2.nncp.neigh = import ./config/nncp-relays.nix;
c3d2.nncp.neigh = import ./modules/nncp-relays.nix;
};
cluster = ./modules/cluster;
cluster-network = ./modules/cluster-network.nix;

View File

@ -7,7 +7,7 @@
./hydra.nix
./updater.nix
./nomad-server.nix
../../config/c3d2.nix
../../modules/c3d2.nix
];
nixpkgs.config.allowUnfree = true;

View File

@ -2,8 +2,7 @@
{
imports = [
../../../config
../../../config/lxc-container.nix
../../../modules/lxc-container.nix
];
networking = {

View File

@ -1,9 +1,12 @@
# This module defines options for use by all C3D2 machines.
{ options, config, lib, pkgs, ... }:
{ zentralwerk, hostRegistry, config, options, lib, pkgs, ... }:
let
cfg = config.c3d2;
hqPrefix64 = lib.removeSuffix "::" (builtins.head (
builtins.split "/" zentralwerk.lib.config.site.net.c3d2.subnets6.dn42
));
neighMod = with lib; types.submodule {
options = {
addrs = mkOption {
@ -18,145 +21,157 @@ let
} // (with builtins; let value = mkOption { type = types.str; }; in
listToAttrs (map (name: { inherit name value; }) [ "exchpub" "id" "noisepub" "signpub" ]));
};
# Generate a deterministic IPv6 address for a 64 bit prefix
# and seed string. Prefix must not contain trailing ':'.
toIpv6Address = prefix64: seed:
with builtins;
let
digest = builtins.hashString "sha256" seed;
hextets = map (i: substring (4 * i) 4 digest) [ 0 1 2 3 ];
in
concatStringsSep ":" ([ prefix64 ] ++ hextets);
# Generate a deterministic public IPv6 addresses
# for the HQ networking using a seed string.
toHqPrivateAddress = toIpv6Address hqPrefix64;
in
{
options.c3d2 = with lib;
with lib.types; {
imports = [
./stats.nix
./audio-server.nix
./logging.nix
];
acmeEmail = mkOption {
type = str;
default = "mail@c3d2.de";
options.c3d2 = with lib; {
acmeEmail = mkOption {
type = types.str;
default = "mail@c3d2.de";
description = ''
Admin email address to use for Letsencrypt
'';
};
allUsersCanSshRoot = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Let all people in <literal>c3d2.users</literal>
login as root for deployment via SSH.
'';
};
isInHq = mkEnableOption "HQ presence (TODO: what is this? association to VLAN 5?)";
enableMotd = mkOption {
type = types.bool;
default = cfg.isInHq;
defaultText = literalExample "config.c3d2.isInHq";
};
mergeHostsFile = mkOption {
type = types.bool;
default = cfg.isInHq;
description = ''
Whether to add <literal>c3d2.hosts</literal> to /etc/hosts.
'';
};
mergeNncpSettings = mkEnableOption ''
Whether to merge <literal>c3d2.nncp.<>.nncp</literal>
into <literal>programs.nncp.settings</literal>.
'';
k-ot.enable = mkEnableOption ''
Add k-ot user to this machine. Anyone with an SSH key listed in
<literal>c3d2.users</literal> can log in as this user.
'';
hq = {
interface = mkOption {
type = with types; nullOr str;
default = null;
example = "eth0";
description = ''
Admin email address to use for Letsencrypt
Configure the given interface name with an internal IP address.
'';
};
allUsersCanSshRoot = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Let all people in <literal>c3d2.users</literal>
login as root for deployment via SSH.
'';
};
isInHq = mkEnableOption "HQ presence (TODO: what is this? association to VLAN 5?)";
enableMotd = mkOption {
type = bool;
enableBinaryCache = mkOption {
type = types.bool;
default = cfg.isInHq;
defaultText = literalExample "config.c3d2.isInHq";
description = "Whether to enable the local Nix binary cache";
};
mergeHostsFile = mkOption {
type = bool;
default = cfg.isInHq;
enableMpdProxy = mkOption {
type = types.bool;
default = false;
description = "Whether to proxy the local MPD database";
};
journalToMqtt = mkOption {
type = types.bool;
default = true;
};
};
hosts =
mkOption {
type = types.attrsOf (types.submodule {
options = {
ether = mkOption {
type = with types; nullOr str;
default = null;
};
ip4 = mkOption {
type = with types; nullOr str;
default = null;
};
ip6 = mkOption {
type = with types; nullOr str;
default = null;
};
publicKey = mkOption {
type = with types; nullOr str;
default = null;
};
wol = mkOption {
type = types.bool;
default = false;
};
serial = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Hardware serial number to help identification when netbooting.
'';
};
};
});
};
nncp = {
neigh = mkOption {
type = with types; attrsOf neighMod;
default = { };
description = ''
Whether to add <literal>c3d2.hosts</literal> to /etc/hosts.
Attrset of NNCP neighbours for relaying packets.
User endpoints go in <literal>c3d2.users</literal>.
'';
};
mergeNncpSettings = mkEnableOption ''
Whether to merge <literal>c3d2.nncp.<>.nncp</literal>
into <literal>programs.nncp.settings</literal>.
'';
k-ot.enable = mkEnableOption ''
Add k-ot user to this machine. Anyone with an SSH key listed in
<literal>c3d2.users</literal> can log in as this user.
'';
hq = {
interface = mkOption {
type = nullOr str;
default = null;
example = "eth0";
description = ''
Configure the given interface name with an internal IP address.
'';
};
enableBinaryCache = mkOption {
type = bool;
default = cfg.isInHq;
defaultText = literalExample "config.c3d2.isInHq";
description = "Whether to enable the local Nix binary cache";
};
enableMpdProxy = mkOption {
type = bool;
default = false;
description = "Whether to proxy the local MPD database";
};
journalToMqtt = mkOption {
type = bool;
default = true;
};
};
hosts =
mkOption {
type = attrsOf (submodule {
options = {
ether = mkOption {
type = with types; nullOr str;
default = null;
};
ip4 = mkOption {
type = with types; nullOr str;
default = null;
};
ip6 = mkOption {
type = with types; nullOr str;
default = null;
};
publicKey = mkOption {
type = with types; nullOr str;
default = null;
};
wol = mkOption {
type = types.bool;
default = false;
};
serial = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Hardware serial number to help identification when netbooting.
'';
};
};
});
};
nncp = {
neigh = mkOption {
type = with types; attrsOf neighMod;
default = { };
description = ''
Attrset of NNCP neighbours for relaying packets.
User endpoints go in <literal>c3d2.users</literal>.
'';
};
};
users =
mkOption {
type = attrsOf
(submodule {
options = {
sshKeys = mkOption {
type = with types;
listOf str;
default = [ ];
};
};
});
};
};
users = mkOption {
type = types.attrsOf (types.submodule {
options.sshKeys = mkOption {
type = with types; listOf str;
default = [ ];
};
});
};
};
config =
let
adminKeys = (with builtins; lib.lists.flatten (
@ -164,6 +179,7 @@ in
(getAttr "sshKeys")
(attrValues cfg.users)
));
mkIfIsInHq = x: lib.mkIf cfg.isInHq (lib.mkDefault x);
in
{
networking.hosts = lib.mkIf cfg.mergeHostsFile
@ -175,16 +191,13 @@ in
(lib.attrsets.filterAttrs (n: v: v.ip6 != null) cfg.hosts)
));
programs = lib.optionalAttrs (options.programs ? nncp) {
nncp.settings = lib.optionalAttrs cfg.mergeNncpSettings cfg.nncp;
};
programs.nncp.settings = lib.optionalAttrs cfg.mergeNncpSettings cfg.nncp;
users.motd = lib.mkIf cfg.enableMotd (builtins.readFile ./motd);
users = {
users = {
k-ot = lib.mkIf cfg.k-ot.enable {
packages = with pkgs; [ screen tmux ];
createHome = true;
isNormalUser = true;
uid = 1000;
@ -210,7 +223,7 @@ in
current_boot_only = true;
};
sinks.mqtt = {
inputs = ["journal"];
inputs = [ "journal" ];
type = "mqtt";
host = "broker.serv.zentralwerk.org";
# port = 8883;
@ -239,7 +252,8 @@ in
done
echo '}'
'';
in {
in
{
type = "exec";
command = [
catSecrets
@ -251,13 +265,261 @@ in
};
sops.secrets = lib.mkIf config.c3d2.hq.journalToMqtt {
"mqtt/user" = {
sopsFile = ../config/mqtt.yaml;
sopsFile = ../modules/mqtt.yaml;
owner = config.systemd.services.vector.serviceConfig.User;
};
"mqtt/password" = {
sopsFile = ../config/mqtt.yaml;
sopsFile = ../modules/mqtt.yaml;
owner = config.systemd.services.vector.serviceConfig.User;
};
};
assertions = [
{
assertion = cfg.isInHq -> (config.users.users.root.password == null);
message = "Root passwords not allowed in HQ";
}
{
assertion = cfg.hq.enableBinaryCache -> cfg.mergeHostsFile;
message = "mergeHostsFile must be enabled for enableBinaryCache";
}
{
assertion = cfg.hq.enableMpdProxy -> cfg.mergeHostsFile;
message = "mergeHostsFile must be enabled for enableMpdProxy";
}
{
assertion = cfg.isInHq -> builtins.hasAttr config.networking.hostName cfg.hosts;
message = "${config.networking.hostName} is not registered in ${toString ../host-registry.nix}";
}
(
# Check for host registry address collisions
let
getAddrHosts = key:
builtins.foldl'
(result: host:
if cfg.hosts.${host}.${key} != null
then
let
addr = cfg.hosts."${host}"."${key}";
in
if result ? "${addr}"
then result // {
"${addr}" = result."${addr}" ++ [ host ];
}
else result // {
"${addr}" = [ host ];
}
else result
)
{ }
(builtins.attrNames cfg.hosts);
dupHosts =
builtins.concatMap
(hosts:
if builtins.length hosts == 1
then [ ]
else hosts
)
(
builtins.attrValues (
getAddrHosts "ip4" // getAddrHosts "ip6"
)
);
in
{
assertion = dupHosts == [ ];
message = "Hosts have duplicate addresses: ${lib.concatStringsSep " " dupHosts}";
}
)
];
boot.cleanTmpDir = true;
c3d2.allUsersCanSshRoot = lib.mkDefault true;
i18n = {
defaultLocale = "en_US.UTF-8";
supportedLocales = [
"en_US.UTF-8"
"de_DE.UTF-8"
];
};
systemd.network.networks = lib.mkIf (cfg.hq.interface != null && config.networking.useNetworkd) {
"40-eth0".routes = [{
routeConfig = {
Gateway = "172.22.99.4";
GatewayOnLink = true;
};
}];
};
networking = {
defaultGateway = lib.mkIf (!config.networking.useNetworkd) (
mkIfIsInHq "172.22.99.4"
);
domain = mkIfIsInHq "hq.c3d2.de";
interfaces = lib.mkIf (cfg.hq.interface != null) {
"${cfg.hq.interface}".ipv6.addresses = [{
address = toHqPrivateAddress config.networking.hostName;
prefixLength = 64;
}];
};
nameservers = with hostRegistry.hosts.dnscache; [
ip4
ip6
"9.9.9.9"
];
useHostResolvConf = lib.mkIf (!config.services.resolved.enable) true;
};
environment.etc."resolv.conf" = lib.mkIf (!config.services.resolved.enable) {
text = lib.concatMapStrings
(ns: ''
nameserver ${ns}
'')
config.networking.nameservers;
};
nix = {
settings = {
auto-optimise-store = true;
trusted-public-keys = lib.mkIf (config.networking.hostName != "hydra") [
(builtins.readFile ../hosts/hydra/cache-pub.key)
];
substituters = lib.mkIf (config.networking.hostName != "hydra") (
lib.mkBefore [ "https://nix-serve.hq.c3d2.de" ]
);
};
gc = {
automatic = true;
dates = "06:00";
options = "--delete-older-than 21d";
randomizedDelaySec = "6h";
};
registry.c3d2 = {
from = {
id = "c3d2";
type = "indirect";
};
to = {
type = "git";
url = "https://gitea.c3d2.de/C3D2/nix-config.git";
};
};
extraOptions = ''
experimental-features = nix-command flakes
builders-use-substitutes = true
'';
};
services.openssh = {
# Required for deployment
enable = true;
permitRootLogin = "prohibit-password";
};
sops.age.sshKeyPaths = lib.mkDefault [ "/etc/ssh/ssh_host_ed25519_key" ];
environment = {
systemPackages = with pkgs; [
# Network fetchers
curl
wget
git
# System monitors
htop
iotop
bmon
ripgrep
# Terminal managers
tmux
screen
# Editors
vim
# Pipeview
pv
# Network debugging
tcpdump
ethtool
mtr
];
variables = {
# TERM = "xterm-256color";
};
# breaks various package builds
noXlibs = lib.mkForce false;
};
programs = {
ssh.knownHosts = with builtins;
let
intersectKeys = intersectAttrs {
publicKey = null;
publicKeyFile = null;
};
list = map
(name:
let
host = getAttr name cfg.hosts;
sshAttrs = intersectKeys host;
in
if sshAttrs == { } then
null
else {
inherit name;
value =
let ip6 = if host.ip6 != null then host.ip6 else toHqPrivateAddress name;
in
{
publicKey = null;
publicKeyFile = null;
hostNames = [ ip6 "${name}.hq.c3d2.de" "${name}.hq" name ];
} // sshAttrs;
})
(builtins.attrNames cfg.hosts);
keyedHosts = filter (x: x.value.publicKey != null || x.value.publicKeyFile != null) list;
in
listToAttrs keyedHosts;
vim.defaultEditor = true;
};
services.nginx = lib.mkIf config.services.nginx.enable {
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
};
time.timeZone = lib.mkDefault "Europe/Berlin";
# Reboot on hang
systemd.watchdog = lib.mkIf (!config.boot.isContainer) {
runtimeTime = "15s";
rebootTime = "15s";
};
# Defaults for LetsEncrypt
security.acme =
if options.security.acme ? defaults
then {
acceptTerms = true;
# NixOS>=22.05
defaults = {
email = cfg.acmeEmail;
# letsencrypt staging server with way higher rate limits
# server = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
}
else {
acceptTerms = true;
# TODO: NixOS<=21.05
email = cfg.acmeEmail;
};
zramSwap.enable = true;
};
}