initial(ish) commit

This commit is contained in:
Philipp Hochkamp 2022-03-01 23:53:11 +01:00
commit b744693f0e
No known key found for this signature in database
GPG key ID: 3676AB4CB36E5641
88 changed files with 4925 additions and 0 deletions

View file

@ -0,0 +1,62 @@
{ pkgs, inputs, lib, ... }:
with lib;
with lib.my;
{
users.users.ragon = {
name = "ragon";
home = "/Users/ragon";
};
programs.gnupg.agent.enable = true;
home-manager.users.ragon = { pkgs, lib, inputs, config, ... }: {
programs.home-manager.enable = true;
home.stateVersion = "21.11";
home.sessionVariables = {
EDITOR = "nvim";
VISUAL = "nvim";
PATH = "$PATH:$HOME/development/flutter/bin:/Applications/Android Studio.app/Contents/bin/:/Applications/Docker.app/Contents/Resources/bin:/Applications/Android Studio.app/Contents/jre/Contents/Home/bin:$HOME/.nix-profile/bin:/nix/var/nix/profiles/system/sw/bin";
JAVA_HOME = "/Applications/Android Studio.app/Contents/jre/Contents/Home/";
};
home.packages = with pkgs; [
terraform-ls
terraform
#tectonic
pandoc
yabai
google-cloud-sdk
];
home.activation = {
aliasApplications =
let
apps = pkgs.buildEnv {
name = "home-manager-applications";
paths = config.home.packages;
pathsToLink = "/Applications";
};
in
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# Install MacOS applications to the user environment.
HM_APPS="$HOME/Applications/Home Manager Apps"
# Reset current state
[ -e "$HM_APPS" ] && $DRY_RUN_CMD rm -r "$HM_APPS"
$DRY_RUN_CMD mkdir -p "$HM_APPS"
# .app dirs need to be actual directories for Finder to detect them as Apps.
# The files inside them can be symlinks though.
$DRY_RUN_CMD cp --recursive --symbolic-link --no-preserve=mode -H ${apps}/Applications/* "$HM_APPS" || true # can fail if no apps exist
# Modes need to be stripped because otherwise the dirs wouldn't have +w,
# preventing us from deleting them again
# In the env of Apps we build, the .apps are symlinks. We pass all of them as
# arguments to cp and make it dereference those using -H
'';
};
};
}

213
hosts/ds9/default.nix Normal file
View file

@ -0,0 +1,213 @@
# 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).
{ config, inputs, pkgs, lib, ... }:
{
imports =
[
# Include the results of the hardware scan.
./hardware-configuration.nix
];
# Don't Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
services.syncthing.enable = true;
services.syncthing.user = "ragon";
security.sudo.wheelNeedsPassword = false;
networking.useDHCP = true;
networking.bridges."br0".interfaces = [ ];
networking.hostId = "7b4c2932";
boot.binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ];
boot.initrd.network = {
enable = true;
postCommands = ''
zpool import rpool
echo "zfs load-key -a; killall zfs" >> /root/.profile
'';
ssh = {
enable = true;
port = 2222;
hostKeys = [
"/persistent/etc/nixos/secrets/initrd/ssh_host_rsa_key"
"/persistent/etc/nixos/secrets/initrd/ssh_host_ed25519_key"
];
authorizedKeys = pkgs.pubkeys.ragon.computers;
};
};
# services.restic.backups."ds9" = {
# rcloneConfigFile = "/run/secrets/ds9rcloneConfig";
# passwordFile = "/run/secrets/ds9resticPassword";
# pruneOpts = [
# "--keep-daily 7"
# "--keep-weekly 5"
# "--keep-monthly 12"
# "--keep-yearly 75"
# ];
# initialize = true;
# repository = "rclone:ds9:/ds9";
# paths = [
# "/data"
# "/persistent/var/lib"
# ];
# };
ragon.agenix.secrets."ds9rcloneConfig" = { };
ragon.agenix.secrets."ds9resticPassword" = { };
# Enable Scanning
hardware.sane.enable = true;
hardware.sane.extraBackends = [ pkgs.sane-airscan ];
services.avahi.enable = true;
services.avahi.nssmdns = true;
services.avahi.publish.enable = true;
services.avahi.extraServiceFiles.smb = ''
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_smb._tcp</type>
<port>445</port>
<host-name>ds9.hailsatan.eu</host-name>
</service>
<service>
<type>_device-info._tcp</type>
<port>0</port>
<txt-record>model=MacPro7,1@ECOLOR=226,226,224</txt-record>
</service>
<service>
<type>_adisk._tcp</type>
<txt-record>sys=waMa=0,adVF=0x100</txt-record>
<txt-record>dk0=adVN=TimeMachine,adVF=0x82</txt-record>
<host-name>ds9.hailsatan.eu</host-name>
</service>
</service-group>
'';
# Webhook service to trigger scanning the ADF from HomeAssistant
systemd.services.scanhook = {
description = "webhook go server to trigger scanning";
documentation = [ "https://github.com/adnanh/webhook" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ bash ];
serviceConfig = {
TemporaryFileSystem = "/:ro";
BindReadOnlyPaths = [
"/nix/store"
"-/etc/resolv.conf"
"-/etc/nsswitch.conf"
"-/etc/hosts"
"-/etc/localtime"
];
BindPaths = [
"/data/applications/paperless-consumption"
];
LockPersonality = true;
NoNewPrivileges = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectHome = true;
ProtectControlGroups = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
DynamicUser = true;
ExecStart =
let
scanScript = pkgs.writeScript "plscan.sh" ''
#!/usr/bin/env bash
export PATH=${lib.makeBinPath [ pkgs.strace pkgs.gnugrep pkgs.coreutils pkgs.sane-backends pkgs.sane-airscan pkgs.imagemagick ]}
export LD_LIBRARY_PATH=${config.environment.sessionVariables.LD_LIBRARY_PATH} # Adds SANE Libraries to the ld library path of this script
set -x
date="''$(date --iso-8601=seconds)"
filename="Scan ''$date.pdf"
tmpdir="''$(mktemp -d)"
pushd "''$tmpdir"
scanimage --batch=out%d.jpg --format=jpeg --mode Gray -d "airscan:e0:Canon MB5100 series" --source "ADF Duplex" --resolution 300
for i in $(ls out*.jpg | grep 'out.*[24680]\.jpg'); do convert $i -rotate 180 $i; done # rotate even stuff
convert out*.jpg /data/applications/paperless-consumption/"''$filename"
chmod 666 /data/applications/paperless-consumption/"''$filename"
popd
rm -r "''$tmpdir"
'';
hooksFile = pkgs.writeText "webhook.json" (builtins.toJSON [
{
id = "scan-webhook";
execute-command = "${scanScript}";
}
]);
in
"${pkgs.webhook}/bin/webhook -hooks ${hooksFile} -verbose";
};
};
networking.firewall.allowedTCPPorts = [ 9000 ];
# Immutable users due to tmpfs
users.mutableUsers = false;
services.samba.extraConfig = ''
min protocol = SMB3
vfs objects = acl_xattr catia fruit streams_xattr
fruit:nfs_aces = no
inherit permissions = yes
fruit:posix_rename = yes
fruit:model = MacPro7,1@ECOLOR=226,226,224
fruit:veto_appledouble = no
fruit:wipe_intentionally_left_blank_rfork = yes
fruit:delete_empty_adfiles = yes
fruit:metadata = stream
'';
ragon = {
cli.enable = true;
user.enable = true;
home-manager.enable = true;
persist.enable = true;
persist.extraDirectories = [ "/var/lib/syncthing" ];
services = {
samba.enable = true;
samba.shares = {
TimeMachine = {
path = "/backups/DaedalusTimeMachine";
comment = "DaedalusTimeMachine";
"write list" = "@wheel";
"fruit:time machine" = "yes";
"fruit:time machine max size" = "2050G";
"vfs objects" = "acl_xattr fruit streams_xattr";
"inherit acls" = "yes";
};
data = {
path = "/data";
comment = "some data for the people";
"write list" = "@wheel";
};
};
docker.enable = true;
ssh.enable = true;
nginx.enable = true;
jellyfin.enable = true;
tailscale.enable = true;
grafana.enable = true;
libvirt.enable = true;
paperless.enable = true;
unifi.enable = true;
};
};
}

View file

@ -0,0 +1,52 @@
# 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, pkgs, modulesPath, ... }:
{
imports = [ "${modulesPath}/installer/scan/not-detected.nix" ];
boot.initrd.availableKernelModules = [ "r8169" "ahci" "vfio-pci" "xhci_pci" "ehci_pci" "nvme" "usbhid" "sd_mod" "sr_mod" ];
boot.kernelModules = [ "kvm-amd" ];
nix.maxJobs = lib.mkDefault 12;
powerManagement.cpuFreqGovernor = "performance";
services.zfs.autoScrub.enable = true;
ragon.system.fs = {
enable = true;
mediadata = false;
swap = false;
persistentSnapshot = false;
nix = "rpool/content/local/nix";
varlog = "rpool/content/local/journal";
persistent = "rpool/content/safe/persist";
arcSize = 8;
};
services.syncoid.enable = false; # TODO setup offsite backups
services.sanoid.datasets."rpool/content/safe".recursive = true;
services.sanoid.datasets."rpool/content/local/backups" = { };
services.sanoid.enable = true;
swapDevices = [ { device = "/dev/disk/by-id/nvme-eui.000000000000000100a075202c247839-part1"; randomEncryption = true; } ];
fileSystems."/boot".device = "/dev/disk/by-uuid/149F-23AA";
fileSystems."/data" = {
device = "rpool/content/safe/data";
fsType = "zfs";
};
fileSystems."/data/media" = {
device = "rpool/content/safe/data/media";
fsType = "zfs";
};
fileSystems."/backups" = {
device = "rpool/content/local/backups";
fsType = "zfs";
};
fileSystems."/data/media/nzbr" = {
device = "10.0.1.2:/storage/media";
fsType = "nfs";
options = [ "x-systemd.automount" "noauto" ];
};
}

63
hosts/picard/default.nix Normal file
View file

@ -0,0 +1,63 @@
# 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).
{ inputs, config, pkgs, ... }:
{
imports =
[
# Include the results of the hardware scan.
./hardware-configuration.nix
];
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
boot.loader.systemd-boot.enable = false;
services.syncthing.enable = true;
networking.interfaces."ens3" = {
ipv6 = {
addresses = [
{
address = "2a03:4000:54:a98::1";
prefixLength = 64;
}
];
};
};
networking.defaultGateway6 = { address = "fe80::1"; interface = "enp0s3"; };
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
# networking.interfaces.eno1.useDHCP = true;
networking.hostId = "7c21236a";
# Immutable users due to tmpfs
users.mutableUsers = false;
services.postgresql.package = pkgs.postgresql_13;
ragon = {
cli.enable = true;
user.enable = true;
home-manager.enable = true;
persist.enable = true;
persist.extraDirectories = [ "/var/lib/syncthing" ];
services = {
ssh.enable = true;
bitwarden.enable = true;
gitlab.enable = true; # TODO gitlab-runner
synapse.enable = true;
tailscale.enable = true;
hedgedoc.enable = true;
ts3.enable = true;
nginx.enable = true;
nginx.domain = "ragon.xyz";
};
};
}

View file

@ -0,0 +1,42 @@
# 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, pkgs, modulesPath, ... }:
{
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
boot.zfs.requestEncryptionCredentials = true;
ragon.system.fs.enable = true;
ragon.system.fs.nix = "rpool/nix";
ragon.system.fs.varlog = "rpool/varlog";
ragon.system.fs.persistent = "rpool/persist";
ragon.system.fs.swap = false;
ragon.system.fs.mediadata = false;
swapDevices = [
{ device = "/dev/sda2"; randomEncryption.enable = true; }
];
services.syncoid.enable = false; # disable failing zfs syncing
boot.initrd = {
network = {
enable = true;
postCommands = ''
zpool import rpool
echo "zfs load-key -a; killall zfs" >> /root/.profile
'';
ssh = {
enable = true;
port = 2222;
hostKeys = [
"/persistent/etc/nixos/secrets/initrd/ssh_host_rsa_key"
"/persistent/etc/nixos/secrets/initrd/ssh_host_ed25519_key"
];
authorizedKeys = pkgs.pubkeys.ragon.user;
};
};
};
powerManagement.cpuFreqGovernor = "performance";
}

View file

@ -0,0 +1,92 @@
{ config, inputs, pkgs, lib, ... }:
{
imports = [
"${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
"${inputs.nixos-hardware}/raspberry-pi/4/default.nix"
./router.nix
];
nixpkgs.overlays = [
(final: super: {
makeModulesClosure = x:
super.makeModulesClosure (x // { allowMissing = true; });
})
];
boot.loader.systemd-boot.enable = false;
boot.kernelPackages = pkgs.linuxPackages_rpi4;
boot.supportedFilesystems = lib.mkForce [ "reiserfs" "vfat" "zfs" "ext4" ]; # we dont need zfs here
boot.inird.supportedFilesystems = lib.mkForce [ "reiserfs" "vfat" "zfs" "ext4" ]; # we dont need zfs here
networking.hostId = "eec43f51";
# networking.usePredictableInterfaceNames = false;
documentation.enable = false;
documentation.nixos.enable = false;
nix = {
autoOptimiseStore = true;
gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
# Free up to 1GiB whenever there is less than 100MiB left.
extraOptions = ''
min-free = ${toString (100 * 1024 * 1024)}
max-free = ${toString (1024 * 1024 * 1024)}
'';
};
powerManagement.cpuFreqGovernor = "ondemand";
# Assuming this is installed on top of the disk image.
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
options = [ "noatime" ];
};
};
ragon.networking.router.enable = true;
ragon.services.ssh.enable = true;
ragon.cli.enable = true;
ragon.cli.maximal = false;
services.lorri.enable = false;
ragon.services.ddns.enable = true;
ragon.services.tailscale.enable = true;
ragon.services.nginx.enable = true;
services.nginx.virtualHosts."h.hailsatan.eu" = {
forceSSL = true;
useACMEHost = "hailsatan.eu";
extraConfig = ''
proxy_buffering off;
'';
locations."/".proxyPass = "http://10.0.0.20:8123";
locations."/".proxyWebsockets = true;
};
services.nginx.virtualHosts."hailsatan.eu" = {
forceSSL = true;
useACMEHost = "hailsatan.eu";
root = pkgs.runCommand "homepage" { } ''
mkdir -p $out
echo "Hail Satan" > $out/index.html
echo "User-agent: *" > $out/robots.txt
echo "Disallow: /" >> $out/robots.txt
'';
};
services.nginx.virtualHosts."j.hailsatan.eu" = {
forceSSL = true;
useACMEHost = "hailsatan.eu";
extraConfig = ''
proxy_buffering off;
'';
locations."/".proxyPass = "https://j.hailsatan.eu";
locations."/".proxyWebsockets = true;
};
users.users.root.openssh.autorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDiKJEYNUU+ZpbOyJf9k9ZZdTTL0qLiZ6fXEBVCjNfas"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIkFgHr6OMwsnGhdG4TwKdthlJC/B9ELqZfrmJ9Sf7qk"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCJVa1zAjR6stS4qIEysQbR9n0/AV2h62QRfsRvNfQiL19ExKFR7ZHaUFBr6jnGjzl5eyK0DtwZMlyaDlTR/AXiTZHJrvEPL1lna42wK252uZb66DXAG23L+iFeXySq3f+a6Prw8NU3HvIvC/YkEYwjjbqPKEjvnIHd2dJ1FZ9T9FeoKup3nMWYGDRqrja8NcRwCY9OpPd3ZKZJlNJcPfbfAipGAuQ6EGgGi0GzqoYP9OqZx9PBQQEY7a5+cUgYYEI75NJNuk4/WBm8fkFKrcOmvhTOEb90kbNmpHusDOrFEo8LATdpmJSG013DpPb1W7pMxMq+YgFF4INqIxrhBGht"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH8RjUQ6DDDDgsVbqq+6zz1q6cBkus/BLUGa9JoWsqB4"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWwrrROqkBEZi8t7Czu1jDDJjSqomGXx7dhIx73GTb3bBlgThqUSsrG+NpP9mxNl4sYgmJYQ9idpUW/RTX3/sXBvNQi4rOqv9z1qdEyzF86CcyWGk4f+D2hJffLlcIbvbDCJ92PF+k5NbH+PC/yVZKSIRC3ENBHf38l8n25ABuBcpCI16bPCIbqbpekqStXClug//uAyENuS6+orHFQg3muUihEedEhJly1QAfDhOzZRlBxTGQcDvZA/XMaIyjAqbXaNVRsDLmKezm/Dg5M3jMIRxApUd9hcuZlfemxUgD0qqnJSTahb9rMxUKk5jdY95EthAp0s2e6tc2O76sYPqb"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH99QITZa3hSa+7sMo4M5IC5mXWEjsRqXUSaYKKRyQfE"
];
}

422
hosts/wormhole/router.nix Normal file
View file

@ -0,0 +1,422 @@
{ config, lib, pkgs, ... }:
with lib;
with lib.my;
let
wgEnabled = hasAttrByPath [ "hosts" config.networking.hostName ] (importTOML ../../data/wireguard.toml);
cfg = config.ragon.networking.router;
waninterface = cfg.waninterface;
laninterface = cfg.laninterface;
prefixSize = cfg.prefixSize;
statics = cfg.statics;
domain = cfg.domain;
disableFirewallFor = cfg.disableFirewallFor;
lan = {
name = "lan";
internet = true;
allowipv6 = true;
ipv4addr = "10.0.0.1";
netipv4addr = "10.0.0.0";
dhcpv4start = "10.0.10.1";
dhcpv4end = "10.0.255.240";
routes = [
{ address = "10.12.0.0"; prefixLength = 16; via = "10.0.1.2"; }
{ address = "10.13.0.0"; prefixLength = 16; via = "10.0.1.2"; }
];
ipv4size = 16;
vlan = 4;
};
iot = {
name = "iot";
internet = false;
allowipv6 = false;
ipv4addr = "10.1.0.1";
netipv4addr = "10.1.0.0";
dhcpv4start = "10.1.1.1";
dhcpv4end = "10.1.255.240";
routes = [ ];
ipv4size = 16;
vlan = 2;
};
guest = {
name = "guest";
internet = true;
allowipv6 = false;
ipv4addr = "192.168.2.1";
netipv4addr = "192.168.2.0";
dhcpv4start = "192.168.2.10";
dhcpv4end = "192.168.2.240";
routes = [ ];
ipv4size = 24;
vlan = 3;
};
nets = [ lan iot guest ];
ipv6nets = builtins.filter (a: a.allowipv6) nets;
interfaceGenerator = obj: {
"${obj.name}".ipv4 = {
addresses = [{
address = obj.ipv4addr;
prefixLength = obj.ipv4size;
}];
routes = [{
address = obj.netipv4addr;
prefixLength = obj.ipv4size;
}] ++ obj.routes;
};
};
in
{
options.ragon.networking.router.enable = mkBoolOpt false;
options.ragon.networking.router.waninterface =
lib.mkOption {
type = lib.types.str;
default = "eth1";
};
options.ragon.networking.router.laninterface =
lib.mkOption {
type = lib.types.str;
default = "eth0";
};
options.ragon.networking.router.domain =
lib.mkOption {
type = lib.types.str;
default = "hailsatan.eu";
};
options.ragon.networking.router.prefixSize =
lib.mkOption {
type = lib.types.int;
default = 59;
};
options.ragon.networking.router.statics =
lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [
{ name = "j.hailsatan.eu"; ip = "10.0.0.2"; }
{ name = "paperless.hailsatan.eu"; ip = "10.0.0.2"; }
{ name = "unifi.hailsatan.eu"; ip = "10.0.0.2"; }
{ name = "nix.hailsatan.eu"; ip = "10.0.0.2"; }
{ name = "h.hailsatan.eu"; ip = "10.0.0.1"; }
{ name = "grafana.hailsatan.eu"; ip = "10.0.0.2"; }
{ name = "nzbr.de"; ip = "10.0.1.2"; }
{ name = "storm.nzbr.de"; ip = "45.9.63.165"; }
{ name = "avalanche.nzbr.de"; ip = "202.61.247.0"; }
];
};
options.ragon.networking.router.disableFirewallFor =
lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [
{ hostname = "enterprise"; mac = "d8:cb:8a:76:09:0a"; tcpports = [ 22 ]; udpports = [ ]; }
{ hostname = "earthquake"; mac = "78:24:af:bc:0c:07"; tcpports = [ 22 22000 ]; udpports = [ 22000 51820 ]; }
];
};
options.ragon.networking.router.staticDHCPs =
lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [
# ragon - machines
{ name = "enterprise"; ip = "10.0.0.9"; mac = "d8:cb:8a:76:09:0a"; }
{ name = "ds9"; ip = "10.0.0.2"; mac = "f4:b5:20:0e:21:d5"; }
# ragon - vms
{ name = "homeassistant"; ip = "10.0.0.20"; mac = "52:54:00:a1:04:14"; }
{ name = "enterprise-win"; ip = "10.0.0.201"; mac = "52:54:00:f3:ab:dd"; }
# ragon - iot
{ name = "zbbridge"; ip = "10.1.0.5"; mac = "98:f4:ab:e2:b6:a3"; }
{ name = "wled-Schrank-Philipp"; ip = "10.1.0.10"; mac = "2c:f4:32:20:74:60"; }
{ name = "wled-Betthintergrund-Phi"; ip = "10.1.0.11"; mac = "2c:3a:e8:0e:ab:71"; }
# nzbr - machines
{ name = "earthquake"; ip = "10.0.1.2"; mac = "78:24:af:bc:0c:07"; }
{ name = "comet"; ip = "10.0.1.4"; mac = "0c:98:38:d3:16:8f"; }
{ name = "meteor"; ip = "10.0.1.8"; mac = "54:27:1e:5c:1f:ed"; } # Wireless
{ name = "meteor"; ip = "10.0.1.16"; mac = "00:21:cc:5c:f5:dc"; } # Wired
{ name = "hurricane"; ip = "10.0.1.32"; mac = "f0:2f:74:1b:af:e0"; }
# nzbr - vms
{ name = "earthquake-macos"; ip = "10.0.1.201"; mac = "52:54:00:8e:e2:66"; }
{ name = "earthquake-win"; ip = "10.0.1.202"; mac = "52:54:00:97:37:69"; }
# nzbr - consoles
{ name = "xbox"; ip = "10.0.2.1"; mac = "58:82:a8:30:2d:1c"; }
{ name = "wii"; ip = "10.0.2.2"; mac = "00:23:cc:50:78:00"; }
{ name = "switch"; ip = "10.0.2.3"; mac = "dc:68:eb:bb:01:fc"; } # Wireless
];
};
options.ragon.networking.router.forwardedPorts =
lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [
#{ proto = "tcp"; sourcePort = "5060-5061"; destination = "10.0.0.11"; }
];
};
config = {
# https://www.willghatch.net/blog/2020/06/22/nixos-raspberry-pi-4-google-fiber-router/
# Youd better forward packets if you actually want a router.
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1;
"net.ipv6.conf.all.forwarding" = 1;
"net.ipv6.conf.default.forwarding" = 1;
"net.ipv6.conf.6rdtun.forwarding" = 1;
};
networking.vlans =
let
genVlan = obj: {
"${obj.name}" = {
id = obj.vlan;
interface = laninterface;
};
};
in
lib.foldl (a: b: a // b) { } (map genVlan nets);
networking.interfaces =
let
genVlanConf = lib.foldl (a: b: a // b) { } (map interfaceGenerator nets);
in
{
"${waninterface}" = {
useDHCP = true;
};
} // genVlanConf;
networking.dhcpcd = {
enable = true;
allowInterfaces = [
"${waninterface}"
] ++ (map (a: a.name) ipv6nets);
extraConfig =
let
genDesc = obj: ''
# We dont want dhcpcd to give us an address on the ${obj.name} interface.
interface ${obj.name}
noipv4
'';
allGenIntDescs = builtins.concatStringsSep "\n" (map genDesc ipv6nets);
in
''
# The man page says that ipv6rs should be disabled globally when
# using a prefix delegation.
noipv6rs
interface ${waninterface}
# On the wan interface, we want to ask for a prefix delegation.
iaid 0
ipv6rs
ia_pd 0/::/${toString prefixSize} lan/0/${toString prefixSize}
${allGenIntDescs}
'';
runHook = ''
if [[ "$reason" == "BOUND6" ]] || [[ "$reason" == "REBIND6" ]]; then
${pkgs.python3}/bin/python3 ${pkgs.writeScript "dhcpcd-runHook.py" ''
import json
import sys
import subprocess
import os
prefix = os.environ.get("new_dhcp6_ia_pd1_prefix1")[:-1]
# https://stackoverflow.com/a/37316533/12852285
def mac2ipv6(mac):
# only accept MACs separated by a colon
parts = mac.split(":")
# modify parts to match IPv6 value
parts.insert(3, "ff")
parts.insert(4, "fe")
parts[0] = "%x" % (int(parts[0], 16) ^ 2)
# format output
ipv6Parts = []
for i in range(0, len(parts), 2):
ipv6Parts.append("".join(parts[i:i+2]))
ipv6 = "%s%s" % (prefix, ":".join(ipv6Parts))
return ipv6
data = json.loads("""${builtins.toJSON disableFirewallFor}""")
for host in data:
print('setting firewall rules for ' + host["hostname"])
IP = mac2ipv6(host["mac"])
if len(host["tcpports"]) > 0:
subprocess.run(["${pkgs.nftables}/bin/nft", "insert", "rule", "inet", "filter", "forward", "ip6", "daddr", IP, "tcp", "dport", f'{{ {", ".join(map(str, host["tcpports"]))} }}', "accept" ])
if len(host["udpports"]) > 0:
subprocess.run(["${pkgs.nftables}/bin/nft", "insert", "rule", "inet", "filter", "forward", "ip6", "daddr", IP, "udp", "dport", f'{{ {", ".join(map(str, host["udpports"]))} }}', "accept" ])
subprocess.run(["${pkgs.nftables}/bin/nft", "insert", "rule", "inet", "filter", "forward", "ip6", "daddr", IP, "icmpv6", "type", "{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert }", "accept"])
''}
fi
'';
};
networking.firewall.enable = false; # disable iptables cause it's ass to set up
networking.nftables.enable = true;
networking.nftables.ruleset =
let
unsafeInterfaces = (map (x: x.name) (filter (x: x.internet == false) nets));
safeInterfaces = (map (x: x.name) (filter (x: x.internet == true) nets)) ++ [ "lo" ] ++ (optionals (wgEnabled) [ "wg0" ]);
allInternalInterfaces = (map (x: x.name) nets) ++ [ "lo" ];
portForwards = concatStringsSep "\n" (map (x: "iifname ${waninterface} ${x.proto} dport ${toString x.sourcePort} dnat ${x.destination}") cfg.forwardedPorts);
dropUnsafe = concatStringsSep "\n" (map (x: "iifname ${x} drop") unsafeInterfaces);
allowSafe = concatStringsSep "\n" (map (x: "iifname ${x} accept") safeInterfaces);
allowSafeOif = concatStringsSep "\n" (map (x: "oifname ${x} ct state { established, related } accept") safeInterfaces);
allowAll = concatStringsSep "\n" (map (x: "iifname ${x} accept") (allInternalInterfaces ++ (optionals (wgEnabled) [ "wg0" ])));
in
''
define unsafe_interfaces = {
${concatStringsSep ",\n" unsafeInterfaces}
}
define safe_interfaces = {
lo,
${concatStringsSep ",\n" safeInterfaces}
}
define all_interfaces = {
lo,
${concatStringsSep ",\n" allInternalInterfaces}
}
table inet filter {
chain input {
type filter hook input priority 0;
# allow established/related connections
ct state { established, related } accept
# early drop of invalid connections
ct state invalid drop
# allow from loopback and internal nic
${allowAll}
# allow icmp
ip protocol icmp icmp type echo-request limit rate over 1/second burst 5 packets drop
ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 1/second burst 5 packets drop
ip protocol icmp icmp type { destination-unreachable, echo-reply, echo-request, source-quench, time-exceeded } accept
ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, echo-reply, echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert, packet-too-big, parameter-problem, time-exceeded } accept
# open port 22, but only allow 2 new connections per minute from each ip
tcp dport 22 ct state new flow table ssh-ftable { ip saddr limit rate 2/minute } accept
tcp dport 80 accept
tcp dport 443 accept
udp dport 51820 accept
# everything else
reject with icmp type port-unreachable
}
chain forward {
type filter hook forward priority 0;
# allow from loopback and internal nic
${allowSafe}
# allow established/related connections
${allowSafeOif}
# Drop everything else
drop
}
chain output {
type filter hook output priority 0
# dont allow any trafic from iot and stuff to escape to the wild
${dropUnsafe}
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority 0
${portForwards}
}
chain postrouting {
type nat hook postrouting priority 0
oifname ${waninterface} masquerade
}
}
'';
services.dnsmasq = {
enable = true;
alwaysKeepRunning = true;
extraConfig =
let
inherit (pkgs) runCommand;
gen = obj: ''
interface=${obj.name}
dhcp-range=${obj.name},${obj.dhcpv4start},${obj.dhcpv4end},12h
'';
genHosts = obj: ''
dhcp-host=${obj.mac},${obj.ip},${obj.name}
'';
genall = builtins.concatStringsSep "\n" (map gen nets);
genallHosts = builtins.concatStringsSep "\n" (map genHosts cfg.staticDHCPs);
genstatics = builtins.concatStringsSep "\n" (map (a: "address=/${a.name}/${a.ip}") statics);
netbootxyz = builtins.fetchurl {
url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.40/netboot.xyz.efi";
sha256 = "1gvgvlaxhjkr9i0b2bjq85h12ni9h5fn6r8nphsag3il9kificcc";
};
netbootxyzpath = runCommand "netbootpath" { } ''
mkdir $out
ln -s ${netbootxyz} $out/netbootxyz.efi
'';
in
''
no-resolv
# unbound broke
# server=127.0.0.1#5353 # unbound
server=1.1.1.1
server=1.0.0.1
# https://hveem.no/using-dnsmasq-for-dhcpv6
# don't ever listen to anything on wan and stuff
except-interface=${waninterface},${laninterface}
listen-address=0.0.0.0,::
# don't send bogus requests out on the internets
bogus-priv
# enable IPv6 Route Advertisements
enable-ra
# Construct a valid IPv6 range from reading the address set on the interface. The :: part refers to the ifid in dhcp6c.conf. Make sure you get this right or dnsmasq will get confused.
dhcp-range=lan,::,constructor:lan, ra-names,slaac, 12h
# ra-names enables a mode which gives DNS names to dual-stack hosts which do SLAAC for IPv6.
# Add your local-only LAN domain
local=/${domain}/
# have your simple hosts expanded to domain
expand-hosts
# set your domain for expand-hosts
domain=${domain}
# forward .kube domains to coredns
server=/kube/10.13.0.10
${genall}
'' +
optionalString wgEnabled ''
interface=wg0
no-dhcp-interface=wg0
addn-hosts=/run/wireguard-hosts
'' + ''
interface=lo # otherwise localhost dns does not work
${genstatics}
${genallHosts}
dhcp-boot=netbootxyz.efi
enable-tftp
tftp-root=${netbootxyzpath}
# set authoritative mode
dhcp-authoritative
'';
};
};
}