POSA_LEAKSMS/tools/harden_pi.sh
cw2007 7a3b571b05 feat(deploy): 기기별 턴키 배포 번들 + Pi OS 하드닝 + 원샷 설치
tools/make_deploy_bundles.py: deploy/ 아래 서버1+Pi3대 폴더 생성 —
API_KEY 1개 자동 생성·4곳 동일 주입, 관리자 bcrypt(비번 stdin 전달), LF 정규화,
단일 schema.sql, var/ 런타임 상태 제외, CRON_SECRET 사전 계산. 난독화 경로
WEB_DIR 은 소스가 아닌 deploy/.secrets/web_dir.txt 에서 로드(커밋 트리 유출 차단).
시크릿 생성 전에 deploy/.gitignore 선작성 + 루트 .gitignore 에 deploy/ 이중 차단.

tools/harden_pi.sh: SSH 완전 비활성(outbound 전용 설계) · 인바운드 전부 차단
(nftables) · 불필요 서비스 제거(avahi/bluetooth/cups 등) · 자동 보안 패치
(unattended-upgrades, 자동 리부팅 끔) · config_local.py 권한 600 · 검증 증적 출력.

setup.sh(번들 생성): 설치→서비스→검증→하드닝 원샷. 검증 실패 시 set -e 로
하드닝(SSH 차단) 전에 중단 → SSH 유지되어 디버깅 가능.

검색 비노출 하드닝: php/.htaccess X-Robots-Tag noindex + Referrer-Policy
no-referrer, api/photo_upload.php CORS 제거, test_mobile.php 세션 인증 가드.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821XP36Q6t9eWKTPSEumnC
2026-07-02 18:51:46 +09:00

91 lines
4.4 KiB
Bash

#!/bin/bash
# =============================================================================
# harden_pi.sh — Raspberry Pi OS (Bookworm) 최소 하드닝 (센서 노드 공통)
#
# 정책 (2026-07 확정):
# - SSH 완전 비활성 — 이 시스템은 outbound 전용(Pi → 서버 HTTPS POST).
# 유지보수는 현장 물리 접근(모니터+키보드). 재활성이 필요하면 SD 카드의
# boot 파티션에 빈 파일 `ssh` 를 만들고 부팅한다(물리 접근 필요 — 의도됨).
# - 인바운드 전부 차단(nftables), 아웃바운드는 허용(apt/NTP/서버 POST).
# - 자동 보안 패치(unattended-upgrades) 켬, 자동 리부팅은 끔(센서 공백 방지).
#
# 실행: 모듈 설치(install.sh)와 검증(test_connection.py)이 전부 끝난 "마지막"에.
# sudo bash harden_pi.sh
# ⚠️ SSH 로 접속해 실행 중이면 완료 즉시 세션이 끊기고 재접속 불가 — 마지막 단계!
# =============================================================================
set -euo pipefail
[ "$(id -u)" -eq 0 ] || { echo "[ERROR] sudo 로 실행하세요: sudo bash harden_pi.sh"; exit 1; }
echo "[1/6] SSH 완전 비활성..."
systemctl disable --now ssh.service ssh.socket 2>/dev/null || true
# boot 파티션 `ssh` 파일에 의한 재활성 경로(sshswitch)는 남겨둔다 — 물리 접근
# 없이는 사용할 수 없고, 현장 복구 수단으로 유용하다.
echo "[2/6] 불필요 리스닝/주변 서비스 비활성..."
for svc in avahi-daemon.service avahi-daemon.socket bluetooth.service hciuart.service \
triggerhappy.service triggerhappy.socket cups.service cups.socket \
cups-browsed.service ModemManager.service; do
systemctl disable --now "$svc" 2>/dev/null || true
done
echo "[3/6] 방화벽(nftables) — 인바운드 전부 차단..."
apt-get install -y -q nftables >/dev/null
cat > /etc/nftables.conf <<'NFT'
#!/usr/sbin/nft -f
# 센서 노드 방화벽 — outbound 전용 장비. 인바운드는 루프백/응답 트래픽만.
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif "lo" accept
ct state established,related accept
# 현장 진단용 ping 허용 (원치 않으면 아래 2줄 삭제)
ip protocol icmp icmp type echo-request accept
ip6 nexthdr ipv6-icmp accept
}
chain forward { type filter hook forward priority 0; policy drop; }
chain output { type filter hook output priority 0; policy accept; }
}
NFT
systemctl enable --now nftables
nft -f /etc/nftables.conf
echo "[4/6] 자동 보안 패치(unattended-upgrades)..."
apt-get install -y -q unattended-upgrades >/dev/null
# Raspberry Pi OS 는 기본 Origins 패턴(Debian 전용)에 안 걸린다 — Raspbian/RPF origin 추가.
cat > /etc/apt/apt.conf.d/51unattended-upgrades-raspi <<'APT'
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
"origin=Raspbian,codename=${distro_codename},label=Raspbian";
"origin=Raspberry Pi Foundation,codename=${distro_codename},label=Raspberry Pi Foundation";
};
// 자동 리부팅 금지 — 커널 패치 후에도 센서 공백을 만들지 않는다(수동 리부팅).
Unattended-Upgrade::Automatic-Reboot "false";
APT
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'APT'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT
systemctl enable --now unattended-upgrades 2>/dev/null || true
echo "[5/6] 로컬 권한 조이기..."
# 시크릿 파일 소유자 전용 (API_KEY 포함)
for f in /home/pi/raspberry_pi4/config_local.py /home/pi/raspberry_pi4_sht30/config_local.py; do
[ -f "$f" ] && { chmod 600 "$f"; chown pi:pi "$f"; echo " 600: $f"; }
done
# 콘솔 자동 로그인 비활성 (물리 콘솔도 로그인 요구)
if command -v raspi-config >/dev/null 2>&1; then
raspi-config nonint do_boot_behaviour B1 || true
fi
echo "[6/6] 검증 (증적 캡처 대상 — SOURCE_SECURITY_EVIDENCE.md §4)..."
echo "-- ssh 상태 (disabled 여야 함):"
systemctl is-enabled ssh.service 2>&1 || true
echo "-- 리스닝 소켓 (비어 있거나 로컬 전용이어야 함):"
ss -lntup || true
echo "-- nftables 룰 (input policy drop 확인):"
nft list chain inet filter input | head -12
echo ""
echo "완료. 위 출력을 캡처해 보안 증적으로 보관할 것."
echo "리부팅 권장: sudo reboot (이후 ss -lntup 재확인)"