Last active
July 25, 2023 04:30
-
-
Save fraichu/2d03340b0d9f13e64a1e7ef484671fdb to your computer and use it in GitHub Desktop.
Raspberry Pi NixOS Config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
configuration.nix | |
{ config, pkgs, libs, lib, ... }: | |
let secrets = import ./secrets.nix; | |
in | |
{ | |
imports = [ | |
./inadyn.nix | |
]; | |
nixpkgs.overlays = [ | |
(self: super: { | |
linuxPackages_rpi4_custom = super.linuxPackagesFor (super.linux_rpi4.override { | |
argsOverride = rec { | |
src = super.fetchFromGitHub { | |
owner = "raspberrypi"; | |
repo = "linux"; | |
rev = "dc6771425e9604650d1d57f7c69948be405f59a5"; | |
sha256 = "162qqwrv11nj6fs9zq2411lkkf7yz1f8jzl8iianpzk74kkblm94"; | |
}; | |
version = "${modDirVersion}-${tag}"; | |
tag = "1.20220308"; | |
modDirVersion = "5.10.103"; | |
}; | |
}); | |
}) | |
]; | |
boot = { | |
extraModulePackages = [ ]; | |
initrd = { | |
availableKernelModules = [ "xhci_pci" "usbhid" ]; | |
kernelModules = [ ]; | |
}; | |
kernelPackages = pkgs.linuxPackages_rpi4_custom; | |
loader = { | |
generic-extlinux-compatible.enable = true; | |
grub.enable = false; | |
raspberryPi = { | |
enable = true; | |
version = 4; | |
}; | |
}; | |
tmpOnTmpfs = false; | |
}; | |
environment.systemPackages = with pkgs; [ | |
bind | |
curl | |
elinks | |
file | |
fzf | |
gcc | |
git | |
hdparm | |
htop | |
inadyn | |
libraspberrypi | |
lsof | |
nginx | |
nmon | |
parted | |
php | |
pstree | |
python | |
python3 | |
ripgrep | |
ncdu | |
nixpkgs-fmt | |
raspberrypi-eeprom | |
shadowsocks-libev | |
stress-ng | |
tmux | |
tree | |
vim | |
usbutils | |
wireguard | |
wget | |
]; | |
environment.variables = { | |
EDITOR = "vim"; | |
}; | |
fileSystems = { | |
"/" = { | |
device = "/dev/disk/by-label/NIXOS_SD"; | |
fsType = "ext4"; | |
options = [ "noatime" ]; | |
}; | |
"/n" = { | |
device = "/dev/disk/by-uuid/9622cbd4-eca3-408e-8600-84f6bc3b3e02"; | |
fsType = "ext4"; | |
options = [ "noatime" ]; | |
neededForBoot = true; | |
}; | |
"/var/log" = { | |
device = "/n/root/var/log"; | |
options = [ "bind" ]; | |
depends = [ "/n" ]; | |
neededForBoot = true; | |
}; | |
"/var/lib/jellyfin" = { | |
device = "/n/root/var/lib/jellyfin"; | |
options = [ "bind" ]; | |
depends = [ "/n" ]; | |
}; | |
"/var/lib/docker" = { | |
device = "/n/root/var/lib/docker"; | |
options = [ "bind" ]; | |
depends = [ "/n" ]; | |
}; | |
"/tmp" = { | |
device = "/n/root/tmp"; | |
options = [ "bind" ]; | |
depends = [ "/n" ]; | |
}; | |
}; | |
hardware.enableRedistributableFirmware = true; | |
i18n.defaultLocale = "en_US.UTF-8"; | |
console = { | |
font = "Lat2-Terminus16"; | |
keyMap = "us"; | |
}; | |
networking = { | |
nameservers = [ "1.1.1.1" "1.0.0.1" "2606:4700:4700::1111" "2606:4700:4700::1001" ]; | |
wireless = { | |
enable = true; | |
interfaces = [ "wlan0" ]; | |
networks = { | |
"${secrets.wifiSSID}" = { | |
psk = "${secrets.wifiPassword}"; | |
}; | |
}; | |
extraConfig = "country=${secrets.wifiCountry}"; | |
}; | |
firewall = { | |
allowedTCPPorts = [ 22 80 443 ]; | |
allowedUDPPorts = [ 51820 ]; | |
trustedInterfaces = [ "wg0" "ve-${secrets.containerHostName}" ]; | |
}; | |
hostName = "${secrets.hostName}"; | |
useDHCP = false; | |
interfaces = { | |
eth0.useDHCP = true; | |
wlan0.useDHCP = true; | |
}; | |
# 2. WireGuard Setup | |
nat = { | |
enable = true; | |
externalInterface = "eth0"; | |
internalInterfaces = [ "wg0" "ve-${secrets.containerHostName}" ]; | |
}; | |
wg-quick.interfaces = { | |
wg0 = { | |
address = [ "192.168.100.1/24" "fdc9:281f:04d7:9ee9::1/64" ]; | |
listenPort = 51820; | |
privateKeyFile = "/root/wireguard-keys/server.key"; | |
postUp = '' | |
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT | |
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 192.168.100.1/24 -o eth0 -j MASQUERADE | |
${pkgs.iptables}/bin/ip6tables -A FORWARD -i wg0 -j ACCEPT | |
${pkgs.iptables}/bin/ip6tables -t nat -A POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | |
''; | |
preDown = '' | |
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT | |
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 192.168.100.1/24 -o eth0 -j MASQUERADE | |
${pkgs.iptables}/bin/ip6tables -D FORWARD -i wg0 -j ACCEPT | |
${pkgs.iptables}/bin/ip6tables -t nat -D POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | |
''; | |
peers = [ | |
{ | |
# p1 | |
publicKey = "${secrets.wg0Peer1PublicKey}"; | |
allowedIPs = [ "192.168.100.2/32" "fdc9:281f:04d7:9ee9::2/128" ]; | |
} | |
# More peers can be added here. | |
]; | |
}; | |
}; | |
}; | |
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)} | |
''; | |
#builders-use-substitutes = true # Move this inside extraOptions after max-free above | |
#buildMachines = [ { | |
# hostName = "[email protected]"; | |
# systems = ["x86_64-linux" "aarch64-linux"]; | |
# maxJobs = 15; | |
# speedFactor = 9001; | |
# supportedFeatures = [ ]; | |
# mandatoryFeatures = [ ]; | |
#} ]; | |
#distributedBuilds = true; | |
}; | |
nixpkgs.config = { | |
allowUnfree = true; | |
}; | |
powerManagement.cpuFreqGovernor = "ondemand"; | |
security.acme = { | |
certs = { | |
"${secrets.internetHostName}" = { | |
extraDomainNames = [ "track.${secrets.internetHostName}" "watch.${secrets.internetHostName}" "nextcloud.${secrets.internetHostName}" "manage.${secrets.internetHostName}" "get.${secrets.internetHostName}" ]; | |
email = "acme@${secrets.internetHostName}"; | |
}; | |
}; | |
acceptTerms = true; | |
}; | |
# Enable the OpenSSH daemon. | |
services.openssh = { | |
enable = true; | |
permitRootLogin = "no"; | |
passwordAuthentication = false; | |
}; | |
services.nginx = { | |
enable = true; | |
# Root domain | |
virtualHosts."${secrets.internetHostName}" = { | |
forceSSL = true; | |
enableACME = true; | |
root = "/var/www/${secrets.internetHostName}"; | |
}; | |
# Catch-All | |
virtualHosts."_" = { | |
root = "/var/www/${secrets.internetHostName}"; | |
}; | |
# Services | |
virtualHosts."nextcloud.${secrets.internetHostName}" = { | |
forceSSL = true; | |
useACMEHost = "${secrets.internetHostName}"; | |
}; | |
virtualHosts."manage.${secrets.internetHostName}" = { | |
forceSSL = true; | |
useACMEHost = "${secrets.internetHostName}"; | |
locations."/" = { | |
proxyPass = "http://127.0.0.1:8989"; | |
proxyWebsockets = true; | |
extraConfig = '' | |
allow 192.168.100.0/24; | |
allow fdc9:281f:04d7:9ee9::/64; | |
deny all; | |
''; | |
}; | |
}; | |
virtualHosts."get.${secrets.internetHostName}" = { | |
forceSSL = true; | |
useACMEHost = "${secrets.internetHostName}"; | |
locations."/" = { | |
proxyPass = "http://192.168.101.2:9091"; | |
proxyWebsockets = true; | |
extraConfig = '' | |
allow 192.168.100.0/24; | |
allow fdc9:281f:04d7:9ee9::/64; | |
deny all; | |
proxy_buffering off; | |
''; | |
}; | |
}; | |
virtualHosts."track.${secrets.internetHostName}" = { | |
forceSSL = true; | |
useACMEHost = "${secrets.internetHostName}"; | |
locations."/" = { | |
proxyPass = "http://192.168.101.2:9117"; | |
proxyWebsockets = true; | |
extraConfig = '' | |
allow 192.168.100.0/24; | |
allow fdc9:281f:04d7:9ee9::/64; | |
deny all; | |
''; | |
}; | |
}; | |
virtualHosts."watch.${secrets.internetHostName}" = { | |
forceSSL = true; | |
useACMEHost = "${secrets.internetHostName}"; | |
locations."/" = { | |
proxyPass = "http://127.0.0.1:8096"; | |
proxyWebsockets = true; | |
}; | |
}; | |
#appendHttpConfig = "types_hash_max_size 4096;"; | |
}; | |
services.nextcloud = { | |
enable = true; | |
package = pkgs.nextcloud22; | |
hostName = "nextcloud.${secrets.internetHostName}"; | |
home = "/n/nextcloud"; | |
config = { | |
dbtype = "pgsql"; | |
dbuser = "nextcloud"; | |
dbhost = "/run/postgresql"; # nextcloud will add /.s.PGSQL.5432 by itself | |
dbname = "nextcloud"; | |
dbpassFile = "/var/nextcloud-db-pass"; | |
adminuser = "root"; | |
adminpassFile = "/var/nextcloud-admin-pass"; | |
overwriteProtocol = "https"; | |
defaultPhoneRegion = "IN"; | |
}; | |
}; | |
services.postgresql = { | |
enable = true; | |
ensureDatabases = [ "nextcloud" ]; | |
dataDir = "/n/postgresql/13"; | |
ensureUsers = [ | |
{ | |
name = "nextcloud"; | |
ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES"; | |
} | |
]; | |
}; | |
services.dnsmasq = { | |
enable = true; | |
extraConfig = '' | |
interface=wg0 | |
address=/${secrets.internetHostName}/192.168.100.1 | |
local=/${secrets.internetHostName}/ | |
domain=${secrets.internetHostName},192.168.100.0/24,local | |
expand-hosts | |
''; | |
}; | |
services.sonarr = { | |
enable = true; | |
}; | |
services.transmission = { | |
enable = true; | |
}; | |
services.jellyfin = { | |
enable = true; | |
}; | |
services.inadyn = { | |
enable = true; | |
inadynConf = '' | |
period = 300 | |
allow-ipv6 = true | |
custom dynv6_http_api { | |
username = ${secrets.dynv6_username} | |
password = n/a | |
hostname = dyn.${secrets.internetHostName} | |
ddns-server = "dynv6.com" | |
ddns-path = "/api/update?hostname=%h&ipv6=%i&token=%u" | |
checkip-command = "ip -6 addr list scope global -deprecated -noprefixroute eth0 | grep inet6" | |
} | |
''; | |
}; | |
services.fail2ban = { | |
enable = true; | |
maxretry = 5; | |
ignoreIP = [ | |
"127.0.0.0/8" | |
"10.0.0.0/8" | |
"172.16.0.0/12" | |
"192.168.0.0/16" | |
]; | |
}; | |
services.journald.extraConfig = '' | |
SystemMaxUse=4G | |
''; | |
systemd = { | |
services."nextcloud-setup" = { | |
requires = [ "postgresql.service" ]; | |
after = [ "postgresql.service" ]; | |
}; | |
services."container@${secrets.containerHostName}" = { | |
requires = [ "network-online.target" ]; | |
after = [ "network-online.target" ]; | |
}; | |
services."wg-quick-wg0" = { | |
requires = [ "network-online.target" ]; | |
after = [ "network-online.target" ]; | |
}; | |
services."transmission" = { | |
wantedBy = lib.mkForce [ ]; | |
}; | |
}; | |
system.stateVersion = "21.11"; | |
time.timeZone = "${secrets.timeZone}"; | |
users.users = { | |
"${secrets.primary_username}" = { | |
isNormalUser = true; | |
home = "/home/${secrets.primary_username}"; | |
extraGroups = [ "video" "wheel" "sonarr" "transmission" ]; | |
openssh.authorizedKeys.keys = [ "${secrets.ssh_key}" ]; | |
}; | |
sonarr.extraGroups = [ "transmission" ]; | |
}; | |
containers."${secrets.containerHostName}" = { | |
ephemeral = true; | |
autoStart = true; | |
privateNetwork = true; | |
enableTun = true; | |
hostAddress = "192.168.101.1"; | |
localAddress = "192.168.101.2"; | |
bindMounts = { | |
"/host" = { | |
hostPath = "/n/systemd_containers/${secrets.containerHostName}"; | |
isReadOnly = false; | |
}; | |
"/n/torrents" = { | |
hostPath = "/n/torrents"; | |
isReadOnly = false; | |
}; | |
}; | |
config = { config, pkgs, ... }: { | |
networking.firewall = { | |
extraCommands = '' | |
iptables -A INPUT -s 192.168.101.0/24 -j ACCEPT | |
iptables -A OUTPUT -d 192.168.101.0/24 -j ACCEPT | |
iptables -A INPUT -s ${secrets.vpn_ip} -i eth0 -j ACCEPT | |
iptables -A OUTPUT -d ${secrets.vpn_ip} -o eth0 -j ACCEPT | |
iptables -A INPUT -i eth0 -j REJECT | |
ip6tables -A INPUT -i eth0 -j REJECT | |
iptables -A OUTPUT -o eth0 -j REJECT | |
ip6tables -A OUTPUT -o eth0 -j REJECT | |
''; | |
}; | |
environment.etc = { | |
"resolv.conf".text = "nameserver 1.1.1.1\n"; | |
}; | |
environment.systemPackages = with pkgs; [ | |
curl | |
elinks | |
file | |
htop | |
nmon | |
pstree | |
ripgrep | |
tmux | |
tree | |
vim | |
wget | |
]; | |
services = { | |
openvpn.servers = { | |
nordvpn = { | |
config = '' config /host/${secrets.vpn_config_path} ''; | |
authUserPass = { | |
username = "${secrets.vpn_username}"; | |
password = "${secrets.vpn_password}"; | |
}; | |
}; | |
}; | |
transmission = { | |
enable = true; | |
openPeerPorts = true; | |
home = "/host/transmission"; | |
settings = { | |
download-dir = "/n/torrents"; | |
incomplete-dir = "/n/torrents/.incomplete"; | |
incomplete-dir-enabled = true; | |
rpc-bind-address = "192.168.101.2"; | |
rpc-whitelist = "192.168.101.1"; | |
}; | |
}; | |
jackett = { | |
enable = true; | |
dataDir = "/host/jackett"; | |
}; | |
}; | |
systemd = { | |
services."transmission" = { | |
requires = [ "network-online.target" "openvpn-nordvpn.service" "firewall.service" ]; | |
after = [ "network-online.target" "openvpn-nordvpn.service" "firewall.service" ]; | |
}; | |
services."jackett" = { | |
requires = [ "network-online.target" "openvpn-nordvpn.service" "firewall.service" ]; | |
after = [ "network-online.target" "openvpn-nordvpn.service" "firewall.service" ]; | |
}; | |
}; | |
}; | |
}; | |
} | |
inadyn.nix | |
{ config, pkgs, lib, ... }: | |
with lib; | |
let | |
cfg = config.services.inadyn; | |
inadynConf = pkgs.writeText "inadyn.conf" '' | |
${cfg.inadynConf} | |
''; | |
in | |
{ | |
options = { | |
services.inadyn = { | |
enable = mkOption { | |
default = false; | |
type = types.bool; | |
description = '' | |
Internet Dynamic DNS Client | |
''; | |
}; | |
inadynConf = mkOption { | |
default = ""; | |
type = types.lines; | |
description = '' | |
Configuration lines in inadyn.conf | |
''; | |
}; | |
}; | |
}; | |
config = mkIf cfg.enable { | |
systemd.services.inadyn = { | |
wantedBy = [ "multi-user.target" ]; | |
requires = [ "network-online.target" ]; | |
after = [ "network-online.target" ]; | |
path = [ | |
pkgs.inadyn | |
pkgs.iproute2 | |
]; | |
description = "Internet Dynamic DNS Client"; | |
documentation = [ "man:inadyn" "man:inadyn.conf" "https://github.com/troglobit/inadyn" ]; | |
serviceConfig = { | |
Type = "forking"; | |
ExecStart = ''${pkgs.inadyn}/bin/inadyn --config ${inadynConf} --cache-dir /var/cache/inadyn --pidfile /var/run/inadyn.pid''; | |
Restart = "always"; | |
RestartSec = "10min"; | |
}; | |
}; | |
environment.systemPackages = [ | |
pkgs.inadyn | |
pkgs.iproute2 | |
]; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment