1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-11 07:10:39 +02:00

refactor: major refactor into proper reusable modules. No logical changes.

This commit is contained in:
oddlama 2023-06-29 00:27:54 +02:00
parent 04872f6ec5
commit 84ac34cb6c
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
80 changed files with 761 additions and 776 deletions

View file

@ -1,10 +0,0 @@
{lib, ...}: {
boot.loader = {
grub = {
enable = true;
efiSupport = false;
};
timeout = lib.mkDefault 2;
};
console.earlySetup = true;
}

View file

@ -1,44 +0,0 @@
{config, ...}: {
imports = [
./impermanence.nix
./inputrc.nix
./issue.nix
./net.nix
./nix.nix
./resolved.nix
./ssh.nix
./system.nix
./xdg.nix
../../../users/root
../../../modules/deteministic-ids.nix
../../../modules/distributed-config.nix
../../../modules/extra.nix
../../../modules/interface-naming.nix
../../../modules/microvms.nix
../../../modules/oauth2-proxy.nix
../../../modules/promtail.nix
../../../modules/provided-domains.nix
../../../modules/repo.nix
../../../modules/telegraf.nix
../../../modules/wireguard.nix
];
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
verbose = true;
};
# If the host defines microvms, ensure that this core module and
# some boilerplate is imported automatically.
extra.microvms.commonImports = [
./.
{home-manager.users.root.home.minimal = true;}
];
# Required even when using home-manager's zsh module since the /etc/profile load order
# is partly controlled by this. See nix-community/home-manager#3681.
programs.zsh.enable = true;
}

View file

@ -1,160 +0,0 @@
{
config,
lib,
...
}: {
# Give agenix access to the hostkey independent of impermanence activation
age.identityPaths = ["/persist/etc/ssh/ssh_host_ed25519_key"];
# State that should be kept across reboots, but is otherwise
# NOT important information in any way that needs to be backed up.
environment.persistence."/state" = {
hideMounts = true;
directories =
[
{
directory = "/var/lib/systemd";
user = "root";
group = "root";
mode = "0755";
}
{
directory = "/var/log";
user = "root";
group = "root";
mode = "0755";
}
#{ directory = "/tmp"; user = "root"; group = "root"; mode = "1777"; }
#{ directory = "/var/tmp"; user = "root"; group = "root"; mode = "1777"; }
{
directory = "/var/spool";
user = "root";
group = "root";
mode = "0755";
}
]
++ lib.optionals config.networking.wireless.iwd.enable [
{
directory = "/var/lib/iwd";
user = "root";
group = "root";
mode = "0700";
}
];
};
# State that should be kept forever, and backed up accordingly.
environment.persistence."/persist" = {
hideMounts = true;
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
];
directories =
[
{
directory = "/var/lib/nixos";
user = "root";
group = "root";
mode = "0755";
}
]
++ lib.optionals config.security.acme.acceptTerms [
{
directory = "/var/lib/acme";
user = "acme";
group = "acme";
mode = "0755";
}
]
++ lib.optionals config.services.printing.enable [
{
directory = "/var/lib/cups";
user = "root";
group = "root";
mode = "0700";
}
]
++ lib.optionals config.services.fail2ban.enable [
{
directory = "/var/lib/fail2ban";
user = "fail2ban";
group = "fail2ban";
mode = "0750";
}
]
++ lib.optionals config.services.postgresql.enable [
{
directory = "/var/lib/postgresql";
user = "postgres";
group = "postgres";
mode = "0700";
}
]
++ lib.optionals config.services.gitea.enable [
{
directory = config.services.gitea.stateDir;
user = "gitea";
group = "gitea";
mode = "0700";
}
]
++ lib.optionals config.services.caddy.enable [
{
directory = config.services.caddy.dataDir;
user = "caddy";
group = "caddy";
mode = "0700";
}
]
++ lib.optionals config.services.loki.enable [
{
directory = "/var/lib/loki";
user = "loki";
group = "loki";
mode = "0700";
}
]
++ lib.optionals config.services.grafana.enable [
{
directory = config.services.grafana.dataDir;
user = "grafana";
group = "grafana";
mode = "0700";
}
]
++ lib.optionals config.services.kanidm.enableServer [
{
directory = "/var/lib/kanidm";
user = "kanidm";
group = "kanidm";
mode = "0700";
}
]
++ lib.optionals config.services.vaultwarden.enable [
{
directory = "/var/lib/vaultwarden";
user = "vaultwarden";
group = "vaultwarden";
mode = "0700";
}
]
++ lib.optionals config.services.influxdb2.enable [
{
directory = "/var/lib/influxdb2";
user = "influxdb2";
group = "influxdb2";
mode = "0700";
}
]
++ lib.optionals config.services.telegraf.enable [
{
directory = "/var/lib/telegraf";
user = "telegraf";
group = "telegraf";
mode = "0700";
}
];
};
}

View file

@ -1,110 +0,0 @@
{
environment.etc."inputrc".text = ''
# /etc/inputrc: initialization file for readline
#
# For more information on how this file works, please see the
# INITIALIZATION FILE section of the readline(3) man page
#
# Quick dirty little note:
# To get the key sequence for binding, you can abuse bash.
# While running bash, hit CTRL+V, and then type the key sequence.
# So, typing 'ALT + left arrow' in Konsole gets you back:
# ^[[1;3D
# The readline entry to make this skip back a word will then be:
# "\e[1;3D" backward-word
#
# Customization note:
# You don't need to put all your changes in this file. You can create
# ~/.inputrc which starts off with the line:
# $include /etc/inputrc
# Then put all your own stuff after that.
#
# do not bell on tab-completion
set bell-style none
set history-size -1
set meta-flag on
set input-meta on
set convert-meta off
set output-meta on
# dont output everything on first line
set horizontal-scroll-mode off
# append slash to completed directories & symlinked directories
set mark-directories on
set mark-symlinked-directories on
# dont expand ~ in tab completion
set expand-tilde off
# instead of ringing bell, show list of ambigious completions directly, also show up to 300 items before asking
set show-all-if-ambiguous on
set completion-query-items 300
$if mode=emacs
# for linux console and RH/Debian xterm
# allow the use of the Home/End keys
"\e[1~": beginning-of-line
"\e[4~": end-of-line
# map "page up" and "page down" to search history based on current cmdline
"\e[5~": history-search-backward
"\e[6~": history-search-forward
# allow the use of the Delete/Insert keys
"\e[3~": delete-char
"\e[2~": quoted-insert
# gnome / others (escape + arrow key)
"\e[5C": forward-word
"\e[5D": backward-word
# konsole / xterm / rxvt (escape + arrow key)
"\e\e[C": forward-word
"\e\e[D": backward-word
# gnome / konsole / others (control + arrow key)
"\e[1;5C": forward-word
"\e[1;5D": backward-word
# aterm / eterm (control + arrow key)
"\eOc": forward-word
"\eOd": backward-word
# konsole (alt + arrow key)
"\e[1;3C": forward-word
"\e[1;3D": backward-word
# Chromebooks remap alt + backspace so provide alternative (alt + k)
"\ek": backward-kill-word
$if term=rxvt
"\e[8~": end-of-line
"\e[3^": kill-line
"\e[3@": backward-kill-line
$endif
# for non RH/Debian xterm, can't hurt for RH/Debian xterm
"\eOH": beginning-of-line
"\eOF": end-of-line
# for freebsd console
"\e[H": beginning-of-line
"\e[F": end-of-line
# fix Home and End for German users
"\e[7~": beginning-of-line
"\e[8~": end-of-line
# ctrl [+ shift] + del = kill line [backward]
"\e[3;5~": kill-line
"\e[3;6~": backward-kill-line
$endif
# Up and Down should search history based on current cmdline
"\e[A": history-search-backward
"\e[B": history-search-forward
'';
}

View file

@ -1,12 +0,0 @@
let
# IP addresses: ${"${interface} \e{halfbright}\4{${interface}}\e{reset} \e{halfbright}\6{${interface}}\e{reset}"}
issue_text = ''
\d \t
\e{halfbright}\4\e{reset} \e{halfbright}\6\e{reset}
This is \e{cyan}\n\e{reset} [\e{lightblue}\l\e{reset}] (\s \m \r)
'';
in {
environment.etc."issue".text = issue_text;
environment.etc."issue.logo".text = issue_text;
}

View file

@ -1,92 +0,0 @@
{
config,
lib,
pkgs,
nodeName,
...
}: let
inherit
(lib)
concatStringsSep
head
mapAttrsToList
mkDefault
mkForce
;
in {
networking = {
hostName = nodeName;
useDHCP = mkForce false;
useNetworkd = true;
dhcpcd.enable = false;
nftables = {
firewall.enable = true;
stopRuleset = mkDefault ''
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state invalid drop
ct state {established, related} accept
iifname lo accept
meta l4proto ipv6-icmp accept
meta l4proto icmp accept
tcp dport ${toString (head config.services.openssh.ports)} accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
'';
};
# TODO mkForce nftables
nftables.firewall = {
zones = lib.mkForce {
local.localZone = true;
};
rules = lib.mkForce {
icmp = {
early = true;
after = ["ct"];
from = "all";
to = ["local"];
extraLines = [
"ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept"
"ip protocol icmp icmp type { echo-request, destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept"
#"ip6 saddr fe80::/10 ip6 daddr fe80::/10 udp dport 546 accept" # (dhcpv6)
];
};
ssh = {
early = true;
after = ["ct"];
from = "all";
to = ["local"];
allowedTCPPorts = config.services.openssh.ports;
};
untrusted-to-local = {
from = ["untrusted"];
to = ["local"];
inherit
(config.networking.firewall)
allowedTCPPorts
allowedUDPPorts
;
};
};
};
};
systemd.network.enable = true;
# Rename known network interfaces
extra.networking.renameInterfacesByMac = lib.mapAttrs (_: v: v.mac) (config.repo.secrets.local.networking.interfaces or {});
}

View file

@ -1,58 +0,0 @@
{
inputs,
pkgs,
stateVersion,
...
}: {
environment.etc."nixos/configuration.nix".source = pkgs.writeText "configuration.nix" ''
assert builtins.trace "This is a dummy config, use colmena!" false;
{ }
'';
nix = {
settings = {
auto-optimise-store = true;
allowed-users = ["@wheel"];
trusted-users = ["root" "@wheel"];
substituters = [
"https://nix-config.cachix.org"
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"nix-config.cachix.org-1:Vd6raEuldeIZpttVQfrUbLvXJHzzzkS0pezXCVVjDG4="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
cores = 0;
max-jobs = "auto";
};
daemonCPUSchedPolicy = "batch";
daemonIOSchedPriority = 5;
distributedBuilds = true;
extraOptions = ''
builders-use-substitutes = true
experimental-features = nix-command flakes
flake-registry = /etc/nix/registry.json
'';
nixPath = ["nixpkgs=/run/current-system/nixpkgs"];
optimise.automatic = true;
gc = {
automatic = true;
dates = "monthly";
options = "--delete-older-than 90d";
};
# Define global flakes for this system
registry = {
nixpkgs.flake = inputs.nixpkgs;
p.flake = inputs.nixpkgs;
pkgs.flake = inputs.nixpkgs;
templates.flake = inputs.templates;
};
};
system = {
extraSystemBuilderCmds = ''
ln -sv ${pkgs.path} $out/nixpkgs
'';
inherit stateVersion;
};
}

View file

@ -1,76 +0,0 @@
{
config,
lib,
...
}: {
services.resolved = {
enable = true;
dnssec = "allow-downgrade";
fallbackDns = [
"1.1.1.1"
"2606:4700:4700::1111"
"8.8.8.8"
"2001:4860:4860::8844"
];
llmnr = "false";
extraConfig = ''
Domains=~.
MulticastDNS=true
'';
};
system.nssDatabases.hosts = lib.mkMerge [
(lib.mkBefore ["mdns_minimal [NOTFOUND=return]"])
(lib.mkAfter ["mdns"])
];
# TODO mkForce nftables
# Open port 5353 for any interfaces that have MulticastDNS enabled
networking.nftables.firewall = let
# Determine all networks that have MulticastDNS enabled
networksWithMulticast =
lib.filter
(n: config.systemd.network.networks.${n}.networkConfig.MulticastDNS or false)
(lib.attrNames config.systemd.network.networks);
# Determine all known mac addresses and the corresponding link name
# based on the renameInterfacesByMac option.
knownMacs =
lib.mapAttrs'
(k: v: lib.nameValuePair v k)
config.extra.networking.renameInterfacesByMac;
# A helper that returns the link name for the given mac address,
# or null if it doesn't exist or the given mac was null.
linkNameFor = mac:
if mac == null
then null
else knownMacs.${mac} or null;
# Calls the given function for each network that has MulticastDNS enabled,
# and collects all non-null values.
mapNetworks = f: lib.filter (v: v != null) (map f networksWithMulticast);
# All interfaces on which MulticastDNS is used
mdnsInterfaces = lib.unique (
# For each network that is matched by MAC, lookup the link name
# and if map the definition name to the link name.
mapNetworks (x: linkNameFor (config.systemd.network.networks.${x}.matchConfig.MACAddress or null))
# For each network that is matched by name, map the definition
# name to the link name.
++ mapNetworks (x: config.systemd.network.networks.${x}.matchConfig.Name or null)
);
in
lib.mkIf (mdnsInterfaces != []) {
zones = lib.mkForce {
mdns.interfaces = mdnsInterfaces;
};
rules = lib.mkForce {
mdns-to-local = {
from = ["mdns"];
to = ["local"];
allowedUDPPorts = [5353];
};
};
};
}

View file

@ -1,17 +0,0 @@
{lib, ...}: {
services.openssh = {
enable = true;
authorizedKeysFiles = lib.mkForce ["/etc/ssh/authorized_keys.d/%u"];
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "yes";
};
hostKeys = [
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
};
}

View file

@ -1,442 +0,0 @@
{
config,
extraLib,
inputs,
lib,
nodePath,
pkgs,
...
}: {
# IP address math library
# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba
# Plus some extensions by us
lib = let
libWithNet = (import "${inputs.lib-net}/net.nix" {inherit lib;}).lib;
in
lib.recursiveUpdate libWithNet {
net = {
cidr = rec {
# host :: (ip | mac | integer) -> cidr -> ip
#
# Wrapper that extends the original host function to
# check whether the argument `n` is in-range for the given cidr.
#
# Examples:
#
# > net.cidr.host 255 "192.168.1.0/24"
# "192.168.1.255"
# > net.cidr.host (256) "192.168.1.0/24"
# <fails with an error message>
# > net.cidr.host (-1) "192.168.1.0/24"
# "192.168.1.255"
# > net.cidr.host (-256) "192.168.1.0/24"
# "192.168.1.0"
# > net.cidr.host (-257) "192.168.1.0/24"
# <fails with an error message>
host = i: n: let
cap = libWithNet.net.cidr.capacity n;
in
assert lib.assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}";
libWithNet.net.cidr.host i n;
# hostCidr :: (ip | mac | integer) -> cidr -> cidr
#
# Returns the nth host in the given cidr range (like cidr.host)
# but as a cidr that retains the original prefix length.
#
# Examples:
#
# > net.cidr.hostCidr 2 "192.168.1.0/24"
# "192.168.1.2/24"
hostCidr = n: x: "${libWithNet.net.cidr.host n x}/${toString (libWithNet.net.cidr.length x)}";
# ip :: (cidr | ip) -> ip
#
# Returns just the ip part of the cidr.
#
# Examples:
#
# > net.cidr.ip "192.168.1.100/24"
# "192.168.1.100"
# > net.cidr.ip "192.168.1.100"
# "192.168.1.100"
ip = x: lib.head (lib.splitString "/" x);
# canonicalize :: cidr -> cidr
#
# Replaces the ip of the cidr with the canonical network address
# (first contained address in range)
#
# Examples:
#
# > net.cidr.canonicalize "192.168.1.100/24"
# "192.168.1.0/24"
canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x);
# mergev4 :: [cidrv4 | ipv4] -> (cidrv4 | null)
#
# Returns the smallest cidr network that includes all given networks.
# If no cidr mask is given, /32 is assumed.
#
# Examples:
#
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
# "192.168.0.0/21"
mergev4 = addrs_: let
# Append /32 if necessary
addrs = map (x:
if lib.hasInfix "/" x
then x
else "${x}/32")
addrs_;
# The smallest occurring length is the first we need to start checking, since
# any greater cidr length represents a smaller address range which
# wouldn't contain all of the original addresses.
startLength = lib.foldl' lib.min 32 (map libWithNet.net.cidr.length addrs);
possibleLengths = lib.reverseList (lib.range 0 startLength);
# The first ip address will be "expanded" in cidr length until it covers all other
# used addresses.
firstIp = ip (lib.head addrs);
# Return the first (i.e. greatest length -> smallest prefix) cidr length
# in the list that covers all used addresses
bestLength = lib.head (lib.filter
# All given addresses must be contained by the generated address.
(len:
lib.all
(x:
libWithNet.net.cidr.contains
(ip x)
(libWithNet.net.cidr.make len firstIp))
addrs)
possibleLengths);
in
assert lib.assertMsg (!lib.any (lib.hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
if addrs == []
then null
else libWithNet.net.cidr.make bestLength firstIp;
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
#
# Returns the smallest cidr network that includes all given networks.
# If no cidr mask is given, /128 is assumed.
#
# Examples:
#
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
# "fd00:c000::/18"
mergev6 = addrs_: let
# Append /128 if necessary
addrs = map (x:
if lib.hasInfix "/" x
then x
else "${x}/128")
addrs_;
# The smallest occurring length is the first we need to start checking, since
# any greater cidr length represents a smaller address range which
# wouldn't contain all of the original addresses.
startLength = lib.foldl' lib.min 128 (map libWithNet.net.cidr.length addrs);
possibleLengths = lib.reverseList (lib.range 0 startLength);
# The first ip address will be "expanded" in cidr length until it covers all other
# used addresses.
firstIp = ip (lib.head addrs);
# Return the first (i.e. greatest length -> smallest prefix) cidr length
# in the list that covers all used addresses
bestLength = lib.head (lib.filter
# All given addresses must be contained by the generated address.
(len:
lib.all
(x:
libWithNet.net.cidr.contains
(ip x)
(libWithNet.net.cidr.make len firstIp))
addrs)
possibleLengths);
in
assert lib.assertMsg (lib.all (lib.hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
if addrs == []
then null
else libWithNet.net.cidr.make bestLength firstIp;
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
#
# Returns the smallest cidr network that includes all given networks,
# but yields two separate result for all given ipv4 and ipv6 addresses.
# Equivalent to calling mergev4 and mergev6 on a partition individually.
merge = addrs: let
v4_and_v6 = lib.partition (lib.hasInfix ":") addrs;
in {
cidrv4 = mergev4 v4_and_v6.wrong;
cidrv6 = mergev6 v4_and_v6.right;
};
# assignIps :: cidr -> [int | ip] -> [string] -> [ip]
#
# Assigns a semi-stable ip address from the given cidr network to each hostname.
# The algorithm is based on hashing (abusing sha256) with linear probing.
# The order of hosts doesn't matter. No ip (or offset) from the reserved list
# will be assigned. The network address and broadcast address will always be reserved
# automatically.
#
# Examples:
#
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c"]
# { a = "192.168.100.202"; b = "192.168.100.74"; c = "192.168.100.226"; }
#
# > net.cidr.assignIps "192.168.100.1/24" [] ["a" "b" "c" "a-new-elem"]
# { a = "192.168.100.202"; a-new-elem = "192.168.100.88"; b = "192.168.100.74"; c = "192.168.100.226"; }
#
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
assignIps = net: reserved: hosts: let
cidrSize = libWithNet.net.cidr.size net;
capacity = libWithNet.net.cidr.capacity net;
# The base address of the network. Used to convert ip-based reservations to offsets
baseAddr = host 0 net;
# Reserve some values for the network, host and broadcast address.
# The network and broadcast address should never be used, and we
# want to reserve the host address for the host. We also convert
# any ips to offsets here.
init = lib.unique (
[0 (capacity - 1)]
++ lib.flip map reserved (x:
if builtins.typeOf x == "int"
then x
else -(libWithNet.net.ip.diff baseAddr x))
);
nHosts = builtins.length hosts;
nInit = builtins.length init;
# Pre-sort all hosts, to ensure ordering invariance
sortedHosts =
lib.warnIf
((nInit + nHosts) > 0.3 * capacity)
"assignIps: hash stability may be degraded since utilization is >30%"
(builtins.sort builtins.lessThan hosts);
# Generates a hash (i.e. offset value) for a given hostname
hashElem = x:
builtins.bitAnd (capacity - 1)
(extraLib.hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
# Do linear probing. Returns the first unused value at or after the given value.
probe = avoid: value:
if lib.elem value avoid
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
# class of x modulo 2^n, so we can use bitAnd instead.
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
else value;
# Hash a new element and avoid assigning any existing values.
assignOne = {
assigned,
used,
}: x: let
value = probe used (hashElem x);
in {
assigned =
assigned
// {
${x} = host value net;
};
used = [value] ++ used;
};
in
assert lib.assertMsg (cidrSize >= 2 && cidrSize <= 62)
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
assert lib.assertMsg (nHosts <= capacity - nInit)
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order
(lib.foldl' assignOne {
assigned = {};
used = init;
}
sortedHosts)
.assigned;
};
ip = rec {
# Checks whether the given address (with or without cidr notation) is an ipv4 address.
isv4 = x: !isv6 x;
# Checks whether the given address (with or without cidr notation) is an ipv6 address.
isv6 = lib.hasInfix ":";
};
mac = {
# Adds offset to the given base address and ensures the result is in
# a locally administered range by replacing the second nibble with a 2.
addPrivate = base: offset: let
added = libWithNet.net.mac.add base offset;
pre = lib.substring 0 1 added;
suf = lib.substring 2 (-1) added;
in "${pre}2${suf}";
# assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac]
#
# Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname.
# The algorithm is based on hashing (abusing sha256) with linear probing.
# The order of hosts doesn't matter. No mac (or offset) from the reserved list
# will be assigned.
#
# Examples:
#
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c"]
# { a = "11:22:33:1b:bd:ca"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
#
# > net.mac.assignMacs "11:22:33:00:00:00" 24 [] ["a" "b" "c" "a-new-elem"]
# { a = "11:22:33:1b:bd:ca"; a-new-elem = "11:22:33:d6:5d:58"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
#
# > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"]
# { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
assignMacs = base: size: reserved: hosts: let
capacity = extraLib.pow 2 size;
baseAsInt = libWithNet.net.mac.diff base "00:00:00:00:00:00";
init = lib.unique (
lib.flip map reserved (x:
if builtins.typeOf x == "int"
then x
else libWithNet.net.mac.diff x base)
);
nHosts = builtins.length hosts;
nInit = builtins.length init;
# Pre-sort all hosts, to ensure ordering invariance
sortedHosts =
lib.warnIf
((nInit + nHosts) > 0.3 * capacity)
"assignMacs: hash stability may be degraded since utilization is >30%"
(builtins.sort builtins.lessThan hosts);
# Generates a hash (i.e. offset value) for a given hostname
hashElem = x:
builtins.bitAnd (capacity - 1)
(extraLib.hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x)));
# Do linear probing. Returns the first unused value at or after the given value.
probe = avoid: value:
if lib.elem value avoid
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
# class of x modulo 2^n, so we can use bitAnd instead.
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
else value;
# Hash a new element and avoid assigning any existing values.
assignOne = {
assigned,
used,
}: x: let
value = probe used (hashElem x);
in {
assigned =
assigned
// {
${x} = libWithNet.net.mac.add value base;
};
used = [value] ++ used;
};
in
assert lib.assertMsg (size >= 2 && size <= 62)
"assignMacs: size=${toString size} is not in [2, 62].";
assert lib.assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0)
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
assert lib.assertMsg (nHosts <= capacity - nInit)
"assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order
(lib.foldl' assignOne {
assigned = {};
used = init;
}
sortedHosts)
.assigned;
};
};
};
# Define local repo secrets
repo.secretFiles = let
local = nodePath + "/secrets/local.nix.age";
in
{
global = ../../../secrets/global.nix.age;
}
// lib.optionalAttrs (nodePath != null && lib.pathExists local) {inherit local;};
# Setup secret rekeying parameters
age.rekey = {
inherit
(inputs.self.secretsConfig)
masterIdentities
extraEncryptionPubkeys
;
# This is technically impure, but intended. We need to rekey on the
# current system due to yubikey availability.
forceRekeyOnSystem = builtins.extraBuiltins.unsafeCurrentSystem;
hostPubkey = let
pubkeyPath =
if nodePath == null
then null
else nodePath + "/secrets/host.pub";
in
lib.mkIf (pubkeyPath != null && lib.pathExists pubkeyPath) pubkeyPath;
};
age.generators.dhparams.script = {pkgs, ...}: "${pkgs.openssl}/bin/openssl dhparam 4096";
age.generators.basic-auth.script = {
pkgs,
lib,
decrypt,
deps,
...
}:
lib.flip lib.concatMapStrings deps ({
name,
host,
file,
}: ''
echo " -> Aggregating "${lib.escapeShellArg host}":"${lib.escapeShellArg name}"" >&2
${decrypt} ${lib.escapeShellArg file} \
| ${pkgs.apacheHttpd}/bin/htpasswd -niBC 12 ${lib.escapeShellArg host}"+"${lib.escapeShellArg name} \
|| die "Failure while aggregating basic auth hashes"
'');
boot = {
initrd.systemd = {
enable = true;
emergencyAccess = config.repo.secrets.global.root.hashedPassword;
# TODO good idea? targets.emergency.wants = ["network.target" "sshd.service"];
extraBin = with pkgs; {
ip = "${iproute2}/bin/ip";
};
};
# Add "rd.systemd.unit=rescue.target" to debug initrd
kernelParams = ["log_buf_len=10M"];
tmp.useTmpfs = true;
};
# Just before switching, remove the agenix directory if it exists.
# This can happen when a secret is used in the initrd because it will
# then be copied to the initramfs under the same path. This materializes
# /run/agenix as a directory which will cause issues when the actual system tries
# to create a link called /run/agenix. Agenix should probably fail in this case,
# but doesn't and instead puts the generation link into the existing directory.
# TODO See https://github.com/ryantm/agenix/pull/187.
system.activationScripts.removeAgenixLink.text = "[[ ! -L /run/agenix ]] && [[ -d /run/agenix ]] && rm -rf /run/agenix";
system.activationScripts.agenixNewGeneration.deps = ["removeAgenixLink"];
# Disable sudo which is entierly unnecessary.
security.sudo.enable = false;
time.timeZone = lib.mkDefault "Europe/Berlin";
i18n.defaultLocale = "C.UTF-8";
console.keyMap = "de-latin1-nodeadkeys";
systemd.enableUnifiedCgroupHierarchy = true;
users.mutableUsers = false;
users.deterministicIds = let
uidGid = id: {
uid = id;
gid = id;
};
in {
systemd-oom = uidGid 999;
systemd-coredump = uidGid 998;
sshd = uidGid 997;
nscd = uidGid 996;
polkituser = uidGid 995;
microvm = uidGid 994;
promtail = uidGid 993;
grafana = uidGid 992;
acme = uidGid 991;
kanidm = uidGid 990;
loki = uidGid 989;
vaultwarden = uidGid 988;
oauth2_proxy = uidGid 987;
influxdb2 = uidGid 986;
telegraf = uidGid 985;
rtkit = uidGid 984;
};
}

View file

@ -1,12 +0,0 @@
{
environment.etc."xdg/user-dirs.defaults".text = ''
DESKTOP=tmp
DOWNLOAD=download
TEMPLATES=tmp
PUBLICSHARE=opt
DOCUMENTS=documents
MUSIC=music
PICTURES=pictures
VIDEOS=tmp
'';
}

View file

@ -1,8 +0,0 @@
{
imports = [
./documentation.nix
];
environment.enableDebugInfo = true;
repo.defineNixExtraBuiltins = true;
}

View file

@ -1,12 +0,0 @@
{
lib,
pkgs,
...
}: {
environment.systemPackages = with pkgs; [man-pages];
documentation = {
dev.enable = true;
man.enable = true;
info.enable = lib.mkForce false;
};
}

View file

@ -1,11 +0,0 @@
{lib, ...}: {
boot.loader = {
efi.canTouchEfiVariables = true;
systemd-boot = {
enable = true;
configurationLimit = 15;
};
timeout = lib.mkDefault 2;
};
console.earlySetup = true;
}

View file

@ -1,6 +0,0 @@
{pkgs, ...}: {
imports = [
./fonts.nix
./wayland.nix
];
}

View file

@ -1,53 +0,0 @@
{pkgs, ...}: {
fonts = {
enableDefaultFonts = false;
enableGhostscriptFonts = false;
fontDir.enable = false;
fontconfig = {
defaultFonts = {
sansSerif = ["IBM Plex Sans"];
serif = ["IBM Plex Sans"];
monospace = ["FiraCode Nerd Font"];
emoji = ["Noto Color Emoji"];
};
localConf = ''
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias binding="weak">
<family>monospace</family>
<prefer>
<family>emoji</family>
</prefer>
</alias>
<alias binding="weak">
<family>sans-serif</family>
<prefer>
<family>emoji</family>
</prefer>
</alias>
<alias binding="weak">
<family>serif</family>
<prefer>
<family>emoji</family>
</prefer>
</alias>
</fontconfig>
'';
};
fonts = with pkgs; [
(nerdfonts.override {fonts = ["FiraCode"];})
ibm-plex
dejavu_fonts
unifont
freefont_ttf
gyre-fonts # TrueType substitutes for standard PostScript fonts
liberation_ttf
noto-fonts
noto-fonts-cjk-sans
noto-fonts-cjk-serif
noto-fonts-emoji
noto-fonts-extra
];
};
}

View file

@ -1,10 +0,0 @@
{pkgs, ...}: {
environment.systemPackages = with pkgs; [wayland];
services.dbus.enable = true;
xdg.portal = {
enable = true;
wlr.enable = true;
# gtk portal needed to make gtk apps happy
extraPortals = with pkgs; [xdg-desktop-portal-gtk];
};
}

View file

@ -1,26 +0,0 @@
{pkgs, ...}: {
environment.systemPackages = with pkgs; [bluetuith];
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
disabledPlugins = ["sap"];
settings = {
General = {
FastConnectable = "true";
JustWorksRepairing = "always";
MultiProfile = "multiple";
};
};
};
hardware.pulseaudio = {
package = pkgs.pulseaudio.override {bluetoothSupport = true;};
extraConfig = ''
load-module module-bluetooth-discover
load-module module-bluetooth-policy
load-module module-switch-on-connect
'';
extraModules = with pkgs; [pulseaudio-modules-bt];
};
}

View file

@ -1,3 +0,0 @@
{
boot.initrd.availableKernelModules = ["virtio_pci" "virtio_net" "virtio_scsi" "virtio_blk"];
}

View file

@ -1,3 +0,0 @@
{
powerManagement.cpuFreqGovernor = "powersave";
}

View file

@ -1,19 +0,0 @@
{
boot.blacklistedKernelModules = ["nouveau"];
hardware = {
nvidia = {
modesetting.enable = true;
nvidiaPersistenced = true;
};
opengl = {
enable = true;
driSupport32Bit = true;
};
};
services.xserver.videoDrivers = ["nvidia"];
virtualisation.docker.enableNvidia = true;
virtualisation.podman.enableNvidia = true;
}

View file

@ -1,33 +0,0 @@
{
lib,
config,
nixos-hardware,
pkgs,
...
}: {
imports = [
nixos-hardware.common-pc-ssd
./physical.nix
];
boot.initrd.availableKernelModules = [
"usbhid"
"usb_storage"
# Ethernet
"dwmac_generic"
"dwmac_meson8b"
"cfg80211"
# HDMI
"snd_soc_meson_g12a_tohdmitx"
"snd_soc_meson_g12a_toacodec"
"mdio_mux_meson_g12a"
"dw_hdmi"
"meson_vdec"
"meson_dw_hdmi"
"meson_drm"
"meson_rng"
"drm"
"display_connector"
];
boot.kernelParams = ["console=ttyAML0,115200n8" "console=tty0"];
}

View file

@ -1,13 +0,0 @@
# Configuration for actual physical machines
{config, ...}: {
hardware = {
enableRedistributableFirmware = true;
enableAllFirmware = true;
};
services = {
fwupd.enable = true;
smartd.enable = true;
thermald.enable = builtins.elem config.nixpkgs.system ["x86_64-linux"];
};
}

View file

@ -1,40 +0,0 @@
{
config,
pkgs,
nodePath,
...
}: {
age.secrets.initrd_host_ed25519_key = {
rekeyFile = nodePath + "/secrets/initrd_host_ed25519_key.age";
# Generate only an ssh-ed25519 private key
generator.script = {
pkgs,
lib,
...
}: ''
(exec 3>&1; ${pkgs.openssh}/bin/ssh-keygen -q -t ed25519 -N "" -f /proc/self/fd/3 <<<y >/dev/null 2>&1)
'';
};
boot.initrd.network.enable = true;
boot.initrd.network.ssh = {
enable = true;
port = 4;
hostKeys = [config.age.secrets.initrd_host_ed25519_key.path];
};
# Make sure that there is always a valid initrd hostkey available that can be installed into
# the initrd. When bootstrapping a system (or re-installing), agenix cannot succeed in decrypting
# whatever is given, since the correct hostkey doesn't even exist yet. We still require
# a valid hostkey to be available so that the initrd can be generated successfully.
# The correct initrd host-key will be installed with the next update after the host is booted
# for the first time, and the secrets were rekeyed for the the new host identity.
system.activationScripts.agenixEnsureInitrdHostkey = {
text = ''
[[ -e ${config.age.secrets.initrd_host_ed25519_key.path} ]] \
|| ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f ${config.age.secrets.initrd_host_ed25519_key.path}
'';
deps = ["agenixInstall"];
};
system.activationScripts.agenixChown.deps = ["agenixEnsureInitrdHostkey"];
}

View file

@ -1,21 +0,0 @@
{pkgs, ...}: {
systemd.network.wait-online.anyInterface = true;
services = {
tlp.enable = true;
physlock.enable = true;
logind = {
lidSwitch = "ignore";
lidSwitchDocked = "ignore";
lidSwitchExternalPower = "ignore";
extraConfig = ''
HandlePowerKey=suspend
HandleSuspendKey=suspend
HandleHibernateKey=suspend
PowerKeyIgnoreInhibited=yes
SuspendKeyIgnoreInhibited=yes
HibernateKeyIgnoreInhibited=yes
'';
};
};
}

View file

@ -1,35 +0,0 @@
{
lib,
pkgs,
...
}: {
environment.systemPackages = with pkgs; [pulseaudio pulsemixer];
sound.enable = false; # ALSA
hardware.pulseaudio.enable = lib.mkForce false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
jack.enable = true;
pulse.enable = true;
media-session.enable = false;
wireplumber.enable = true;
config = {
pipewire."context.properties"."default.clock.allowed-rates" = [
44100
48000
88200
96000
176400
192000
358000
384000
716000
768000
];
pipewire-pulse."stream.properties"."resample.quality" = 15;
client."stream.properties"."resample.quality" = 15;
client-rt."stream.properties"."resample.quality" = 15;
};
};
}

View file

@ -1,5 +0,0 @@
{pkgs, ...}: {
environment.systemPackages = with pkgs; [yubikey-manager yubikey-personalization age-plugin-yubikey];
services.udev.packages = with pkgs; [yubikey-personalization libu2f-host];
services.pcscd.enable = true;
}

View file

@ -1,30 +0,0 @@
{
config,
lib,
pkgs,
...
}: {
boot.supportedFilesystems = ["zfs"];
boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
# The root pool should never be imported forcefully.
# Failure to import is important to notice!
boot.zfs.forceImportRoot = false;
environment.systemPackages = with pkgs; [zfs];
services.zfs = {
autoScrub = {
enable = true;
interval = "weekly";
};
trim = {
enable = true;
interval = "weekly";
};
};
services.telegraf.extraConfig.inputs = lib.mkIf config.services.telegraf.enable {
zfs.poolMetrics = true;
};
}

View file

@ -8,19 +8,17 @@
nixos-hardware.common-gpu-intel
nixos-hardware.common-pc-laptop
nixos-hardware.common-pc-laptop-ssd
../../modules/optional/hardware/intel.nix
../../modules/optional/hardware/physical.nix
../common/core
../common/dev
../common/graphical
../common/hardware/intel.nix
../common/hardware/physical.nix
../common/efi.nix
../common/initrd-ssh.nix
../common/laptop.nix
# ../common/sound.nix
../common/yubikey.nix
../common/zfs.nix
../../modules
../../modules/optional/boot-efi.nix
../../modules/optional/initrd-ssh.nix
../../modules/optional/dev
../../modules/optional/graphical
../../modules/optional/laptop.nix
#../../modules/optional/sound.nix
../../modules/optional/zfs.nix
../../users/myuser
@ -30,10 +28,8 @@
boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod"];
hardware.opengl.enable = true;
console = {
font = "ter-v28n";
packages = with pkgs; [terminus_font];
packages = [pkgs.terminus_font];
};
}

View file

@ -17,5 +17,5 @@ in {
reloadServices = ["nginx"];
};
};
extra.acme.wildcardDomains = acme.domains;
security.acme.wildcardDomains = acme.domains;
}

View file

@ -4,32 +4,32 @@
...
}: {
imports = [
../common/core
../common/hardware/hetzner-cloud.nix
../common/bios-boot.nix
../common/initrd-ssh.nix
../common/zfs.nix
../../modules/optional/hardware/hetzner-cloud.nix
./fs.nix
./net.nix
../../modules
../../modules/optional/boot-bios.nix
../../modules/optional/initrd-ssh.nix
../../modules/optional/zfs.nix
./acme.nix
./fs.nix
./net.nix
./oauth2.nix
];
users.groups.acme.members = ["nginx"];
services.nginx.enable = true;
extra.promtail = {
meta.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${config.extra.wireguard.proxy-sentinel.ipv4} = [config.providedDomains.influxdb];
extra.telegraf = {
networking.hosts.${config.meta.wireguard.proxy-sentinel.ipv4} = [config.networking.providedDomains.influxdb];
meta.telegraf = {
enable = true;
influxdb2.domain = config.providedDomains.influxdb;
influxdb2.domain = config.networking.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};

View file

@ -52,7 +52,7 @@
};
};
extra.wireguard.proxy-sentinel.server = {
meta.wireguard.proxy-sentinel.server = {
host = config.networking.fqdn;
port = 51443;
reservedAddresses = ["10.43.0.0/24" "fd00:43::/120"];

View file

@ -4,7 +4,7 @@
pkgs,
...
}: {
extra.oauth2_proxy = {
meta.oauth2_proxy = {
enable = true;
cookieDomain = config.repo.secrets.local.personalDomain;
portalDomain = "oauth2.${config.repo.secrets.local.personalDomain}";
@ -22,15 +22,15 @@
in {
provider = "oidc";
scope = "openid email";
loginURL = "https://${config.providedDomains.kanidm}/ui/oauth2";
redeemURL = "https://${config.providedDomains.kanidm}/oauth2/token";
validateURL = "https://${config.providedDomains.kanidm}/oauth2/openid/${clientId}/userinfo";
loginURL = "https://${config.networking.providedDomains.kanidm}/ui/oauth2";
redeemURL = "https://${config.networking.providedDomains.kanidm}/oauth2/token";
validateURL = "https://${config.networking.providedDomains.kanidm}/oauth2/openid/${clientId}/userinfo";
clientID = clientId;
keyFile = config.age.secrets.oauth2-proxy-secret.path;
email.domains = ["*"];
extraConfig = {
oidc-issuer-url = "https://${config.providedDomains.kanidm}/oauth2/openid/${clientId}";
oidc-issuer-url = "https://${config.networking.providedDomains.kanidm}/oauth2/openid/${clientId}";
provider-display-name = "Kanidm";
#skip-provider-button = true;
};

View file

@ -7,13 +7,13 @@
imports = [
nixos-hardware.common-cpu-intel
nixos-hardware.common-pc-ssd
../../modules/optional/hardware/intel.nix
../../modules/optional/hardware/physical.nix
../common/core
../common/hardware/intel.nix
../common/hardware/physical.nix
../common/initrd-ssh.nix
../common/efi.nix
../common/zfs.nix
../../modules
../../modules/optional/boot-efi.nix
../../modules/optional/initrd-ssh.nix
../../modules/optional/zfs.nix
./fs.nix
./net.nix
@ -21,16 +21,16 @@
boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" "sdhci_pci" "r8169"];
extra.promtail = {
meta.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${nodes.sentinel.config.extra.wireguard.proxy-sentinel.ipv4} = [nodes.sentinel.config.providedDomains.influxdb];
extra.telegraf = {
networking.hosts.${nodes.sentinel.config.meta.wireguard.proxy-sentinel.ipv4} = [nodes.sentinel.config.networking.providedDomains.influxdb];
meta.telegraf = {
enable = true;
influxdb2.domain = nodes.sentinel.config.providedDomains.influxdb;
influxdb2.domain = nodes.sentinel.config.networking.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
@ -38,7 +38,11 @@
# TODO track my github stats
# services.telegraf.extraConfig.inputs.github = {};
extra.microvms.vms = let
meta.microvms.commonImports = [
./microvms/common.nix
];
meta.microvms.vms = let
defaults = {
system = "x86_64-linux";
autostart = true;

View file

@ -8,30 +8,10 @@
sentinelCfg = nodes.sentinel.config;
adguardhomeDomain = "adguardhome.${sentinelCfg.repo.secrets.local.personalDomain}";
in {
imports = [
../../../../modules/proxy-via-sentinel.nix
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [config.services.adguardhome.settings.bind_port];
};
meta.wireguard-proxy.sentinel.allowedTCPPorts = [config.services.adguardhome.settings.bind_port];
nodes.sentinel = {
providedDomains.adguard = adguardhomeDomain;
networking.providedDomains.adguard = adguardhomeDomain;
services.nginx = {
upstreams.adguardhome = {
@ -43,7 +23,7 @@ in {
};
virtualHosts.${adguardhomeDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert adguardhomeDomain;
useACMEWildcardHost = true;
oauth2.enable = true;
oauth2.allowedGroups = ["access_adguardhome"];
locations."/" = {
@ -57,7 +37,7 @@ in {
services.adguardhome = {
enable = true;
settings = {
bind_host = config.extra.wireguard.proxy-sentinel.ipv4;
bind_host = config.meta.wireguard.proxy-sentinel.ipv4;
bind_port = 3000;
#dns = {
# edns_client_subnet.enabled = false;

View file

@ -0,0 +1,18 @@
{nodes, ...}: let
sentinelCfg = nodes.sentinel.config;
in {
meta.wireguard-proxy.sentinel = {};
meta.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.meta.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.networking.providedDomains.influxdb];
meta.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.networking.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
}

View file

@ -9,27 +9,7 @@
sentinelCfg = nodes.sentinel.config;
grafanaDomain = "grafana.${sentinelCfg.repo.secrets.local.personalDomain}";
in {
imports = [
../../../../modules/proxy-via-sentinel.nix
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [config.services.grafana.settings.server.http_port];
};
meta.wireguard-proxy.sentinel.allowedTCPPorts = [config.services.grafana.settings.server.http_port];
age.secrets.grafana-secret-key = {
rekeyFile = ./secrets/grafana-secret-key.age;
@ -55,7 +35,7 @@ in {
config.age.secrets.grafana-loki-basic-auth-password
];
providedDomains.grafana = grafanaDomain;
networking.providedDomains.grafana = grafanaDomain;
services.nginx = {
upstreams.grafana = {
@ -67,7 +47,7 @@ in {
};
virtualHosts.${grafanaDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert grafanaDomain;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://grafana";
proxyWebsockets = true;
@ -87,7 +67,7 @@ in {
root_url = "https://${grafanaDomain}";
enforce_domain = true;
enable_gzip = true;
http_addr = config.extra.wireguard.proxy-sentinel.ipv4;
http_addr = config.meta.wireguard.proxy-sentinel.ipv4;
http_port = 3001;
};
@ -111,9 +91,9 @@ in {
client_secret = "aZKNCM6KpjBy4RqwKJXMLXzyx9rKH6MZTFk4wYrKWuBqLj6t"; # TODO temporary test not a real secret
scopes = "openid email profile";
login_attribute_path = "prefered_username";
auth_url = "https://${sentinelCfg.providedDomains.kanidm}/ui/oauth2";
token_url = "https://${sentinelCfg.providedDomains.kanidm}/oauth2/token";
api_url = "https://${sentinelCfg.providedDomains.kanidm}/oauth2/openid/grafana/userinfo";
auth_url = "https://${sentinelCfg.networking.providedDomains.kanidm}/ui/oauth2";
token_url = "https://${sentinelCfg.networking.providedDomains.kanidm}/oauth2/token";
api_url = "https://${sentinelCfg.networking.providedDomains.kanidm}/oauth2/openid/grafana/userinfo";
use_pkce = true;
# Allow mapping oauth2 roles to server admin
allow_assign_grafana_admin = true;
@ -128,7 +108,7 @@ in {
name = "InfluxDB (servers)";
type = "influxdb";
access = "proxy";
url = "https://${sentinelCfg.providedDomains.influxdb}";
url = "https://${sentinelCfg.networking.providedDomains.influxdb}";
orgId = 1;
secureJsonData.token = "$__file{${config.age.secrets.grafana-influxdb-token.path}}";
jsonData.version = "Flux";
@ -140,7 +120,7 @@ in {
name = "Loki";
type = "loki";
access = "proxy";
url = "https://${sentinelCfg.providedDomains.loki}";
url = "https://${sentinelCfg.networking.providedDomains.loki}";
orgId = 1;
basicAuth = true;
basicAuthUser = "${nodeName}+grafana-loki-basic-auth-password";

View file

@ -10,31 +10,10 @@
influxdbPort = 8086;
in {
microvm.mem = 1024;
imports = [
../../../../modules/proxy-via-sentinel.nix
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [influxdbPort];
};
meta.wireguard-proxy.sentinel.allowedTCPPorts = [influxdbPort];
nodes.sentinel = {
providedDomains.influxdb = influxdbDomain;
networking.providedDomains.influxdb = influxdbDomain;
services.nginx = {
upstreams.influxdb = {
@ -46,7 +25,7 @@ in {
};
virtualHosts.${influxdbDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert influxdbDomain;
useACMEWildcardHost = true;
oauth2.enable = true;
oauth2.allowedGroups = ["access_influxdb"];
locations."/" = {
@ -54,7 +33,7 @@ in {
proxyWebsockets = true;
extraConfig = ''
satisfy any;
${lib.concatMapStrings (ip: "allow ${ip};\n") sentinelCfg.extra.wireguard.proxy-sentinel.server.reservedAddresses}
${lib.concatMapStrings (ip: "allow ${ip};\n") sentinelCfg.meta.wireguard.proxy-sentinel.server.reservedAddresses}
deny all;
'';
};
@ -66,7 +45,7 @@ in {
enable = true;
settings = {
reporting-disabled = true;
http-bind-address = "${config.extra.wireguard.proxy-sentinel.ipv4}:${toString influxdbPort}";
http-bind-address = "${config.meta.wireguard.proxy-sentinel.ipv4}:${toString influxdbPort}";
};
};

View file

@ -10,27 +10,7 @@
kanidmDomain = "auth.${sentinelCfg.repo.secrets.local.personalDomain}";
kanidmPort = 8300;
in {
imports = [
../../../../modules/proxy-via-sentinel.nix
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [kanidmPort];
};
meta.wireguard-proxy.sentinel.allowedTCPPorts = [kanidmPort];
age.secrets."kanidm-self-signed.crt" = {
rekeyFile = ./secrets/kanidm-self-signed.crt.age;
@ -45,7 +25,7 @@ in {
};
nodes.sentinel = {
providedDomains.kanidm = kanidmDomain;
networking.providedDomains.kanidm = kanidmDomain;
services.nginx = {
upstreams.kanidm = {
@ -57,7 +37,7 @@ in {
};
virtualHosts.${kanidmDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert kanidmDomain;
useACMEWildcardHost = true;
locations."/".proxyPass = "https://kanidm";
# Allow using self-signed certs to satisfy kanidm's requirement
# for TLS connections. (Although this is over wireguard anyway)
@ -76,7 +56,7 @@ in {
origin = "https://${kanidmDomain}";
tls_chain = config.age.secrets."kanidm-self-signed.crt".path;
tls_key = config.age.secrets."kanidm-self-signed.key".path;
bindaddress = "${config.extra.wireguard.proxy-sentinel.ipv4}:${toString kanidmPort}";
bindaddress = "${config.meta.wireguard.proxy-sentinel.ipv4}:${toString kanidmPort}";
trust_x_forward_for = true;
};
};

View file

@ -8,30 +8,10 @@
sentinelCfg = nodes.sentinel.config;
lokiDomain = "loki.${sentinelCfg.repo.secrets.local.personalDomain}";
in {
imports = [
../../../../modules/proxy-via-sentinel.nix
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port];
};
meta.wireguard-proxy.sentinel.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port];
nodes.sentinel = {
providedDomains.loki = lokiDomain;
networking.providedDomains.loki = lokiDomain;
age.secrets.loki-basic-auth-hashes = {
rekeyFile = ./secrets/loki-basic-auth-hashes.age;
@ -52,7 +32,7 @@ in {
};
virtualHosts.${lokiDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert lokiDomain;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://loki";
proxyWebsockets = true;
@ -86,7 +66,7 @@ in {
auth_enabled = false;
server = {
http_listen_address = config.extra.wireguard.proxy-sentinel.ipv4;
http_listen_address = config.meta.wireguard.proxy-sentinel.ipv4;
http_listen_port = 3100;
log_level = "warn";
};

View file

@ -8,68 +8,50 @@
sentinelCfg = nodes.sentinel.config;
vaultwardenDomain = "pw.${sentinelCfg.repo.secrets.local.personalDomain}";
in {
imports = [
../../../../modules/proxy-via-sentinel.nix
meta.wireguard-proxy.sentinel.allowedTCPPorts = [
config.services.vaultwarden.config.rocketPort
config.services.vaultwarden.config.websocketPort
];
extra.promtail = {
enable = true;
proxy = "sentinel";
};
# Connect safely via wireguard to skip authentication
networking.hosts.${sentinelCfg.extra.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.providedDomains.influxdb];
extra.telegraf = {
enable = true;
influxdb2.domain = sentinelCfg.providedDomains.influxdb;
influxdb2.organization = "servers";
influxdb2.bucket = "telegraf";
};
age.secrets.vaultwarden-env = {
rekeyFile = ./secrets/vaultwarden-env.age;
mode = "440";
group = "vaultwarden";
};
networking.nftables.firewall.rules = lib.mkForce {
sentinel-to-local.allowedTCPPorts = [
config.services.vaultwarden.config.rocketPort
config.services.vaultwarden.config.websocketPort
];
};
nodes.sentinel = {
providedDomains.vaultwarden = vaultwardenDomain;
networking.providedDomains.vaultwarden = vaultwardenDomain;
upstreams.vaultwarden = {
servers."${config.services.vaultwarden.config.rocketAddress}:${toString config.services.vaultwarden.config.rocketPort}" = {};
extraConfig = ''
zone vaultwarden 64k;
keepalive 2;
'';
};
upstreams.vaultwarden-websocket = {
servers."${config.services.vaultwarden.config.websocketAddress}:${toString config.services.vaultwarden.config.websocketPort}" = {};
extraConfig = ''
zone vaultwarden-websocket 64k;
keepalive 2;
'';
};
virtualHosts.${vaultwardenDomain} = {
forceSSL = true;
useACMEHost = sentinelCfg.lib.extra.matchingWildcardCert vaultwardenDomain;
extraConfig = ''
client_max_body_size 256M;
'';
locations."/".proxyPass = "http://vaultwarden";
locations."/notifications/hub" = {
proxyPass = "http://vaultwarden-websocket";
proxyWebsockets = true;
services.nginx = {
upstreams.vaultwarden = {
servers."${config.services.vaultwarden.config.rocketAddress}:${toString config.services.vaultwarden.config.rocketPort}" = {};
extraConfig = ''
zone vaultwarden 64k;
keepalive 2;
'';
};
locations."/notifications/hub/negotiate" = {
proxyPass = "http://vaultwarden";
proxyWebsockets = true;
upstreams.vaultwarden-websocket = {
servers."${config.services.vaultwarden.config.websocketAddress}:${toString config.services.vaultwarden.config.websocketPort}" = {};
extraConfig = ''
zone vaultwarden-websocket 64k;
keepalive 2;
'';
};
virtualHosts.${vaultwardenDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
extraConfig = ''
client_max_body_size 256M;
'';
locations."/".proxyPass = "http://vaultwarden";
locations."/notifications/hub" = {
proxyPass = "http://vaultwarden-websocket";
proxyWebsockets = true;
};
locations."/notifications/hub/negotiate" = {
proxyPass = "http://vaultwarden";
proxyWebsockets = true;
};
};
};
};
@ -84,9 +66,9 @@ in {
webVaultEnabled = true;
websocketEnabled = true;
websocketAddress = config.extra.wireguard.proxy-sentinel.ipv4;
websocketAddress = config.meta.wireguard.proxy-sentinel.ipv4;
websocketPort = 3012;
rocketAddress = config.extra.wireguard.proxy-sentinel.ipv4;
rocketAddress = config.meta.wireguard.proxy-sentinel.ipv4;
rocketPort = 8012;
signupsAllowed = false;

View file

@ -172,12 +172,12 @@ in {
systemd.services.kea-dhcp4-server.after = ["sys-subsystem-net-devices-${utils.escapeSystemdPath "lan-self"}.device"];
extra.microvms.networking = {
meta.microvms.networking = {
baseMac = config.repo.secrets.local.networking.interfaces.lan.mac;
macvtapInterface = "lan";
wireguard.openFirewallRules = ["lan-to-local"];
};
# Allow accessing influx
extra.wireguard.proxy-sentinel.client.via = "sentinel";
meta.wireguard.proxy-sentinel.client.via = "sentinel";
}

View file

@ -6,26 +6,25 @@
...
}: {
imports = [
../common/core
../common/hardware/odroid-n2plus.nix
../common/initrd-ssh.nix
../common/zfs.nix
../common/bios-boot.nix
../../modules/optional/hardware/odroid-n2plus.nix
./fs.nix
./net.nix
../../modules
../../modules/optional/boot-bios.nix
../../modules/optional/initrd-ssh.nix
../../modules/optional/zfs.nix
#./dnsmasq.nix
./esphome.nix
./fs.nix
./home-assistant.nix
./hostapd.nix
./mosquitto.nix
./net.nix
./nginx.nix
./zigbee2mqtt.nix
];
# TODO boot.loader.grub.devices = ["/dev/disk/by-id/${config.repo.secrets.local.disk.main}"];
console.earlySetup = true;
# Fails if there are no SMART devices
services.smartd.enable = lib.mkForce false;

View file

@ -4,9 +4,6 @@
pkgs,
...
}: {
imports = [../../modules/hostapd.nix];
disabledModules = ["services/networking/hostapd.nix"];
# Associates each known client to a unique password
age.secrets.wifi-clients.rekeyFile = ./secrets/wifi-clients.age;