RPi4 누수+온습도 2-모듈 단일 서버 통합 + 보안 하드닝 #1

Merged
cw2007 merged 3 commits from feat/rpi4-dual-module-unified-server into main 2026-06-24 09:27:07 +09:00
Owner

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-body
    X-Signature HMAC
    (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_type ENUM에 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/ 노출 전부 방어 확인.

적용한 하드닝(저위험, 무회귀):

  1. cron_heartbeat.php — api_key_is_default() fail-closed (신규설치 예측가능 CRON_SECRET 무력화)
  2. setup_mfa.php — MFA 설정완료 후 부트스트랩 토큰 경로 차단
  3. var/·var/photos/ — PHP 실행차단(engine off + FilesMatch + RemoveHandler) → 업로드→RCE 폴리글랏 봉쇄
  4. 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 그래프 + 자가점검(CRC 0xBEEF→0x92 등) 통과.
  • 적대 검증 2라운드(통합/보안) blocking 0.

7. 배포 절차

각 라즈베리파이4 (모듈당 1대 또는 한 대에 둘 다 — 핀 충돌 없음)

  1. 해당 폴더 업로드
  2. cp config_local.example.py config_local.py → API_URL/API_KEY/DEVICE_ID(+1번 PHOTO_UPLOAD_URL)
  3. ./install.sh → systemd enable

cafe24 서버 (1회)

  1. DB: 기존 온습도 서버 → sql/migration_add_leak.sql; 신규 → sql/schema_sht30.sql
  2. php/ 업로드 (신규 photo.php, api/photo_upload.php, var/.htaccess 포함)
  3. php/var/photos/ 웹서버 쓰기권한
  4. 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.sql 1회 적용
  • 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).
# 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-body `X-Signature` HMAC**(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_type` ENUM에 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/` 노출 전부 방어 확인. 적용한 하드닝(저위험, 무회귀): 1. `cron_heartbeat.php` — `api_key_is_default()` fail-closed (신규설치 예측가능 CRON_SECRET 무력화) 2. `setup_mfa.php` — MFA 설정완료 후 부트스트랩 토큰 경로 차단 3. `var/`·`var/photos/` — PHP 실행차단(`engine off` + FilesMatch + RemoveHandler) → 업로드→RCE 폴리글랏 봉쇄 4. `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 그래프 + 자가점검(CRC `0xBEEF→0x92` 등) 통과. - 적대 검증 2라운드(통합/보안) blocking 0. --- ## 7. 배포 절차 **각 라즈베리파이4** (모듈당 1대 또는 한 대에 둘 다 — 핀 충돌 없음) 1. 해당 폴더 업로드 2. `cp config_local.example.py config_local.py` → `API_URL`/`API_KEY`/`DEVICE_ID`(+1번 `PHOTO_UPLOAD_URL`) 3. `./install.sh` → systemd enable **cafe24 서버 (1회)** 1. DB: 기존 온습도 서버 → `sql/migration_add_leak.sql`; 신규 → `sql/schema_sht30.sql` 2. `php/` 업로드 (신규 `photo.php`, `api/photo_upload.php`, `var/.htaccess` 포함) 3. `php/var/photos/` 웹서버 쓰기권한 4. `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.sql` 1회 적용 - [ ] `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).
cw2007 added 3 commits 2026-06-24 09:22:00 +09:00
2번 모듈 신규(raspberry_pi4_sht30/): SHT30 온습도 → cafe24, raw-body X-Signature HMAC, 폐쇄망 오프라인 설치(vendor/install), systemd, CRC/포맷 자가점검. firmware sht30_fw wire 포맷과 byte-parity 검증.

1번 모듈 통합(raspberry_pi4/): 누수+카메라를 동일 서버로. 레거시 sha256(key+json) body-sig → X-Signature HMAC 전환, 사진 canonical=device_id|sensor_id|log_id|timestamp.

서버 통합(php/): api/sensor_data.php 누수 이벤트 수신, api/photo_upload.php 신규(HMAC·finfo·서버생성 파일명·nonce 멱등·sensor 결속), photo.php 인증 사진서빙, dashboard.php 누수 패널+사진, sms_send.php 누수 SMS, config.php 태그/요약.

DB: sql/migration_add_leak.sql(가산·멱등), schema_sht30.sql 통합 스키마.

보안 하드닝(외부 펜테스트 대비, 확정 CRITICAL/HIGH 0): cron_heartbeat api_key fail-closed, setup_mfa 부트스트랩 토큰 post-enrollment 잠금, var/ PHP 실행차단, HSTS.

문서/테스트: SYSTEM_OVERVIEW.md, security_hardening_test.php 갱신. 참고: 사용자 요청으로 세션 외 기존 미커밋 변경도 함께 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cw2007 added the
enhancement
iot
security
labels 2026-06-24 09:23:57 +09:00
cw2007 self-assigned this 2026-06-24 09:24:37 +09:00
cw2007 merged commit 239153a5b2 into main 2026-06-24 09:27:07 +09:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: cw2007/POSA_LEAKSMS#1
No description provided.