feat: add updns, add unbound, change hass and firewall
This commit is contained in:
@@ -78,6 +78,11 @@ in {
|
|||||||
example = "example";
|
example = "example";
|
||||||
description = "key for updns";
|
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 = {
|
vpn = {
|
||||||
enable = lib.mkOption {
|
enable = lib.mkOption {
|
||||||
|
|||||||
@@ -1,34 +1,51 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
let
|
let
|
||||||
domain = "home-assistant.${config.cloonar-assistant.domain}";
|
domain = config.cloonar-assistant.domain;
|
||||||
pkgs-with-home-assistant = import (builtins.fetchGit {
|
pkgs-with-home-assistant = import (builtins.fetchGit {
|
||||||
name = "new-home-assistant";
|
name = "new-home-assistant";
|
||||||
url = "https://github.com/nixos/nixpkgs/";
|
url = "https://github.com/nixos/nixpkgs/";
|
||||||
rev = "18dd725c29603f582cf1900e0d25f9f1063dbf11";
|
rev = "18dd725c29603f582cf1900e0d25f9f1063dbf11";
|
||||||
}) {};
|
}) {};
|
||||||
networkPrefix = config.networkPrefix;
|
|
||||||
home-assistant-config = config.home-assistant;
|
home-assistant-config = config.home-assistant;
|
||||||
home-assistant-config.package = pkgs-with-home-assistant.home-assistant;
|
home-assistant-config.package = pkgs-with-home-assistant.home-assistant;
|
||||||
|
uid = config.ids.uids.hass;
|
||||||
|
gid = config.ids.gids.hass;
|
||||||
certDir = "/var/lib/ssl/home-assistant";
|
|
||||||
certFile = "${certDir}/selfsigned.crt";
|
|
||||||
keyFile = "${certDir}/selfsigned.key";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
users.users.hass = {
|
users.users.hass = {
|
||||||
home = "/var/lib/hass";
|
home = "/var/lib/hass";
|
||||||
createHome = true;
|
createHome = true;
|
||||||
group = "hass";
|
group = "hass";
|
||||||
uid = config.ids.uids.hass;
|
uid = uid;
|
||||||
extraGroups = [ "dialout" ];
|
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}" = {
|
security.acme.certs."${domain}" = {
|
||||||
group = "nginx";
|
group = "ssl-users";
|
||||||
|
allowKeysForGroup = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
users.groups.ssl-users = {};
|
||||||
|
|
||||||
sops.secrets."home-assistant-secrets.yaml" = {
|
sops.secrets."home-assistant-secrets.yaml" = {
|
||||||
owner = "hass";
|
owner = "hass";
|
||||||
restartUnits = [ "container@hass.service" ];
|
restartUnits = [ "container@hass.service" ];
|
||||||
@@ -38,13 +55,8 @@ in
|
|||||||
autoStart = true;
|
autoStart = true;
|
||||||
ephemeral = false;
|
ephemeral = false;
|
||||||
privateNetwork = true;
|
privateNetwork = true;
|
||||||
hostBridge = "server";
|
hostAddress = "10.233.0.1";
|
||||||
hostAddress = "${networkPrefix}.97.1";
|
localAddress = "10.233.0.2";
|
||||||
localAddress = "${networkPrefix}.97.20/24";
|
|
||||||
extraFlags = [
|
|
||||||
"--capability=CAP_NET_ADMIN"
|
|
||||||
"--capability=CAP_MKNOD"
|
|
||||||
];
|
|
||||||
bindMounts = {
|
bindMounts = {
|
||||||
"/etc/localtime" = {
|
"/etc/localtime" = {
|
||||||
hostPath = "/etc/localtime";
|
hostPath = "/etc/localtime";
|
||||||
@@ -53,85 +65,18 @@ in
|
|||||||
hostPath = "/var/lib/hass/";
|
hostPath = "/var/lib/hass/";
|
||||||
isReadOnly = false;
|
isReadOnly = false;
|
||||||
};
|
};
|
||||||
"/var/lib/acme/hass/" = {
|
|
||||||
hostPath = "${config.security.acme.certs.${domain}.directory}";
|
|
||||||
};
|
|
||||||
"/var/lib/hass/secrets.yaml" = {
|
"/var/lib/hass/secrets.yaml" = {
|
||||||
hostPath = config.sops.secrets."home-assistant-secrets.yaml".path;
|
hostPath = config.sops.secrets."home-assistant-secrets.yaml".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = { lib, config, pkgs, ... }: {
|
config = { lib, config, pkgs, ... }: {
|
||||||
networkPrefix = networkPrefix;
|
|
||||||
imports = [
|
imports = [
|
||||||
];
|
];
|
||||||
|
|
||||||
networking = {
|
|
||||||
hostName = "home-assistant";
|
|
||||||
useHostResolvConf = false;
|
|
||||||
defaultGateway = {
|
|
||||||
address = "${networkPrefix}.96.1";
|
|
||||||
interface = "eth0";
|
|
||||||
};
|
|
||||||
firewall.enable = false;
|
|
||||||
nameservers = [ "${networkPrefix}.97.1" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.mariadb
|
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 = home-assistant-config;
|
||||||
|
|
||||||
services.home-assistant.extraComponents = [
|
services.home-assistant.extraComponents = [
|
||||||
@@ -243,6 +188,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
users.users.hass.extraGroups = [ "dialout" ];
|
users.users.hass.extraGroups = [ "dialout" ];
|
||||||
|
networking.firewall.allowedTCPPorts = [ 8123 ];
|
||||||
system.stateVersion = "23.05";
|
system.stateVersion = "23.05";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
./interfaces.nix
|
./interfaces.nix
|
||||||
./dhcp.nix
|
./dhcp.nix
|
||||||
./firewall.nix
|
./firewall.nix
|
||||||
|
./unbound.nix
|
||||||
./wireguard.nix
|
./wireguard.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ in {
|
|||||||
# enable flow offloading for better throughput
|
# enable flow offloading for better throughput
|
||||||
# ip protocol { tcp, udp } flow offload @f
|
# 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
|
# multimedia airplay
|
||||||
${lib.optionalString config.cloonar-assistant.multiroom-audio.enable ''
|
${lib.optionalString config.cloonar-assistant.multiroom-audio.enable ''
|
||||||
iifname "multimedia" oifname { "lan" } counter accept
|
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
|
iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 tcp dport { 1883 } counter accept
|
||||||
|
|
||||||
# lan and vpn to any
|
# 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 { "infrastructure", "setup" } oifname { "server", "vserver" } counter accept
|
||||||
iifname { "lan", "wan" } udp dport { 8211, 27015 } counter accept comment "palworld"
|
iifname { "lan", "wan" } udp dport { 8211, 27015 } counter accept comment "palworld"
|
||||||
''}
|
''}
|
||||||
@@ -114,8 +117,6 @@ in {
|
|||||||
"podman*",
|
"podman*",
|
||||||
"guest",
|
"guest",
|
||||||
"setup",
|
"setup",
|
||||||
"vb-*",
|
|
||||||
"vm-*",
|
|
||||||
} oifname {
|
} oifname {
|
||||||
"wan",
|
"wan",
|
||||||
} counter accept comment "Allow trusted LAN to 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"
|
"set -euo pipefail"
|
||||||
""
|
""
|
||||||
"# Where our secret lives (encrypted)"
|
"# 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"
|
"# Where we record the last‐seen IP"
|
||||||
"LAST_IP_FILE=/var/lib/updns-client/last-ip"
|
"LAST_IP_FILE=/var/lib/updns-client/last-ip"
|
||||||
""
|
""
|
||||||
|
|||||||
Reference in New Issue
Block a user