feat: initial
This commit is contained in:
commit
bba9ceff39
18 changed files with 750 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
users/
|
||||||
|
ssh_keys/
|
||||||
|
results
|
||||||
108
configuration.nix
Normal file
108
configuration.nix
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
nixosHardwareVersion = "7f1836531b126cfcf584e7d7d71bf8758bb58969";
|
||||||
|
|
||||||
|
timeZone = "Europe/Berlin";
|
||||||
|
defaultLocale = "en_US.UTF-8";
|
||||||
|
storageConfig = import ./data/storage.nix;
|
||||||
|
fileSystemDefinition = lib.mapAttrs' (
|
||||||
|
name: value: {
|
||||||
|
name = storageConfig.${name}.path;
|
||||||
|
value = {
|
||||||
|
device = storageConfig.${name}.source;
|
||||||
|
fsType = storageConfig.${name}.type;
|
||||||
|
options = storageConfig.${name}.options;
|
||||||
|
};
|
||||||
|
}) storageConfig;
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
"${fetchTarball "https://github.com/NixOS/nixos-hardware/archive/${nixosHardwareVersion}.tar.gz"}/raspberry-pi/4"
|
||||||
|
./network/static-ip.nix
|
||||||
|
./services/openssh.nix
|
||||||
|
#./services/blocky.nix #dns
|
||||||
|
./services/unbound.nix #dns
|
||||||
|
./services/kea.nix #dhcp
|
||||||
|
./services/nginx.nix #reverse proxy
|
||||||
|
./services/qbittorrent.nix #torrent
|
||||||
|
./services/kiwix.nix #wiki mirror
|
||||||
|
./services/kiwix-updater.nix #wiki mirror update
|
||||||
|
./users
|
||||||
|
./programs
|
||||||
|
];
|
||||||
|
|
||||||
|
fileSystems = fileSystemDefinition;
|
||||||
|
|
||||||
|
networking.hostName = "raspberry";
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
docker-compose
|
||||||
|
docker-client
|
||||||
|
podman
|
||||||
|
podman-compose
|
||||||
|
];
|
||||||
|
|
||||||
|
time.timeZone = timeZone;
|
||||||
|
|
||||||
|
virtualisation.docker = {
|
||||||
|
enable = true;
|
||||||
|
autoPrune.enable = true;
|
||||||
|
daemon.settings = {
|
||||||
|
"log-driver" = "json-file";
|
||||||
|
"log-opts" = {
|
||||||
|
"max-size" = "10m";
|
||||||
|
"max-file" = "3";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
virtualisation.podman = {
|
||||||
|
enable = true;
|
||||||
|
defaultNetwork.settings.dns_enabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
i18n = {
|
||||||
|
defaultLocale = defaultLocale;
|
||||||
|
extraLocaleSettings = {
|
||||||
|
LC_ADDRESS = defaultLocale;
|
||||||
|
LC_IDENTIFICATION = defaultLocale;
|
||||||
|
LC_MEASUREMENT = defaultLocale;
|
||||||
|
LC_MONETARY = defaultLocale;
|
||||||
|
LC_NAME = defaultLocale;
|
||||||
|
LC_NUMERIC = defaultLocale;
|
||||||
|
LC_PAPER = defaultLocale;
|
||||||
|
LC_TELEPHONE = defaultLocale;
|
||||||
|
LC_TIME = defaultLocale;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
mutableUsers = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable passwordless sudo.
|
||||||
|
security.sudo.extraRules = [
|
||||||
|
{
|
||||||
|
users = ["nudelerde"];
|
||||||
|
commands = [
|
||||||
|
{
|
||||||
|
command = "ALL";
|
||||||
|
options = ["NOPASSWD"];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
nix.gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than +5"; # Keep last 5 generations
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable GPU acceleration
|
||||||
|
hardware.raspberry-pi."4".fkms-3d.enable = true;
|
||||||
|
|
||||||
|
system.stateVersion = "23.11";
|
||||||
|
}
|
||||||
67
data/network.nix
Normal file
67
data/network.nix
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
let
|
||||||
|
lib = import <nixpkgs/lib>;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
network = {
|
||||||
|
subnet = "192.168.2.0/24";
|
||||||
|
subnet_base = "192.168.2.0";
|
||||||
|
gateway = ips.router;
|
||||||
|
cidr = 24;
|
||||||
|
};
|
||||||
|
|
||||||
|
ips = {
|
||||||
|
pi = "192.168.2.100";
|
||||||
|
desktop = "192.168.2.101";
|
||||||
|
router = "192.168.2.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
dhcp = {
|
||||||
|
pool_start = "192.168.2.50";
|
||||||
|
pool_end = "192.168.2.90";
|
||||||
|
default_lease = 3600;
|
||||||
|
max_lease = 86400;
|
||||||
|
reservations = [{
|
||||||
|
ip-address = ips.desktop;
|
||||||
|
hw-address = "30:9c:23:81:91:ea";
|
||||||
|
hostname = "desktop";
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
fallback_dns_servers = [
|
||||||
|
"1.1.1.1"
|
||||||
|
"8.8.8.8"
|
||||||
|
];
|
||||||
|
|
||||||
|
local_domain = "home";
|
||||||
|
|
||||||
|
services = {
|
||||||
|
"pi" = {
|
||||||
|
ip = ips.pi;
|
||||||
|
};
|
||||||
|
"desktop" = {
|
||||||
|
ip = ips.desktop;
|
||||||
|
};
|
||||||
|
"torrent" = {
|
||||||
|
ip = ips.pi;
|
||||||
|
reverse_proxy = {
|
||||||
|
port = 8085;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"wiki" = {
|
||||||
|
ip = ips.pi;
|
||||||
|
reverse_proxy = {
|
||||||
|
port = 8086;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"router" = {
|
||||||
|
ip = ips.router;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dnsMappings = builtins.listToAttrs (map (name: {
|
||||||
|
name = "${name}.${local_domain}";
|
||||||
|
value = services.${name}.ip;
|
||||||
|
}) (builtins.attrNames services));
|
||||||
|
|
||||||
|
reverse_proxy = lib.filterAttrs (name: value: value ? reverse_proxy) services;
|
||||||
|
}
|
||||||
41
data/services.nix
Normal file
41
data/services.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
let
|
||||||
|
lib = import <nixpkgs/lib>;
|
||||||
|
storage_data = import ./storage.nix;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
qbittorrent = {
|
||||||
|
root_dir = "${storage_data.ssd.path}/qbittorrent";
|
||||||
|
vpn = {
|
||||||
|
username = "KNLdup50RYT1911K";
|
||||||
|
password = "FQCd6rfszoze0BJGgBhMHa3pIzpUdtyt";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
kiwix = {
|
||||||
|
root_dir = "${storage_data.ssd.path}/kiwix";
|
||||||
|
urls = [
|
||||||
|
"https://ftp.fau.de/kiwix/zim/wikipedia/wikipedia_en_all_nopic_2025-08.zim"
|
||||||
|
"https://download.kiwix.org/zim/wikipedia/wikipedia_de_all_nopic_2026-01.zim"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
37
data/ssh.nix
Normal file
37
data/ssh.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
let
|
||||||
|
allKeyDir = "/etc/nixos/ssh_keys";
|
||||||
|
readKeyFile = filePath:
|
||||||
|
let
|
||||||
|
content = builtins.readFile filePath;
|
||||||
|
# Split on newlines and filter out empty strings
|
||||||
|
lines = builtins.filter (line: line != "") (
|
||||||
|
builtins.filter builtins.isString (
|
||||||
|
builtins.split "\n" content
|
||||||
|
)
|
||||||
|
);
|
||||||
|
in lines;
|
||||||
|
|
||||||
|
# Get all keys for a user
|
||||||
|
getUserKeys = username:
|
||||||
|
let
|
||||||
|
userDir = "${allKeyDir}/${username}";
|
||||||
|
in
|
||||||
|
if builtins.pathExists userDir then
|
||||||
|
let
|
||||||
|
files = builtins.attrNames (builtins.readDir userDir);
|
||||||
|
# Read all key files and flatten the list
|
||||||
|
allKeys = builtins.concatMap (file:
|
||||||
|
readKeyFile "${userDir}/${file}"
|
||||||
|
) files;
|
||||||
|
in allKeys
|
||||||
|
else [];
|
||||||
|
users = builtins.attrNames (builtins.readDir allKeyDir);
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
keys = builtins.listToAttrs (map (user: {
|
||||||
|
name = user;
|
||||||
|
value = getUserKeys user;
|
||||||
|
}) users);
|
||||||
|
ssh_users = users;
|
||||||
|
getKeys = getUserKeys;
|
||||||
|
}
|
||||||
14
data/storage.nix
Normal file
14
data/storage.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
rec {
|
||||||
|
sdcard = {
|
||||||
|
path = "/";
|
||||||
|
type = "ext4";
|
||||||
|
source = "/dev/disk/by-label/NIXOS_SD";
|
||||||
|
options = ["noatime"];
|
||||||
|
};
|
||||||
|
ssd = {
|
||||||
|
path = "/mnt/ssd";
|
||||||
|
type = "ext4";
|
||||||
|
source = "/dev/disk/by-uuid/a3ffb02e-fe9f-4bce-bd94-af0294ebff8f";
|
||||||
|
options = ["noatime"];
|
||||||
|
};
|
||||||
|
}
|
||||||
18
network/static-ip.nix
Normal file
18
network/static-ip.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Set a static IP address
|
||||||
|
networking.interfaces.eth0.ipv4.addresses = [{
|
||||||
|
address = net.ips.pi;
|
||||||
|
prefixLength = net.network.cidr;
|
||||||
|
}];
|
||||||
|
|
||||||
|
# Set default gateway (your router's IP)
|
||||||
|
networking.defaultGateway = net.ips.router;
|
||||||
|
|
||||||
|
# Set DNS servers (fallback when Blocky isn't working)
|
||||||
|
networking.nameservers = net.fallback_dns_servers;
|
||||||
|
}
|
||||||
3
programs/default.nix
Normal file
3
programs/default.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{...}: {
|
||||||
|
imports = [./git.nix];
|
||||||
|
}
|
||||||
16
programs/git.nix
Normal file
16
programs/git.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
{
|
||||||
|
programs.git = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
user = {
|
||||||
|
name = "Katharina Heidenreich";
|
||||||
|
email = "katharina.heidenreich02@gmail.com";
|
||||||
|
};
|
||||||
|
init.defaultBranch = "main";
|
||||||
|
pull.rebase = false;
|
||||||
|
core.editor = "nano";
|
||||||
|
safe.directory = ["/etc/nixos"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
13
services/README.md
Normal file
13
services/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Services
|
||||||
|
|
||||||
|
## List
|
||||||
|
- DHCP
|
||||||
|
- Kea
|
||||||
|
- DNS
|
||||||
|
- Blocky
|
||||||
|
- Reverse Proxy
|
||||||
|
- nginx
|
||||||
|
- Torrent
|
||||||
|
- qbittorrent
|
||||||
|
- Wiki
|
||||||
|
- kiwix
|
||||||
61
services/blocky.nix
Normal file
61
services/blocky.nix
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Enable Blocky
|
||||||
|
services.blocky = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
# Listen on port 53 (standard DNS port)
|
||||||
|
ports.dns = 53;
|
||||||
|
|
||||||
|
# Custom DNS entries for your local services
|
||||||
|
customDNS = {
|
||||||
|
# This maps your domains to your Pi's IP
|
||||||
|
mapping = net.dnsMappings;
|
||||||
|
# mapping = dnsMappings;
|
||||||
|
};
|
||||||
|
|
||||||
|
conditional = {
|
||||||
|
fallbackUpstream = false;
|
||||||
|
mapping = builtins.mapAttrs (name: value: net.ips.router) net.dnsMappings;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Upstream DNS servers (with fallback)
|
||||||
|
upstreams = {
|
||||||
|
groups = {
|
||||||
|
default =
|
||||||
|
["https://cloudflare-dns.com/dns-query"] ++ net.fallback_dns_servers;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Bootstrap DNS (for initially resolving DoH servers)
|
||||||
|
bootstrapDns = {
|
||||||
|
upstream = "https://1.1.1.1/dns-query";
|
||||||
|
ips = ["1.1.1.1" "1.0.0.1"];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable caching for better performance
|
||||||
|
caching = {
|
||||||
|
minTime = "5m";
|
||||||
|
maxTime = "30m";
|
||||||
|
prefetching = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# blocking = {
|
||||||
|
# denylists = {
|
||||||
|
# ads = ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"];
|
||||||
|
# };
|
||||||
|
# clientGroupsBlock = {
|
||||||
|
# default = ["ads"];
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Allow DNS through the firewall
|
||||||
|
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||||
|
}
|
||||||
56
services/kea.nix
Normal file
56
services/kea.nix
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.kea.dhcp4 = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
interfaces-config = {
|
||||||
|
interfaces = [ "eth0" ];
|
||||||
|
};
|
||||||
|
lease-database = {
|
||||||
|
name = "/var/lib/kea/dhcp4.leases";
|
||||||
|
type = "memfile";
|
||||||
|
};
|
||||||
|
subnet4 = [{
|
||||||
|
id = 1;
|
||||||
|
subnet = net.network.subnet;
|
||||||
|
pools = [{
|
||||||
|
pool = "${net.dhcp.pool_start} - ${net.dhcp.pool_end}";
|
||||||
|
}];
|
||||||
|
option-data = [
|
||||||
|
{
|
||||||
|
name = "routers";
|
||||||
|
data = net.ips.router;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "domain-name-servers";
|
||||||
|
data = builtins.concatStringsSep ", " ([net.ips.pi] ++ net.fallback_dns_servers);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "domain-name";
|
||||||
|
data = net.local_domain;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "domain-search";
|
||||||
|
data = net.local_domain;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
reservations = net.dhcp.reservations;
|
||||||
|
}];
|
||||||
|
|
||||||
|
valid-lifetime = net.dhcp.default_lease;
|
||||||
|
renew-timer = net.dhcp.default_lease / 2;
|
||||||
|
rebind-timer = net.dhcp.default_lease * 3 / 4;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Firewall rules for DHCP
|
||||||
|
networking.firewall = {
|
||||||
|
allowedUDPPorts = [ 67 68 ]; # DHCP ports
|
||||||
|
checkReversePath = false; # Sometimes needed for DHCP
|
||||||
|
};
|
||||||
|
}
|
||||||
87
services/kiwix-updater.nix
Normal file
87
services/kiwix-updater.nix
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# Import service data (make sure this path is correct)
|
||||||
|
service_data = import ../data/services.nix;
|
||||||
|
kiwix = service_data.kiwix;
|
||||||
|
zimUrls = kiwix.urls;
|
||||||
|
updater = pkgs.writeShellScriptBin "kiwix-updater" ''
|
||||||
|
set -e
|
||||||
|
|
||||||
|
URLS=(
|
||||||
|
${builtins.concatStringsSep " " zimUrls}
|
||||||
|
)
|
||||||
|
|
||||||
|
download() {
|
||||||
|
local url=''$1
|
||||||
|
local filename=''$(basename "''$url")
|
||||||
|
local filepath="${kiwix.root_dir}"/''$filename
|
||||||
|
|
||||||
|
if [ -f "''$filepath" ]; then
|
||||||
|
echo "''$filepath exists!"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
cd ${kiwix.root_dir}
|
||||||
|
${pkgs.wget}/bin/wget --continue --quiet "''$url" -O "''$filename.tmp"
|
||||||
|
mv ''$filename.tmp ''$filename
|
||||||
|
}
|
||||||
|
|
||||||
|
build_lib() {
|
||||||
|
{
|
||||||
|
echo '<?xml version="1.0" encoding="UTF-8"?>'
|
||||||
|
echo '<library>'
|
||||||
|
for zim in "${kiwix.root_dir}"/*.zim; do
|
||||||
|
if [ -f "''$zim" ]; then
|
||||||
|
filename=''$(basename "''$zim")
|
||||||
|
size=''$(stat -c%s "''$zim")
|
||||||
|
case "''$filename" in
|
||||||
|
*_en_*)
|
||||||
|
title="English Wikipedia (Text Only)"
|
||||||
|
lang="eng"
|
||||||
|
;;
|
||||||
|
*_de_*)
|
||||||
|
title="German Wikipedia (Text Only)"
|
||||||
|
lang="deu"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
title="''$filename"
|
||||||
|
lang="unknown"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
cat << EOF
|
||||||
|
<book id="''$filename" path="/data/''$filename" title="''$title" language="''$lang">
|
||||||
|
<title>''$title</title>
|
||||||
|
<language>''$lang</language>
|
||||||
|
<path>/data/''$filename</path>
|
||||||
|
<size>''$size</size>
|
||||||
|
</book>
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo '</library>'
|
||||||
|
} > "${kiwix.root_dir}/library.xml.tmp"
|
||||||
|
mv "${kiwix.root_dir}/library.xml.tmp" "${kiwix.root_dir}/library.xml"
|
||||||
|
}
|
||||||
|
|
||||||
|
for url in "''${URLS[@]}"; do
|
||||||
|
download "''$url"
|
||||||
|
done
|
||||||
|
|
||||||
|
build_lib
|
||||||
|
|
||||||
|
${pkgs.systemd}/bin/systemctl restart podman-kiwix-serve.service
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
wget
|
||||||
|
curl
|
||||||
|
];
|
||||||
|
|
||||||
|
services.cron = {
|
||||||
|
enable = true;
|
||||||
|
systemCronJobs = [
|
||||||
|
"0 3 * * * root ${updater}/bin/kiwix-updater >/dev/null 2>&1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
36
services/kiwix.nix
Normal file
36
services/kiwix.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
service_data = import ../data/services.nix;
|
||||||
|
kiwix = service_data.kiwix;
|
||||||
|
in {
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${kiwix.root_dir} 0755 root root - -"
|
||||||
|
"d ${kiwix.root_dir}/data 0755 root root - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers = {
|
||||||
|
kiwix-serve = {
|
||||||
|
image = "ghcr.io/kiwix/kiwix-serve:3.8.2";
|
||||||
|
ports = ["8086:8080"];
|
||||||
|
volumes = ["${kiwix.root_dir}/:/data:ro"];
|
||||||
|
cmd = [
|
||||||
|
"--monitorLibrary"
|
||||||
|
"--library" "/data/library.xml"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
TZ = "Europe/Berlin";
|
||||||
|
};
|
||||||
|
extraOptions = [
|
||||||
|
"--memory=512m" # Limit container to 512MB RAM
|
||||||
|
"--memory-swap=512m" # Disable swap usage
|
||||||
|
"--cpus=1" # Limit to 1 CPU core
|
||||||
|
];
|
||||||
|
autoStart = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
allowedTCPPorts = [8086];
|
||||||
|
};
|
||||||
|
}
|
||||||
47
services/nginx.nix
Normal file
47
services/nginx.nix
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
network = import ../data/network.nix;
|
||||||
|
rproxyServices = builtins.mapAttrs (name: service: {
|
||||||
|
serverName = "${name}.${network.local_domain}";
|
||||||
|
listen = [ {addr = "0.0.0.0"; port = 80;} ];
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${builtins.toString service.reverse_proxy.port}/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
allow ${network.network.subnet};
|
||||||
|
deny all;
|
||||||
|
'';
|
||||||
|
}) network.reverse_proxy;
|
||||||
|
serviceNamesMessage = builtins.toString (builtins.attrNames network.reverse_proxy);
|
||||||
|
fallback = {
|
||||||
|
serverName = "_";
|
||||||
|
listen = [ {addr = "0.0.0.0"; port = 80;} ];
|
||||||
|
locations."/" = {
|
||||||
|
return = "404";
|
||||||
|
extraConfig = ''
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = ''
|
||||||
|
return 404 "This domain is not configured. Available services: ${serviceNamesMessage}";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
recommendedTlsSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
|
||||||
|
virtualHosts = rproxyServices // {fallback = fallback;};
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO add 443 for https
|
||||||
|
networking.firewall.allowedTCPPorts = [80];
|
||||||
|
}
|
||||||
17
services/openssh.nix
Normal file
17
services/openssh.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
ssh_data = import ../data/ssh.nix;
|
||||||
|
in {
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = true;
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
AllowUsers = ssh_data.ssh_users;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users = builtins.mapAttrs (username: value: {
|
||||||
|
openssh.authorizedKeys.keys = ssh_data.keys.${username};
|
||||||
|
}) ssh_data.keys;
|
||||||
|
}
|
||||||
92
services/qbittorrent.nix
Normal file
92
services/qbittorrent.nix
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
serviceData = import ../data/services.nix;
|
||||||
|
qbt = serviceData.qbittorrent;
|
||||||
|
in {
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${qbt.root_dir} 0755 root root - -"
|
||||||
|
"d ${qbt.root_dir}/gluetun 0755 root root - -"
|
||||||
|
"d ${qbt.root_dir}/downloads 0755 root root - -"
|
||||||
|
"d ${qbt.root_dir}/config 0755 root root - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.etc."qbittorrent-compose/docker-compose.yml" = {
|
||||||
|
mode = "0444";
|
||||||
|
text = ''
|
||||||
|
services:
|
||||||
|
gluetun:
|
||||||
|
image: docker.io/qmcgaw/gluetun:latest
|
||||||
|
pull_policy: always
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
network_mode: bridge
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:8085:8085 # qBittorrent
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
volumes:
|
||||||
|
- ${qbt.root_dir}/gluetun/:/gluetun
|
||||||
|
environment:
|
||||||
|
- VPN_SERVICE_PROVIDER=protonvpn
|
||||||
|
- SERVER_HOSTNAME=node-nl-28.protonvpn.net,node-ch-06.protonvpn.net,node-nl-13.protonvpn.net,node-ch-06.protonvpn.net,node-es-04.protonvpn.net
|
||||||
|
- UPDATER_PERIOD=24h
|
||||||
|
|
||||||
|
- OPENVPN_USER=${qbt.vpn.username}
|
||||||
|
- OPENVPN_PASSWORD=${qbt.vpn.password}
|
||||||
|
|
||||||
|
- DOT_PROVIDERS=cloudflare,google
|
||||||
|
- BLOCK_ADS=off
|
||||||
|
- BLOCK_MALICIOUS=off
|
||||||
|
- BLOCK_SURVEILLANCE=off
|
||||||
|
|
||||||
|
- TZ=Europe/Berlin
|
||||||
|
|
||||||
|
qbittorrent:
|
||||||
|
image: lscr.io/linuxserver/qbittorrent:latest
|
||||||
|
pull_policy: always
|
||||||
|
network_mode: 'service:gluetun'
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Berlin
|
||||||
|
- WEBUI_PORT=8085
|
||||||
|
volumes:
|
||||||
|
- ${qbt.root_dir}/config/:/config
|
||||||
|
- ${qbt.root_dir}/downloads/:/downloads
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
systemd.services.qbittorrent-stack = {
|
||||||
|
description = "qbittorrent stack";
|
||||||
|
after = ["docker.service" "network.target"];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
WorkingDirectory = qbt.root_dir;
|
||||||
|
ExecStart = "${pkgs.writeShellScript "torrent-start" ''
|
||||||
|
set -e
|
||||||
|
# Copy compose file to working directory
|
||||||
|
cp /etc/qbittorrent-compose/docker-compose.yml ${qbt.root_dir}/
|
||||||
|
cd ${qbt.root_dir}
|
||||||
|
${pkgs.docker-compose}/bin/docker-compose up -d
|
||||||
|
''}";
|
||||||
|
ExecStop = "${pkgs.writeShellScript "torrent-stop" ''
|
||||||
|
cd ${qbt.root_dir}
|
||||||
|
${pkgs.docker-compose}/bin/docker-compose down
|
||||||
|
''}";
|
||||||
|
ExecReload = "${pkgs.writeShellScript "torrent-reload" ''
|
||||||
|
cd ${qbt.root_dir}
|
||||||
|
${pkgs.docker-compose}/bin/docker-compose restart
|
||||||
|
''}";
|
||||||
|
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
allowedTCPPorts = [8085];
|
||||||
|
};
|
||||||
|
}
|
||||||
34
services/unbound.nix
Normal file
34
services/unbound.nix
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
net = import ../data/network.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.unbound = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
|
||||||
|
server = {
|
||||||
|
interface = ["0.0.0.0" "::0"];
|
||||||
|
|
||||||
|
access-control = ["127.0.0.1 allow" "${net.network.subnet} allow"];
|
||||||
|
|
||||||
|
local-zone = "\"${net.local_domain}.\" static";
|
||||||
|
local-data =
|
||||||
|
(map (name:
|
||||||
|
let ip = net.dnsMappings.${name}; in
|
||||||
|
"\"${name}. IN A ${ip}\""
|
||||||
|
) (builtins.attrNames net.dnsMappings));
|
||||||
|
};
|
||||||
|
|
||||||
|
forward-zone = {
|
||||||
|
name = ".";
|
||||||
|
forward-addr = net.fallback_dns_servers;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Allow DNS through the firewall
|
||||||
|
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue