feat: add updns, add unbound, change hass and firewall

This commit is contained in:
2025-04-27 23:03:07 +02:00
parent 2318855dd3
commit 5de8b96816
6 changed files with 183 additions and 88 deletions

View File

@@ -78,6 +78,11 @@ in {
example = "example";
description = "key for updns";
};
secretFile = lib.mkOption {
type = with types; nullOr str;
example = "/private/updns_secret";
description = "File pointing to secret as generated by {command}`wg genpsk`.";
};
};
vpn = {
enable = lib.mkOption {

View File

@@ -1,34 +1,51 @@
{ config, pkgs, ... }:
let
domain = "home-assistant.${config.cloonar-assistant.domain}";
domain = config.cloonar-assistant.domain;
pkgs-with-home-assistant = import (builtins.fetchGit {
name = "new-home-assistant";
url = "https://github.com/nixos/nixpkgs/";
rev = "18dd725c29603f582cf1900e0d25f9f1063dbf11";
}) {};
networkPrefix = config.networkPrefix;
home-assistant-config = config.home-assistant;
home-assistant-config.package = pkgs-with-home-assistant.home-assistant;
certDir = "/var/lib/ssl/home-assistant";
certFile = "${certDir}/selfsigned.crt";
keyFile = "${certDir}/selfsigned.key";
uid = config.ids.uids.hass;
gid = config.ids.gids.hass;
in
{
users.users.hass = {
home = "/var/lib/hass";
createHome = true;
group = "hass";
uid = config.ids.uids.hass;
uid = uid;
extraGroups = [ "dialout" ];
};
users.groups.hass.gid = config.ids.gids.hass;
users.groups.hass.gid = gid;
services.nginx.enable = true;
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://10.233.0.2:8123;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
security.acme.certs."${domain}" = {
group = "nginx";
group = "ssl-users";
allowKeysForGroup = true;
};
users.groups.ssl-users = {};
sops.secrets."home-assistant-secrets.yaml" = {
owner = "hass";
restartUnits = [ "container@hass.service" ];
@@ -38,13 +55,8 @@ in
autoStart = true;
ephemeral = false;
privateNetwork = true;
hostBridge = "server";
hostAddress = "${networkPrefix}.97.1";
localAddress = "${networkPrefix}.97.20/24";
extraFlags = [
"--capability=CAP_NET_ADMIN"
"--capability=CAP_MKNOD"
];
hostAddress = "10.233.0.1";
localAddress = "10.233.0.2";
bindMounts = {
"/etc/localtime" = {
hostPath = "/etc/localtime";
@@ -53,85 +65,18 @@ in
hostPath = "/var/lib/hass/";
isReadOnly = false;
};
"/var/lib/acme/hass/" = {
hostPath = "${config.security.acme.certs.${domain}.directory}";
};
"/var/lib/hass/secrets.yaml" = {
hostPath = config.sops.secrets."home-assistant-secrets.yaml".path;
};
};
config = { lib, config, pkgs, ... }: {
networkPrefix = networkPrefix;
imports = [
];
networking = {
hostName = "home-assistant";
useHostResolvConf = false;
defaultGateway = {
address = "${networkPrefix}.96.1";
interface = "eth0";
};
firewall.enable = false;
nameservers = [ "${networkPrefix}.97.1" ];
};
environment.systemPackages = [
pkgs.mariadb
];
systemd.services.generate-selfsigned-cert = {
description = "Generate/renew self-signed SSL certificate";
wantedBy = [ "nginx.service" ];
path = [ pkgs.openssl pkgs.gnugrep ];
script = ''
if [ -f ${certFile} ]; then
expiry=$(openssl x509 -enddate -noout -in ${certFile} | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s)
current_epoch=$(date +%s)
days_left=$(( (expiry_epoch - current_epoch) / 86400 ))
if [ $days_left -lt 30 ]; then # Regenerate if expiring in <30 days
echo "Certificate expiring soon, regenerating..."
rm ${certFile} ${keyFile}
fi
fi
if [ ! -f ${certFile} ] || [ ! -f ${keyFile} ]; then
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout ${keyFile} \
-out ${certFile} \
-subj "/CN=${domain}"
fi
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
};
services.nginx.enable = true;
services.nginx.virtualHosts."${domain}" = {
sslCertificate = certFile;
sslCertificateKey = keyFile;
forceSSL = true;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:8123;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
services.home-assistant = home-assistant-config;
services.home-assistant.extraComponents = [
@@ -243,6 +188,7 @@ in
};
users.users.hass.extraGroups = [ "dialout" ];
networking.firewall.allowedTCPPorts = [ 8123 ];
system.stateVersion = "23.05";
};
};

View File

@@ -3,6 +3,7 @@
./interfaces.nix
./dhcp.nix
./firewall.nix
./unbound.nix
./wireguard.nix
];
}

View File

@@ -77,6 +77,9 @@ in {
# enable flow offloading for better throughput
# ip protocol { tcp, udp } flow offload @f
# allow home assistant to wan
iifname "ve-hass" oifname wan counter accept comment "Allow home assistant to WAN"
# multimedia airplay
${lib.optionalString config.cloonar-assistant.multiroom-audio.enable ''
iifname "multimedia" oifname { "lan" } counter accept
@@ -92,7 +95,7 @@ in {
iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 tcp dport { 1883 } counter accept
# lan and vpn to any
iifname { "lan", "server", "vserver", "wg_cloonar" } oifname { "lan", "vb-*", "vm-*", "server", "vserver", "infrastructure", "multimedia", "smart", "wg_cloonar", "guest", "setup" } counter accept
iifname { "lan", "server", "vserver", "wg_cloonar" } oifname { "lan", "server", "vserver", "infrastructure", "multimedia", "smart", "wg_cloonar", "guest", "setup" } counter accept
iifname { "infrastructure", "setup" } oifname { "server", "vserver" } counter accept
iifname { "lan", "wan" } udp dport { 8211, 27015 } counter accept comment "palworld"
''}
@@ -114,8 +117,6 @@ in {
"podman*",
"guest",
"setup",
"vb-*",
"vm-*",
} oifname {
"wan",
} counter accept comment "Allow trusted LAN to WAN"

View File

@@ -0,0 +1,142 @@
{ config, ... }: {
}
{ config, pkgs, ... }:
let
cfg = {
remote-control.control-enable = true;
server = {
interface = [ "0.0.0.0" "::0" ];
interface-automatic = "yes";
access-control = [
"127.0.0.0/8 allow"
"${config.networkPrefix}.96.0/24 allow"
"${config.networkPrefix}.97.0/24 allow"
"${config.networkPrefix}.98.0/24 allow"
"${config.networkPrefix}.99.0/24 allow"
"${config.networkPrefix}.101.0/24 allow"
"0.0.0.0/0 allow"
];
tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt";
local-zone = "\"${config.cloonar-assistant.domain}\" transparent";
local-data = [
"\"localhost A 127.0.0.1\""
"\"localhost.${config.cloonar-assistant.domain} A 127.0.0.1\""
"\"localhost AAAA ::1\""
"\"localhost.${config.cloonar-assistant.domain} AAAA ::1\""
"\"fw.${config.cloonar-assistant.domain} A ${config.networkPrefix}.97.1\""
"\"fw A ${config.networkPrefix}.97.1\""
"\"mopidy.${config.cloonar-assistant.domain} IN A ${config.networkPrefix}.97.21\""
"\"snapcast.${config.cloonar-assistant.domain} IN A ${config.networkPrefix}.97.21\""
"\"home-assistant.${config.cloonar-assistant.domain} IN A ${config.networkPrefix}.97.20\""
];
local-data-ptr = [
"\"127.0.0.1 localhost\""
"\"::1 localhost\""
"\"${config.networkPrefix}.97.1 fw.${config.cloonar-assistant.domain}\""
"\"${config.networkPrefix}.97.20 home-assistant.${config.cloonar-assistant.domain}\""
"\"${config.networkPrefix}.97.21 snapcast.${config.cloonar-assistant.domain}\""
];
# ssl-upstream = "yes";
};
forward-zone = [
{
name = ".";
forward-tls-upstream = "yes";
forward-first = "no";
forward-addr = [
"9.9.9.9@853#dns9.quad9.net"
"149.112.112.11@853#dns11.quad9.net"
];
}
];
};
in {
users.users.unbound = {
group = "unbound";
isSystemUser = true;
extraGroups = [ "ssl-users" ];
};
users.groups.unbound = { };
services.resolved.enable = false;
services.unbound = {
enable = true;
settings = cfg;
};
systemd.services.unbound-sync = lib.mkIf config.cloonar-assistant.firewall.enable {
enable = true;
path = with pkgs; [ unbound inotify-tools ];
script = ''
function readFile() {
if [[ "''\$2" == "A" ]] ; then
cat "''\$1" | tail -n +2 | while IFS=, read -r address hwaddr client_id valid_lifetime expire subnet_id fqdn_fwd fqdn_rev hostname state user_context
do
echo "''\${address},''\${hostname}"
done
else
cat "''\$1" | tail -n +2 | while IFS=, read -r address duid valid_lifetime expire subnet_id pref_lifetime lease_type iaid prefix_len fqdn_fwd fqdn_rev hostname hwaddr state user_context hwtype hwaddr_source
do
echo "''\${address},''\${hostname}"
done
fi
}
function readFileUnique() {
readFile "''\$1" ''\$2 | uniq | while IFS=, read -r address hostname
do
if echo "''\${1}" | grep -Eq '.*\.(${config.cloonar-assistant.domain})'; then
echo ''\${hostname} ''\$2 ''\${address}
unbound-control local_data ''\${hostname} ''\$2 ''\${address} > /dev/null 2>&1
if [[ "''\$2" == "A" ]] ; then
echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3
do
unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname} > /dev/null 2>&1
unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname} > /dev/null 2>&1
done
fi
else
if [[ "''\$2" == "A" ]] ; then
echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3
do
if [[ "''\${hostname}" != "" ]]; then
domain=${config.cloonar-assistant.domain}
if [[ "''\${hostname}" != *. ]]; then
unbound-control local_data ''\${hostname}.''\${domain} ''\$2 ''\${address} > /dev/null 2>&1
else
unbound-control local_data ''\${hostname}''\${domain} ''\$2 ''\${address} > /dev/null 2>&1
fi
fi
unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname} > /dev/null 2>&1
unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname} > /dev/null 2>&1
done
fi
fi
done
}
function syncFile() {
# readFileUnique "''\$1" "''\$2"
while true; do
readFileUnique "''\$1" "''\$2"
sleep 10
done
}
syncFile "/var/lib/kea/dhcp4.leases" A &
# syncFile "/var/lib/kea/dhcp6.leases" AAAA &
wait
'';
wants = [ "network-online.target" "unbound.service" ];
after = [ "network-online.target" "unbound.service" ];
partOf = [ "unbound.service" ];
wantedBy = [ "multi-user.target" ];
};
networking.firewall.allowedUDPPorts = [ 53 5353 ];
}

View File

@@ -16,7 +16,7 @@
"set -euo pipefail"
""
"# Where our secret lives (encrypted)"
"SECRET=${config.sops.secrets.updns-client.path}"
"SECRET=${config.cloonar-assistant.updns-client.secretFile}"
"# Where we record the lastseen IP"
"LAST_IP_FILE=/var/lib/updns-client/last-ip"
""