diff --git a/src/system/machines/server/system.nix b/src/system/machines/server/system.nix index 30777dd..6420a4e 100644 --- a/src/system/machines/server/system.nix +++ b/src/system/machines/server/system.nix @@ -121,6 +121,14 @@ 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 + ''; + extraStopCommands = '' + iptables -D FORWARD -m mac --mac-source 00:1f:54:c2:d1:b1 -j DROP || true + ''; }; }; diff --git a/src/system/modules/backup/default.nix b/src/system/modules/backup/default.nix index 07a3895..511b332 100644 --- a/src/system/modules/backup/default.nix +++ b/src/system/modules/backup/default.nix @@ -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 = []; diff --git a/src/system/modules/frigate/README.md b/src/system/modules/frigate/README.md new file mode 100644 index 0000000..24cb6fa --- /dev/null +++ b/src/system/modules/frigate/README.md @@ -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 | | | | +| living_room | W463AQ | | | | +| kitchen | W463AQ | | | | +| 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@/cam/realmonitor?channel=&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 diff --git a/src/system/modules/frigate/default.nix b/src/system/modules/frigate/default.nix index 5b5df16..955338a 100644 --- a/src/system/modules/frigate/default.nix +++ b/src/system/modules/frigate/default.nix @@ -71,5 +71,34 @@ in forceSSL = true; }; + # Store frigate data on /data instead of root + systemd.tmpfiles.rules = [ + "d /data/frigate 0750 frigate frigate -" + "d /data/frigate/lib 0750 frigate frigate -" + "d /data/frigate/cache 0750 frigate frigate -" + "d /data/frigate/nginx-cache 0750 nginx nginx -" + ]; + + fileSystems."/var/lib/frigate" = { + device = "/data/frigate/lib"; + options = [ "bind" ]; + }; + + fileSystems."/var/cache/frigate" = { + device = "/data/frigate/cache"; + options = [ "bind" ]; + }; + + fileSystems."/var/cache/nginx/frigate" = { + device = "/data/frigate/nginx-cache"; + options = [ "bind" ]; + }; + + # Backup recordings/database, exclude caches + modules.system.backup = { + paths = [ "/data/frigate" ]; + exclude = [ "*/cache" "*/nginx-cache" ]; + }; + }; }