feat: add updns, add unbound, change hass and firewall
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
./interfaces.nix
|
||||
./dhcp.nix
|
||||
./firewall.nix
|
||||
./unbound.nix
|
||||
./wireguard.nix
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
142
modules/cloonar-assistant/networking/unbound.nix
Normal file
142
modules/cloonar-assistant/networking/unbound.nix
Normal 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 ];
|
||||
}
|
||||
@@ -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 last‐seen IP"
|
||||
"LAST_IP_FILE=/var/lib/updns-client/last-ip"
|
||||
""
|
||||
|
||||
Reference in New Issue
Block a user