적대적 보안 감사에서 확정된 medium 6건 보완 (critical/high 0): - systemd 유닛(양 노드): NoNewPrivileges + ProtectSystem=strict/ ProtectHome=read-only/ProtectKernel*/ProtectControlGroups/RestrictSUIDSGID/ LockPersonality/RestrictRealtime/RestrictAddressFamilies(+AF_NETLINK) 샌드박싱. - harden_pi.sh: SSH 차단을 마지막 단계([6/6])로 재정렬 — 앞 단계 실패 시 set -e 로 SSH 가 살아있는 상태에서 중단(원격 복구 가능). nftables·unattended-upgrades 를 오프라인(debs/ --no-download)으로도 설치 지원(폐쇄망 harden). 미발견 secret 경고. - install.sh(양 노드): config_local.py 설치 즉시 chmod 600 (harden 전 노출창 제거). - vendor.sh(양 노드): .deb 를 파일명이 아닌 dpkg-deb Architecture 필드로 실검사 (arm64/all 만 허용, 그 외 hard-fail). nftables·unattended-upgrades 를 번들에 포함. 검증(3-리뷰어 워크플로)이 잡은 회귀 동반 수정: - [critical] ProtectHome=read-only 가 lgpio 의 작업디렉터리(.lgd-nfy 파이프) 쓰기를 막아 add_event_detect(누수 인터럽트 등록) 실패 → 데몬 크래시루프 → 감지 불능. Environment=LG_WD=/tmp 로 PrivateTmp 쓰기가능 /tmp 이동(누수 노드 한정, SHT30 무관). - [medium] 오프라인 설치에 --no-install-recommends 추가(vendor --no-recommends 정합, 네트워크 폴백 방지) — install.sh x2 + harden_pi.sh. - SupplementaryGroups=video gpio 명시 · nft|head 에 ||true(SIGPIPE 중단 방지) · online harden 분기에 apt-get update. ※ 온디바이스 런타임 테스트 1회 필요(빌드머신 Windows 에선 systemd 실행 불가): 실 Pi 에서 systemctl start 후 add_event_detect 콜백·카메라 촬영·업로드 정상 확인. |
||
|---|---|---|
| .. | ||
| systemd | ||
| .gitignore | ||
| config.py | ||
| config_local.example.py | ||
| install.sh | ||
| README.md | ||
| requirements.txt | ||
| sht30_driver.py | ||
| sht30_sensor.py | ||
| test_connection.py | ||
| test_sensor.py | ||
| vendor.sh | ||
| wire.py | ||
| WIRING.html | ||
온습도(SHT30) 센서 모듈 — Raspberry Pi 4 Model B 전용 (2번 모듈)
Sensirion SHT30(한진데이터 P4422-3, I2C) 온습도 센서를 Raspberry Pi 4 Model B 의
I2C 버스로 읽어, 주기적으로 cafe24 서버(php/api/sensor_data.php)에 보고하는 모듈입니다.
서버가 측정값을 저장하고, 고온/저온/고습/저습 임계 판정과 SMS, 대시보드 표시를 모두 수행합니다.
이 폴더는 STM32F407 펌웨어(
sht30_fw)의 동작을 Raspberry Pi 4 + Python 으로 이식한 "2번 모듈"입니다. wire 포맷(본문/서명)·CRC·변환식은 펌웨어와 바이트 단위로 동일합니다. "1번 모듈"(누수+카메라)은../raspberry_pi4/폴더입니다. 전체 구성은 루트의SYSTEM_OVERVIEW.md를 보세요.
1. 구성 파일
| 파일 | 역할 |
|---|---|
sht30_sensor.py |
메인 — 주기 측정 → 본문 생성/서명 → POST(재시도), startup→periodic |
sht30_driver.py |
SHT30 I2C 측정 + CRC-8 검증 + 물리값 변환 (smbus2, 시뮬레이션 폴백) |
wire.py |
본문 JSON + raw-body HMAC 서명 (펌웨어 jsonbody.c/sig.c와 바이트 동일) |
config.py |
비밀 아닌 기본 설정(센서 id/주기/범위/ I2C). config_local 에서 비밀값 로드 |
config_local.example.py |
운영·비밀값 템플릿 → config_local.py 로 복사해 사용(미커밋) |
requirements.txt |
Python 의존성(requests, smbus2, 핀 고정) |
install.sh |
Pi 4 설치(폐쇄망 오프라인 + 온라인 폴백, I2C 활성화 포함) |
vendor.sh |
폐쇄망용 의존성 사전 다운로드(빌드 머신에서 실행) |
test_connection.py |
서버 연결/서명 POST 점검 |
test_sensor.py |
CRC/포맷 자가점검 + 측정 1회(하드웨어 없이도 포맷 점검 통과) |
systemd/sht30-sensor.service |
부팅 자동 실행 서비스 유닛 |
2. 하드웨어 (Pi 4 Model B 기준)
- Raspberry Pi 4 Model B (BCM2711) + Raspberry Pi OS Bookworm 64-bit
- SHT30 온습도 센서 모듈 (I2C, 한진데이터 P4422-3)
- I2C 풀업 저항 4.7kΩ × 2 (모듈에 내장된 경우 생략)
- 전원: USB-C 5V/3A, 네트워크: Pi 4 Gigabit Ethernet(유선) 또는 WiFi
2.1 핀 배치 (I2C 버스 1)
| 신호 | Pi 4 핀 | 비고 |
|---|---|---|
| SHT30 VCC | 3.3V (물리 1) | 5V 금지 |
| SHT30 GND | GND (물리 6/9/14/…) | |
| SHT30 SDA | GPIO2 / SDA1 (물리 3) | + 4.7kΩ 풀업 → 3.3V |
| SHT30 SCL | GPIO3 / SCL1 (물리 5) | + 4.7kΩ 풀업 → 3.3V |
| SHT30 ADDR | GND → 주소 0x44 | VDD 면 0x45 (config.py I2C_ADDR) |
3. 서버 호환성 (반드시 확인)
이 모듈은 **현재 repo 의 php/ 서버(=cafe24 SHT30 대시보드 서버)**로 보고합니다.
서버는 raw-body HMAC 서명을 요구합니다.
X-Signature = lowercase_hex( HMAC-SHA256( key=API_KEY, msg=요청본문 바이트 ) )
config_local.py의API_KEY는 서버config.local.php의API_KEY와 정확히 동일해야 합니다.- 서버는
timestamp신선도(±600초)로 재전송을 막으므로 Pi 시각이 NTP 로 동기되어 있어야 합니다(Raspberry Pi OS 기본 활성). sensor_id=2로 보고하며, 서버 대시보드(dashboard.php)는 기본 차트로 sensor 2 를 표시합니다.- 임계 경보(고온/저온/고습/저습)·SMS·복구 판정은 모두 서버가 수행합니다. 이 모듈은 측정값만 보고합니다.
서버 측 스키마/배포는
../sql/schema_sht30.sql,../docs/INSTALL_PI_SERVER.md,../docs/SHT30_SENSOR2_SETUP.md참고.
4. 설치
4.1 설정값 준비
cp config_local.example.py config_local.py
# config_local.py 를 열어 API_URL / API_KEY / DEVICE_ID / DEVICE_LOCATION 채움
config_local.py 가 없으면 실행 시 명확한 오류로 즉시 중단됩니다(fail closed).
config_local.py 와 API_KEY 는 절대 커밋하지 마세요(.gitignore 처리됨).
4.2 온라인 설치 (인터넷 가능 환경)
chmod +x install.sh
./install.sh
sudo reboot # I2C 를 새로 켠 경우 1회 재부팅
4.3 폐쇄망(air-gapped) 오프라인 설치
인터넷이 되는 동일 아키텍처(aarch64, 가급적 동일 Pi 4 + Bookworm 64-bit) 머신에서 의존성을 먼저 받아 번들을 만든 뒤, 타깃 Pi 4 로 옮겨 설치합니다.
# (빌드 머신, 인터넷 O) wheelhouse/ + debs/ 생성
./vendor.sh
# wheelhouse/ 와 debs/ 를 타깃 Pi 4 의 이 폴더로 복사한 뒤:
# (타깃 Pi 4, 인터넷 X)
./install.sh # debs/ · wheelhouse/ 가 있으면 자동으로 오프라인 설치
smbus2는 순수 파이썬(arch 무관)이라 x86 빌드 머신에서 받아도 됩니다.requests등 다른 wheel/deb 는 aarch64 용을 받아야 합니다(타깃 arch 에서vendor.sh권장).
5. 실행 / 점검
# 센서가 보이는지 (0x44)
i2cdetect -y 1
# 포맷/CRC 자가점검 + 측정 1회 (하드웨어 없으면 시뮬레이션)
source venv/bin/activate
python3 test_sensor.py
# 서버 연결/서명 점검
python3 test_connection.py
# 실제 실행
python3 sht30_sensor.py
smbus2 미설치 또는 I2C 미활성 환경(개발 PC 등)에서는 자동으로 시뮬레이션 모드로 동작합니다.
6. 부팅 자동 실행 (systemd)
# 폴더를 /home/pi/raspberry_pi4_sht30 에 배치했다고 가정 (다르면 .service 의 경로 수정)
sudo cp systemd/sht30-sensor.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable sht30-sensor
sudo systemctl start sht30-sensor
sudo systemctl status sht30-sensor
journalctl -u sht30-sensor -f
SupplementaryGroups=i2c 가 /dev/i2c-1 접근에 필요합니다.
7. 보안 메모
- 비밀값(
API_KEY)·서버 URL·기기 정보는config_local.py(미커밋)에만 둡니다. 커밋되는 파일에는 placeholder 만 존재합니다. - 보고 본문은
HMAC-SHA256(API_KEY, raw_body)서명(X-Signature헤더)으로 위변조를 방지합니다 (길이확장 공격에 면역). 레거시 body-field sha256 폴백은 서버에서 기본 비활성입니다. - 서명하는 바이트와 전송하는 바이트는 항상 동일합니다(
wire.py가 본문을 직접 만들어 그 바이트에 서명 후 raw POST). 펌웨어sig.c/reference.py/ 서버verify_signature_raw와 바이트 단위로 일치합니다. config_local.py노출 시 즉시API_KEY를 교체(서버 동시 변경)하세요.