8.5 KiB
Firmware Boot Hardening — dpworld-network-apply-hardened.sh
근본 원인 (PMU 워치독 재부팅 루프, 2026-06-15)
.56 (telechips-tcc8030-main)이 하드웨어 재부팅 루프에 진입했다. 타임라인:
- 트리거: WiFi 국가 코드 변경이 DB에 대기(pending) 상태였다.
- 부팅 경로:
dpworld-network-seed.service가 펌웨어 내장/usr/bin/dpworld-network-apply.sh --boot를 실행했다. - 행(Hang): 원본 펌웨어 스크립트가
modprobe -r wlan을 호출해 새 국가 코드로 WiFi 모듈을 라이브 재로드하려 했다. QCA6490 (cnss_pci / Qualcomm WLAN.HSP.1.1) 모듈에서 이 호출은 무한 정지 상태가 된다 — PCIe 링크 해제가 완료되지 않기 때문이다. - 리셋: Telechips PMU 하드웨어 워치독이 20초 후 발동 → 하드 리셋 → 2단계부터 루프 반복.
- 해결: 하드닝 스크립트를 라이브 디바이스에 설치(~07:44). 이후 디바이스 안정 유지 중.
하드닝 스크립트
deploy/dpworld-network-apply-hardened.sh는 펌웨어 내장
dpworld-network-apply.sh를 POSIX sh로 독립 재작성한 것이다.
펌웨어 원본의 문서화된 6가지 문제(P1–P6, CC)를 수정하며,
부팅 하드닝 수정은 P2에 해당한다.
P2: 부팅 시 modprobe -r 제거
| 펌웨어 원본 | 하드닝 버전 | |
|---|---|---|
--boot + 국가 코드 변경 |
reload_wifi_modules() 호출 → modprobe -r wlan → HANG |
modprobe.d 기록 + reboot-required 마커 설정; modprobe 호출 없음 |
--country-now (전문가 모드) |
동일한 reload_wifi_modules() |
호환성을 위해 인자 수용하나 재부팅 지연 처리 — 의도를 로그에 기록하고 reboot-required 유지; 라이브 모듈 언로드 없음 (v1.11.6: 행(hang)이 드라이버 언로드 경로 자체에서 발생하므로 가드된 전문가 재로드도 미탑재) |
| 국가 코드 검증 | wpa_cli get country (wpa 설정 렌더링 기반 — 거짓 양성 가능) |
live_country(): sysfs /sys/module/wlan/parameters/country_code → iw reg get 폴백 |
세 가지 호출 모드
dpworld-network-apply-hardened.sh # 온디맨드 (apply.service 또는 수동)
dpworld-network-apply-hardened.sh --boot # 부팅 시드 (라이브 터치 없음, /run 만 채움)
dpworld-network-apply-hardened.sh --country-now # 호환 별칭 — 재부팅 지연 처리 (라이브 모듈 재로드 없음)
reboot-required 마커
- 설정: 라디오의 실효 국가 코드(sysfs/iw reg)가 원하는 국가 코드와 다를 때
${STATE_DIR}/reboot-required(/opt/dpworld-network/reboot-required)에 생성. - 해제:
live_country()== 원하는 국가 코드일 때 — 즉, 업데이트된modprobe.d가 모듈 로드 시점에 적용된 재부팅 이후. - Web Configurator는 이 마커를 읽어 대시보드에 "Reboot required"를 표시한다.
빈 네트워크 부트스트랩 (v1.11.6)
--boot 실행 시(및 온디맨드 시), /home/root/network가 비어 있으면
/run/systemd/network 동기화 전에 /etc/dpworld/network-defaults에서
시드를 채운다 — 펌웨어의 팩토리/복구 부트스트랩을 복원하는 동작이다.
디렉터리에 파일이 하나라도 있으면 절대 덮어쓰지 않는다.
v1.6 Country-Deferred 설계와의 관계
v1.6에서 Python apply_engine이 도입됐으며, 이미 국가 코드 변경을
modprobe.d로 지연 처리하고 동일 마커를 설정하도록 설계되어 있다.
하드닝 스크립트는 펌웨어 레이어에서 동일하게 동작하는 계층을 추가한다 —
Python 엔진이 실행 중이지 않은 경우(예: 새 플래시 직후나 서비스 크래시 시)에도
펌웨어 자체 부팅 서비스가 modprobe hang을 유발할 수 없게 된다.
두 레이어 모두 동일한 REBOOT_MARK 경로와 MODPROBE_CONF에 기록하므로
멱등(idempotent)적이며 조합해서 사용할 수 있다. 하드닝 스크립트는 Python
엔진의 country-now 마커(/run/dpworld-network/country-now)를 확인하여
존재하면 COUNTRY_NOW=1로 승격한다 — 엔진 호환성을 유지하기 위함이다.
v1.11.6부터는 COUNTRY_NOW=1이어도 하드닝 스크립트가 WiFi 모듈을 라이브
언로드하지 않는다; 마커를 소비하고, 지연 처리를 로그에 남기며, reboot-required
마커를 유지한다.
설치 / 배포
scripts/deploy.ps1은 배포 시마다 세 가지 아티팩트를 설치한다
(해시 비교 방식, 멱등, 영속적 /lib/systemd/system):
| 로컬 경로 | 디바이스 경로 |
|---|---|
deploy/dpworld-network-apply-hardened.sh |
/usr/bin/dpworld-network-apply-hardened.sh (chmod +x) |
deploy/dpworld-network-apply.service.d/20-hardened.conf |
/lib/systemd/system/dpworld-network-apply.service.d/20-hardened.conf |
deploy/dpworld-network-seed.service.d/20-hardened.conf |
/lib/systemd/system/dpworld-network-seed.service.d/20-hardened.conf |
변경 후에는 systemctl daemon-reload가 호출된다. 세 경로 모두 영속적
읽기-쓰기 오버레이(/lib) 위에 있어 재부팅 및 유저스페이스 펌웨어 재플래시
이후에도 유지된다.
펌웨어 원본 /usr/bin/dpworld-network-apply.sh는 수정하지 않는다.
테스트
tests/test_firmware_apply_hardened.py (23개 테스트, 표준 라이브러리만 사용, 디바이스 불필요):
- 아티팩트가 예상 경로에 존재하는지 확인.
- 스크립트 내
modprobe -r구문이 전혀 없음 (가드된 전문가 경로 포함). safe_module_reload/reload_wifi_modules헬퍼가 잔존하지 않음.- 빈 네트워크 부트스트랩(
seed_defaults_if_empty)이/run동기화 전에/etc/dpworld/network-defaults에서 시드를 채우는지 확인. reboot-required마커가 올바르게 설정 및 해제되는지 확인.- 마커 판단이
live_country()를 사용하는지 확인 (wpa_cli get country아님). - 두 드롭인(drop-in) 모두
ExecStart가 하드닝 스크립트를 가리키는지 확인. deploy.ps1이 스크립트, 두 드롭인을 설치하고daemon-reload를 호출하는지 확인.- 세 아티팩트 모두 LF 줄 끝 (CRLF 없음).
펌웨어 베이킹 (BSP) — 원본 교체 방법
위 "설치 / 배포" 방식은 Web Configurator가 사용하는 라이브 디바이스 오버라이드 방식이다:
dpworld-network-apply-hardened.sh를 별도 파일로 설치하고, 두 개의 systemd 드롭인을
함께 설치하되, 펌웨어 원본 /usr/bin/dpworld-network-apply.sh는 손대지 않는다
(실행 중인 디바이스는 펌웨어에 베이킹된 원본을 덮어쓸 수 없으며, 재플래시하면 원본이 복원된다).
펌웨어 이미지(BSP 베이킹) 시에는 대신 다음과 같이 진행한다:
deploy/dpworld-network-apply-hardened.sh의 내용을/usr/bin/dpworld-network-apply.sh에 설치한다 — 즉, 원본 이름을 유지한 채 펌웨어 원본 스크립트를 교체한다 (root:root, 0755, LF, POSIX /bin/sh). 하드닝 스크립트의 헤더에 이미 이 최종 이름이 명시되어 있다.- 두 오버라이드 드롭인(
dpworld-network-apply.service.d/20-hardened.conf와dpworld-network-seed.service.d/20-hardened.conf)은 베이킹하지 않는다. 이 드롭인들은 라이브 디바이스 오버라이드 전용이며…-hardened.sh를 가리키는데, 베이킹 이미지에는 해당 파일이 존재하지 않는다. 펌웨어 자체의dpworld-network-seed.service/dpworld-network-apply.service가 이미/usr/bin/dpworld-network-apply.sh를 호출하므로 하드닝 동작이 자동으로 적용된다. - 하드닝 스크립트는 원본을 완전히 독립적으로 재작성한 것으로 (모든 호출 모드 포함:
인자 없음 /
--boot/--country-now), 원본을 내부적으로 호출하지 않는다. 베이킹 전 펌웨어 원본과 diff를 수행해 펌웨어 고유 책임 사항이 누락되지 않았는지 확인할 것.
예상되는 동작 변경: 하드닝 스크립트는 QCA6490 / cnss_pci 모듈에서 PMU 워치독
재부팅 루프를 유발하는 라이브 modprobe -r wlan (국가 코드 라이브 재로드)을
제거한다 — 이전의 --country-now 전문가 경로 포함 (v1.11.6). 국가 코드 변경은
항상 재부팅 지연 처리된다: 새 국가 코드가 modprobe.d에 기록되고 reboot-required
마커가 설정되며, 다음 재부팅 시에 적용된다. 이는 P2 수정이며 회귀가 아니다.