# vim: si ai sts=2 sw=2
{
config,
lib,
pkgs,
...
}:
let
wan = "wan0";
lan = "lan0";
hostName = "nano";
domain = "home.arpa";
tailnet = "hydra-pinecone.ts.net";
dnsmasqEnable = true;
in
{
imports = [
../settings/configuration/nix-linux.nix
];
age.secrets = {
dyndns.file = ../secrets/dyndns.age;
acme.file = ../secrets/acme.age;
syncthing.file = ../secrets/syncthing.age;
};
boot.loader.timeout = lib.mkForce 1;
boot.loader.efi.canTouchEfiVariables = false; # is r/o for some reason
boot.loader.systemd-boot = {
installDeviceTree = true;
edk2-uefi-shell = {
enable = true;
};
};
srvos.boot.consoles = [ "ttyS2,1500000" ];
hardware.deviceTree = {
enable = true;
name = "rockchip/rk3588s-nanopi-r6c.dtb";
};
boot.kernelModules = [
"tcp_lp"
];
boot.kernel.sysctl = {
"net.ipv6.conf.all.accept_ra" = 0;
"net.ipv6.conf.all.autoconf" = 0;
"net.ipv6.conf.all.use_tempaddr" = 0;
"net.ipv6.conf.${wan}.accept_ra" = 2;
"net.ipv6.conf.${wan}.autoconf" = 1;
"net.ipv4.tcp_slow_start_after_idle" = 0;
"net.ipv4.tcp_ecn" = 1;
"net.ipv4.tcp_fastopen" = "0x3";
"net.ipv4.tcp_allowed_congestion_control" = "reno cubic lp";
"net.core.default_qdisc" = "fq";
};
networking = {
useDHCP = false;
inherit domain hostName;
hosts = {
"fd7a:115c:a1e0::53" = [
"tailscale"
"ts"
];
"192.168.100.1" = [
"modem"
"pyur"
];
};
nameservers = [
"9.9.9.11"
"149.112.112.11"
"2620:fe::11"
"2620:fe::fe:11"
];
firewall = {
trustedInterfaces = [
lan
"tailscale0"
];
filterForward = true;
extraForwardRules = ''
iifname "tailscale0" oifname "${lan}" accept
iifname "${lan}" oifname "tailscale0" accept
'';
};
nftables.enable = true;
nat = {
enable = true;
externalInterface = wan;
internalInterfaces = [
lan
"tailscale0"
];
};
};
systemd.network = {
enable = true;
config = {
networkConfig = {
IPv6Forwarding = true;
};
};
links = {
"10-${lan}" = {
matchConfig.Path = "platform-a40c00000.pcie-pci-0003:31:00.0";
linkConfig.Name = lan;
};
"10-${wan}" = {
matchConfig.Path = "platform-fe1c0000.ethernet";
linkConfig.Name = wan;
};
};
networks = {
"50-${lan}" = rec {
matchConfig.Name = lan;
address = [
"10.0.0.1/16"
"fd12:d04f:65d:42::1/56"
];
addresses = [
{
Address = "fe80::1/64";
Scope = "link";
}
];
networkConfig = {
IPv6AcceptRA = false;
DHCPPrefixDelegation = true;
ConfigureWithoutCarrier = true;
MulticastDNS = true;
Domains = [ config.networking.domain ];
IPv6SendRA = !dnsmasqEnable;
DHCPServer = !dnsmasqEnable;
DNS = map (a: builtins.head (lib.strings.splitString "/" a)) address;
DNSDefaultRoute = false;
};
dhcpPrefixDelegationConfig = {
UplinkInterface = wan;
SubnetId = "42";
Assign = true;
Token = "::1";
};
dhcpServerConfig = {
DefaultLeaseTimeSec = 86400;
MaxLeaseTimeSec = 86400;
DNS = "_server_address";
IPv6OnlyPreferredSec = 900;
};
};
"50-${wan}" = {
matchConfig.Name = wan;
networkConfig = {
DHCP = true;
IPv6AcceptRA = true;
IPv4Forwarding = true;
MulticastDNS = false;
DNSDefaultRoute = true;
DNSOverTLS = true;
DNS = map (ns: "${ns}#dns11.quad9.net") config.networking.nameservers;
};
dhcpV4Config = {
UseDNS = false;
SendHostname = false;
UseHostname = false;
Label = "${wan}:1";
};
dhcpV6Config = {
UseDNS = false;
SendHostname = false;
RapidCommit = true;
PrefixDelegationHint = "::/56";
};
dhcpPrefixDelegationConfig = {
UplinkInterface = ":self";
};
ipv6AcceptRAConfig = {
UseDNS = false;
};
addresses = [
{
Address = "192.168.100.10/24";
Peer = "192.168.100.1/32";
Label = "${wan}:0";
Scope = "link";
}
];
cakeConfig = {
Bandwidth = "24M";
OverheadBytes = 18;
MPUBytes = 64;
CompensationMode = "none";
NAT = true;
PriorityQueueingPreset = "diffserv8";
};
};
};
};
services.resolved = {
enable = true;
extraConfig = ''
DNS =
LLMNR = false
MulticastDNS = true
'';
};
services.openssh.openFirewall = false;
services.dnsmasq = {
enable = dnsmasqEnable;
alwaysKeepRunning = true;
settings = {
inherit domain;
interface = lan;
except-interface = "lo";
bind-interfaces = true;
dhcp-fqdn = true;
dhcp-authoritative = true;
dhcp-rapid-commit = true;
dhcp-range = [
"10.0.1.10,10.0.250.250,48h"
"fd12:d04f:65d:42::,slaac,ra-names,48h"
"::,constructor:${lan},ra-stateless"
];
quiet-dhcp = true;
quiet-dhcp6 = true;
quiet-ra = true;
enable-ra = true;
cache-size = 0;
no-resolv = true;
server = [ "127.0.0.53" ];
expand-hosts = true;
localise-queries = true;
interface-name = [
"${hostName}.${domain},${lan}"
"ca.${domain},${lan}"
"wan.${domain},${wan}"
];
};
};
systemd.services.dnsmasq.after = [ "network-online.target" ];
systemd.services.dnsmasq.wants = [ "network-online.target" ];
# TODO find script
# systemd.services.dynamic-dns-update = {
# enable = true;
# startAt = [ "hourly" ];
# description = "Update IP addresses";
# path = with pkgs; [ curl iproute2 dig.dnsutils miller ];
# after = [ "sys-subsystem-net-devices-${wan}.device" ];
# bindsTo = [ "sys-subsystem-net-devices-${wan}.device" ];
# serviceConfig = {
# Type = "oneshot";
# ExecStart = "/bin/sh /etc/nixos/update-ip ${config.age.secrets.dyndns.path}";
# };
# };
# services.networkd-dispatcher = {
# # broken?
# enable = true;
# rules = {
# update-home-address = {
# onState = [ "configured" "configuring" ];
# script = ''
# #!${pkgs.runtimeShell}
# set -eu
# if [[ $IFACE == "${wan}" && $OperationalState == "routable" ]]
# then
# systemctl start dynamic-dns-update.service
# fi
# exit 0
# '';
# };
# tailscale-subnet-router-optimisation = {
# onState = [ "routable" ];
# script = ''
# #!${pkgs.runtimeShell}
# set -eu
# if [[ $IFACE == "${wan}" && $OperationalState == "routable" ]]
# then
# ${pkgs.ethtool}/bin/ethtool -K $IFACE rx-udp-gro-forwarding on rx-gro-list off
# fi
# '';
# };
# };
# };
services.tailscale = {
enable = true;
extraUpFlags = [
"--advertise-exit-node"
"--advertise-routes=10.0.0.0/16,fd12:d04f:65d:42::/56"
];
};
time.timeZone = "Europe/Berlin";
i18n.defaultLocale = "en_GB.UTF-8";
programs.vim.defaultEditor = false;
programs.neovim = {
enable = true;
defaultEditor = true;
vimAlias = true;
viAlias = true;
};
environment.systemPackages = with pkgs; [
file
tree
lsof
knot-dns
ethtool
tcpdump
conntrack-tools
];
programs.fish.enable = true;
users.defaultUserShell = pkgs.fish;
users.mutableUsers = lib.mkForce true;
users.users.alan = {
isNormalUser = true;
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJVREjPey2TOIPzfYJoG9yIR4Rui7tNJK2QIKa+pbgsyXg31hhPIw37LRRIic+l53mW8eahHxX3Y1IeTjcMw8IU= alan@secretive.marvin.local"
];
};
users.users.root = {
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHYUyDdw92TNXguAxcmcmZmn/7ECGdRp6ckjxU+5zCw3BCnsS5+xEvHBVnnFdJRoH2XpfMeJjE+fi67zFVhlbn4= root@secretive.marvin.local"
];
};
services.caddy = {
enable = true;
globalConfig = ''
pki {
ca home {
name "Home CA"
}
}
'';
virtualHosts = {
"${hostName}.${domain}" = {
serverAliases = [ "${hostName}.${tailnet}" ];
extraConfig = ''
tls {
issuer internal {
ca home
}
}
root /var/lib/caddy/ca
file_server browse
'';
};
"ca.${domain}" = {
extraConfig = ''
tls {
issuer internal {
ca home
}
}
acme_server {
allow {
domains *.test *.${domain}
}
}
'';
};
};
};
users.groups.linde.members = [ ];
users.users.linde = {
group = "linde";
description = "Backup user for system 'linde'";
isSystemUser = true;
shell = "/bin/sh";
home = "/srv/backup/linde";
homeMode = "755";
createHome = true;
packages = with pkgs; [ rdiff-backup ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ74cPdIX9OlDkzHb6Y1E5sWqtIqMaf0z/SN3Tfy1Fjl root@linde"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINNXwIdGcP1vKyjmgeLw/sJntn7lajaZivepgdzaXvOt rdiff-backup"
];
};
systemd.services.backup-golink = {
enable = true;
startAt = "daily";
description = "Export short links from golink";
path = with pkgs; [
curl
gitMinimal
];
script = ''
[ -d golink ] || git init --quiet golink --initial-branch=main --shared=world
git config --global user.email linde@alanpearce.eu
cd golink
curl https://go.${tailnet}/.export > links.json
git add links.json
git commit -m $(date +%F)
'';
serviceConfig = {
Type = "oneshot";
User = "linde";
WorkingDirectory = config.users.users.linde.home;
};
};
nix = {
distributedBuilds = true;
buildMachines = [
{
protocol = "ssh-ng";
sshUser = "nixremote";
hostName = "linde.alanpearce.eu";
system = "aarch64-linux";
# TODO make secret
sshKey = "/root/.ssh/id_buche.alanpearce.eu_nixremote";
maxJobs = 2;
speedFactor = 4;
supportedFeatures = [ ];
}
];
settings = {
max-jobs = 2;
builders-use-substitutes = true;
trusted-public-keys = [
"mba-1:CxokFjx7YAQWPWMJJKcP50ZpcPUCAFEOrtWdNUMTVjw="
];
};
};
system.autoUpgrade = {
dates = "04:15";
randomizedDelaySec = "59 min";
flake = "git+https://git.alanpearce.eu/nixfiles";
allowReboot = true;
rebootWindow = {
lower = "01:00";
upper = "06:00";
};
};
users.users.syncthing = {
isSystemUser = true;
group = "syncthing";
homeMode = "0755";
};
users.groups.syncthing.members = [ "alan" ];
services.syncthing = {
enable = true;
openDefaultPorts = true;
dataDir = "/srv/syncthing";
user = "syncthing";
group = "syncthing";
key = config.age.secrets.syncthing.path;
cert = toString (
pkgs.writeText "syncthing.crt" ''
-----BEGIN CERTIFICATE-----
MIIBmjCCASCgAwIBAgIIUOEmXGFrrX0wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
c3luY3RoaW5nMB4XDTIyMDcxMzEwMzIxOVoXDTQ5MTIzMTIzNTk1OVowFDESMBAG
A1UEAxMJc3luY3RoaW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPiJT41NqucQf
UXiBwt+yPYnMg9G8oTt9XNA72V99K46D7mIs1F/5oESlDiCSAngXPsajxRY7wyZV
VoiWegfiaBOGZmq+TyaLlQ5bq/hm/Mp/jVED/rUA+BggohoZZMa2oz8wPTAOBgNV
HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud
EwEB/wQCMAAwCgYIKoZIzj0EAwIDaAAwZQIwLp4Gv5EEmjRO9EphbYJ4jxEJks7E
oblgnTmhfWmVWmf9avJyeGB212VYu4X8cCKDAjEAn7tTB9Y6LZvYPaLSwUKY3EzF
hKTYCb7VA/P1dU3tTR1vSQxnu1DsiliD/XcKe2IK
-----END CERTIFICATE-----
''
);
overrideFolders = false;
overrideDevices = false;
settings = {
options = {
maxRecvKbps = 10240;
maxSendKbps = 1024;
globalAnnounceEnabled = false;
relaysEnabled = false;
natEnabled = false;
urAccepted = 4;
trafficClass = 1;
};
};
};
services.timesyncd.enable = false;
services.chrony = {
enable = true;
extraConfig = ''
rtcdevice /dev/rtc0
rtcfile /var/lib/chrony/rtc
rtcautotrim 30
allow 10.0.0.0/8
allow fd12:d04f:65d:42::0/56
'';
};
services.samba = {
# TODO restore /srv
enable = false;
nmbd.enable = false;
settings = {
global = {
"log level" = 1;
"interfaces" = lan;
"min protocol" = "SMB2";
"disable netbios" = true;
"smb ports" = 445;
"socket options" = "IPTOS_LOWDELAY TCP_NODELAY SO_KEEPALIVE SO_RCVBUF=65536 SO_SNDBUF=65536";
"max xmit" = 131072;
"min receivefile size" = 131072;
"aio read size" = 1;
"aio write size" = 1;
"load printers" = false;
"disable spoolss" = true;
"mdns name" = "mdns";
"follow symlinks" = true;
"veto files" = "/Thumbs.db/.DS_Store/._.DS_Store/.apdisk/";
"delete veto files" = true;
};
public = {
path = "/srv/public";
browseable = "yes";
"guest ok" = "yes";
"create mask" = "0666";
"directory mask" = "0777";
"read only" = "no";
};
Homes = {
"read only" = "no";
"valid users" = "%S";
"inherit acls" = "yes";
};
Videos = {
path = "/srv/videos";
"valid users" = "alan";
"create mask" = "0664";
"directory mask" = "0775";
"writeable" = "yes";
};
};
};
services.samba-wsdd = {
enable = true;
interface = lan;
};
system.stateVersion = "24.11";
}
system/hosts/nano.nix (view raw)