Compare commits

..

13 commits

Author SHA1 Message Date
ac95d1c23d disabled until hwacc 2026-03-14 01:17:58 -04:00
87687d6170 doorbell 2026-03-14 00:52:49 -04:00
247b8e2066 added cam 2026-03-14 00:17:49 -04:00
95f2454465 disable btc 2026-03-14 00:02:16 -04:00
60d4e53a6f upd 2026-03-13 23:56:01 -04:00
b36c53fdea frigate configs 2026-03-13 23:53:31 -04:00
a85f993041 thing 2026-03-13 23:00:41 -04:00
6ded432df0 vaapi driver 2026-03-13 22:56:19 -04:00
ceed49531b hwacc 2026-03-13 22:49:23 -04:00
071f0fdca0 disable 2026-03-13 22:40:46 -04:00
1feef552da one 2026-03-13 22:32:16 -04:00
1cfda9c67b dhcp for cams 2026-03-13 22:10:59 -04:00
fbbdba1e4b cam net 2026-03-13 21:54:26 -04:00
5 changed files with 196 additions and 15 deletions

View file

@ -1,4 +1,4 @@
{ config, lib, modulesPath, ... }:
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [
@ -17,6 +17,14 @@
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Enable VAAPI for hardware video acceleration
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
intel-vaapi-driver # i965 driver for Haswell
];
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
}

View file

@ -7,13 +7,13 @@
modules.system = {
nginx.enable = true;
forgejo.enable = true;
frigate.enable = false;
frigate.enable = true;
immich.enable = true;
bitcoin = {
enable = true;
electrum.enable = true;
clightning.enable = true;
};
# bitcoin = {
# enable = true;
# electrum.enable = true;
# clightning.enable = true;
# };
backup = {
enable = true;
@ -120,6 +120,19 @@
firewall = {
enable = true;
allowedTCPPorts = [ 22 ];
allowedUDPPorts = [ 53 67 ]; # DNS + DHCP
extraCommands = ''
# Block specific camera MACs from forwarding (instant DROP, no timeouts)
# Add each camera MAC here as you set them up
iptables -A FORWARD -m mac --mac-source 00:1f:54:c2:d1:b1 -j DROP # parking_lot
iptables -A FORWARD -m mac --mac-source 00:1f:54:b2:9b:1d -j DROP # living_room/kitchen
iptables -A FORWARD -m mac --mac-source 00:1f:54:a9:81:d1 -j DROP # doorbell
'';
extraStopCommands = ''
iptables -D FORWARD -m mac --mac-source 00:1f:54:c2:d1:b1 -j DROP || true
iptables -D FORWARD -m mac --mac-source 00:1f:54:b2:9b:1d -j DROP || true
iptables -D FORWARD -m mac --mac-source 00:1f:54:a9:81:d1 -j DROP || true
'';
};
};
@ -143,11 +156,15 @@
interface = "enp2s0f1";
bind-interfaces = true;
dhcp-range = "192.168.1.100,192.168.1.200,24h";
# No gateway option = cameras can't route to internet
};
};
networking.firewall.allowedUDPPorts = [ 53 ];
# Static DHCP reservations for cameras
dhcp-host = [
"00:1f:54:c2:d1:b1,192.168.1.194,parking_lot"
"00:1f:54:b2:9b:1d,192.168.1.147,living_room_kitchen"
"00:1f:54:a9:81:d1,192.168.1.167,doorbell"
];
};
};
services.fail2ban = {
enable = true;

View file

@ -9,6 +9,7 @@ let
# Convert absolute paths to relative for tar, preserving structure
# e.g., /var/lib/forgejo -> var/lib/forgejo
tarPaths = map (p: removePrefix "/" p) cfg.paths;
excludeArgs = concatMapStrings (e: "--exclude='${e}' ") cfg.exclude;
backupScript = pkgs.writeShellScript "backup" ''
set -euo pipefail
@ -22,7 +23,7 @@ let
echo "Paths: ${concatStringsSep " " cfg.paths}"
export PATH="${pkgs.age-plugin-yubikey}/bin:$PATH"
${pkgs.gnutar}/bin/tar -C / -cf - ${concatStringsSep " " tarPaths} | \
${pkgs.gnutar}/bin/tar -C / ${excludeArgs}-cf - ${concatStringsSep " " tarPaths} | \
${pkgs.age}/bin/age ${recipientArgs} -o "$TEMP_DIR/$BACKUP_NAME"
${pkgs.rclone}/bin/rclone --config /root/.config/rclone/rclone.conf copy "$TEMP_DIR/$BACKUP_NAME" "${cfg.destination}"
@ -49,6 +50,12 @@ in
description = "Absolute paths to include in backup (structure preserved)";
};
exclude = mkOption {
type = types.listOf types.str;
default = [];
description = "Patterns to exclude (passed to tar --exclude)";
};
recipients = mkOption {
type = types.listOf types.str;
default = [];

View file

@ -0,0 +1,119 @@
# Frigate Camera Setup
## Camera Models
| Camera | Model | MAC | IP |
|--------|-------|-----|-----|
| parking_lot | W461ASC | 00:1f:54:c2:d1:b1 | 192.168.1.194 |
| doorbell | B463AJ | 00:1f:54:a9:81:d1 | 192.168.1.167 |
| living_room | W463AQ (ch1) | 00:1f:54:b2:9b:1d | 192.168.1.147 |
| kitchen | W463AQ (ch2) | 00:1f:54:b2:9b:1d | 192.168.1.147 |
| porch | SL300 | | | |
## Network Architecture
- Camera network: 192.168.1.0/24 (isolated, no internet)
- Server NIC: enp2s0f1 @ 192.168.1.1
- WiFi AP: TP-Link RE315 @ 192.168.1.254
- DHCP range: 192.168.1.100-200
## RTSP URL Format
```
rtsp://admin:ocu?u3Su@<IP>/cam/realmonitor?channel=<CH>&subtype=0
```
- channel=1 for single-camera devices
- channel=1,2 for dual-camera devices (W463AQ)
- subtype=0 for main stream, subtype=1 for sub stream
## Camera Reset Procedures
### W461ASC (parking_lot)
1. Keep camera powered on
2. Reset button is on the back of the camera
3. Press and hold reset button for 30-60 seconds until chime sounds
### B463AJ (doorbell)
1. Remove doorbell from mount
2. Locate reset button on the back
3. Press and hold until you hear chime reset sound
4. Reconnect via Lorex app as new device
### W463AQ (living_room/kitchen)
1. Keep camera powered on
2. Rotate the lens upwards to reveal hidden reset button
3. Press and hold reset button until you hear audio prompt
4. Flashing green Smart Security Lighting confirms reset
5. Solid green = not fully reset, repeat if needed
### SL300 (porch)
1. Keep camera powered on
2. Tilt camera lens upwards to reveal reset/microSD card cover
3. Remove the cover
4. Press and hold reset button until audio prompt
5. Replace cover quickly
6. Wait for green LED flash + audio confirmation
## Initial Setup
1. Temporarily enable internet for camera network:
```bash
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o enp2s0f0 -j MASQUERADE
sudo sysctl -w net.ipv4.ip_forward=1
```
2. Connect camera to "cams" WiFi network
3. Use Lorex app to configure camera (requires cloud - CCP middleman)
4. Get camera MAC from DHCP leases:
```bash
cat /var/lib/dnsmasq/dnsmasq.leases
```
5. Add DHCP reservation in `system.nix`:
```nix
dhcp-host = [
"aa:bb:cc:dd:ee:ff,192.168.1.XXX,camera_name"
];
```
6. Add MAC to firewall block list in `system.nix`:
```nix
iptables -A FORWARD -m mac --mac-source aa:bb:cc:dd:ee:ff -j DROP
```
7. Update camera IP in `frigate/default.nix` and enable
8. Deploy and disable internet:
```bash
nixos-rebuild switch --flake .#server --target-host server
sudo iptables -t nat -D POSTROUTING -s 192.168.1.0/24 -o enp2s0f0 -j MASQUERADE
sudo sysctl -w net.ipv4.ip_forward=0
```
## Storage
Frigate data is stored on /data to avoid filling root partition:
| Path | Bind Mount | Contents |
|------|------------|----------|
| /var/lib/frigate | /data/frigate/lib | Database, recordings, clips |
| /var/cache/frigate | /data/frigate/cache | Temporary cache |
| /var/cache/nginx/frigate | /data/frigate/nginx-cache | API response cache |
## Notes
- Lorex cameras are cloud-only for configuration (no local web UI responds)
- RTSP works locally without internet
- Cameras phone home aggressively when internet is available - keep isolated
- Haswell CPU cannot hardware decode HEVC - using CPU decode
- Consider T400 GPU for hardware acceleration if scaling to more cameras
## Port Scan Results (W461ASC)
- 80/tcp - HTTP (non-responsive, proprietary)
- 554/tcp - RTSP (working)
- 8086/tcp - Proprietary
- 35000/tcp - Proprietary

View file

@ -16,38 +16,46 @@ in
services.frigate = {
enable = true;
hostname = "frigate.${domain}";
# vaapiDriver = "i965"; # Haswell only supports H.264, not HEVC
settings = {
mqtt.enabled = false;
# ffmpeg.hwaccel_args = "preset-vaapi"; # Disabled - camera uses HEVC which Haswell can't decode
record.enabled = true;
cameras = {
doorbell = {
enabled = true;
detect.enabled = false;
ffmpeg.inputs = [{
path = "rtsp://admin:ocu?u3Su@192.168.0.134/cam/realmonitor?channel=1&subtype=0";
path = "rtsp://admin:ocu?u3Su@192.168.1.167/cam/realmonitor?channel=1&subtype=0";
roles = [ "record" ];
}];
};
living_room = {
enabled = false;
detect.enabled = false;
ffmpeg.inputs = [{
path = "rtsp://admin:ocu?u3Su@192.168.0.181/cam/realmonitor?channel=1&subtype=0";
path = "rtsp://admin:ocu?u3Su@192.168.1.147/cam/realmonitor?channel=1&subtype=0";
roles = [ "record" ];
}];
};
kitchen = {
enabled = false;
detect.enabled = false;
ffmpeg.inputs = [{
path = "rtsp://admin:ocu?u3Su@192.168.0.181/cam/realmonitor?channel=2&subtype=0";
path = "rtsp://admin:ocu?u3Su@192.168.1.147/cam/realmonitor?channel=2&subtype=0";
roles = [ "record" ];
}];
};
parking_lot = {
enabled = true;
detect.enabled = false;
ffmpeg.inputs = [{
path = "rtsp://admin:ocu?u3Su@192.168.0.59/cam/realmonitor?channel=1&subtype=0";
path = "rtsp://admin:ocu?u3Su@192.168.1.194/cam/realmonitor?channel=1&subtype=0";
roles = [ "record" ];
}];
};
porch = {
enabled = false;
detect.enabled = false;
ffmpeg.inputs = [{
path = "rtsp://admin:ocu?u3Su@192.168.0.43/cam/realmonitor?channel=1&subtype=0";
@ -64,5 +72,27 @@ in
forceSSL = true;
};
# Bind mount caches into the 3TB frigate LVM volume
systemd.tmpfiles.rules = [
"d /var/lib/frigate/cache 0750 frigate frigate -"
"d /var/lib/frigate/nginx-cache 0750 nginx nginx -"
];
fileSystems."/var/cache/frigate" = {
device = "/var/lib/frigate/cache";
options = [ "bind" ];
};
fileSystems."/var/cache/nginx/frigate" = {
device = "/var/lib/frigate/nginx-cache";
options = [ "bind" ];
};
# Backup recordings/database, exclude caches
modules.system.backup = {
paths = [ "/var/lib/frigate" ];
exclude = [ "*/cache" "*/nginx-cache" ];
};
};
}