# ============================================================================= # /etc/systemd/system/pms7003-sensor.service # Service that auto-runs the particulate-matter (PMS7003) monitor on Raspberry Pi 4 boot # # Installation: # sudo cp systemd/pms7003-sensor.service /etc/systemd/system/ # sudo systemctl daemon-reload # sudo systemctl enable pms7003-sensor # sudo systemctl start pms7003-sensor # sudo systemctl status pms7003-sensor # # * Adjust the WorkingDirectory/ExecStart paths to match the actual deployment path. # The example below assumes deployment under /home/pi/raspberry_pi4_pms7003. # ============================================================================= [Unit] Description=PMS7003 Particulate-Matter Sensor Monitor v2 (Pi 4) After=network-online.target Wants=network-online.target [Service] Type=simple User=pi Group=pi # dialout group required to access /dev/serial0 (the primary UART) SupplementaryGroups=dialout WorkingDirectory=/home/pi/raspberry_pi4_pms7003 ExecStart=/home/pi/raspberry_pi4_pms7003/venv/bin/python pms7003_sensor.py Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=pms7003-sensor # Environment variables Environment=PYTHONUNBUFFERED=1 # Process-private /tmp isolation PrivateTmp=true # -- Sandboxing (hardening) -- blocks root escalation and 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 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. UART is unaffected since /dev/serial0 is a char device (not a socket). # AF_NETLINK required -- glibc getaddrinfo/NSS uses netlink to enumerate local addresses # (omitting it makes DNS resolution intermittently fail -> server POST impossible). AF_INET/6=HTTPS, AF_UNIX=DNS/journal. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK # * Do not use PrivateDevices -- it hides /dev/serial0 and makes the sensor inaccessible. # Restart cap (stop after 5 failures within 10 minutes) StartLimitBurst=5 StartLimitInterval=600s [Install] WantedBy=multi-user.target