mirror of
https://github.com/itme-brain/nixos.git
synced 2026-03-23 16:29:42 -04:00
Compare commits
No commits in common. "ac95d1c23d75dd095083e4b914562279b67bf140" and "887dcaf16ffc7350d8261bd211405f1e02e3b009" have entirely different histories.
ac95d1c23d
...
887dcaf16f
5 changed files with 15 additions and 196 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
{ config, lib, pkgs, modulesPath, ... }:
|
{ config, lib, modulesPath, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
|
@ -17,14 +17,6 @@
|
||||||
|
|
||||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
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";
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@
|
||||||
modules.system = {
|
modules.system = {
|
||||||
nginx.enable = true;
|
nginx.enable = true;
|
||||||
forgejo.enable = true;
|
forgejo.enable = true;
|
||||||
frigate.enable = true;
|
frigate.enable = false;
|
||||||
immich.enable = true;
|
immich.enable = true;
|
||||||
# bitcoin = {
|
bitcoin = {
|
||||||
# enable = true;
|
enable = true;
|
||||||
# electrum.enable = true;
|
electrum.enable = true;
|
||||||
# clightning.enable = true;
|
clightning.enable = true;
|
||||||
# };
|
};
|
||||||
|
|
||||||
backup = {
|
backup = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
@ -120,19 +120,6 @@
|
||||||
firewall = {
|
firewall = {
|
||||||
enable = true;
|
enable = true;
|
||||||
allowedTCPPorts = [ 22 ];
|
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
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -156,16 +143,12 @@
|
||||||
interface = "enp2s0f1";
|
interface = "enp2s0f1";
|
||||||
bind-interfaces = true;
|
bind-interfaces = true;
|
||||||
dhcp-range = "192.168.1.100,192.168.1.200,24h";
|
dhcp-range = "192.168.1.100,192.168.1.200,24h";
|
||||||
|
# No gateway option = cameras can't route to internet
|
||||||
# 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"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||||
|
|
||||||
services.fail2ban = {
|
services.fail2ban = {
|
||||||
enable = true;
|
enable = true;
|
||||||
maxretry = 5;
|
maxretry = 5;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ let
|
||||||
# Convert absolute paths to relative for tar, preserving structure
|
# Convert absolute paths to relative for tar, preserving structure
|
||||||
# e.g., /var/lib/forgejo -> var/lib/forgejo
|
# e.g., /var/lib/forgejo -> var/lib/forgejo
|
||||||
tarPaths = map (p: removePrefix "/" p) cfg.paths;
|
tarPaths = map (p: removePrefix "/" p) cfg.paths;
|
||||||
excludeArgs = concatMapStrings (e: "--exclude='${e}' ") cfg.exclude;
|
|
||||||
|
|
||||||
backupScript = pkgs.writeShellScript "backup" ''
|
backupScript = pkgs.writeShellScript "backup" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
@ -23,7 +22,7 @@ let
|
||||||
echo "Paths: ${concatStringsSep " " cfg.paths}"
|
echo "Paths: ${concatStringsSep " " cfg.paths}"
|
||||||
|
|
||||||
export PATH="${pkgs.age-plugin-yubikey}/bin:$PATH"
|
export PATH="${pkgs.age-plugin-yubikey}/bin:$PATH"
|
||||||
${pkgs.gnutar}/bin/tar -C / ${excludeArgs}-cf - ${concatStringsSep " " tarPaths} | \
|
${pkgs.gnutar}/bin/tar -C / -cf - ${concatStringsSep " " tarPaths} | \
|
||||||
${pkgs.age}/bin/age ${recipientArgs} -o "$TEMP_DIR/$BACKUP_NAME"
|
${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}"
|
${pkgs.rclone}/bin/rclone --config /root/.config/rclone/rclone.conf copy "$TEMP_DIR/$BACKUP_NAME" "${cfg.destination}"
|
||||||
|
|
@ -50,12 +49,6 @@ in
|
||||||
description = "Absolute paths to include in backup (structure preserved)";
|
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 {
|
recipients = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [];
|
||||||
|
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
@ -16,46 +16,38 @@ in
|
||||||
services.frigate = {
|
services.frigate = {
|
||||||
enable = true;
|
enable = true;
|
||||||
hostname = "frigate.${domain}";
|
hostname = "frigate.${domain}";
|
||||||
# vaapiDriver = "i965"; # Haswell only supports H.264, not HEVC
|
|
||||||
settings = {
|
settings = {
|
||||||
mqtt.enabled = false;
|
mqtt.enabled = false;
|
||||||
# ffmpeg.hwaccel_args = "preset-vaapi"; # Disabled - camera uses HEVC which Haswell can't decode
|
|
||||||
record.enabled = true;
|
|
||||||
cameras = {
|
cameras = {
|
||||||
doorbell = {
|
doorbell = {
|
||||||
enabled = true;
|
|
||||||
detect.enabled = false;
|
detect.enabled = false;
|
||||||
ffmpeg.inputs = [{
|
ffmpeg.inputs = [{
|
||||||
path = "rtsp://admin:ocu?u3Su@192.168.1.167/cam/realmonitor?channel=1&subtype=0";
|
path = "rtsp://admin:ocu?u3Su@192.168.0.134/cam/realmonitor?channel=1&subtype=0";
|
||||||
roles = [ "record" ];
|
roles = [ "record" ];
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
living_room = {
|
living_room = {
|
||||||
enabled = false;
|
|
||||||
detect.enabled = false;
|
detect.enabled = false;
|
||||||
ffmpeg.inputs = [{
|
ffmpeg.inputs = [{
|
||||||
path = "rtsp://admin:ocu?u3Su@192.168.1.147/cam/realmonitor?channel=1&subtype=0";
|
path = "rtsp://admin:ocu?u3Su@192.168.0.181/cam/realmonitor?channel=1&subtype=0";
|
||||||
roles = [ "record" ];
|
roles = [ "record" ];
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
kitchen = {
|
kitchen = {
|
||||||
enabled = false;
|
|
||||||
detect.enabled = false;
|
detect.enabled = false;
|
||||||
ffmpeg.inputs = [{
|
ffmpeg.inputs = [{
|
||||||
path = "rtsp://admin:ocu?u3Su@192.168.1.147/cam/realmonitor?channel=2&subtype=0";
|
path = "rtsp://admin:ocu?u3Su@192.168.0.181/cam/realmonitor?channel=2&subtype=0";
|
||||||
roles = [ "record" ];
|
roles = [ "record" ];
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
parking_lot = {
|
parking_lot = {
|
||||||
enabled = true;
|
|
||||||
detect.enabled = false;
|
detect.enabled = false;
|
||||||
ffmpeg.inputs = [{
|
ffmpeg.inputs = [{
|
||||||
path = "rtsp://admin:ocu?u3Su@192.168.1.194/cam/realmonitor?channel=1&subtype=0";
|
path = "rtsp://admin:ocu?u3Su@192.168.0.59/cam/realmonitor?channel=1&subtype=0";
|
||||||
roles = [ "record" ];
|
roles = [ "record" ];
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
porch = {
|
porch = {
|
||||||
enabled = false;
|
|
||||||
detect.enabled = false;
|
detect.enabled = false;
|
||||||
ffmpeg.inputs = [{
|
ffmpeg.inputs = [{
|
||||||
path = "rtsp://admin:ocu?u3Su@192.168.0.43/cam/realmonitor?channel=1&subtype=0";
|
path = "rtsp://admin:ocu?u3Su@192.168.0.43/cam/realmonitor?channel=1&subtype=0";
|
||||||
|
|
@ -72,27 +64,5 @@ in
|
||||||
forceSSL = true;
|
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" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue