8.7 KiB
8.7 KiB
RPi4 누수+온습도 2-모듈 단일 서버 통합 + 보안 하드닝
1. 배경 / 목적
서버실 모니터링을 하나의 시스템으로 통합한다. Raspberry Pi 4 센서 모듈 2개가 각각 cafe24 서버로 데이터를 전송하고, 서버가 저장·임계판정·SMS·단일 대시보드 표시를 담당한다.
- 1번 모듈 — 누수 + 카메라 (
raspberry_pi4/,sensor_id=1) - 2번 모듈 — 온습도 SHT30 (
raspberry_pi4_sht30/,sensor_id=2)
기존에는 온습도(이 repo의 php/ 서버)와 누수(레거시 누수 서버)가 분리되어 있었고
서명 방식도 달랐다. 본 PR로 두 모듈을 동일 서버·동일 서명(X-Signature HMAC)으로 통합하여
대시보드 한 화면에서 온습도 + 누수 상태 + 현장 사진을 함께 본다.
┌──────────────────────────┐
│ 1번 모듈 (누수+카메라) │ leak_alert/recovery + 사진
│ raspberry_pi4/ │──┐
└──────────────────────────┘ │ HTTPS POST
▼ X-Signature = HMAC-SHA256(API_KEY, body)
┌──────────────────────────┐ ┌─────────────────────────────┐
│ 2번 모듈 (온습도 SHT30) │ │ cafe24 서버 (php/) │
│ raspberry_pi4_sht30/ │─►│ api/sensor_data.php (수신) │
└──────────────────────────┘ │ api/photo_upload.php (사진) │
│ → MySQL / 임계·누수 SMS │
│ → dashboard.php (통합 대시보드)│
└─────────────────────────────┘
2. 2번 모듈 신규 — raspberry_pi4_sht30/ (온습도)
STM32 펌웨어 sht30_fw 동작을 Raspberry Pi 4 + Python으로 이식.
| 파일 | 역할 |
|---|---|
sht30_sensor.py |
메인 — 5분 주기 측정 → 서명 POST → startup→periodic, SIGTERM 정상종료 |
sht30_driver.py |
SHT30 I2C 측정 + CRC-8 검증 + 변환 (smbus2, 미가용 시 시뮬레이션 폴백) |
wire.py |
본문 JSON + raw-body HMAC 서명 (펌웨어 jsonbody.c/sig.c와 바이트 동일) |
config.py / config_local.example.py |
비밀 아닌 기본값 / 운영·비밀값 템플릿(fail-closed) |
install.sh / vendor.sh |
폐쇄망 오프라인 + 온라인 설치 / 의존성 사전 다운로드, I2C 자동 활성화 |
test_sensor.py / test_connection.py |
CRC·포맷 자가점검 / 서버 서명 POST 점검 |
systemd/sht30-sensor.service |
부팅 자동 실행 |
- I2C 배선: SDA=GPIO2(물리3), SCL=GPIO3(물리5), 3.3V/GND, ADDR=GND→
0x44, 4.7kΩ 풀업 - 측정식:
temp=-45+175·raw_t/65535,rh=100·raw_rh/65535, CRC-8(poly 0x31, init 0xFF) - 보고 필드 순서(서명 호환 고정):
device_id, device_location, sensor_id, sensor_name, event_type, timestamp, metric_type="sht30", temperature_c, humidity_percent, metric_status, app_version - 임계 판정(고온/저온/고습/저습)·SMS는 전부 서버가 수행. 모듈은 측정만 보고.
3. 1번 모듈 통합 — raspberry_pi4/ (누수+카메라)
- 서명 전환: 레거시
sha256(API_KEY + 정렬 JSON)body-field 서명 → raw-bodyX-SignatureHMAC(2번 모듈과 동일 스킴). 본문을 고정 순서 문자열로 만들어 그 바이트에 서명하고 그대로 POST(data=body_bytes). - 누수 이벤트 본문:
{device_id, device_location, sensor_id, sensor_name, event_type, is_leak, timestamp} - 사진 업로드 서명: canonical =
device_id|sensor_id|log_id|timestamp(공백 없음),X-Signature헤더. 서버photo_upload.php재구성 canonical과 바이트 동일. - GPIO 인터럽트 누수감지 / 카메라 촬영 / 재시도 / 스레딩 동작은 유지.
config_local.example.py:API_URL/PHOTO_UPLOAD_URL을 통합 서버로,API_KEY는 서버와 동일.
4. 서버 통합 (php/)
| 파일 | 변경 |
|---|---|
api/sensor_data.php |
leak_alert/leak_recovery 수신, is_leak 처리, leak_incident 개시/종료, leak_alert 응답에 log_id 반환. SHT30 측정 경로 100% 유지 |
api/photo_upload.php (신규) |
멀티파트 JPEG 업로드. HMAC 서명·timestamp 신선도·finfo MIME·크기상한·서버생성 무작위 파일명·nonce 멱등·log_id↔sensor_id 결속 |
photo.php (신규) |
관리자 인증 사진 서빙. 파일명 정규식 + realpath 봉쇄(경로순회 방어), readfile image/jpeg |
dashboard.php |
누수 패널(센서1 누수/정상 + leak_incident 이력 + photo.php?id= 썸네일), 이벤트 라벨 누수감지/누수복구, 제목 "온습도·누수 통합" |
sms_send.php |
send_leak_alert_sms(쿨다운) / send_leak_recovery_sms(경보 후에만) |
config.php |
[누수감지]/[누수복구] 태그, 쿨다운 상수, SMS 요약에 누수 카운트 |
var/.htaccess, var/photos/.htaccess (신규) |
mod_rewrite 무관 Require all denied + PHP 실행차단 |
DB
sql/migration_add_leak.sql(신규, 가산·멱등) — 기존 온습도 서버에 누수 복원 (sensor_log.is_leak/sensor_status.is_leak,event_typeENUM에 leak 값,leak_incident+leak_photo테이블).sensor_metric(SHT30) 보존. 재실행 안전.sql/schema_sht30.sql— 신규(통합) 설치용. 누수 요소 포함.
5. 보안 하드닝 (외부 펜테스트 대비)
2중 독립 리뷰 → 확정 CRITICAL/HIGH = 0.
- 광범위 감사(6도메인 → 적대 반증검증): 고위험 후보 8건 전부 거짓양성 반증 (TOTP 재생·CRON_SECRET 유추·setup_mfa 재등록 등 — 비번/키 선보유 필요거나 시크릿 미영속화)
- 바운티 헌터(pre-auth 원격 익스플로잇): 바운티급 0건. SQLi/경로순회/XSS/SSRF/업로드RCE/
var/노출 전부 방어 확인.
적용한 하드닝(저위험, 무회귀):
cron_heartbeat.php—api_key_is_default()fail-closed (신규설치 예측가능 CRON_SECRET 무력화)setup_mfa.php— MFA 설정완료 후 부트스트랩 토큰 경로 차단var/·var/photos/— PHP 실행차단(engine off+ FilesMatch + RemoveHandler) → 업로드→RCE 폴리글랏 봉쇄php/.htaccess— HSTS 헤더 추가
미반영(문서화): CSP unsafe-inline 제거는 대시보드 인라인 스크립트/스타일 nonce 리팩터
필요 → 회귀위험으로 별도단계. (현 XSS 실위험 없음 — 출력 전부 htmlspecialchars)
6. 검증 내역
- wire byte-parity:
wire.py(2번)·leak_sensor.py(1번) 모두 본문/서명이 펌웨어reference.py및 독립 HMAC과 바이트 일치(한글 UTF-8,"/\이스케이프, round-half-up, max uint32 ts 엣지케이스 포함). 사진 canonical 클라↔서버 동일. - Python: 전 모듈
py_compile+ import 그래프 + 자가점검(CRC0xBEEF→0x92등) 통과. - 적대 검증 2라운드(통합/보안) blocking 0.
7. 배포 절차
각 라즈베리파이4 (모듈당 1대 또는 한 대에 둘 다 — 핀 충돌 없음)
- 해당 폴더 업로드
cp config_local.example.py config_local.py→API_URL/API_KEY/DEVICE_ID(+1번PHOTO_UPLOAD_URL)./install.sh→ systemd enable
cafe24 서버 (1회)
- DB: 기존 온습도 서버 →
sql/migration_add_leak.sql; 신규 →sql/schema_sht30.sql php/업로드 (신규photo.php,api/photo_upload.php,var/.htaccess포함)php/var/photos/웹서버 쓰기권한config.local.php의API_KEY= 라즈베리파이와 동일
8. 배포 전 체크리스트
- cafe24(PHP 있음)에서 문법/테스트:
php -l php/cron_heartbeat.php php/setup_mfa.php php/dashboard.php php/photo.php php/api/photo_upload.php/php tests/security_hardening_test.php(작업 환경엔 PHP 없어 미실행) migration_add_leak.sql1회 적용php/var/photos/쓰기권한- 운영 위생:
API_KEY실값 설정 + MFA 등록 후MFA_SETUP_TOKEN비우기
9. 주의 / 리스크
- ⚠️ 본 PR은 세션 외 기존 미커밋 변경(docs/firmware/기타 php 등)도 함께 포함(사용자 요청).
순수 이 작업분만 보려면 핵심 경로는
raspberry_pi4*/,php/api/,php/photo.php,sql/migration_add_leak.sql,SYSTEM_OVERVIEW.md. - PHP 정적 실행 검증은 작업 환경 제약으로 미수행 → 위
php -l게이트 필수. - 폐쇄망:
requests/smbus2등 의존성은vendor.sh로 사전 번들(aarch64).