POSA_LEAKSMS/docs/INSTALL_PI_SERVER.md
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

10 KiB
Raw Blame History

STM32 펌웨어 및 Cafe24 서버 설치 설명서 v2606

이 문서는 서버실 온습도(SHT30) 모니터링 시스템 v2606을 실제 운영 환경에 배포할 때 STM32F407VGT6 펌웨어와 Cafe24 PHP/MySQL 서버에 각각 무엇을 설치하고 설정해야 하는지 정리한 설치용 문서입니다.

1. 설치 대상

구분 설치 위치 역할
Cafe24 서버 public_html/raspi_leck_detecter/ 센서 API 수신, DB 저장, 임계 판정, SMS 발송, 대시보드/보고서 제공
Cafe24 MySQL Cafe24 DB 센서 로그, 온습도 측정값, SMS 로그, 장비 상태 저장
STM32F407VGT6 (sht30_fw) 펌웨어 플래시 SHT30 I2C 측정, 5분 주기 보고, raw-body 서명
서버 cron Cafe24 cron 또는 외부 스케줄러 cron_heartbeat.php 주기 실행(장비 오프라인 감지)

2. 설치 전 준비물

  • STM32F407VGT6 보드 (STM32F4-DISCOVERY 계열 또는 동등 커스텀 PCB)
  • SHT30 온습도 모듈 (한진데이터 P4422-3, I2C)
  • LAN8720 Ethernet PHY 모듈 (RMII) + LAN 케이블
  • I2C 풀업 저항 4.7kΩ × 2 (모듈 내장 풀업 시 생략)
  • 외부 3.3V USB-UART 어댑터 (USART3 PD8/PD9 콘솔 로그용)
  • Cafe24 PHP 호스팅
  • Cafe24 MySQL DB
  • Cafe24 SMS 서비스 계정 및 secure key
  • 운영 담당자 SMS 수신 번호
  • HTTPS 접속 가능한 도메인 또는 호스팅 경로 + Cafe24 루트 CA

배선은 wiring_diagram.md를 먼저 적용합니다. SHT30 I2C(PB6 SCL / PB7 SDA, 주소 0x44, 3.3V/GND, 4.7kΩ 풀업)와 LAN8720 RMII 결선은 firmware/docs/HARDWARE.md를 따릅니다.

3. 서버에 설치할 파일

Cafe24 웹 루트 아래에 다음 구조로 PHP 파일을 업로드합니다.

public_html/raspi_leck_detecter/
  .htaccess
  blocked.php
  config.php
  config.local.php
  config.local.example.php
  ops_checks.php
  sms_send.php
  cron_heartbeat.php
  dashboard.php
  setup_mfa.php
  admin_security.php
  setup_wizard.php
  security_evidence.php
  monthly_report.php
  login.php
  retention_cleanup.php
  setup_hash.php
  api/
    sensor_data.php
  var/
    .gitkeep

운영 서버에는 config.local.php가 반드시 필요합니다. config.local.example.php를 복사해서 만들고 실제 운영 값으로 교체합니다.

<?php
return [
    'DB_HOST' => 'localhost',
    'DB_PORT' => 3306,
    'DB_NAME' => 'your_db_name',
    'DB_USER' => 'your_db_user',
    'DB_PASS' => 'your_db_password',

    'API_KEY' => 'replace-with-long-random-secret',

    'SMS_USER_ID' => 'your-cafe24-sms-user-id',
    'SMS_SECURE' => 'your-cafe24-sms-secure-key',
    'SMS_SENDER' => '01000000000',
    'SMS_RECIPIENTS' => [
        '01000000000',
    ],

    'ADMIN_USER' => 'admin',
    'ADMIN_PASSWORD_HASH' => '$2y$10$replace.with.password_hash.output',
    'ADMIN_TOTP_SECRET' => 'replace-with-base32-secret',
    'MFA_SETUP_TOKEN' => 'replace-with-temporary-random-token',

    // 온습도 임계 override (기본값은 config.php 의 METRIC_*)
    'METRIC_TEMP_HIGH_C' => 30,
    'METRIC_TEMP_LOW_C' => 10,
    'METRIC_RH_HIGH' => 70,
    'METRIC_RH_LOW' => 20,

    'SMS_LOG_RETENTION_DAYS' => 365,
    'SENSOR_LOG_RETENTION_DAYS' => 365,
    'SENSOR_METRIC_RETENTION_DAYS' => 365,
    'ADMIN_AUDIT_RETENTION_DAYS' => 365,
];

API_KEY는 STM32 펌웨어 firmware/common/secrets.hAPP_API_KEY와 반드시 바이트 단위로 같아야 합니다(raw-body 서명 X-Signature = sha256(API_KEY + 요청본문)).

ADMIN_TOTP_SECRET은 Google Authenticator, Microsoft Authenticator, Authy 등 TOTP 인증 앱에 등록한 Base32 비밀키입니다. 이 값이 없으면 관리자 로그인을 허용하지 않습니다. 담당자 변경 시 관리자 비밀번호와 TOTP 비밀키를 모두 교체하고 변경 기록을 운영 인수인계 자료에 남깁니다.

최초 등록은 MFA_SETUP_TOKEN을 임시로 설정한 뒤 다음 주소에서 진행합니다.

https://your-domain.example/raspi_leck_detecter/setup_mfa.php?token=임시토큰

setup_mfa.php에서 생성된 수동 입력 키를 Google Authenticator에 등록하고 6자리 코드를 검증한 뒤, 화면에 표시되는 ADMIN_TOTP_SECRET 줄을 config.local.php에 반영합니다. 등록 완료 후 MFA_SETUP_TOKEN은 빈 값으로 바꾸거나 삭제합니다.

4. 서버 DB 설치

신규 설치라면 Cafe24 phpMyAdmin 또는 MySQL 콘솔에서 다음 파일을 실행합니다.

SOURCE /path/to/sql/schema_sht30.sql;

phpMyAdmin에서는 schema_sht30.sql 내용을 복사해서 SQL 실행 창에 붙여넣습니다. 생성 테이블은 sensor_log, sensor_status, sensor_metric, sms_log입니다.

기존 누수 설치를 온습도 전용으로 전환하는 경우에는 전환 마이그레이션을 적용합니다.

SQL 파일 적용 상황
schema_sht30.sql 신규 설치(통합 스키마)
migration_drop_leak.sql 기존 누수 설치 → 온습도 전용 전환(레거시 컬럼/테이블 정리, sensor_metric 보장)

이미 적용된 마이그레이션을 중복 실행하면 DB 오류가 날 수 있으므로, 기존 설치에서는 setup_wizard.php의 점검 결과를 먼저 확인합니다.

5. 서버 권한 및 관리자 설정

상태 파일 폴더는 PHP가 쓸 수 있어야 합니다.

php/var/

관리자 비밀번호 해시는 서버 CLI에서 생성합니다.

php setup_hash.php "새관리자비밀번호"

출력된 해시를 config.local.phpADMIN_PASSWORD_HASH에 넣습니다. 관리자 인증 앱 비밀키도 config.local.phpADMIN_TOTP_SECRET에 설정합니다.

서버 설정 후 브라우저에서 아래 화면을 확인합니다.

https://your-domain.example/raspi_leck_detecter/setup_wizard.php

점검 화면에서 확인할 항목은 다음과 같습니다.

  • DB 연결
  • 필수 테이블 존재 여부(sensor_log, sensor_status, sensor_metric, sms_log)
  • API 키 기본값 교체 여부
  • 관리자 해시 설정 여부
  • 관리자 TOTP 설정 여부
  • SMS 수신자 설정 여부
  • 상태 파일 폴더 쓰기 권한
  • 테스트 SMS 발송

6. 서버 cron 설정

장비 오프라인 감지를 위해 cron_heartbeat.php를 주기 실행합니다. Cafe24 cron 또는 외부 스케줄러에서 다음 URL을 1분 간격으로 호출합니다.

https://your-domain.example/raspi_leck_detecter/cron_heartbeat.php

서버 CLI cron을 사용할 수 있다면 다음처럼 구성할 수 있습니다.

* * * * * php /home/hosting_user/public_html/raspi_leck_detecter/cron_heartbeat.php

호스팅 환경마다 실제 절대 경로가 다르므로 Cafe24 파일 관리자 또는 phpinfo()로 경로를 확인합니다. 마지막 수신 후 HEARTBEAT_TIMEOUT_SEC(기본 1200초=20분)을 초과하면 오프라인으로 판정합니다.

7. STM32 펌웨어 빌드 (폐쇄망)

폐쇄망 빌드 머신에서 펌웨어를 빌드합니다. 의존성 벤더링과 오프라인 빌드/플래시 절차는 firmware/docs/BUILD_OFFLINE.md를 따릅니다.

cp firmware/common/secrets.h.example firmware/common/secrets.h
# secrets.h 의 APP_API_KEY 를 서버 config.local.php 의 API_KEY 와 바이트 단위로 동일하게 채운다.

빌드 전 확인할 설정값:

위치 설정
firmware/common/secrets.h APP_API_KEY = 서버 API_KEY
firmware/common/app_config.h APP_API_HOST, APP_API_PATH(/raspi_leck_detecter/api/sensor_data.php), 네트워크(DHCP/static)
firmware/common/app_config.h APP_SHT30_REPORT_INTERVAL_SEC(기본 300초), APP_SHT30_I2C_ADDR(0x44)
firmware/certs/server_ca.c 자리표시자 CA → 실제 Cafe24 루트 CA

sht30_fw 타깃을 빌드해 sht30_fw.bin을 생성하고 보드에 플래시합니다.

8. STM32 펌웨어 동작 확인

USART3(PD8 TX / PD9 RX, 115200 8N1)에 외부 3.3V USB-UART 어댑터를 연결해 콘솔 로그를 확인합니다.

첫 부팅 후 확인할 항목:

  • I2C1 초기화 및 SHT30 주소 0x44 응답
  • 첫 측정값(온도/습도)
  • 네트워크(DHCP/static) → SNTP 시간 동기 → TLS 핸드셰이크
  • 서버 200 응답 및 startup 보고

상태 LED(PD12) 패턴: 부팅=점등 / 정상 보고=느린 토글 / 망 단절·TLS 실패=빠른 점멸.

9. 설치 후 최종 확인

서버에서 확인합니다.

https://your-domain.example/raspi_leck_detecter/setup_wizard.php
https://your-domain.example/raspi_leck_detecter/dashboard.php
https://your-domain.example/raspi_leck_detecter/monthly_report.php
https://your-domain.example/raspi_leck_detecter/security_evidence.php

정상 설치 기준은 다음과 같습니다.

  • setup_wizard.php의 필수 점검 항목이 통과
  • dashboard.php에 최신 온도/습도와 센서 상태(sensor_id=2)가 표시
  • security_evidence.php에서 운영 점검 결과와 보안통제 매트릭스 조회 가능
  • STM32 콘솔 로그에 서버 200 응답 기록 확인
  • sensor_metric에 주기 측정값(periodic) 저장
  • 임계 초과 테스트 시 metric_status 경보 기록 및 SMS 수신
  • 월간 보고서에서 임계 경보/복귀/오프라인 내역 조회 가능

10. 문제 해결

증상 확인 지점
API 403 서버 API_KEY와 펌웨어 APP_API_KEY 일치 여부, raw-body 서명
API 500 Cafe24 PHP 오류 로그, DB 접속 정보, 테이블 생성 여부
로그인 불가 ADMIN_PASSWORD_HASH, ADMIN_TOTP_SECRET, 인증 앱 시간 동기화
SMS 미발송 Cafe24 SMS 계정, secure key, 발신번호 등록, 잔액, sms_log
온습도 미표시 I2C 배선(PB6/PB7, 0x44), 4.7kΩ 풀업, sensor_metric 테이블, 펌웨어 측정 로그
임계 경보 미발송 config.php/config.local.phpMETRIC_*, METRIC_ALERT_COOLDOWN_SEC(1800초)
TLS 실패 firmware/certs/server_ca.c 실제 CA 반영, SNTP 시간 동기(인증서 유효기간 검증)
오프라인 알림 과다/지연 HEARTBEAT_CHECK_INTERVAL_SEC, HEARTBEAT_TIMEOUT_SEC(기본 1200초), cron 주기

11. 운영자가 보관해야 할 값

다음 값은 코드 저장소에 넣지 말고 운영자만 별도로 보관합니다.

  • Cafe24 DB 이름, 계정, 비밀번호
  • API_KEY / 펌웨어 APP_API_KEY
  • Cafe24 SMS user id
  • Cafe24 SMS secure key
  • SMS 발신번호
  • SMS 수신자 번호
  • 관리자 계정과 원문 비밀번호, ADMIN_TOTP_SECRET
  • 서버 업로드 경로
  • 장비 ID(stm32-sht30-01)와 설치 위치