# ============================================================================= # /etc/systemd/system/leak-sensor.service # Service that auto-runs the leak-detection monitor at Raspberry Pi 4 boot # # Installation: # sudo cp systemd/leak-sensor.service /etc/systemd/system/ # sudo systemctl daemon-reload # sudo systemctl enable leak-sensor # sudo systemctl start leak-sensor # sudo systemctl status leak-sensor # # NOTE: Adjust the WorkingDirectory/ExecStart paths to match the actual deployment path. # The values below assume deployment under /home/pi/raspberry_pi4. # ============================================================================= [Unit] Description=e-con 5C-08 Leak Sensor Monitor v2 (Pi 4, with Camera) After=network-online.target Wants=network-online.target [Service] Type=simple User=pi Group=pi # rpi-lgpio needs the gpio group to access /dev/gpiochip0 (root:gpio 660); the camera needs the video group. SupplementaryGroups=video gpio WorkingDirectory=/home/pi/raspberry_pi4 ExecStart=/home/pi/raspberry_pi4/venv/bin/python leak_sensor.py Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=leak-sensor # Environment variables Environment=PYTHONUNBUFFERED=1 # lgpio (rpi-lgpio backend) creates a .lgd-nfy notification pipe in the working # directory on add_event_detect. Since ProtectHome=read-only below makes /home # read-only, the lgpio working directory is moved to PrivateTmp's writable /tmp. # (Otherwise leak interrupt registration fails -> daemon crash-loops -> no leak detection.) LG_WD is the env var the lg library reads. # NOTE: This hardening assumes the chardev backend (python3-rpi-lgpio). Reverting to # the sysfs-based python3-rpi.gpio conflicts with ProtectKernelTunables/ProtectHome, # so do not change the backend (keep the python3-rpi-lgpio pin in vendor.sh/install.sh). Environment=LG_WD=/tmp # Isolate leak-capture temp files to the process-private /tmp (prevents exposure via shared /tmp) PrivateTmp=true # -- Sandboxing (hardening) -- blocks root escalation / filesystem tampering if the daemon is compromised -------- # User=pi is a NOPASSWD sudo account, so NoNewPrivileges blocks the setuid(sudo) escalation path. NoNewPrivileges=true # Root filesystem read-only + only /var/log writable (including log rotation). /tmp is PrivateTmp. ProtectSystem=strict ReadWritePaths=/var/log # /home is read-only (reads its own code/config_local.py only; blocks writes and access to other accounts' homes). ProtectHome=read-only ProtectKernelModules=true ProtectKernelTunables=true ProtectControlGroups=true RestrictSUIDSGID=true LockPersonality=true RestrictRealtime=true # HTTPS(POST) + local sockets (DNS/journal) only. Camera/GPIO are /dev char (not sockets), so unaffected. # AF_NETLINK required -- glibc getaddrinfo/NSS uses netlink to enumerate local addresses # (omitting it makes DNS resolution fail intermittently -> server POST impossible). AF_INET/6=HTTPS, AF_UNIX=DNS/journal. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK # NOTE: PrivateDevices is not used -- it would hide /dev/gpiochip* and the camera node, breaking operation. # Restart cap (stop after 5 failures within 10 minutes) StartLimitBurst=5 StartLimitInterval=600s [Install] WantedBy=multi-user.target