# ============================================================================= # /etc/systemd/system/leak-sensor.service # Raspberry Pi 4 부팅 시 누수감지 모니터를 자동 실행하는 서비스 # # 설치 방법: # 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 # # ※ WorkingDirectory/ExecStart 경로는 실제 배치 경로에 맞게 수정. # 아래는 /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 는 /dev/gpiochip0(root:gpio 660) 접근에 gpio 그룹, 카메라는 video 그룹 필요. 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=PYTHONUNBUFFERED=1 # lgpio(rpi-lgpio 백엔드)는 add_event_detect 시 작업디렉터리에 .lgd-nfy 알림 파이프를 # 만든다. 아래 ProtectHome=read-only 로 /home 이 읽기전용이 되므로, PrivateTmp 의 # 쓰기가능 /tmp 로 lgpio 작업디렉터리를 옮긴다. (없으면 누수 인터럽트 등록이 실패해 # 데몬이 크래시루프 → 누수 감지 불능.) LG_WD 는 lg 라이브러리가 읽는 환경변수. # ※ 이 하드닝은 chardev 백엔드(python3-rpi-lgpio) 를 가정한다. sysfs 기반 # python3-rpi.gpio 로 되돌리면 ProtectKernelTunables/ProtectHome 와 충돌하므로 # 백엔드를 바꾸지 말 것(vendor.sh/install.sh 의 python3-rpi-lgpio 핀 유지). Environment=LG_WD=/tmp # 누수 촬영 임시파일을 프로세스 전용 /tmp 로 격리 (공용 /tmp 노출 방지) PrivateTmp=true # ── 샌드박싱(하드닝) — 데몬 침해 시 root 승격·파일시스템 오염을 차단 ────────── # User=pi 는 NOPASSWD sudo 계정이므로 NoNewPrivileges 로 setuid(sudo) 승격 경로를 막는다. NoNewPrivileges=true # 루트파일시스템 읽기전용 + /var/log 만 쓰기 허용(로그 로테이션 포함). /tmp 는 PrivateTmp. ProtectSystem=strict ReadWritePaths=/var/log # /home 는 읽기전용(자기 코드/config_local.py 읽기만; 쓰기·타 계정 홈 접근 차단). ProtectHome=read-only ProtectKernelModules=true ProtectKernelTunables=true ProtectControlGroups=true RestrictSUIDSGID=true LockPersonality=true RestrictRealtime=true # HTTPS(POST)+로컬소켓(DNS/journal)만. 카메라/GPIO 는 /dev char (소켓 아님)라 무관. # AF_NETLINK 필수 — glibc getaddrinfo/NSS 가 로컬주소 열거에 netlink 를 쓴다 # (빼면 DNS 해석이 간헐 실패 → 서버 POST 불가). AF_INET/6=HTTPS, AF_UNIX=DNS·journal. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK # ※ PrivateDevices 는 쓰지 않는다 — /dev/gpiochip*, 카메라 노드가 숨겨져 동작 불능. # 재시작 상한 (10분 내 5번 실패 시 중단) StartLimitBurst=5 StartLimitInterval=600s [Install] WantedBy=multi-user.target