Skip to content

Instantly share code, notes, and snippets.

@fraichu
Last active July 25, 2023 04:30
Show Gist options
  • Save fraichu/2d03340b0d9f13e64a1e7ef484671fdb to your computer and use it in GitHub Desktop.
Save fraichu/2d03340b0d9f13e64a1e7ef484671fdb to your computer and use it in GitHub Desktop.
Raspberry Pi NixOS Config
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 = "root@192.168.29.33";
# 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