chore: dynamic hosts, builder targets
buildbot/nix-eval Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-exodus Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-isaiah Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-linode Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-zeke Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-jeremiah Build done.
buildbot/nix-build gitea:greg/nixos#checks.aarch64-linux.nixos-nixos Build done.
buildbot/nix-build gitea:greg/nixos#checks.x86_64-linux.nixos-gitlab Build done.
buildbot/nix-build Build done.

Make hosts read from directories, to limit manual changes
Rename vm-gitlab -> gitlab
A few reformatting and lint changes due to altered files
Create builder targets for Darwin builders
This commit is contained in:
Greg Hellings
2026-02-17 16:11:29 -06:00
parent 5cd5a3000f
commit e2181f124a
44 changed files with 67 additions and 43 deletions
+78
View File
@@ -0,0 +1,78 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ lib, pkgs, ... }:
let
adblockUpdate = pkgs.writeShellApplication {
name = "adblock-update";
runtimeInputs = with pkgs; [
curl
gnused
systemd
];
text = ''
curl -s https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | sed '1,33d' > /etc/adblock_hosts
curl -s https://adaway.org/hosts.txt | sed '1,24d' | sed 's/127.0.0.1/0.0.0.0/' >> /etc/adblock_hosts
# Custom domains that I need to preserve for some reason
for f in "segment.com" "segment.io" "branch.io" "dev.visualwebsiteoptimizer.com"; do
sed -i -e "/''${f}/d" /etc/adblock_hosts # Blocks Trelly content for house investors
done
systemctl restart dnsmasq
'';
};
in
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
./networking.nix
];
greg = {
home = true;
gnome.enable = false;
proxies = {
};
};
# Bootloader.
boot.loader = {
efi = {
canTouchEfiVariables = true;
};
systemd-boot = {
enable = true;
configurationLimit = 10;
edk2-uefi-shell.enable = true;
};
};
environment.systemPackages = with pkgs; [
create_ssl
step-ca
];
networking.hostName = "genesis"; # Define your hostname.
systemd = {
services.adblock-update = {
after = [ "network-online.target" ];
requires = [ "network-online.target" ];
script = lib.getExe adblockUpdate;
serviceConfig.Type = "oneshot";
};
timers.adblock-update = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
requires = [ "network-online.target" ];
timerConfig = {
OnCalendar = "daily";
Unit = "adblock-update.service";
};
};
};
}
@@ -0,0 +1,55 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"uas"
"sd_mod"
];
boot.initrd.kernelModules = [ "kvm-intel" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/d26b973f-fe56-4055-ac1b-ed1bf93e5221";
fsType = "btrfs";
options = [ "subvol=@" ];
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/d26b973f-fe56-4055-ac1b-ed1bf93e5221";
fsType = "btrfs";
options = [ "subvol=@home" ];
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/A0B6-DDFA";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
# networking.interfaces.ens19.useDHCP = lib.mkDefault true;
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+146
View File
@@ -0,0 +1,146 @@
{ config, pkgs, ... }:
{
virtualisation.podman.enable = true;
services.home-assistant = {
enable = true;
configDir = "/var/lib/hass";
extraComponents = [
"accuweather"
"calendar"
"cast"
"daikin"
"ipp"
"lovelace"
"met"
"nest"
"nextcloud"
"ping"
"piper"
"radio_browser"
"roborock"
"smart_meter_texas"
"speedtestdotnet"
"solaredge"
"whisper"
"wyoming"
"zwave_js"
];
customComponents = with pkgs.home-assistant-custom-components; [
config.services.home-assistant.package.python.pkgs.daikinone
nest_protect
smartthinq-sensors
];
config = {
default_config = { };
tts = [ { platform = "google_translate"; } ];
http = {
use_x_forwarded_for = true;
trusted_proxies = [
"127.0.0.1"
"::1"
];
server_host = "127.0.0.1";
};
#"automation manual" = *nix config here* and so on
"automation ui" = "!include automations.yaml";
"script ui" = "!include scripts.yaml";
"scene ui" = "!include scenes.yaml";
};
};
# Helps with Voice stuff for Home Assistant
services.wyoming = {
faster-whisper.servers = {
greg = {
enable = true;
beamSize = 1; # wut?
device = "auto"; # Could be CPU or CUDA
language = "en";
model = "base-int8";
uri = "tcp://0.0.0.0:13415";
};
};
piper.servers.greg = {
enable = true;
uri = "tcp://0.0.0.0:13416";
voice = "en_US-amy-medium";
};
};
# Although NixOS has a package for Home Assistant, it is not kept as up to date as the container and the upstream
# is very vocal about only supporting their own container or the HAOS deployments. So we deploy the container here
# and avoid any potential messes from that
virtualisation.oci-containers = {
backend = "podman";
# I have ZWave devices. The easiest way to connect to them is the zwavejs2mqtt service running, so we spin up
# its container and map the ZWave device into it
containers.zwave = {
autoStart = false; # We will try to start it with udev.extraRules listed below, as this option starts it too quickly
image = "zwavejs/zwave-js-ui:latest";
ports = [
"8091:8091"
"3000:3000"
];
volumes = [ "/var/lib/zwave:/usr/src/app/store" ];
extraOptions = [
"--device"
"/dev/serial/by-id/usb-0658_0200-if00:/dev/zwave"
"--pull=newer"
];
environment = {
TZ = "America/Chicago";
CONSOLE_OUTPUT = "true";
};
};
};
# Both of the above container need storage for their configuration and devices, but it is not created correctly by
# the container. So we add the creation of /var/lib/{zwave,hass} to the systemd Unit files
systemd.services = {
"podman-zwave" = {
after = [
"sys-devices-pci0000:00-0000:00:1e.0-0000:02:1b.0-usb2-2\\x2d1-2\\x2d1:1.0-tty-ttyACM0.device"
];
wantedBy = [
"sys-devices-pci0000:00-0000:00:1e.0-0000:02:1b.0-usb2-2\\x2d1-2\\x2d1:1.0-tty-ttyACM0.device"
];
serviceConfig = {
StateDirectory = "zwave";
StateDirectoryMode = pkgs.lib.mkForce "0777";
};
};
};
services.udev.extraRules = ''
SUBSYSTEM=="tty", KERNEL=="ttyACM0", TAG+="systemd"
'';
greg.proxies = {
"smart.home".target = "http://127.0.0.1:8123/";
"smart.thehellings.lan".target = "http://127.0.0.1:8123/";
"zwave.home".target = "http://127.0.0.1:8091/";
};
# Ensure that both ports are up and running. We keep 8123 directly open because we are on the LAN and sometimes want to connect
# directly for troubleshooting Nginx configuration
networking.firewall = {
allowedTCPPorts = [
80
443
];
};
greg.backup.jobs.zwave = {
src = "/var/lib/zwave";
dest = "zwave";
};
greg.backup.jobs.hass-backup = {
src = "/var/lib/hass";
dest = "hass";
};
}
+50
View File
@@ -0,0 +1,50 @@
# Local hosts
10.42.0.1 switch switch.thehellings.lan # Core switch for the network
10.42.0.4 joel.thehellings.lan # Proxmox
10.42.0.5 sanswitch.thehellings.lan # Core switch for the SAN
# Home servers
10.42.1.1 pve1.thehellings.lan
10.42.1.2 opnsense router opnsense.thehellings.lan router.thehellings.lan
10.42.1.3 printer.thehellings.lan
10.42.1.4 chronicles chronicles.thehellings.lan nas.thehellings.lan s3.thehellings.lan
10.42.1.5 genesis genesis.thehellings.lan dns dns.thehellings.lan smart smart.thehellings.lan speedtest.thehellings.lan nixcache.thehellings.lan gitcache.thehellings.lan
10.42.1.6 isaiah isaiah.thehellings.lan minio-01.thehellings.lan
10.42.1.7 hosea hosea.thehellings.lan jellyfin jellyfin.thehellings.lan
10.42.1.8 jeremiah jeremiah.thehellings.lan minio-02.thehellings.lan
10.42.1.9 ivr ivr.thehellings.lan
# 10 - monitor
# 11 - old jude
10.42.1.12 tv
10.42.1.13 zeke zeke.thehellings.lan
10.42.1.14 nas1 nas1.thehellings.lan
# VMs
10.42.4.1 matrix matrix.thehellings.lan
#10.42.4.2 vm-jellyfin vm-jellyfin.thehellings.lan
10.42.4.3 git gitlab git.thehellings.lan gitlab.thehellings.lan
# VIP
10.42.5.1 longhorn.cluster matrix.cluster pgadmin.cluter postgres.cluster immich.cluster
# IPMI
10.42.100.6 isaiahbmc isaiahbmc.thehellings.lan
10.42.100.14 nas1bmc nas1bmc.thehellings.lan
# Tailscale hosts
100.119.228.115 nas.home chronicles.shire-zebra.ts.net
100.88.91.27 dns.home
100.80.99.48 exodus.home
100.96.198.104 genesis.home smart.home zwave.home nixcache.home gitcache.home dashy.home uptime.home speed.home
100.117.28.111 gitlab.home gitlab.shire-zebra.ts.net gitlab.thehellings.lan registry.thehellings.lan git.thehellings.lan
100.68.203.1 hosea.home hosea.shire-zebra.ts.net
100.84.183.79 isaiah.home isaiah.shire-zebra.ts.net pgadmin.kubernetes longhorn.kubernetes
100.102.186.39 jeremiah.home jeremiah.shire-zebra.ts.net matrix.kubernetes immich.kubernetes postgres.kubernetes
100.90.74.19 zeke.home
100.115.57.8 linode.home
100.65.5.38 matrix.home matrix.shire-zebra.ts.net
#100.127.55.22 jellyfin.home
100.114.187.61 nas1.home nas1.shire-zebra.ts.net
# Dev hosts
10.42.101.1 icdm.lan wiki.icdm.lan *.icdm.lan
+123
View File
@@ -0,0 +1,123 @@
{
config,
pkgs,
metadata,
...
}:
let
lan = "enp1s0";
lanIP = metadata.hosts.${config.networking.hostName}.ip;
iot = "enp2s0";
iotIP = "192.168.66.250";
routerIP = metadata.infra.gw;
extraHosts = builtins.readFile ./net/hosts;
proxyPort = 3128;
dnsPort = 53;
dhcpPort = 67;
dnsServers = [
#"9.9.9.9" # Quad 9
#"1.1.1.1" # Cloudflare
#"1.0.0.1" # Cloudflare
#"149.112.112.112" # Quad 9
metadata.infra.gw # Currently using our UniFi router for DNS as well
];
in
{
greg.tailscale = {
enable = true;
tags = [ "home" ];
};
# Really, why do I still have to force-disable this crap?
boot.kernel.sysctl = {
"net.ipv6.conf.${lan}.disable_ipv6" = true;
"net.ipv6.conf.${iot}.disable_ipv6" = true;
"net.ipv6.conf.lo.disable_ipv6" = true;
};
networking = {
defaultGateway = metadata.infra.gw;
enableIPv6 = false;
networkmanager.enable = pkgs.lib.mkForce false;
nameservers = dnsServers;
interfaces = {
# This is our LAN port
"${lan}" = {
useDHCP = false;
ipv4.addresses = [
{
address = "${lanIP}";
prefixLength = 16;
}
];
};
"${iot}" = {
useDHCP = false;
ipv4.addresses = [
{
address = "${iotIP}";
prefixLength = 24;
}
];
};
};
firewall = {
enable = false;
allowedUDPPorts = [
dhcpPort
dnsPort
];
allowedTCPPorts = [
dnsPort
proxyPort
80
];
};
nftables.enable = false;
};
environment.etc."hosts.d/local".text = extraHosts;
services = {
kea = {
dhcp4 = (
import ./networking/dhcp.nix {
inherit
iot
lan
lanIP
routerIP
;
}
);
};
#########
# dnsmasq config
########
dnsmasq = {
enable = true;
settings = {
domain = "thehellings.lan";
expand-hosts = true;
log-queries = true;
no-hosts = true; # Do not read /etc/hosts, which makes genesis resolve to 127.0.0.2
addn-hosts = "/etc/adblock_hosts";
hostsdir = "/etc/hosts.d/";
server = dnsServers;
};
};
prometheus.exporters = {
dnsmasq.enable = true;
};
}; # End of services configuration
environment.systemPackages = with pkgs; [
bind
curl # Used by dnsmasq fetching
sqlite
];
}
+167
View File
@@ -0,0 +1,167 @@
{
lan,
iot,
lanIP,
routerIP,
}:
{
enable = false;
settings = {
control-sockets = [
{
socket-type = "unix";
socket-name = "/run/kea/dhcp4-control.sock";
}
];
valid-lifetime = 43200; # 12 hours, in seconds
renew-timer = 1000;
rebind-timer = 2000;
interfaces-config.interfaces = [
lan
iot
];
lease-database = {
type = "memfile";
persist = true;
name = "/var/lib/kea/dhcp4.leases";
};
subnet4 = [
{
id = 1;
subnet = "10.42.0.0/16";
pools = [ { pool = "10.42.2.0 - 10.42.3.255"; } ];
interface = lan;
# https://kea.readthedocs.io/en/kea-2.6.1/arm/dhcp4-srv.html#dhcp4-std-options-list
option-data = [
{
name = "domain-name-servers";
data = "${lanIP}";
}
{
name = "routers";
data = routerIP;
}
{
name = "domain-search";
data = "home,thehellings.lan";
}
{
name = "lpr-servers";
data = "10.42.1.3";
}
{
name = "domain-name";
data = "thehellings.lan";
}
];
reservations-global = false;
reservations-in-subnet = true;
reservations-out-of-pool = false;
reservations = [
# Static IPs for personal work
{
hw-address = "00:23:24:72:64:32"; # Joel
ip-address = "10.42.0.4";
}
{
hw-address = "2a:5d:23:10:4e:22"; # SAN Switch
ip-address = "10.42.0.5";
}
{
hw-address = "00:00:de:ad:be:ef"; # deadbeef
ip-address = "10.42.2.254";
}
{
hw-address = "01:a8:a1:59:c7:8a:12"; # BMC management interface for isaiah
#ip-address = "10.42.2.253";
ip-address = "10.42.100.6";
}
{
hw-address = "7c:83:34:b9:ee:ec"; # PVE1
ip-address = "10.42.1.1";
}
{
hw-address = "74:ee:2a:66:b3:51"; # printer
ip-address = "10.42.1.3";
}
{
hw-address = "00:11:32:c6:7c:81"; # chronicles
ip-address = "10.42.1.4";
}
{
hw-address = "6a:86:56:45:0b:b1"; # Genesis
ip-address = "10.42.1.5";
}
{
hw-address = "a8:a1:59:c7:20:44"; # isaiah
ip-address = "10.42.1.6";
}
#{
# hw-address = ""; # hosea
# ip-address = "10.42.1.7";
#}
{
hw-address = "b4:2e:99:aa:22:3c"; # jeremiah
ip-address = "10.42.1.8";
}
{
hw-address = "c8:5e:a9:54:9e:c6"; # IVR laptop Wi-Fi
ip-address = "10.42.1.9";
}
{
hw-address = "c8:4b:d6:ca:20:8f";
ip-address = "10.42.1.10"; # The monitor
}
{
hw-address = "04:7c:16:d5:60:6f";
ip-address = "10.42.1.13"; # Zeke - but straight in the motherboard
}
{
hw-address = "24:8a:07:8c:8c:b6";
ip-address = "10.42.1.14"; # nas1 25G port
}
########################################
# VM servers #
########################################
{
hw-address = "52:54:00:2a:74:2f";
ip-address = "10.42.4.1"; # Dendrite
}
{
hw-address = "BC:24:11:8F:91:77";
ip-address = "10.42.4.2"; # Jellyfin
}
{
hw-address = "BC:24:11:6E:0C:40";
ip-address = "10.42.4.3"; # Gitlab
}
];
}
{
id = 66;
subnet = "192.168.66.0/24";
pools = [ { pool = "192.168.66.2 - 192.168.66.254"; } ];
interface = iot;
option-data = [
{
name = "domain-name-servers";
data = "1.0.0.1,1.1.1.1";
}
{
name = "routers";
data = "192.168.66.1";
}
];
reservations = [
{
hw-address = "d0:17:69:c6:09:49"; # Daikin
ip-address = "192.168.66.10";
}
];
}
];
};
}
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env nft -f
table ip filter {
define FRIENDS = { {{ '\"' + ( lanInterfaces | join('\", \"') ) + '\"' }} , "tailscale0" }
define SUS = { {{ limitedLan | join(", ") }} }
define LAN = { $FRIENDS, $SUS }
counter tcp_dns {}
counter udp_dns {}
counter catchall {}
chain output {
type filter hook output priority 100; policy accept;
}
chain input {
type filter hook input priority 0; policy drop;
iifname lo accept
# Router needs to yield up DNS and DHCP for itself, in my case
iifname $LAN tcp dport { 53, 67 } counter name tcp_dns accept
iifname $LAN udp dport { 53, 67 } counter name udp_dns accept
# Open the specific ports that we allow
{% for port in tcpPorts %}
iifname $FRIENDS tcp dport {{ port }} accept
{% endfor %}
{% for port in udpPorts %}
iifname $FRIENDS udp dport {{ port }} accept
{% endfor %}
#iifname $LAN counter name catchall accept comment "Allows LAN traffic and outgoing"
iifname {{ wanInterface }} ct state { established, related } accept comment "Allows existing connections"
iifname {{ wanInterface }} icmp type { echo-request, destination-unreachable, time-exceeded } accept comment "Allow some ICMP traffic"
iifname {{ wanInterface }} counter drop comment "Drop other incoming traffic, and count how much"
iifname "podman0" ct state { established, related } accept comment "Allows existing connections"
iifname "podman0" icmp type { echo-request, destination-unreachable, time-exceeded } accept comment "Allow some ICMP traffic"
iifname "podman0" counter drop comment "Drop other incoming traffic, and count how much"
}
chain forward {
type filter hook forward priority 0; policy drop;
iifname $LAN oifname {{ wanInterface }} accept comment "Forward LAN to WAN"
iifname {{ wanInterface }} oifname $LAN ct state established, related accept comment "Allow incoming established traffic"
iifname $LAN oifname "podman0" accept comment "Forward LAN to WAN"
iifname "podman0" oifname $LAN ct state established, related accept comment "Allow incoming established traffic"
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname { {{ wanInterface }} } masquerade
oifname "podman0" masquerade
}
}
table ip6 filter {
chain input {
type filter hook input priority 0; policy drop;
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}