feat: initial
This commit is contained in:
commit
bba9ceff39
18 changed files with 750 additions and 0 deletions
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