POSA_LEAKSMS/raspberry_pi4/test_connection.py
cw2007 73e49fd528 feat: RPi4 누수+온습도 2-모듈 단일 서버 통합 + 보안 하드닝
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>
2026-06-23 16:21:11 +09:00

112 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""서버 연결 테스트 스크립트 — Pi 4 에서 실행.
비밀값(API_URL/API_KEY)은 config_local.py 에서 로드한다 (config 경유)."""
import hashlib
import hmac
import time
import subprocess
from config import API_URL, API_KEY, DEVICE_ID, DEVICE_LOCATION
# API_URL 에서 베이스 경로 추출 (.../api/sensor_data.php → .../api)
API_BASE = API_URL.rsplit("/", 1)[0]
print("=" * 50)
print(" 서버 연결 테스트 (Pi 4)")
print("=" * 50)
print(f" API_BASE = {API_BASE}")
# ── 1단계: curl로 test.php 호출 ──
print("\n[1/3] curl로 test.php 연결 확인...")
try:
result = subprocess.run(
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
"-A", "LeakSensor/2.0",
"-X", "POST", f"{API_BASE}/test.php"],
capture_output=True, text=True, timeout=10
)
code = result.stdout.strip()
print(f" HTTP 응답코드: {code}")
if code == "200":
print(" → 성공! 서버 연결 정상")
elif code == "403":
print(" → 403 Forbidden: 서버/호스팅 자체가 차단 중")
print(" → 호스팅 설정 또는 상위 .htaccess 확인 필요")
else:
print(f" → 예상 외 응답: {code}")
except Exception as e:
print(f" → curl 실패: {e}")
# ── 2단계: curl로 sensor_data.php 호출 ──
print("\n[2/3] curl로 sensor_data.php 연결 확인...")
try:
result = subprocess.run(
["curl", "-s", "-w", "\n%{http_code}",
"-A", "LeakSensor/2.0",
"-X", "POST", API_URL,
"-H", "Content-Type: application/json",
"-d", '{"device_id":"test","sensor_id":1,"event_type":"startup","is_leak":0,"timestamp":1234567890}'],
capture_output=True, text=True, timeout=10
)
lines = result.stdout.strip().split("\n")
code = lines[-1] if lines else "?"
body = "\n".join(lines[:-1])
print(f" HTTP 응답코드: {code}")
print(f" 응답 내용: {body[:200]}")
except Exception as e:
print(f" → curl 실패: {e}")
# ── 3단계: Python requests로 서명 포함 호출 ──
print("\n[3/3] Python requests로 sensor_data.php 호출...")
try:
import requests
# raw-body X-Signature HMAC (서버 verify_signature_raw 와 동일 — leak_sensor.leak_event_body
# 와 같은 고정 필드 순서로 본문 문자열을 만들고 그 바이트에 서명한 뒤 그대로 POST 한다).
ts = int(time.time())
body = (
'{'
f'"device_id":"{DEVICE_ID}",'
f'"device_location":"{DEVICE_LOCATION}",'
f'"sensor_id":1,'
f'"sensor_name":"테스트 센서",'
f'"event_type":"startup",'
f'"is_leak":0,'
f'"timestamp":{ts}'
'}'
)
body_bytes = body.encode("utf-8")
signature = hmac.new(API_KEY.encode("utf-8"), body_bytes, hashlib.sha256).hexdigest()
resp = requests.post(
API_URL,
data=body_bytes,
timeout=10,
headers={
"Content-Type": "application/json",
"X-Device-Id": DEVICE_ID,
"X-Signature": signature,
"User-Agent": "LeakSensor/2.0",
}
)
print(f" HTTP 응답코드: {resp.status_code}")
print(f" 응답 내용: {resp.text[:200]}")
if resp.status_code == 200:
print(" → 성공! 모든 연결 정상")
elif resp.status_code == 403:
print(" → 403: requests 라이브러리가 차단됨 또는 서명 불일치")
print(f" → User-Agent: {resp.request.headers.get('User-Agent', '?')}")
else:
print(f" → 응답코드: {resp.status_code}")
except ImportError:
print(" → requests 미설치. install.sh 실행 또는 sudo apt install python3-requests")
except Exception as e:
print(f" → 실패: {e}")
print("\n" + "=" * 50)
print(" 테스트 완료")
print("=" * 50)