ESP32-C3용 Pi-hole 스타일 DNS 광고 차단기
요약
본 기사는 ESP32-C3를 활용하여 PSRAM 없이 Pi-hole 스타일 DNS 광고 차단기를 구현하는 방법을 설명합니다. 기존 방식이 RAM에 블록리스트 문자열을 로드해 PSRAM을 요구했던 것과 달리, 이 프로젝트는 도메인을 플래시 메모리에 40비트 해시로 저장하고 이진 탐색하여 효율성을 극대화했습니다.
핵심 포인트
- PSRAM 없이 ESP32-C3에서 DNS 광고 차단기 구현 가능
- 도메인 블록리스트를 플래시의 정렬된 40비트 해시로 저장
- 이진 탐색을 통해 낮은 RAM 사용량(약 50KB)과 빠른 검색 속도(~10ms) 달성
$2 ESP32-C3에서 작동하는 Pi-hole 스타일의 DNS 광고 차단기입니다. PSRAM이 필요하지 않습니다.
📰 Tom's Hardware, XDA Developers, Korben에 소개되었습니다.
모두가 놓치는 트릭: 블록리스트를 RAM에 유지할 필요가 없습니다. 도메인을 플래시(flash)의 정렬된 40비트 해시로 저장하고 이진 탐색(binary-search)합니다. 140,000개 이상의 도메인이 약 0.7 MB의 플래시에 들어가며, ~50 KB의 RAM을 사용하여 ~10ms 만에 일치 여부를 확인할 수 있습니다.
query in ──▶ extract domain ──▶ FNV-1a 해시 (+ 부모 접미사)
──▶ 플래시 해시 테이블 이진 탐색
├─ hit ──▶ answer 0.0.0.0 (sinkholed)
...
대부분의 ESP32 DNS 싱크홀(sinkhole)은 블록리스트(도메인 문자열)를 RAM에 로드하므로 PSRAM을 요구합니다. 이 프로젝트는 대신 고정된 5바이트 (40비트) 해시를 플래시에 저장합니다:
| string-in-RAM 방식 | 본 방식 (hash-in-flash) | |
|---|---|---|
| 하드웨어 | ESP32 + PSRAM (~$8) | ESP32-C3, PSRAM 불필요 (~$2) |
| ... | ||
| 왜 40비트인가? 이 플래시 예산에 가장 적합합니다. 충돌(Collision)은 생일 곡선(birthday bound)을 따르는데, 141k 도메인에서는 ~0개, 537k 도메인에서는 약 1개(즉, 운이 나쁜 도메인이 과도하게 차단되는 경우)가 발생합니다. 32비트로 낮추면 플래시를 20% 절약하지만 250k에서 ~7개의 충돌을 감수해야 하고, 64비트로 올리면 문제가 없는 곳에 도메인당 3바이트를 낭비하게 됩니다. |
같은 트릭은 더 큰 칩에서도 작동합니다. C3의 우회책이 아닙니다. 16 MB ESP32-S3에서는 이 해시가 ~2.7M 도메인을 담을 수 있는 반면, PSRAM 8 MB에 문자열로는 ~466k만 가능합니다. 플래시의 해시는 거의 모든 곳에서 PSRAM의 문자열보다 우수하며, C3는 이를 명확하게 만듭니다.
- 모든 ESP32-C3 보드 (C3 SuperMini에서 테스트), 4 MB 플래시, PSRAM 불필요 - 클래식 ESP32(DevKit / WROOM, 4 MB)도 빌드 가능:
pio run -e esp32dev -t upload
(커뮤니티 기여 및 컴파일 테스트 완료; C3가 테스트 대상입니다) - 안정적인 USB 전원 (휴대폰 충전기 또는 라우터의 USB 포트)에서 전원을 공급하세요. 저렴하거나 느슨한 USB-C→A 어댑터는 WiFi 전송 중 라디오에 전압 강하(brown out)를 일으킬 수 있습니다. - USB-A → USB-C 동글을 사용하면 대부분의 라우터 뒷면 여분 USB 포트에 바로 꽂을 수 있어 전원 공급 장치나 추가 박스가 필요 없습니다.
C3 SuperMini용 인쇄 케이스: hardware/esp32-c3-supermini-enclosure.stl
인쇄 참고 사항:
- 서포터(supports)가 필요 없습니다. 0.2 mm 레이어, 약 15% 채움 밀도(infill)면 충분합니다.
안테나 끝 부분을 비워두세요. C3의 PCB 안테나는 USB-C 포대기 반대편 짧은 가장자리에 있는 지그재그 트레이스입니다. 이것을 단단한 플라스틱 안에 묻거나 금속을 가까이 두지 마십시오. 그렇지 않으면 RSSI(수신 신호 세기)가 저하될 수 있습니다.- 통풍구는 열어두세요: 보드는 약 45–55 °C에서 작동합니다.
작동하려면 USB 플래시 메모리 하나만 있으면 됩니다. 그 이후로는 펌웨어와 블록 목록 모두 WiFi를 통해 업데이트됩니다 (아래 참조).
⚠️ 현재 PlatformIO— VSCode의 PlatformIO 확장 프로그램에 번들된 코어, 또는 pip install -U platformio을 venv에서 사용하십시오.
distro/apt의 platformio 패키지(예: 4.3.4)는 너무 오래되어 AttributeError: ... 'resultcallback' (issue #4) 오류가 발생합니다. 원클릭 브라우저 설치 프로그램이 곧 출시될 예정입니다 (호스팅 장소 미정).
# 1. 시크릿 템플릿(gitignored, 로컬에 유지됨)을 복사하고 편집합니다:
# - WIFI_SSID / WIFI_PASS는 선택 사항입니다 — 플레이스홀더를 그대로 두고 대신 (아래의) 기기 설정 포털을 사용하십시오.
...
build_blocklist.py OUT.bin [SOURCE ...]
은 다음 형식의 모든 URL 및 로컬 파일을 처리합니다:
hosts 파일—0.0.0.0 ads.example.com tracker.example.com
(줄에 있는 모든 도메인이 포함됩니다)일반 도메인 목록—한 줄에 하나의 도메인AdGuard / Adblock 기본 규칙—||ads.example.com^
블록, @@||ok.example.com^
은 도메인을 제거합니다 (예: AdGuard Home 허용 목록 미러링).
차단된 도메인은 해당 서브도메인도 차단합니다. DNS 해시 목록이 표현할 수 없는 규칙(정규식, 와일드카드, $ 수정자, 시각적 ## 규칙)은 건너뛰고 카운트됩니다. @@ 규칙은 정확히 그 항목만 해제합니다 — 차단된 상위 도메인에서 서브도메인을 분리할 수는 없습니다. 소스를 다운로드할 수 없으면 빌드는 중단되며, 작아진 목록을 조용히 생성하려면 (--allow-missing) 옵션을 사용해야 합니다.
연결할 수 없거나 (secrets.h를 설정하지 않은 경우)
)", 이는 개방형 액세스 포인트(C3-AdBlock-XXXX)를 시작하며, 여기에 캡티브 포털이 제공됩니다. 휴대폰으로 접속하여 네트워크를 선택하고 비밀번호를 입력하면 끝입니다. 나중에 다른 네트워크로 옮기려면 대시보드에서 **WiFi 지우기(Forget WiFi)**를 클릭하거나 전원을 켜는 동안 BOOT 버튼을 길게 누르면 설정 포털이 다시 나타납니다. (/forgetwifi는 이제 인증이 필요하므로, 단순히 방문할 수 있는 URL은 아닙니다. 자세한 내용은 아래 보안 섹션을 참조하세요.) **http://c3adblock.local**의 대시보드에서 모든 것을 처리합니다:
블록리스트(Blocklist) — 새로 빌드된 blocklist.bin을 블록리스트 → 업로드에 드롭하거나, 원격 자동 업데이트 아래에 URL을 설정하면 장치가 스케줄에 따라 미리 빌드된 blocklist.bin을 가져옵니다. 기본 목록은 GitHub Actions에 의해 매주 월요일 재빌드되어 안정적인 URL에서 게시되므로, 이 주소만 한 번 붙여넣으면 장치를 스스로 최신 상태로 유지할 수 있습니다: https://github.com/M-Abozaid/esp32-c3-adblock/releases/download/blocklist/blocklist.bin
펌웨어(Firmware) — 펌웨어 → OTA 업데이트 아래에 .pio/build/c3/firmware.bin을 업로드합니다. 장치는 이를 확인하고 새 이미지로 재부팅됩니다. 또는 CLI에서 WiFi를 통해 푸시할 수 있습니다: pio run -t upload --upload-port c3adblock.local --upload-protocol espota
4MB 플래시 트레이드오프: 펌웨어 OTA는 두 개의 앱 슬롯이 필요하며, 이는 블록리스트를 위해 약 1.3MB(최대 ~250k 도메인)를 남깁니다. 공격적인 537k
이것 없이는 LAN에서 장치에 접근할 수 있는 누구나 임의의 펌웨어로 재플래시하거나, 자격 증명 없이 블록리스트를 다시 작성할 수 있습니다. 이 점은 해당 장치가 네트워크의 모든 DNS 쿼리 경로에 놓여 있다는 점에서 알아둘 가치가 있습니다.
사용자 지정 차단 도메인 이름은 대시보드에 표시되기 전에 HTML-이스케이프(HTML-escaped)되어 저장된 XSS(stored-XSS) 경로를 막습니다. 이 경로는 마크업을 포함하는 도메인 문자열이 (/addblock를 통해 추가될 경우) 보는 브라우저에서 실행되는 것을 방지합니다.
여기서의 Basic Auth는 LAN 신뢰 경계 제어일 뿐, 암호화가 아닙니다. 모든 것은 :80 포트에서 평문 HTTP(plain HTTP)입니다. 이 칩은 TLS 서버를 구동할 현실적인 예산이 없습니다. Basic Auth 자격 증명은 base64로 인코딩되었을 뿐(암호화되지 않음)이며, 인증된 요청마다 전송됩니다. 이미 LAN 트래픽을 스니핑(sniffing)할 수 있는 사람(open/guest WiFi, ARP 스푸핑 등)이라면 와이어에서 이를 읽어낼 수 있습니다. 이는 일반적인 경우—즉, 네트워크의 다른 장치가 자격 증명 없이 API에 접근하거나, 브라우저 탭이 CSRF를 시도하는 경우—에는 강하지만, 경로상의 네트워크 공격자에게는 그렇지 않습니다.
캐시된 Basic Auth를 통한 CSRF: 브라우저는 캐시된 Basic Auth 자격 증명을 이미 인증된 원본(origin)으로의 모든 후속 요청에 자동으로 첨부합니다. 여기에는 같은 브라우저가 나중에 방문하는 완전히 관련 없는 페이지로 인해 촉발되는 경우도 포함됩니다 (예: `<img src=
으로 첨부합니다. 여기에는 같은 브라우저가 나중에 방문하는 완전히 관련 없는 페이지로 인해 촉발되는 경우도 포함됩니다 (예: <img src= , 장치가 '비밀번호'를 가지고 부팅되지만, 이 비밀번호는 공개적입니다 (이 레포의 예제 파일에 있습니다). 펌웨어는 시리얼 포트를 통해 경고를 기록하고 대시보드에 배너를 표시하지만, 여전히 부팅되고 실행되므로 secrets.h에 실제 값을 설정하는 것을 건너뛰지 마십시오.
완전히 통제하지 않는 네트워크에서 이 기능을 신뢰하기 전에 다음 사항을 유의하십시오.
현재 범위 외: WiFi 설정 포털의 액세스 포인트 (C3-AdBlock-XXXX)는 설계상 여전히 열려 있습니다 (암호화되지 않음). 이는 비밀번호를 모른 채로 먼저 접속할 수 있어야 하기 때문입니다. 포털에 입력하는 실제 WiFi 비밀번호는 짧은 설정 시간 동안 로컬 라디오 링크만큼만 안전합니다.
장치의 DNS를 C3의 IP 주소로 지정하거나, 주요 DNS 뒤에 **보조 리졸버(secondary resolver)**로 추가하십시오. 테스트:
dig @<c3-ip> doubleclick.net # -> 0.0.0.0 (차단됨)
dig @<c3-ip> github.com # -> 실제 IP (포워딩됨)
ModemManager(Fedora/Ubuntu의 기본값)가 /dev/ttyACM0를 가져와 DTR/RTS를 토글하며, 이는 C3를 재설정하고 시리얼 포트를 차단합니다. 해결책: sudo systemctl stop ModemManager 후 `echo 'ATTRS{idVendor}==
s60sc/ESP32_AdBlocker에서 영감을 받은, '차단 목록에 있는 도메인에 대해 0.0.0.0을 응답'하는 아이디어입니다. 이는 PSRAM이 없는 칩을 위한 hash-in-flash 최적화에 중점을 둔 독립적인 처음부터 구현체입니다.
MIT — LICENSE를 참조하십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub Trending All (daily)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기