Compare commits
9 Commits
attic
...
66bfc62566
| Author | SHA1 | Date | |
|---|---|---|---|
| 66bfc62566 | |||
| 91874b9d53 | |||
| 50fc0a53d2 | |||
| 0b3322afda | |||
| b32f6fa315 | |||
| fe41ffc788 | |||
| eac443f280 | |||
| d557820d6c | |||
| 4d658e10d3 |
@@ -1,4 +1,4 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
system.stateVersion = "23.11";
|
||||
|
||||
networking.useDHCP = false;
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
|
||||
networking.firewall.enable = true;
|
||||
networking.firewall.allowPing = true;
|
||||
|
||||
@@ -37,17 +37,5 @@ in
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.enable = true;
|
||||
services.nginx.virtualHosts."s0.koi-bebop.ts.net" = {
|
||||
default = true;
|
||||
addSSL = true;
|
||||
serverAliases = [ "s0" ];
|
||||
sslCertificate = "/secret/ssl/s0.koi-bebop.ts.net.crt";
|
||||
sslCertificateKey = "/secret/ssl/s0.koi-bebop.ts.net.key";
|
||||
locations."/" = {
|
||||
proxyPass = "http://localhost:${toString cfg.port}";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
./dashy.nix
|
||||
./librechat.nix
|
||||
./actualbudget.nix
|
||||
./unifi.nix
|
||||
];
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ in
|
||||
HOST = "0.0.0.0";
|
||||
MONGO_URI = "mongodb://host.containers.internal:27017/LibreChat";
|
||||
ENDPOINTS = "openAI,google,bingAI,gptPlugins";
|
||||
REFRESH_TOKEN_EXPIRY = toString (1000 * 60 * 60 * 24 * 30); # 30 days
|
||||
};
|
||||
environmentFiles = [
|
||||
"/run/agenix/librechat-env-file"
|
||||
|
||||
@@ -4,6 +4,10 @@ let
|
||||
cfg = config.services.nginx;
|
||||
in
|
||||
{
|
||||
options.services.nginx = {
|
||||
openFirewall = lib.mkEnableOption "Open firewall ports 80 and 443";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.nginx = {
|
||||
recommendedGzipSettings = true;
|
||||
@@ -12,6 +16,8 @@ in
|
||||
recommendedTlsSettings = true;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
services.nginx.openFirewall = lib.mkDefault true;
|
||||
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ 80 443 ];
|
||||
};
|
||||
}
|
||||
|
||||
25
common/server/unifi.nix
Normal file
25
common/server/unifi.nix
Normal file
@@ -0,0 +1,25 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.unifi;
|
||||
in
|
||||
{
|
||||
options.services.unifi = {
|
||||
# Open select Unifi ports instead of using openFirewall to avoid opening access to unifi's control panel
|
||||
openMinimalFirewall = lib.mkEnableOption "Open bare minimum firewall ports";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.unifi.unifiPackage = pkgs.unifi8;
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openMinimalFirewall {
|
||||
allowedUDPPorts = [
|
||||
3478 # STUN
|
||||
10001 # used for device discovery.
|
||||
];
|
||||
allowedTCPPorts = [
|
||||
8080 # Used for device and application communication.
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -118,14 +118,6 @@
|
||||
|
||||
# proxied web services
|
||||
services.nginx.enable = true;
|
||||
services.nginx.virtualHosts."jellyfin.neet.cloud" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://s0.koi-bebop.ts.net";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
services.nginx.virtualHosts."navidrome.neet.cloud" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
@@ -60,73 +60,65 @@ sections:
|
||||
- &ref_0
|
||||
title: Jellyfin
|
||||
icon: hl-jellyfin
|
||||
url: http://s0:8097
|
||||
url: https://jellyfin.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://jellyfin.s0
|
||||
statusCheck: false
|
||||
id: 0_1956_jellyfin
|
||||
- &ref_1
|
||||
title: Sonarr
|
||||
description: Manage TV
|
||||
icon: hl-sonarr
|
||||
url: http://s0:8989
|
||||
url: https://sonarr.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://sonarr.s0
|
||||
statusCheck: false
|
||||
id: 1_1956_sonarr
|
||||
- &ref_2
|
||||
title: Radarr
|
||||
description: Manage Movies
|
||||
icon: hl-radarr
|
||||
url: http://s0:7878
|
||||
url: https://radarr.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://radarr.s0
|
||||
statusCheck: false
|
||||
id: 2_1956_radarr
|
||||
- &ref_3
|
||||
title: Lidarr
|
||||
description: Manage Music
|
||||
icon: hl-lidarr
|
||||
url: http://s0:8686
|
||||
url: https://lidarr.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://lidarr.s0
|
||||
statusCheck: false
|
||||
id: 3_1956_lidarr
|
||||
- &ref_4
|
||||
title: Prowlarr
|
||||
description: Indexers
|
||||
icon: hl-prowlarr
|
||||
url: http://prowlarr.s0
|
||||
url: https://prowlarr.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://prowlarr.s0
|
||||
statusCheck: false
|
||||
id: 4_1956_prowlarr
|
||||
- &ref_5
|
||||
title: Bazarr
|
||||
description: Subtitles
|
||||
icon: hl-bazarr
|
||||
url: http://s0:6767
|
||||
url: https://bazarr.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://bazarr.s0
|
||||
statusCheck: false
|
||||
id: 5_1956_bazarr
|
||||
- &ref_6
|
||||
title: Navidrome
|
||||
description: Play Music
|
||||
icon: hl-navidrome
|
||||
url: http://s0:4534
|
||||
url: https://music.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://music.s0
|
||||
statusCheck: false
|
||||
id: 6_1956_navidrome
|
||||
- &ref_7
|
||||
title: Transmission
|
||||
description: Torrenting
|
||||
icon: hl-transmission
|
||||
url: http://s0:9091
|
||||
url: https://transmission.s0.neet.dev
|
||||
target: sametab
|
||||
statusCheck: true
|
||||
statusCheckUrl: http://transmission.s0
|
||||
statusCheck: false
|
||||
id: 7_1956_transmission
|
||||
filteredItems:
|
||||
- *ref_0
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
./frigate.nix
|
||||
./home-automation.nix
|
||||
];
|
||||
|
||||
@@ -165,61 +166,96 @@
|
||||
};
|
||||
|
||||
# nginx
|
||||
services.nginx.enable = true;
|
||||
services.nginx.virtualHosts."bazarr.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 6767; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://vpn.containers:6767";
|
||||
};
|
||||
services.nginx.virtualHosts."radarr.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 7878; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://vpn.containers:7878";
|
||||
};
|
||||
services.nginx.virtualHosts."lidarr.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 8686; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://vpn.containers:8686";
|
||||
};
|
||||
services.nginx.virtualHosts."sonarr.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 8989; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://vpn.containers:8989";
|
||||
};
|
||||
services.nginx.virtualHosts."prowlarr.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 9696; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://vpn.containers:9696";
|
||||
};
|
||||
services.nginx.virtualHosts."music.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 4534; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/".proxyPass = "http://localhost:4533";
|
||||
};
|
||||
services.nginx.virtualHosts."jellyfin.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 8097; } { addr = "0.0.0.0"; port = 80; }];
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
openFirewall = false; # All nginx services are internal
|
||||
virtualHosts =
|
||||
let
|
||||
mkVirtualHost = external: internal:
|
||||
{
|
||||
${external} = {
|
||||
useACMEHost = "s0.neet.dev"; # Use wildcard cert
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://localhost:8096";
|
||||
proxyPass = internal;
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
services.nginx.virtualHosts."jellyfin.neet.cloud".locations."/" = {
|
||||
proxyPass = "http://localhost:8096";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
services.nginx.virtualHosts."transmission.s0" = {
|
||||
listen = [{ addr = "0.0.0.0"; port = 9091; } { addr = "0.0.0.0"; port = 80; }];
|
||||
locations."/" = {
|
||||
proxyPass = "http://vpn.containers:9091";
|
||||
proxyWebsockets = true;
|
||||
in
|
||||
lib.mkMerge [
|
||||
(mkVirtualHost "bazarr.s0.neet.dev" "http://vpn.containers:6767")
|
||||
(mkVirtualHost "radarr.s0.neet.dev" "http://vpn.containers:7878")
|
||||
(mkVirtualHost "lidarr.s0.neet.dev" "http://vpn.containers:8686")
|
||||
(mkVirtualHost "sonarr.s0.neet.dev" "http://vpn.containers:8989")
|
||||
(mkVirtualHost "prowlarr.s0.neet.dev" "http://vpn.containers:9696")
|
||||
(mkVirtualHost "transmission.s0.neet.dev" "http://vpn.containers:9091")
|
||||
(mkVirtualHost "unifi.s0.neet.dev" "https://localhost:8443")
|
||||
(mkVirtualHost "music.s0.neet.dev" "http://localhost:4533")
|
||||
(mkVirtualHost "jellyfin.s0.neet.dev" "http://localhost:8096")
|
||||
(mkVirtualHost "s0.neet.dev" "http://localhost:56815")
|
||||
(mkVirtualHost "ha.s0.neet.dev" "http://localhost:8123") # home assistant
|
||||
(mkVirtualHost "esphome.s0.neet.dev" "http://localhost:6052")
|
||||
(mkVirtualHost "zigbee.s0.neet.dev" "http://localhost:55834")
|
||||
{
|
||||
# Landing page LAN redirect
|
||||
"s0" = {
|
||||
default = true;
|
||||
redirectCode = 302;
|
||||
globalRedirect = "s0.neet.dev";
|
||||
};
|
||||
"frigate.s0.neet.dev" = {
|
||||
# Just configure SSL, frigate module configures the rest of nginx
|
||||
useACMEHost = "s0.neet.dev";
|
||||
forceSSL = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# Problem #1: Keeping certain programs from being accessed from certain external networks/VLANs
|
||||
# Solution #1: Isolate that service in a container system that automatically fowards the ports to the right network interface(s)
|
||||
# Solution #2: Don't open the firewall for these services, manually open the ports instead for the specific network interface(s) (trickier and easy to miss ports or ports can change)
|
||||
# Untrusted network list:
|
||||
# - VLANs [cameras]
|
||||
|
||||
# Problem #2: Untrusted internal services. Prevent them from accessing certain internal services (usually key unauth'd services like frigate)
|
||||
# Solution #1: Isolate the untrusted services into their own container
|
||||
# Untrusted services list:
|
||||
# - Unifi? (it already has access to the cameras anyway?)
|
||||
# - torrenting, *arr (worried about vulns)
|
||||
|
||||
|
||||
tailscaleAuth = {
|
||||
enable = true;
|
||||
virtualHosts = [
|
||||
"bazarr.s0.neet.dev"
|
||||
"radarr.s0.neet.dev"
|
||||
"lidarr.s0.neet.dev"
|
||||
"sonarr.s0.neet.dev"
|
||||
"prowlarr.s0.neet.dev"
|
||||
"transmission.s0.neet.dev"
|
||||
"unifi.s0.neet.dev"
|
||||
# "music.s0.neet.dev" # messes up navidrome
|
||||
"jellyfin.s0.neet.dev"
|
||||
"s0.neet.dev"
|
||||
# "ha.s0.neet.dev" # messes up home assistant
|
||||
"esphome.s0.neet.dev"
|
||||
"zigbee.s0.neet.dev"
|
||||
];
|
||||
expectedTailnet = "koi-bebop.ts.net";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
6767
|
||||
7878
|
||||
8686
|
||||
8989
|
||||
9696
|
||||
4534
|
||||
8097
|
||||
9091
|
||||
8443 # unifi
|
||||
];
|
||||
# Get wildcard cert
|
||||
security.acme.certs."s0.neet.dev" = {
|
||||
dnsProvider = "digitalocean";
|
||||
credentialsFile = "/run/agenix/digitalocean-dns-credentials";
|
||||
extraDomainNames = [ "*.s0.neet.dev" ];
|
||||
group = "nginx";
|
||||
dnsResolver = "1.1.1.1:53";
|
||||
dnsPropagationCheck = false; # sadly this erroneously fails
|
||||
};
|
||||
age.secrets.digitalocean-dns-credentials.file = ../../../secrets/digitalocean-dns-credentials.age;
|
||||
|
||||
virtualisation.oci-containers.backend = "podman";
|
||||
virtualisation.podman.dockerSocket.enable = true; # TODO needed?
|
||||
@@ -230,8 +266,7 @@
|
||||
|
||||
services.unifi = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
unifiPackage = pkgs.unifi8;
|
||||
openMinimalFirewall = true;
|
||||
};
|
||||
|
||||
boot.binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ];
|
||||
|
||||
155
machines/storage/s0/frigate.nix
Normal file
155
machines/storage/s0/frigate.nix
Normal file
@@ -0,0 +1,155 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
frigateHostname = "frigate.s0.neet.dev";
|
||||
|
||||
mkGo2RtcStream = name: url: withAudio: {
|
||||
${name} = [
|
||||
url
|
||||
"ffmpeg:${name}#video=copy${if withAudio then "#audio=copy" else ""}"
|
||||
];
|
||||
};
|
||||
|
||||
# Assumes camera is set to output:
|
||||
# - rtsp
|
||||
# - H.264 + AAC
|
||||
# - a downscaled substream for detection
|
||||
mkCamera = name: primaryUrl: detectUrl: {
|
||||
# Reference https://docs.frigate.video/configuration/reference/
|
||||
services.frigate.settings = {
|
||||
cameras.${name} = {
|
||||
ffmpeg = {
|
||||
# Camera feeds are relayed through go2rtc
|
||||
inputs = [
|
||||
{
|
||||
path = "rtsp://127.0.0.1:8554/${name}";
|
||||
# input_args = "preset-rtsp-restream";
|
||||
input_args = "preset-rtsp-restream-low-latency";
|
||||
roles = [ "record" ];
|
||||
}
|
||||
{
|
||||
path = detectUrl;
|
||||
roles = [ "detect" ];
|
||||
}
|
||||
];
|
||||
output_args = {
|
||||
record = "preset-record-generic-audio-copy";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
services.go2rtc.settings.streams = lib.mkMerge [
|
||||
(mkGo2RtcStream name primaryUrl false)
|
||||
|
||||
# Sadly having the detection stream go through go2rpc too makes the stream unreadable by frigate for some reason.
|
||||
# It might need to be re-encoded to work. But I am not interested in wasting the processing power if only frigate
|
||||
# need the detection stream anyway. So just let frigate grab the stream directly since it works.
|
||||
# (mkGo2RtcStream detectName detectUrl false)
|
||||
];
|
||||
};
|
||||
|
||||
mkDahuaCamera = name: address:
|
||||
let
|
||||
# go2rtc and frigate have a slightly different syntax for inserting env vars. So the URLs are not interchangable :(
|
||||
# - go2rtc: ${VAR}
|
||||
# - frigate: {VAR}
|
||||
primaryUrl = "rtsp://admin:\${FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=0";
|
||||
detectUrl = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@${address}/cam/realmonitor?channel=1&subtype=1";
|
||||
in
|
||||
mkCamera name primaryUrl detectUrl;
|
||||
|
||||
mkEsp32Camera = name: address: {
|
||||
services.frigate.settings.cameras.${name} = {
|
||||
ffmpeg = {
|
||||
input_args = "";
|
||||
inputs = [{
|
||||
path = "http://${address}:8080";
|
||||
roles = [ "detect" "record" ];
|
||||
}];
|
||||
|
||||
output_args.record = "-f segment -pix_fmt yuv420p -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c:v libx264 -preset ultrafast -an ";
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mkMerge [
|
||||
(mkDahuaCamera "dog-cam" "192.168.10.31")
|
||||
# (mkEsp32Camera "dahlia-cam" "dahlia-cam.lan")
|
||||
{
|
||||
services.frigate = {
|
||||
enable = true;
|
||||
hostname = frigateHostname;
|
||||
settings = {
|
||||
mqtt = {
|
||||
enabled = true;
|
||||
host = "localhost:1883";
|
||||
};
|
||||
rtmp.enabled = false;
|
||||
snapshots = {
|
||||
enabled = true;
|
||||
bounding_box = true;
|
||||
};
|
||||
record = {
|
||||
enabled = true;
|
||||
# sync_recordings = true; # detect if recordings were deleted outside of frigate (expensive)
|
||||
retain = {
|
||||
days = 2; # Keep video for 2 days
|
||||
mode = "motion";
|
||||
};
|
||||
events = {
|
||||
retain = {
|
||||
default = 10; # Keep video with detections for 10 days
|
||||
mode = "motion";
|
||||
# mode = "active_objects";
|
||||
};
|
||||
};
|
||||
};
|
||||
# Make frigate aware of the go2rtc streams
|
||||
go2rtc.streams = config.services.go2rtc.settings.streams;
|
||||
detect.enabled = true;
|
||||
objects = {
|
||||
track = [ "person" "dog" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.go2rtc = {
|
||||
enable = true;
|
||||
settings = {
|
||||
rtsp.listen = ":8554";
|
||||
webrtc.listen = ":8555";
|
||||
};
|
||||
};
|
||||
|
||||
# Pass in env file with secrets to frigate/go2rtc
|
||||
systemd.services.frigate.serviceConfig.EnvironmentFile = "/run/agenix/frigate-credentials";
|
||||
systemd.services.go2rtc.serviceConfig.EnvironmentFile = "/run/agenix/frigate-credentials";
|
||||
age.secrets.frigate-credentials.file = ../../../secrets/frigate-credentials.age;
|
||||
}
|
||||
{
|
||||
# hardware encode/decode with amdgpu vaapi
|
||||
systemd.services.frigate = {
|
||||
environment.LIBVA_DRIVER_NAME = "radeonsi";
|
||||
serviceConfig = {
|
||||
SupplementaryGroups = [ "render" "video" ]; # for access to dev/dri/*
|
||||
AmbientCapabilities = "CAP_PERFMON";
|
||||
};
|
||||
};
|
||||
services.frigate.settings.ffmpeg.hwaccel_args = "preset-vaapi";
|
||||
}
|
||||
{
|
||||
# Coral TPU for frigate
|
||||
services.udev.packages = [ pkgs.libedgetpu ];
|
||||
users.groups.apex = { };
|
||||
systemd.services.frigate.environment.LD_LIBRARY_PATH = "${pkgs.libedgetpu}/lib";
|
||||
systemd.services.frigate.serviceConfig.SupplementaryGroups = [ "apex" ];
|
||||
|
||||
# Coral PCIe driver
|
||||
kernel.enableGasketKernelModule = true;
|
||||
|
||||
services.frigate.settings.detectors.coral = {
|
||||
type = "edgetpu";
|
||||
device = "pci";
|
||||
};
|
||||
}
|
||||
]
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
# boot
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.systemd-boot.memtest86.enable = true;
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "uas" "sd_mod" "rtsx_pci_sdmmc" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
@@ -58,8 +59,16 @@
|
||||
};
|
||||
swapDevices = [ ];
|
||||
|
||||
networking.interfaces.eth0.useDHCP = true;
|
||||
networking.interfaces.eth1.useDHCP = true;
|
||||
networking.vlans = {
|
||||
default = {
|
||||
id = 1;
|
||||
interface = "eth1";
|
||||
};
|
||||
iot = {
|
||||
id = 2;
|
||||
interface = "eth1";
|
||||
};
|
||||
};
|
||||
|
||||
powerManagement.cpuFreqGovernor = "powersave";
|
||||
}
|
||||
|
||||
@@ -1,100 +1,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
frigateHostname = "frigate.s0";
|
||||
frigatePort = 61617;
|
||||
|
||||
mkEsp32Cam = address: {
|
||||
ffmpeg = {
|
||||
input_args = "";
|
||||
inputs = [{
|
||||
path = address;
|
||||
roles = [ "detect" "record" ];
|
||||
}];
|
||||
|
||||
output_args.record = "-f segment -pix_fmt yuv420p -segment_time 10 -segment_format mp4 -reset_timestamps 1 -strftime 1 -c:v libx264 -preset ultrafast -an ";
|
||||
};
|
||||
rtmp.enabled = false;
|
||||
snapshots = {
|
||||
enabled = true;
|
||||
bounding_box = true;
|
||||
};
|
||||
record = {
|
||||
enabled = true;
|
||||
retain.days = 10; # Keep video for 10 days
|
||||
events.retain = {
|
||||
default = 30; # Keep video with detections for 30 days
|
||||
mode = "active_objects";
|
||||
};
|
||||
};
|
||||
detect = {
|
||||
enabled = true;
|
||||
width = 800;
|
||||
height = 600;
|
||||
fps = 10;
|
||||
};
|
||||
objects = {
|
||||
track = [ "person" ];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# 1883 # mqtt
|
||||
55834 # mqtt zigbee frontend
|
||||
frigatePort
|
||||
4180 # oauth proxy
|
||||
];
|
||||
|
||||
services.frigate = {
|
||||
enable = true;
|
||||
hostname = frigateHostname;
|
||||
settings = {
|
||||
mqtt = {
|
||||
enabled = true;
|
||||
host = "localhost:1883";
|
||||
};
|
||||
cameras = {
|
||||
dahlia-cam = mkEsp32Cam "http://dahlia-cam.lan:8080";
|
||||
};
|
||||
# ffmpeg = {
|
||||
# hwaccel_args = "preset-vaapi";
|
||||
# };
|
||||
detectors.coral = {
|
||||
type = "edgetpu";
|
||||
device = "pci";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# AMD GPU for vaapi
|
||||
systemd.services.frigate.environment.LIBVA_DRIVER_NAME = "radeonsi";
|
||||
|
||||
# Coral TPU for frigate
|
||||
services.udev.packages = [ pkgs.libedgetpu ];
|
||||
users.groups.apex = { };
|
||||
systemd.services.frigate.environment.LD_LIBRARY_PATH = "${pkgs.libedgetpu}/lib";
|
||||
systemd.services.frigate.serviceConfig = {
|
||||
SupplementaryGroups = "apex";
|
||||
};
|
||||
# Coral PCIe driver
|
||||
kernel.enableGasketKernelModule = true;
|
||||
|
||||
# Allow accessing frigate UI on a specific port in addition to by hostname
|
||||
services.nginx.virtualHosts.${frigateHostname} = {
|
||||
listen = [{ addr = "0.0.0.0"; port = frigatePort; } { addr = "0.0.0.0"; port = 80; }];
|
||||
};
|
||||
|
||||
services.esphome = {
|
||||
enable = true;
|
||||
address = "0.0.0.0";
|
||||
openFirewall = true;
|
||||
};
|
||||
# TODO remove after upgrading nixos version
|
||||
systemd.services.esphome.serviceConfig.ProcSubset = lib.mkForce "all";
|
||||
systemd.services.esphome.serviceConfig.ProtectHostname = lib.mkForce false;
|
||||
systemd.services.esphome.serviceConfig.ProtectKernelLogs = lib.mkForce false;
|
||||
systemd.services.esphome.serviceConfig.ProtectKernelTunables = lib.mkForce false;
|
||||
services.esphome.enable = true;
|
||||
|
||||
# TODO lock down
|
||||
services.mosquitto = {
|
||||
@@ -121,7 +28,7 @@ in
|
||||
# base_topic = "zigbee2mqtt";
|
||||
};
|
||||
frontend = {
|
||||
host = "0.0.0.0";
|
||||
host = "localhost";
|
||||
port = 55834;
|
||||
};
|
||||
};
|
||||
@@ -129,60 +36,55 @@ in
|
||||
|
||||
services.home-assistant = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
configWritable = true;
|
||||
extraComponents = [
|
||||
"default_config"
|
||||
"esphome"
|
||||
"met"
|
||||
"radio_browser"
|
||||
"wled"
|
||||
"mqtt"
|
||||
"apple_tv" # why is this even needed? I get `ModuleNotFoundError: No module named 'pyatv'` errors otherwise for some reason.
|
||||
"unifi"
|
||||
"digital_ocean"
|
||||
"downloader"
|
||||
"mailgun"
|
||||
"minecraft_server"
|
||||
"mullvad"
|
||||
"nextcloud"
|
||||
"ollama"
|
||||
"openweathermap"
|
||||
"jellyfin"
|
||||
"transmission"
|
||||
"radarr"
|
||||
"sonarr"
|
||||
"syncthing"
|
||||
"tailscale"
|
||||
"weather"
|
||||
"whois"
|
||||
"youtube"
|
||||
"homekit_controller"
|
||||
"zha"
|
||||
"bluetooth"
|
||||
];
|
||||
# config = null;
|
||||
config = {
|
||||
# Includes dependencies for a basic setup
|
||||
# https://www.home-assistant.io/integrations/default_config/
|
||||
default_config = { };
|
||||
};
|
||||
|
||||
# Enable reverse proxy support
|
||||
http = {
|
||||
use_x_forwarded_for = true;
|
||||
trusted_proxies = [
|
||||
"127.0.0.1"
|
||||
"::1"
|
||||
];
|
||||
};
|
||||
|
||||
# TODO need services.oauth2-proxy.cookie.domain ?
|
||||
services.oauth2-proxy =
|
||||
let
|
||||
nextcloudServer = "https://neet.cloud/";
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
|
||||
httpAddress = "http://0.0.0.0:4180";
|
||||
|
||||
nginx.domain = frigateHostname;
|
||||
# nginx.virtualHosts = [
|
||||
# frigateHostname
|
||||
# ];
|
||||
|
||||
email.domains = [ "*" ];
|
||||
|
||||
cookie.secure = false;
|
||||
|
||||
provider = "nextcloud";
|
||||
|
||||
# redirectURL = "http://s0:4180/oauth2/callback"; # todo forward with nginx?
|
||||
clientID = "4FfhEB2DNzUh6wWhXTjqQQKu3Ibm6TeYpS8TqcHe55PJC1DorE7vBZBELMKDjJ0X";
|
||||
keyFile = "/run/agenix/oauth2-proxy-env";
|
||||
|
||||
loginURL = "${nextcloudServer}/index.php/apps/oauth2/authorize";
|
||||
redeemURL = "${nextcloudServer}/index.php/apps/oauth2/api/v1/token";
|
||||
validateURL = "${nextcloudServer}/ocs/v2.php/cloud/user?format=json";
|
||||
|
||||
# todo --cookie-refresh
|
||||
|
||||
extraConfig = {
|
||||
# cookie-csrf-per-request = true;
|
||||
# cookie-csrf-expire = "5m";
|
||||
# user-id-claim = "preferred_username";
|
||||
"automation manual" = [
|
||||
];
|
||||
# Allow using automations generated from the UI
|
||||
"automation ui" = "!include automations.yaml";
|
||||
};
|
||||
};
|
||||
|
||||
age.secrets.oauth2-proxy-env.file = ../../../secrets/oauth2-proxy-env.age;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"pia"
|
||||
"binary-cache"
|
||||
"gitea-actions-runner"
|
||||
"frigate"
|
||||
];
|
||||
|
||||
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwiXcUFtAvZCayhu4+AIcF+Ktrdgv9ee/mXSIhJbp4q";
|
||||
|
||||
11
secrets/digitalocean-dns-credentials.age
Normal file
11
secrets/digitalocean-dns-credentials.age
Normal file
@@ -0,0 +1,11 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 WBT1Hw wjZGPvilRXGZsC2+7dWm/Nbau8Allv29WwQCr0XSAWU
|
||||
uTOf/sokutOGDyc8fbTbBWXqCVQCFhGdHxwA6SXqhdA
|
||||
-> ssh-ed25519 6AT2/g NU068qwqOWiKk0QwqP9vU4xJaND2OR4bo8xkmdWATgY
|
||||
uGd0sb5PH+rREn9pgLOFwk29CX66aPBQMvr4rBazylc
|
||||
-> ssh-ed25519 hPp1nw r2JRiZ7fsHPYDlte6Oh2Gx1KkugekFeeg3xSjziI+hQ
|
||||
xnO0gscMdR25mj5uAX7D42FCbCQhqbU0wkiLX4OmVqk
|
||||
-> ssh-ed25519 w3nu8g F03mPU63WwEs1SLUFErLOVCkARoggGIvvz9TFZfMOBY
|
||||
HOdVA3xW9pqUPhclO6VueSfXg3ux06Ch3fucF6Vr4hM
|
||||
--- niyo231HPT/+2dzflP+zhYjL9XiWsk7svesCYdkU1jA
|
||||
DÑØQî¬5–-ô@<40>¢¿—ßÐN5<4E> Ãÿ$Ø‚™’Çž…êÐ<C3AA>X=ŒHŽDÁ`P×5ZA´÷¼YóäÓ?¡é^[³1”6ÕK*mP݈ªæ1æç÷ß›ƒ:$^ÑfDœ*î†ÿ“š-zi´"·Tàuÿüò
|
||||
7
secrets/frigate-credentials.age
Normal file
7
secrets/frigate-credentials.age
Normal file
@@ -0,0 +1,7 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 hPp1nw Chke1ZtpXxN1c1+AnJ6Cd5kpM1KfQKTwymrfPW53QCA
|
||||
jUcw8eitC7r0rwefjllndZjARIqpWoVqGCnefHfjQ6Y
|
||||
-> ssh-ed25519 w3nu8g KY/5bU1B5uvmfGHF2d6qBL1NYy64qo324rdvkgnXoDA
|
||||
OBvuFtzZXQ0RmmEXelyzHMMiVqZir7zQJMA36ZH2siE
|
||||
--- CSd7lYSYQ2fCTjkJLPGdaNGL8eVpE9IBEyFo0LW907M
|
||||
£³$šO†ÈIß/À//Êw*ƒ™õD¤@u5o[¼â:·äš¥t¾˜]Jñ쮸™@Ùhþu£Àk;?·XüÁHRº’ѰE5¥ÍçÜ9
|
||||
@@ -51,4 +51,10 @@ with roles;
|
||||
|
||||
# Librechat
|
||||
"librechat-env-file.age".publicKeys = librechat;
|
||||
|
||||
# For ACME DNS Challenge
|
||||
"digitalocean-dns-credentials.age".publicKeys = server;
|
||||
|
||||
# Frigate (DVR)
|
||||
"frigate-credentials.age".publicKeys = frigate;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user