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>
182 lines
8.1 KiB
PowerShell
182 lines
8.1 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
제3자 의존성 벤더링 (NETWORKED 빌드 머신 전용) — firmware/third_party 채우기.
|
|
|
|
.DESCRIPTION
|
|
폐쇄망(air-gapped) 타깃으로 전달하기 위해, 빌드/스테이징 머신에서 핀 고정된
|
|
제3자 의존성을 내려받아 SHA-256 으로 무결성을 검증한 뒤 firmware/third_party
|
|
아래에 펼친다. **네트워크가 허용되는 유일한 단계**다. 이후 build.ps1 은
|
|
네트워크 없이 빌드한다.
|
|
|
|
벤더링 대상(핀 고정):
|
|
- STM32CubeF4 (HAL + CMSIS + startup/system) TODO(vendor): 버전 핀 확정
|
|
- FreeRTOS-Kernel (네이티브 API, ARM_CM4F 포트) TODO(vendor)
|
|
- lwip (LwIP TCP/IP + apps/sntp) TODO(vendor)
|
|
- mbedtls (TLS 1.2 클라이언트) TODO(vendor)
|
|
|
|
중요: 아래 $Deps 의 Url/Sha256/Tag 는 운영 환경에서 **사용할 정확한 핀 버전**으로
|
|
교체해야 한다. SHA-256 미설정('TODO') 인 항목은 다운로드 후 계산된 해시를
|
|
출력만 하고(최초 1회 기록용) 검증은 건너뛴다 — 기록 후 반드시 채워 넣을 것.
|
|
|
|
.PARAMETER Force
|
|
이미 존재하는 third_party 하위 트리를 덮어쓴다(재벤더링).
|
|
|
|
.PARAMETER KeepArchives
|
|
내려받은 압축 파일을 third_party/_archives 에 보존한다(오프라인 재현용).
|
|
|
|
.EXAMPLE
|
|
pwsh firmware/scripts/vendor.ps1
|
|
pwsh firmware/scripts/vendor.ps1 -Force -KeepArchives
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[switch]$Force,
|
|
[switch]$KeepArchives,
|
|
# 엄격 핀 모드: SHA-256 이 'TODO'(미설정)인 의존성이 있으면 실패(릴리스 빌드용).
|
|
[switch]$Strict
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$FwRoot = Split-Path -Parent $ScriptDir
|
|
$ThirdParty = Join-Path $FwRoot 'third_party'
|
|
$ArchiveDir = Join-Path $ThirdParty '_archives'
|
|
|
|
# =============================================================================
|
|
# 핀 고정 의존성 목록
|
|
# Name : third_party 아래 디렉터리명 (CMakeLists 의 경로와 일치해야 함)
|
|
# Tag : 릴리스/태그(추적용)
|
|
# Url : 다운로드 URL (release tarball/zip)
|
|
# Sha256 : 압축 파일 SHA-256 (소문자 hex). 'TODO' 면 검증 생략(해시 출력만).
|
|
# Strip : 압축 최상위 폴더를 벗겨낼지(대부분 release 는 단일 최상위 폴더)
|
|
#
|
|
# TODO(vendor): Url/Sha256/Tag 를 운영에서 쓸 정확한 핀 버전으로 교체.
|
|
# STM32CubeF4 는 GitHub release zip 또는 ST 공식 배포본을 사용한다.
|
|
# =============================================================================
|
|
$Deps = @(
|
|
@{
|
|
Name = 'STM32CubeF4'
|
|
Tag = 'v1.28.0'
|
|
Url = 'https://github.com/STMicroelectronics/STM32CubeF4/archive/refs/tags/v1.28.0.zip'
|
|
Sha256 = 'TODO' # TODO(vendor): 실제 zip SHA-256 기입
|
|
Strip = $true
|
|
},
|
|
@{
|
|
Name = 'FreeRTOS-Kernel'
|
|
Tag = 'V11.1.0'
|
|
Url = 'https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V11.1.0.zip'
|
|
Sha256 = 'TODO' # TODO(vendor)
|
|
Strip = $true
|
|
},
|
|
@{
|
|
Name = 'lwip'
|
|
Tag = 'STABLE-2_2_0_RELEASE'
|
|
Url = 'https://github.com/lwip-tcpip/lwip/archive/refs/tags/STABLE-2_2_0_RELEASE.zip'
|
|
Sha256 = 'TODO' # TODO(vendor)
|
|
Strip = $true
|
|
},
|
|
@{
|
|
Name = 'mbedtls'
|
|
Tag = 'v3.6.2'
|
|
Url = 'https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-3.6.2/mbedtls-3.6.2.tar.bz2'
|
|
Sha256 = 'TODO' # TODO(vendor): release tarball 은 공식 SHA-256 제공됨
|
|
Strip = $true
|
|
}
|
|
)
|
|
|
|
New-Item -ItemType Directory -Force -Path $ThirdParty | Out-Null
|
|
if ($KeepArchives) { New-Item -ItemType Directory -Force -Path $ArchiveDir | Out-Null }
|
|
|
|
function Get-FileSha256([string]$path) {
|
|
(Get-FileHash -Algorithm SHA256 -Path $path).Hash.ToLowerInvariant()
|
|
}
|
|
|
|
# 압축 해제(zip / tar.bz2 / tar.gz). tar 는 Windows 10+ 내장 bsdtar 사용.
|
|
function Expand-Archive2([string]$archive, [string]$dest) {
|
|
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
|
if ($archive -match '\.zip$') {
|
|
Expand-Archive -Path $archive -DestinationPath $dest -Force
|
|
}
|
|
elseif ($archive -match '\.(tar\.bz2|tbz2|tar\.gz|tgz|tar\.xz)$') {
|
|
if (-not (Get-Command 'tar' -ErrorAction SilentlyContinue)) {
|
|
throw "tar 가 필요합니다(.tar.* 해제). Windows 10+ 내장 tar 또는 git tar 사용."
|
|
}
|
|
& tar -xf $archive -C $dest
|
|
if ($LASTEXITCODE -ne 0) { throw "tar 해제 실패: $archive" }
|
|
}
|
|
else {
|
|
throw "지원하지 않는 압축 형식: $archive"
|
|
}
|
|
}
|
|
|
|
foreach ($dep in $Deps) {
|
|
$name = $dep.Name
|
|
$target = Join-Path $ThirdParty $name
|
|
|
|
Write-Host "──────────────────────────────────────────────" -ForegroundColor Cyan
|
|
Write-Host " 벤더링: $name ($($dep.Tag))" -ForegroundColor Cyan
|
|
|
|
if ((Test-Path $target) -and -not $Force) {
|
|
Write-Host " 이미 존재 — 건너뜀(재벤더링은 -Force). $target" -ForegroundColor Yellow
|
|
continue
|
|
}
|
|
if (Test-Path $target) {
|
|
Remove-Item -Recurse -Force $target
|
|
}
|
|
|
|
# ── 다운로드 ─────────────────────────────────────────────────────────────
|
|
$tmp = New-Item -ItemType Directory -Force `
|
|
-Path (Join-Path ([IO.Path]::GetTempPath()) ("vendor_" + [Guid]::NewGuid()))
|
|
$fileName = [IO.Path]::GetFileName(([Uri]$dep.Url).AbsolutePath)
|
|
$archive = Join-Path $tmp $fileName
|
|
|
|
Write-Host " 다운로드: $($dep.Url)"
|
|
Invoke-WebRequest -Uri $dep.Url -OutFile $archive -UseBasicParsing
|
|
|
|
# ── 무결성 검증(SHA-256) ────────────────────────────────────────────────
|
|
$actual = Get-FileSha256 $archive
|
|
if ($dep.Sha256 -eq 'TODO' -or [string]::IsNullOrWhiteSpace($dep.Sha256)) {
|
|
if ($Strict) {
|
|
throw "Strict 모드: $name 의 SHA-256 핀이 미설정(TODO) 입니다. 계산값을 `$Deps 에 기록 후 재실행하세요(재현성 고정): $actual"
|
|
}
|
|
Write-Host " [경고] SHA-256 미설정 — 검증 생략. 계산값을 기록하세요:" -ForegroundColor Yellow
|
|
Write-Host " $name : $actual" -ForegroundColor Yellow
|
|
}
|
|
elseif ($actual -ne $dep.Sha256.ToLowerInvariant()) {
|
|
throw "SHA-256 불일치($name)`n 기대: $($dep.Sha256)`n 실제: $actual"
|
|
}
|
|
else {
|
|
Write-Host " SHA-256 확인됨: $actual" -ForegroundColor Green
|
|
}
|
|
|
|
# ── 해제 + 최상위 폴더 벗기기 ────────────────────────────────────────────
|
|
$extract = Join-Path $tmp 'x'
|
|
Expand-Archive2 $archive $extract
|
|
|
|
if ($dep.Strip) {
|
|
$top = @(Get-ChildItem -Directory $extract)
|
|
if ($top.Count -eq 1) {
|
|
Move-Item -Path $top[0].FullName -Destination $target
|
|
}
|
|
else {
|
|
# 최상위가 단일 폴더가 아니면 그대로 이동.
|
|
Move-Item -Path $extract -Destination $target
|
|
}
|
|
}
|
|
else {
|
|
Move-Item -Path $extract -Destination $target
|
|
}
|
|
|
|
if ($KeepArchives) {
|
|
Copy-Item -Path $archive -Destination (Join-Path $ArchiveDir $fileName) -Force
|
|
}
|
|
Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
|
|
|
|
Write-Host " 완료 → $target" -ForegroundColor Green
|
|
}
|
|
|
|
Write-Host "──────────────────────────────────────────────" -ForegroundColor Cyan
|
|
Write-Host " 벤더링 완료. 이제 폐쇄망 빌드 가능: pwsh firmware/scripts/build.ps1" -ForegroundColor Green
|
|
Write-Host " TODO(vendor): 위 출력된 SHA-256 값을 \$Deps 에 채워 재현성을 고정하세요." -ForegroundColor Yellow
|