업데이트로 돌아가기
UpdatedAug 6, 2026

foxcage — Updated!

루트리스 Podman 컨테이너에서 capabilities를 제거하고 네트워킹을 격리하며 임시 스토리지를 사용하여 Firefox를 실행함으로써 샌드박스 탈출을 격리하고 호스트 손상을 방지합니다.

공유

foxcage 아이콘 foxcage

루트리스 Podman 컨테이너에서 Firefox를 실행하여 보안 격리를 제공합니다. 브라우저는 거의 모든 Linux capability 없이, 자체 사용자 및 네트워크 네임스페이스에서 호스트와 격리된 상태로 실행되면서도 완전한 GPU 가속, 오디오, DRM 지원을 유지합니다.

왜 foxcage인가?

Firefox는 이미 Linux 네임스페이스와 seccomp-bpf를 사용하여 웹 콘텐츠 렌더러를 격리하는 다중 프로세스 샌드박스를 갖추고 있습니다. 대부분의 위협에 대해 이는 효과적입니다. foxcage는 두 번째 방어벽을 추가합니다: 공격자가 Firefox의 샌드박스를 탈출하는 취약점을 악용하면(실제로 발생하며, 이에 대한 CVE가 존재합니다), 전체 사용자 세션 대신 잠긴 컨테이너 안에 갇히게 됩니다.

foxcage가 보호하는 것

  • 익스플로잇 후 파일 접근. 일반 Firefox에서 샌드박스 탈출은 사용자가 읽을 수 있는 모든 것에 접근할 수 있게 합니다: ~/.ssh, ~/.gnupg, 다른 브라우저의 프로필, 비밀번호 관리자 데이터베이스, 문서, 소스 코드. foxcage에서는 공격자가 명시적으로 마운트한 것만 볼 수 있습니다.
  • 디스크 추적 잔여물. @tmp 임시 케이지는 창을 닫은 후 디스크에 흔적을 남기지 않습니다 — Firefox의 개인 브라우징이 여전히 유지하는 확장 프로그램, HSTS 상태, TLS 세션 캐시, DNS 캐시를 포함합니다. 여러 @tmp 케이지는 서로 간섭 없이 동시에 실행됩니다.
  • 지속성. 일반 Firefox에서는 악성 코드가 ~/.config/autostart, ~/.bashrc, cron 또는 다른 곳에 작성하여 재부팅 후에도 생존할 수 있습니다. foxcage의 임시 컨테이너(--rm)는 바인드 마운트하지 않는 한 아무것도 유지되지 않음을 의미합니다.
  • 네트워크 측면 이동. 기본적으로 컨테이너는 localhost의 서비스를 탐색할 수 없습니다. 일반 Firefox에서는 샌드박스 탈출이 전체 네트워크 접근 권한을 갖습니다. (케이지가 localhost 접근이 필요한 경우, 예: 로컬 개발, [network] mode = "host"를 사용하세요 — 단, "네트워킹" 아래의 주의 사항을 참조하세요: 호스트 모드는 호스트의 추상 Unix 소켓도 노출합니다.)
  • 권한 상승. 컨테이너는 CAP_SYS_CHROOT을 제외한 모든 Linux capability를 제거하고 새로운 권한 획득을 차단합니다. Setuid 바이너리, 난해한 syscall을 통한 커널 익스플로잇 및 유사한 상승 경로가 차단됩니다.

foxcage가 보호하지 않는 것

  • 브라우저 수준 공격. 피싱, 악성 확장 프로그램 및 Firefox의 정상 기능 내에서 작동하는 모든 것은 영향을 받지 않습니다 — foxcage는 컨테이너를 호스트로부터 격리할 뿐, 사용자를 브라우저로부터 격리하지 않습니다.
  • 바인드 마운트된 디렉터리. 마운트한 모든 것(profile, downloads_dir, 추가 바인드 마운트)은 손상된 브라우저가 완전히 접근할 수 있습니다. 호스트 프로필 디렉터리를 마운트하면 공격자가 일반 Firefox에서와 마찬가지로 이를 변조할 수 있습니다.
  • PulseAudio를 통한 오디오 캡처. PulseAudio 소켓이 컨테이너에 바인드 마운트됩니다. 파일시스템 수준에서 읽기 전용으로 마운트되지만, Unix 도메인 소켓은 양방향입니다 — 손상된 프로세스가 여전히 소켓을 통해 녹음 요청을 보낼 수 있습니다. 브라우저 샌드박스 탈출은 호스트 마이크에서 오디오를 녹음할 수 있습니다.
  • Wayland 컴포지터 익스플로잇. Wayland 소켓이 전달됩니다. Wayland 컴포지터는 설계상 클라이언트를 서로 격리하지만, 컴포지터 자체의 취약점은 도달 가능합니다.

보안 구성

컨테이너는 다음으로 실행됩니다:

  • 모든 Linux capability 제거(CAP_SYS_CHROOT만 Firefox 콘텐츠 샌드박스를 위해 다시 추가; init.root가 구성된 경우 CAP_SETUID/CAP_SETGID가 일시적으로 추가)
  • 권한 상승을 방지하는 no-new-privileges
  • 루트리스 사용자 네임스페이스(--userns keep-id)
  • 호스트와 공유되지 않는 전용 /dev/shmshm_size로 크기 구성 가능
  • 호스트 루프백이 기본적으로 차단된 pasta를 통한 격리된 네트워킹
  • DNS는 기본적으로 호스트 DNS 사용(network.dns로 구성 가능)
  • XDG_RUNTIME_DIR의 특정 소켓만 바인드 마운트됨(Wayland, PulseAudio, PipeWire 및 필터링된 D-Bus 프록시) — 전체 호스트 런타임 디렉터리는 절대 노출되지 않음
  • 호스트의 D-Bus 세션 버스 접근은 항상 호스트에서 실행되는 필터링된 xdg-dbus-proxy를 통해 중재됩니다. org.freedesktop.Notifications, org.freedesktop.portal.Desktop, org.mozilla.* 및 (포크의 경우) 포크 자체 네임스페이스(예: org.librewolf.*)만 도달 가능합니다 — 키링 및 SSH/GPG 에이전트와 같은 세션 서비스는 차단됩니다
  • 포털 접근은 광범위합니다. org.freedesktop.portal.Desktop 전체가 허용됩니다. 파일 선택기, "다른 앱에서 링크 열기", 화면 공유가 이 방식으로 작동하기 때문입니다. 또한 RemoteDesktop(전체 세션에 대한 가상 키보드/마우스), CameraLocation도 노출합니다. 이들은 foxcage가 아닌 데스크톱 자체의 승인 대화 상자에 의해 제어됩니다 — 그리고 RemoteDesktop 프롬프트는 화면 공유 프롬프트와 유사하므로 승인 대화 상자를 수락하기 전에 읽어보세요. xdg-dbus-proxy에는 "인터페이스 하나 거부" 규칙이 없으므로, 이를 좁히려면 Firefox가 필요로 하는 모든 인터페이스를 열거해야 합니다. 기본적으로 이 작업을 수행하지 않는 이유는 docs/DESIGN.md를 참조하세요
  • 모든 바인드 마운트(profile, downloads_dir, 추가 [mounts] bind)는 nosuid,noexec 사용
  • 브라우저 다운로드는 GPG 서명으로 검증: Firefox는 Mozilla의 서명된 SHA-512 체크섬, LibreWolf는 LibreWolf 유지관리자의 분리 서명과 함께 SHA-256을 검증합니다. 검증은 gpg --verify보다 엄격합니다. gpg --verify해지된 키로 만든 서명과 키링의 모든 키에 대해 종료 코드 0을 반환하기 때문입니다. foxcage는 추가로 서명이 고정된 기본 키로 체이닝되도록 요구하며, 소유자가 손상된 것으로 해지한 하위 키로 서명된 릴리스를 거부합니다 — 해지된 서명 키 참조
  • 임시 컨테이너(--rm) — 파일시스템 쓰기는 종료 시 손실됨
  • 명시적으로 활성화하지 않는 한 호스트 장치(웹캠, 보안 키, 프린터) 전달 없음

활성화하는 각 [network][mounts] 옵션은 편의성을 위해 일부 격리를 희생합니다. 기본값은 여전히 사용 가능한 브라우저를 제공하는 가장 제한적인 구성입니다.

요구 사항

  • Python 3.11+
  • Podman(루트리스)
  • Wayland 컴포지터(X11은 지원되지 않음)
  • pasta(sudo apt install passt) — network.mode = "host"가 아닌 경우
  • xdg-dbus-proxy(sudo apt install xdg-dbus-proxy)
  • PulseAudio 또는 PulseAudio 호환성을 갖춘 PipeWire(오디오용)
  • DRI 지원 GPU — 선택 사항; /dev/dri가 없으면 foxcage가 경고하고 Firefox가 소프트웨어로 렌더링합니다. Intel, AMD 및 nouveau용 VA-API 드라이버가 이미지에 설치되어 있어 호스트 드라이버 패키지 없이 하드웨어 비디오 디코딩이 작동합니다 — 하드웨어 비디오 디코딩 참조

foxcage를 루트나 sudo가 아닌 일반 데스크톱 사용자로 실행하세요 — 샌드박스가 사용자를 컨테이너에 매핑하며, 루트로 실행하면 foxcage가 존재하는 이유인 격리가 제거됩니다. 루트로 시작하는 것을 거부합니다.

테스트 환경: GNOME 3이 포함된 Debian 13(Trixie). 다른 Linux 배포판과 Wayland 컴포지터는 작동할 수 있지만 테스트되지 않았습니다.

설치

foxcage는 Python 표준 라이브러리 외부에 의존성이 없는 단일 Python 스크립트입니다. PATH의 디렉터리에 복사하세요:```sh sudo cp foxcage /usr/local/bin/foxcage

사용자 로컬 설치의 경우:```sh
cp foxcage ~/.local/bin/foxcage

스크립트를 실행 가능하게 만드세요(chmod +x foxcage).

foxcage --version으로 어떤 리비전을 사용 중인지 확인하세요 — foxcage는 단일 파일을 복사하여 설치되므로 문제를 보고할 때 유용합니다.

사용법```sh

./foxcage

첫 실행 시 스크립트가 컨테이너 이미지를 빌드하고(Mozilla에서 Firefox를 다운로드하고, 최소한의 Debian 의존성을 설치) 그다음 Firefox를 시작합니다. 이후 실행에서는 foxcage가 Firefox 업데이트를 확인하고, 새 버전이 사용 가능하면 이미지를 자동으로 다시 빌드합니다. 이미지는 시스템 패키지 업데이트를 반영하기 위해 주기적으로(기본적으로 7일마다) 다시 빌드됩니다. 업데이트 확인이 실패하면(네트워크 오류, 시간 초과) 경고가 기록되고 기존 이미지가 사용됩니다 — 시작이 차단되는 일은 없습니다.

Firefox에 인수를 전달합니다:```sh
./foxcage https://example.com

명명된 케이지를 Firefox 플래그와 결합:```sh ./foxcage @work --kiosk https://example.com

케이지가 이미 실행 중이면, 두 번째 컨테이너를 시작하는 대신 URL이 기존 브라우저의 새 탭에서 열립니다. 실행 중인 케이지에 대해 URL 없이 `foxcage`(또는 `foxcage @cage`)를 실행하면 "cage is already running" 메시지와 함께 정상적으로 종료됩니다. foxcage는 컨테이너 외부에서 기존 Wayland 창을 띄울 수 없으므로 시도하지 않습니다.

케이지가 이미 실행 중일 때는 실행 시 플래그가 적용되지 **않습니다**. `--dns`, `--ipv4-only`, `--lifetime`, `--color`, `--fork`는 컨테이너가 시작될 때 사용되며, 실행 중인 컨테이너의 설정은 외부에서 변경할 수 없으므로 경고와 함께 무시됩니다. 적용하려면 케이지를 닫고 다시 실행하세요.

> 비공개 모드 세션에는 `private_browsing` 구성 키를 사용하세요. Firefox의 원시 `--private-window` CLI 플래그가 **아닙니다**. 구성 키는 세션 전체에 비공개 모드를 설정하므로(`browser.privatebrowsing.autostart`), 이후 `foxcage @cage URL` 호출로 탭에서 다시 열 수 있습니다. Firefox 패스스루로서의 `--private-window`는 첫 번째 창만 비공개로 만들고 위의 탭 재열기 동작을 깨뜨립니다.
>
> **참고:** `private_browsing = true`로 활성화된 세션은 Firefox의 일반적인 비공개 창 UI 표시(보라색 강조 막대, 마스크 아이콘, 제목의 "(Private Browsing)")를 보여주지 않습니다. 세션의 모든 창이 비공개이므로 Firefox가 시각적으로 대비할 비공개가 아닌 창이 없어 표시기를 숨기기 때문입니다. 세션은 실제로 진정한 비공개입니다. 확인하려면 케이지에서 `about:privatebrowsing`(표준 비공개 브라우징 정보 페이지 표시) 또는 `about:config`에서 `browser.privatebrowsing.autostart = true`를 확인하세요.

### `@tmp`로 임시 브라우징

흔적을 남기지 않아야 하는 일회성 링크에는 예약된 `tmp` 케이지를 사용하세요.```sh
./foxcage @tmp https://somewhere-suspicious.example

Every @tmp 실행은 프로필이 유지되지 않는 일회용 Firefox입니다. 창이 닫히면 모든 것이 사라집니다 — 쿠키, 캐시, 기록, 확장 프로그램, HSTS 상태, TLS 세션 캐시, DNS 캐시, 저장된 탭 상태. 이는 확장 프로그램과 상당량의 디스크 상태를 여전히 유지하는 Firefox 비공개 브라우징보다 더 철저합니다.

여러 @tmp 격리 환경이 동시에 실행되며, 각각 서로 격리됩니다. 메뉴 표시줄에 FoxCage - tmp (<짧은 id>)가 표시되어 동시에 실행되는 임시 창을 구분할 수 있습니다.

임시 격리 환경은 시작 시 빈 페이지와 빈 새 탭을 엽니다 — 기본 Firefox 홈 페이지와 새 탭 콘텐츠(인기 사이트, Pocket 추천, 활동 스트림)는 곧 폐기될 새 프로필에서 순수한 노이즈일 뿐이므로 억제됩니다. 영구 격리 환경은 Firefox 기본값을 유지합니다.

이름이 지정된 임시 격리 환경

일회용 세션에 의미 있는 이름을 붙이고 싶다면(예: 새 탭에서 다시 열고 싶은 연구 토끼굴), @tmp-<이름>을 사용하세요:```sh ./foxcage @tmp-research https://example.com # first call → new window ./foxcage @tmp-research https://another.example # second call → new tab in the existing window

`@tmp-<name>`은 여전히 임시적입니다. 창을 닫으면 모든 것이 사라집니다. 일반 `@tmp`와 다른 점은, 같은 이름으로 두 번째 실행하면 **기존 창을 재사용**한다는 것입니다(영구 케이지와 동일). 따라서 나중에 병렬 복사본을 시작하지 않고도 탭을 더 추가할 수 있습니다. 일반 `@tmp`는 "실행할 때마다 새로 생성되는 일회용" 동작을 유지합니다.

메뉴 바 레이블에는 선택한 이름(`FoxCage - tmp-research`)이 표시되므로 창이 의미 있게 라벨링됩니다.

#### 임시 기본값 사용자 지정

`~/.config/foxcage/tmp.toml`을 생성하여 모든 임시 케이지(일반 `@tmp` 및 모든 `@tmp-<name>`)의 기본값을 설정할 수 있습니다. 예를 들어:```toml
private_browsing = true
lifetime = "30m"
extensions = ["ublock-origin"]

[network]
dns = "cloudflare"

매번 임시 실행은 이제 비공개 창, uBlock Origin, Cloudflare DoH를 갖추고 30분 후 자동으로 종료됩니다 — 완전한 임시성이 유지된 채로 말이죠. 이름이 지정된 임시 케이지는 기본적으로 tmp.toml을 상속합니다. 이름별로 재정의하려면 ~/.config/foxcage/tmp-<name>.toml을 생성하세요. 그러면 해당 파일이 tmp.toml 대신 적용됩니다 — 병합은 없으며, 더 구체적인 파일이 완전히 우선합니다. 공통 기본값을 원한다면 그 파일에 복사해 넣으세요.

일반 케이지의 설정에서 설정할 수 있는 모든 것은 여기서도 작동합니다. 단, 임시성 자체를 무력화시키는 다음 키 하나는 예외입니다:

  • profile — 하드 오류.

이 키는 호스트에 영구적으로 존재하는 프로필 디렉터리를 가리키며, 이는 @tmp의 목적과 정면으로 모순됩니다. 영구 프로필을 가진 샌드박스 케이지를 원한다면 tmp-로 시작하지 않는 일반 이름 케이지(@work, @research 등)를 사용하세요.

사전 설치된 확장 프로그램

케이지는 이미지에 확장 프로그램을 내장하고 모든 실행 시 강제 설치하도록 할 수 있습니다:```toml extensions = ["ublock-origin"]

`~/.config/foxcage/tmp.toml`에 넣으면 **모든 임시 케이지가 uBlock Origin이 이미 실행된 상태로 시작됩니다** — 이는 중요한데, 일회용 케이지는 그렇지 않으면 가장 보호받지 못하는 브라우저이기 때문이며, 정확히 가장 신뢰도가 낮은 링크에서 사용됩니다. 새 `@tmp` 프로필에는 확장 기능이 전혀 없으며, 닫히면 스스로 삭제되는 세션에서 수동으로 설치하는 것은 의미가 없습니다.

각 항목은 애드온의 addons.mozilla.org URL에 있는 짧은 이름, 목록 URL 자체 또는 애드온 ID일 수 있습니다:```toml
extensions = [
    "ublock-origin",
    "https://addons.mozilla.org/firefox/addon/noscript/",
    "[email protected]",
]

추가 기능은 시작 시 addons.mozilla.org API를 통해 확인되며, 이미지 빌드 중에 다운로드되고 AMO가 게시하는 SHA-256과 대조하여 검증됩니다. 확인된 버전은 Containerfile의 일부이므로, 확장 프로그램의 새 릴리스는 이미지 해시를 변경하고 재빌드를 트리거합니다. 확장 프로그램은 Firefox와 동일한 방식으로, 그리고 동일한 이유로 업데이트됩니다. 즉, 런타임에 아무것도 설치되지 않으므로 새 임시 프로필은 어떤 것도 다시 다운로드하지 않습니다.

엔터프라이즈 정책으로 수동 설치가 아닌 방식으로 설치되기 때문에:

  • 케이지 내부에서 제거하거나 비활성화할 수 없습니다(about:addons에 조직에서 설치한 것으로 표시됨).
  • 비공개 창에서 활성화되므로 private_browsing = true로 실행되는 케이지에서도 계속 작동합니다. 이 기능은 Firefox 136 또는 ESR 128.8이 필요합니다. 이전 버전은 설정을 무시하고 비공개 창에서 추가 기능을 비활성 상태로 둡니다.
  • 해당 아이콘이 도구 모음에 배치되므로 차단기가 실제로 있는지 확인할 수 있습니다.
  • 브라우저 내 확장 프로그램 업데이트는 꺼져 있습니다. 업데이트는 이미지 재빌드와 함께 제공됩니다.

제한 사항:

  • AMO 전용. 확장 프로그램은 addons.mozilla.org에 등재되어 있어야 합니다. 해당 조회가 정책 항목에 필요한 추가 기능 ID와 빌드가 검증하는 다이제스트를 제공합니다. 단순한 .xpi URL은 거부됩니다.
  • 버전 고정 없음. 재빌드는 AMO가 현재 추가 기능의 최신 버전으로 나열하는 것을 가져옵니다.
  • 빌드하려면 AMO에 연결할 수 있어야 합니다. 조회가 실패하고 케이지에 이미 이미지가 있으면 foxcage는 경고하고 기존 이미지로 시작합니다. 케이지에 아직 이미지가 없으면 요청한 추가 기능이 누락된 케이지를 조용히 빌드하는 대신 종료합니다.
  • 모든 임시 케이지는 하나의 이미지를 공유합니다. @tmp와 모든 @tmp-<name>은 단일 foxcage-tmp 이미지를 빌드하므로, 하나의 이름이 지정된 임시 케이지에 tmp.toml과 다른 extensions 목록을 지정하면 두 케이지가 번갈아 실행될 때 서로를 재빌드하게 됩니다. 임시 확장 프로그램 목록은 tmp.toml에 유지하세요.

실행별 DNS 재정의

--dns 플래그(및 동등한 network.dns 구성 키)는 세 가지 형식을 허용합니다.```sh ./foxcage @tmp --dns 1.1.1.1 https://example.com # IP ./foxcage @tmp --dns cloudflare https://example.com # alias ./foxcage @tmp --dns https://dns.nextdns.io/ # custom DoH URI

**값이 알려진 제공자(별칭 또는 IP 기준)와 일치하면 foxcage는 해당 제공자에 대한 강제 DNS over HTTPS를 자동으로 활성화합니다.** Firefox의 TRR은 모드 3(엄격 모드, 평문 폴백 없음)으로 설정되고 부트스트랩 주소가 채워져 시작 시 암호화되지 않은 해석 누출이 발생하지 않습니다. `Enabling DNS over HTTPS via Cloudflare` 같은 한 줄 알림이 stderr에 표시됩니다.

내장 별칭:

| 별칭 | IP | 필터링 |
|-------|------|-----------|
| `cloudflare` | 1.1.1.1 | 없음 |
| `cloudflare-security` | 1.1.1.2 | 악성코드 차단 |
| `cloudflare-family` | 1.1.1.3 | 악성코드 + 성인 콘텐츠 차단 |
| `google` | 8.8.8.8 | 없음 |
| `quad9` | 9.9.9.9 | 악성코드 차단(Quad9 기본값) |
| `quad9-unfiltered` | 9.9.9.10 | 없음 |
| `adguard` | 94.140.14.14 | 광고 + 추적기 차단 |
| `adguard-family` | 94.140.14.15 | 광고 + 추적기 + 성인 콘텐츠 차단 |
| `opendns` | 208.67.222.222 | 일부 |

표에 없는 IP(예: LAN의 Pi-hole)는 평문으로만 유지됩니다. foxcage가 해당 DoH 엔드포인트를 알지 못하므로 DoH가 활성화되지 않습니다. 그런 경우 URI 형식을 사용하세요: `--dns https://pi.hole/dns-query`(유효한 인증서 포함)는 DoH를 활성화하고 컨테이너 DNS는 그대로 둡니다.

URI 형식은 컨테이너의 평문 DNS 설정을 건너뛰므로 컨테이너 내부에서 Firefox가 아닌 모든 것은 여전히 호스트의 DNS를 사용합니다. 이는 의도된 동작입니다. `--dns URI`는 "Firefox가 이 DoH 리졸버를 사용하게 하라"는 의미이며, 그 이상도 이하도 아닙니다.

`--dns`는 이미 전체 호스트 네트워크 액세스 권한이 있는 `network.mode = "host"`와 호환되지 않습니다.

### 시각적 케이지 식별

모든 이름이 지정된 케이지는 메뉴 바에서 강조 색상을 얻으므로 창을 한눈에 구분할 수 있습니다. **아무것도 구성할 필요가 없습니다.** 색상은 케이지 이름에서 결정적으로 파생됩니다(SHA256 해시를 색상조로 변환하고 채도와 명도는 고정). `@banking`, `@work`, `@personal`, `@tmp-research`는 모두 별도의 안정적인 색상을 자동으로 얻습니다.

기본(익명) 케이지는 내장된 주황색을 유지합니다.

자동 파생 색상을 재정의하려면 명시적으로 설정하세요:```toml
# ~/.config/foxcage/banking.toml
color = "#dc2626"   # red — overrides the auto-derived colour

安装

通过 pip 安装

pip install pywhat

通过源码安装

git clone https://github.com/bee-san/pyWhat
cd pyWhat
pip install -r requirements.txt
python -m pywhat

通过 Docker 安装

docker pull ghcr.io/bee-san/pywhat:latest
docker run --rm -it ghcr.io/bee-san/pywhat:latest --help

使用

命令行

pywhat "Hello, my name is John and my email is [email protected]"

Python 库

import pywhat

# 识别文本中的内容
results = pywhat.identify("My email is [email protected]")
print(results)

Docker

docker run --rm -it ghcr.io/bee-san/pywhat:latest "Hello, my name is John and my email is [email protected]"

功能

  • 识别文本中的电子邮件地址、URL、IP 地址、哈希值、信用卡号等
  • 支持多种数据格式的识别
  • 可扩展的规则系统,允许用户添加自定义识别规则
  • 支持从文件或标准输入读取文本
  • 输出格式灵活,支持 JSON 输出

规则

pyWhat 使用规则来识别文本中的内容。规则定义在 pywhat/rules.py 文件中。每个规则包含:

  • name:规则的名称
  • regex:用于匹配文本的正则表达式
  • description:规则的描述
  • tags:规则的标签,用于分类

用户可以通过修改 rules.py 文件或创建自定义规则文件来添加新的识别规则。

输出

pyWhat 的输出包含以下信息:

  • Text:匹配到的文本
  • Rule:匹配的规则名称
  • Description:规则的描述
  • Tags:规则的标签

输出格式可以是纯文本或 JSON,通过 --json 参数指定。

贡献

欢迎贡献代码、报告问题或提出新功能建议。请参阅 CONTRIBUTING.md 了解贡献指南。

许可证

pyWhat 使用 MIT 许可证。详情请参阅 LICENSE 文件。```sh ./foxcage @experiment --color "#10b981" https://example.com # teal, one-off

Accepts standard CSS hex: `#rgb`, `#rrggbb`, or `#rrggbbaa` (with alpha). The auto-derived colours are tuned to be visible on both light and dark menubars (lightness fixed at 55%, saturation at 75%), so you shouldn't need to override for theme reasons.

### 시간 제한 케이지

`--lifetime` 플래그(및 동일한 `lifetime` 구성 키)는 설정된 기간 후에 케이지를 자동으로 닫습니다. 형식은 `<number><unit>`이며 단위는 `s`, `m`, 또는 `h`입니다:```sh
./foxcage @tmp --lifetime 10m https://example.com
./foxcage @work --lifetime 2h

카운트다운은 Firefox가 케이지 안에서 실제로 실행될 때 시작됩니다 — 컨테이너 시작 및 이미지 빌드 시간은 예산에 포함되지 않습니다. 케이지의 메뉴 바 라벨에는 케이지 식별자와 함께 카운트다운이 표시됩니다 — 예: FoxCage - tmp (a3f2b1) | 9m — 1분 이상 남았을 때는 분당 한 번, 마지막 1분 동안은 초당 한 번 업데이트됩니다. 카운트다운이 0에 도달하면 Firefox가 스스로 종료되고 컨테이너가 종료됩니다. 수명이 끝나기 전에 직접 Firefox를 닫으면 특별한 일은 발생하지 않습니다.

각 케이지의 구성에서 기본 수명을 설정하세요:```toml

~/.config/foxcage/tmp.toml — every @tmp launch auto-closes after 15 minutes

lifetime = "15m" private_browsing = true

`--lifetime` 명령줄 옵션은 모든 구성 값보다 우선합니다.

전체 이미지 재빌드 강제 실행(Firefox 및 모든 시스템 패키지 재다운로드):```sh
./foxcage --rebuild

실행 중인 컨테이너는 foxcage가 이미지 태그를 다시 빌드한 후에도 시작할 때 사용한 이미지를 유지합니다. --rebuild, Firefox 업데이트 또는 예약된 재빌드로 인해 이미지가 업데이트된 케이지에서 탭을 열려고 하면 foxcage는 오류(데스크톱 알림으로도 표시됨)를 표시하며 거부하고, Firefox를 종료한 후 다시 실행하도록 요청합니다. 그러면 현재 이미지로 새 컨테이너가 시작됩니다. --rebuild를 실행 중인 케이지에서 사용하면 foxcage는 사전에 경고하고 빌드를 수행한 후 동일한 검사를 적용합니다.

업데이트

foxcage는 실행할 때마다 새 브라우저 릴리스를 확인합니다. Firefox는 Mozilla의 릴리스 API를, LibreWolf는 GitLab의 릴리스 엔드포인트를 사용합니다. 업데이트가 있으면 컨테이너 이미지가 자동으로 다시 빌드됩니다. 또한 Debian 보안 업데이트를 적용하기 위해 이미지가 주기적으로(기본적으로 7일마다) 다시 빌드됩니다. 브라우저의 내장 자동 업데이트 기능은 업데이트가 이미지 수준에서 처리되므로 비활성화됩니다.

업데이트 확인이 실패하면(네트워크 없음, API 시간 초과) 경고가 출력되고 기존 이미지가 사용됩니다. 언제든지 브라우징할 수 있습니다.

업데이트 주기는 구성의 최상위 수준에 있으며, 버전 및 채널 고정은 포크별 섹션에 있습니다.```toml rebuild_days = 14 # rebuild for base-image updates every 14 days (0 to disable)

[firefox] channel = "beta" # track the beta channel instead of stable (firefox only) version = "149" # pin to Firefox 149.x (latest patch release)

**ESR 버전을 고정할 때는 채널도 함께 지정해야 합니다.** Mozilla의 버전 인덱스는 ESR 릴리스를 다운로드에 포함된 `esr` 접미사 없이 나열하므로, 기본 채널에서 단순히 `version = "140"`을 지정하면 존재하지 않는 릴리스를 가리키게 됩니다. 둘 다 설정하세요:```toml
[firefox]
channel = "esr"
version = "140"        # → 140.13.0esr

릴리스와 일치하지 않는 핀은 최신 릴리스로 조용히 대체되는 대신, 이제 해당 핀을 명시하는 오류가 발생합니다. Mozilla API에 일시적으로 연결하지 못하면 여전히 경고를 표시하고 기존 이미지로 계속 진행하므로, 불안정한 네트워크가 시작을 차단하지 않습니다.

접미사가 붙은 핀은 완전히 자격을 갖춰야 합니다. "140.13.0esr""150.0b9"는 작동하지만, "140esr""150b9"는 해당 릴리스가 절대 일치할 수 없으므로 구성 로드 시 거부됩니다. LibreWolf 개정판에도 동일하게 적용됩니다. "146.0.1-1"은 작동하지만 "146-1"은 작동하지 않습니다.

즉시 전체 재빌드를 강제하려면: ./foxcage --rebuild

해지된 서명 키

foxcage는 업스트림 프로젝트가 손상됨(RFC 4880 해지 사유 0x02)으로 해지한 서명 하위 키로 서명된 브라우저 빌드의 설치를 거부합니다. gpg --verify는 자체적으로 이 작업을 수행하지 않습니다. 경고를 출력하고 종료 코드 0으로 종료하므로, 추가 검사가 없으면 유출된 서명 키가 여전히 변조된 다운로드를 인증하게 됩니다.

거부는 다음과 같이 표시되며, 설치 대신 빌드를 실패시킵니다.``` foxcage: REFUSING /tmp/SHA512SUMS - signed by 09BEED63F3462A2DFFAB3B875ECB6497C1A20256, which its owner revoked as compromised. This build cannot be trusted; wait for upstream to re-sign this release with a current key.

Mozilla의 2026년 8월 키 순환. Mozilla는 2026-08-06에 암호화되지 않은 사본이 비공개 GitHub 저장소에 커밋된 후 서명 하위 키 `09BEED63…C1A20256`을 폐기하고 `827E6586…76767AA3`으로 교체했습니다. 이전 하위 키로 서명된 Firefox 릴리스(2025-03-13부터 2026-08-06 사이의 모든 버전, 작성 시점 기준으로 현재 ESR(`140.13.0esr`) 및 해당 기간 내의 모든 `version` 고정 포함)는 위 검사에서 거부됩니다. 릴리스 및 베타 채널은 영향을 받지 않습니다. foxcage는 `keyserver.ubuntu.com` 대신 `keys.openpgp.org`에서 키를 가져옵니다. 후자는 순환 후 며칠 동안 교체 하위 키도 폐기도 제공하지 않았기 때문입니다. 오래된 키 서버는 빌드를 완전히 중단시키는 동시에 폐기 검사를 조용히 무효화(no-op)할 수 있습니다.

### Firefox 포크 (LibreWolf)

foxcage는 업스트림 Firefox 대신 개인정보 보호 중심의 Firefox 포크를 실행할 수 있습니다:```toml
fork = "librewolf"         # default is "firefox"

[librewolf]
version = "146.0.1-1"      # optional pin; partial pins ("146", "146.0.1") also work

Or per-launch via CLI:```sh foxcage @tmp --fork librewolf https://example.com

**LibreWolf**: Firefox의 프라이버시 강화 포크 — 엄격한 추적 보호, DoH, RFP, 원격 측정(telemetry)이 기본적으로 잠겨 있음. GitLab(`librewolf-community/browser/bsys6`)에서 서명된 Linux tarball 제공, LibreWolf 유지관리자 키 `662E 3CDD 6FE3 2900 2D0C A5BB 4033 9DD8 2B12 EF16`에 대해 GPG 검증 및 형제 파일 `.sha256sum` 교차 확인 수행, Firefox와 동일한 [폐기 규칙](#revoked-signing-keys) 적용. LibreWolf에 번들된 `librewolf.cfg`는 보존되며, foxcage는 덮어쓰지 않고 자체 prefs를 그 위에 추가함. 유지관리자들은 2026-04-25에 이유를 밝히지 않은 채 서명 하위 키를 교체했으며, 현재 tarball은 해당 날짜 이전에 서명되었으므로 거부되지 않고 경고와 함께 설치됨.

**채널은 Firefox 전용임**: `fork`가 `"firefox"`가 아닌 경우 `firefox.channel = "beta" | "esr"`은 거부됨. LibreWolf는 단일 릴리스 트랙만 있음.

`fork` 전환(설정 또는 `--fork` 통해)은 Containerfile 해시를 변경하며, 다음 실행 시 재빌드를 트리거함 — 수동 `--rebuild` 불필요.

#### 프로필 호환성

> **포크마다 전용 프로필을 사용하세요.** 가장 안전한 기본값은 foxcage가 자체 프로필을 프로비저닝하도록 두는 것(설정에서 `profile` 생략)이거나, 호스트에서 동시에 열지 않는 디렉터리로 `profile`을 지정하는 것입니다.

- **LibreWolf**: 호스트 Firefox 프로필과 공유해도 *일반적으로* 문제없음 — LibreWolf는 Firefox 버전을 며칠 내로 따라가므로 `compatibility.ini` 스키마 충돌은 드묾. 위험: (1) 순차 사용만 안전함(Firefox의 잠금 파일이 동시 열기를 방지함); (2) Firefox 안정 버전 출시 직후의 짧은 기간에 Firefox를 먼저 실행한 다음 LibreWolf를 실행하면 "새 버전에서 사용됨" 마이그레이션 대화상자가 나타날 수 있음; (3) LibreWolf가 제거한 기능(Sync, Pocket, Mozilla 계정)은 조용히 작동하지 않지만 데이터를 손상시키지는 않음.

### 명명된 케이지(Named cages)

자체 설정과 Firefox 프로필을 가진 별도의 샌드박스 인스턴스를 실행:```sh
./foxcage @work

이렇게 하면 ~/.config/foxcage/work.toml이 로드되고 별도의 이미지(foxcage-work), 컨테이너(foxcage-work), 볼륨(foxcage-work-profile)이 사용됩니다. 이름이 지정된 케이지에는 구성 파일이 반드시 존재해야 합니다. 케이지 이름에는 문자, 숫자, 하이픈, 밑줄만 포함될 수 있습니다.

구성

구성 파일은 $XDG_CONFIG_HOME/foxcage/에 위치합니다(기본값은 ~/.config/foxcage/).

  • config.toml — 기본 케이지(선택 사항, 없으면 합리적인 기본값 사용)
  • <name>.toml — 이름이 지정된 케이지, @<name>으로 로드됨(필수)

알 수 없는 구성 키는 오류와 함께 거부됩니다. 기본값이 포함된 모든 사용 가능한 옵션은 config.toml.example을 참조하세요.

config.toml 예시```toml

Bind-mount a host Firefox profile directory into the cage

profile = "~/.mozilla/firefox/xxxxxxxx.default-release"

Allow downloading files to ~/Downloads

downloads_dir = "~/Downloads"

Extensions pre-installed into the cage (addons.mozilla.org short name,

listing URL, or add-on ID). Most useful in tmp.toml.

extensions = ["ublock-origin"]

Shared memory size for Firefox IPC (default: 256m)

shm_size = "256m"

Pass through webcam devices (/dev/video*)

webcam = true

Pass through host CUPS socket for locally-connected printers (e.g. USB)

local_printers = true

Pass through FIDO2/U2F security key devices (/dev/hidraw*)

security_keys = true

Always open Firefox in private browsing mode

private_browsing = true

Auto-close the cage after a duration ( with unit s, m, or h)

lifetime = "30m"

Accent colour for the menu-bar label. Named cages get a colour derived

from the name automatically; set this to override it.

color = "#4a90e2"

Browser fork: "firefox" (default) or "librewolf"

fork = "librewolf"

Full image rebuild interval in days for base-image updates (default: 7, 0 to disable)

rebuild_days = 7

[firefox]

Firefox release channel: "release" (default), "beta", "esr".

Only valid when fork = "firefox".

channel = "release"

Pin to a specific Firefox version (overrides channel).

Partial versions like "149" or "149.0" resolve to the latest patch release.

Suffixed versions must be fully qualified ("140.13.0esr", "150.0b9"); to

follow the ESR line by major version, pair a numeric pin with

channel = "esr" above.

version = "149.0.2"

[librewolf]

Pin to a specific LibreWolf version. Tags are "-",

e.g. "146.0.1-1". Partial pins like "146" or "146.0.1" also work.

version = "146.0.1-1"

[network]

"host" for full host networking (needed if the cage has to reach services

on the host's localhost), or omit for isolated pasta (default)

mode = "host"

DNS server (isolated mode only, default: host DNS)

dns = "1.1.1.1"

Disable IPv6 in the cage (isolated mode only)

ipv4_only = true

[mounts]

Additional bind mounts into the container. Supported forms:

"~/Documents" — same path in container

"/Documents:/Documents" — ~ expanded on both sides

"~/Documents:/home/user/Documents" — explicit container path

Append :ro for read-only, e.g. "~/Documents:ro"

nosuid,noexec are always enforced on bind mounts; an explicit "exec" or

"suid" is rejected rather than silently dropped.

Host paths must be absolute or start with "~/".

bind = [ "~/Documents:ro", ]

[init]

Commands to run at image build time (as root). Changes trigger a rebuild.

build = ["apt-get update && apt-get install -y --no-install-recommends vim"]

Commands to run at container startup as root, before Firefox.

root = ["chown user:user /some/path"]

Commands to run at container startup as your user, before Firefox.

user = ["mkdir -p ~/custom-dir"]

### 호스트 Firefox 프로필

케이지와 호스트 Firefox 프로필을 공유하려면 `profile`을 프로필 디렉터리로 설정하세요. 호스트의 Firefox에서 `about:profiles`를 방문하여 프로필 경로를 찾을 수 있습니다. 또는 호스트에 유지되는 깨끗한 프로필로 케이지를 시작하려면 새 빈 디렉터리를 지정하면 됩니다.```toml
profile = "~/.mozilla/firefox/xxxxxxxx.default-release"

이 디렉터리 하나만 케이지(cage)에 바인드 마운트됩니다. ~/.mozilla/firefox/ 아래의 형제 프로필과 profiles.ini 레지스트리는 노출되지 않습니다. 손상된 케이지는 이를 변조할 수 없습니다.

profile이 설정되지 않은 경우, 이름이 지정된 Podman 볼륨이 Firefox 프로필을 대신 저장합니다(아래 "유지되는 항목" 참조). 동일한 프로필이 호스트의 Firefox에서 이미 열려 있으면, Firefox의 프로필별 잠금 파일로 인해 충돌이 발생합니다. 케이지마다 전용 프로필을 사용하세요.

네트워킹

기본적으로 컨테이너는 호스트 루프백이 차단되고 호스트 DNS가 사용되는 pasta를 사용합니다. pasta는 podman 4.4 이상이 필요합니다(podman 5.0부터 루트리스 기본값이 되었습니다).

호스트 네트워킹은 네트워크 격리를 완전히 제거합니다. 케이지가 호스트의 localhost에 있는 서비스(예: 로컬 개발 서버, 127.0.0.1의 데이터베이스)에 접근해야 할 때 이 옵션을 사용하세요:```toml [network] mode = "host"

`dns`는 `mode = "host"`와 함께 사용할 수 없습니다. 호스트 네트워킹은 이미 호스트의 리졸버를 사용하기 때문입니다.

> **호스트 모드는 localhost 이상을 포기하는 것입니다.** 케이지를 호스트의 네트워크 네임스페이스에 두며, 추상 유닉스 소켓은 파일시스템이 아닌 해당 네임스페이스로 범위가 지정됩니다. 따라서 호스트 모드의 케이지는 호스트의 추상 주소 소켓에 직접 도달할 수 있습니다. 여기에는 X11 또는 Xwayland를 실행하는 경우 Xwayland의 `@/tmp/.X11-unix/X0`(foxcage가 Wayland 전용임에도 입력 로깅 가능)과 `unix:abstract=…`로 구성된 세션 버스가 포함되며, 이는 필터링된 D-Bus 프록시를 우회할 수 있습니다. 이는 네트워크 스택을 공유하는 데서 비롯된 본질적인 특성으로, foxcage가 필터링할 수 있는 것이 아닙니다. 필요할 때 호스트 모드를 사용하고, 해당 목적으로만 실행하는 별도의 명명된 케이지를 선호하십시오.

**IPv4 전용 케이지**는 IPv6를 완전히 비활성화합니다:```toml
[network]
ipv4_only = true

Or per launch with the --ipv4-only flag (short form -4, as in ssh/curl/pasta):```sh ./foxcage @tmp -4 https://example.com

이것은 파스타를 IPv4 전용 모드(`-4`)로 실행하므로 컨테이너에는 IPv6 스택이 전혀 없으며, 추가로 Firefox에서 `network.dns.disableIPv6`를 설정하여 AAAA 레코드를 해석하지 않도록 합니다. 이는 DoH가 활성화된 경우 중요합니다. DoH 응답은 컨테이너의 리졸버를 우회하기 때문입니다. `ipv4_only`는 `mode = "host"`와 함께 사용할 수 없습니다. 호스트 네트워킹은 호스트의 네트워크 스택을 직접 사용하므로, 대신 호스트에서 IPv6를 비활성화하세요.

### 초기화 명령

`[init]`를 통해 빌드 시점 또는 컨테이너 시작 시 사용자 지정 명령을 실행합니다:

- **`build`** — 이미지 빌드 시점에 root로 실행됩니다. 패키지 설치 또는 기타 느린 설정에 사용합니다. 빌드 명령이 변경되면 자동으로 이미지가 다시 빌드됩니다.
- **`root`** — 컨테이너 시작 시 Firefox 실행 전에 root로 실행됩니다. 빠른 런타임 root 작업(권한 조정, 구성 파일 작성)에 사용합니다.
- **`user`** — 컨테이너 시작 시 Firefox 실행 전에 사용자 계정으로 실행됩니다. 디렉터리 생성, 사용자 수준 상태 설정에 사용합니다.```toml
[init]
build = [
    "apt-get update && apt-get install -y --no-install-recommends fonts-noto-cjk",
    "rm -rf /var/lib/apt/lists/*",
]
root = ["chmod 777 /tmp/shared"]
user = ["mkdir -p ~/workspace"]

All three keys are lists of shell command strings. If any command fails, the container exits without starting Firefox.

Security note: When init.root is set, the container starts as root with CAP_SETUID and CAP_SETGID added (on top of the default CAP_SYS_CHROOT) so it can drop back to the regular user. These capabilities are only held during the root init phase — after the privilege drop, the regular-user process has no extra capabilities. Without init.root, the container runs with the default minimal capability set.

What persists

Without configuration, a named Podman volume stores the Firefox profile (bookmarks, settings, extensions, Widevine DRM plugin). Everything else is ephemeral.

  • Default cage: foxcage-profile
  • Named cage: foxcage-<name>-profile

To start fresh, remove the volume:```sh podman volume rm foxcage-profile

`profile`이 설정되면 호스트 디렉터리가 직접 바인드 마운트되며 볼륨은 생성되지 않습니다.

`extensions`에 나열된 확장 프로그램은 해당 상태의 일부가 아닙니다. 확장 프로그램은 이미지에 존재하며 실행할 때마다 다시 설치되므로, 볼륨을 제거하거나(또는 볼륨이 없는 임시 케이지를 사용하거나) 확장 프로그램이 손실되지 않습니다.

### 디스크 사용량

각 케이지 이미지는 약 1GB입니다. 재빌드는 이미지에 새 태그를 지정하고 이전 이미지를 태그 없는 `<none>` 항목으로 남겨두므로, foxcage는 성공적인 빌드 후 방금 대체된 이미지를 제거합니다. 해당 특정 이미지만 제거하며, 실행 중인 케이지가 여전히 사용 중인 이미지는 절대 제거하지 않습니다.

이 동작이 생기기 전에 고아가 된 이미지는 소급하여 정리되지 않습니다. 이를 회수하려면:```sh
podman images --filter dangling=true    # review first
podman image prune                      # then remove

Firefox 업데이트는 각 실행 시 자동으로 감지됩니다. 전체 재빌드를 강제하려면(예: 시스템 보안 업데이트를 즉시 적용하려면):```sh ./foxcage --rebuild

## 테마 적용

foxcage는 호스트에서 다음 항목을 자동으로 전달하므로, 컨테이너 안의 Firefox가 네이티브 애플리케이션처럼 보이고 느껴집니다:

- **글꼴.** 시스템 글꼴(`/usr/share/fonts`)과 사용자 글꼴(`~/.local/share/fonts`)이 읽기 전용으로 바인드 마운트됩니다. `~/.config/fontconfig`의 글꼴 구성도 전달됩니다.
- **GTK 테마 및 다크 모드.** `GTK_THEME` 또는 `gsettings`를 통해 감지되어 컨테이너로 전달됩니다. `~/.config/gtk-3.0` 및 `~/.config/gtk-4.0`의 GTK 구성은 읽기 전용으로 바인드 마운트됩니다.
- **시간대.** 호스트의 시간대 이름(`TZ`, `/etc/localtime` 심볼릭 링크 또는 `/etc/timezone`에서 감지)이 `TZ`로 컨테이너에 전달되고, `/etc/localtime`은 읽기 전용으로 바인드 마운트됩니다. 둘 다 필요합니다: Firefox는 JavaScript 시간대를 파일 내용이 아닌 영역 *이름*에서 파생하므로, `TZ`가 없으면 웹사이트가 UTC 시간으로 표시됩니다.
- **로케일.** `LANG`이 전달됩니다. 호스트의 로케일은 빌드 시점에 컨테이너 이미지에 생성됩니다.

**Cage 라벨.** Firefox 메뉴 바에 "FoxCage"(또는 이름이 지정된 cage의 경우 "FoxCage - name")가 표시되어 컨테이너화된 세션에 있다는 것을 한눈에 알 수 있습니다. 메뉴 바는 엔터프라이즈 정책을 통해 항상 표시됩니다.

컨테이너에는 Adwaita GTK 테마만 포함되어 있습니다. GNOME 데스크톱에서는 기본적으로 바로 작동합니다. KDE 또는 다른 데스크톱에서는 GTK 테마(예: Breeze)가 컨테이너에 설치되어 있지 않으면 Firefox가 Adwaita로 대체됩니다. 다크 모드 감지는 `gsettings` 또는 `GTK_THEME`를 통해 기본 설정이 지정되어 있으면 계속 작동합니다.

## 하드웨어 비디오 디코딩(VA-API)

컨테이너에는 자체 유저랜드가 있으므로 호스트에 설치된 VA-API 드라이버는
무관합니다 — 이미지에는 자체 드라이버가 포함되어 있습니다. `va-driver-all`은
`i965-va-driver`(구형 Intel) 및 `mesa-va-drivers`(AMD, nouveau)를 가져오며,
`intel-media-va-driver-non-free`(Intel Gen8+, iHD 드라이버) 및
`libva2`/`libva-drm2`(Firefox가 런타임에 로드)도 함께 포함됩니다.

하드웨어 디코딩에는 `/dev/dri` 전달이 필요하며, foxcage는 호스트에
있을 때마다 자동으로 수행합니다. 구성할 항목이 없습니다.

Intel 드라이버는 **비자유(non-free)** 빌드이므로, 이미지는 Debian의 `non-free`
구성 요소를 활성화합니다. Debian의 자유(free) `intel-media-va-driver`는
재배포 불가능한 코덱 커널이 제거된 `+dfsg` 리패키지이며, 여기서 손실되는 것은
AV1 디코딩입니다 — YouTube가 현재 기본으로 제공하는 형식입니다.
자유 빌드는 모든 Intel 머신에서 AV1을 소프트웨어로 대체하게 만듭니다.

실제로 작동하는지 확인하려면 라이브 cage 안에서 `vainfo`를 실행하세요:```bash
podman exec foxcage-<name> vainfo

드라이버(Intel에서는 iHD, AMD에서는 radeonsi)와 지원되는 프로파일(VAProfileH264*, VAProfileVP9Profile0, VAProfileAV1Profile0 등)을 나열해야 합니다. 브라우저 내부에서 확인하는 동등한 방법은 about:support → 미디어로, 하드웨어 디코딩 열에 H264, VP8, VP9, HEVC 및 AV1에 대해 Supported로 표시되어야 합니다. 비디오가 재생되는 동안 호스트의 intel_gpu_top은 Video 엔진에서 활동을 보여줍니다.

하드웨어 인코딩은 별개이며, Intel에서는 동일한 드라이버에서 제공됩니다. H264와 HEVC는 해당 열에 Supported로 표시되어야 하며, 이는 WebRTC가 화상 통화의 발신 카메라 스트림에 사용하고 MediaRecorder가 사용하는 것입니다. VP8, VP9 및 AV1 인코딩은 Unsupported로 유지됩니다 — Firefox는 GPU가 무엇을 할 수 있든 H264 및 HEVC VA-API 인코더만 연결합니다.

오디오 코덱(AAC, MP3, Opus, Vorbis, FLAC, Wave)은 모든 머신에서 하드웨어 디코딩 아래에 Unsupported로 표시됩니다 — 소비자용 GPU에는 오디오 디코드 블록이 없습니다. 해당 행은 잘못된 구성이 아닙니다.

vainfofailed to initialize display를 보고하면 컨테이너가 /dev/dri/renderD128을 열 수 없는 것입니다. 일반적인 systemd 데스크톱에서는 logind가 해당 장치에 대한 ACL을 사용자에게 부여하므로, 이는 일반적으로 foxcage가 시트(seat)를 소유하지 않은 세션(SSH, 다른 TTY)에서 실행되고 있음을 의미합니다.

DRM (Netflix, Disney+ 등)

Widevine DRM은 기본적으로 작동합니다. DRM으로 보호된 사이트를 처음 방문하면 Firefox가 Widevine CDM을 자동으로 다운로드합니다. 이 작업은 잠시 걸릴 수 있습니다.

호스트 통합 (항상 활성화)

foxcage는 필터링된 D-Bus 프록시를 사용하여 Firefox가 호스트의 XDG Desktop Portal 및 알림 데몬에 접근할 수 있게 합니다. 이러한 기능은 모든 접근이 사용자 매개 방식이므로 안전합니다 — 호스트는 사용자가 상호작용해야 하는 네이티브 대화 상자를 표시합니다. 손상된 브라우저는 호스트 리소스에 조용히 접근할 수 없습니다.

  • 파일 업로드 — 호스트의 네이티브 파일 선택기(공유할 파일을 직접 선택)
  • 외부 링크mailto:, magnet 링크 등이 호스트 앱 선택기를 통해 열림
  • 데스크톱 알림 — 호스트 알림 데몬으로 전달됨
  • 화면 공유 — 포털 화면 선택기 + PipeWire 비디오 스트림(호스트에 PipeWire 필요)

장치 패스스루 (선택 사항)

이 기능은 호스트 장치를 컨테이너에 직접 전달하며 기본적으로 꺼져 있습니다 — 위의 포털 기능과 달리 호스트 측 확인이 없습니다. 손상된 브라우저가 하드웨어를 조용히 사용할 수 있습니다.```toml webcam = true # /dev/video* — webcam for video calls local_printers = true # CUPS socket — USB printers (network printers work by default) security_keys = true # /dev/hidraw* — FIDO2/U2F hardware keys

## 아직 지원되지 않는 기능

일부 웹 플랫폼 기능은 호스트 통합이 없어 컨테이너에서 작동하지 않습니다. 투명성을 위해 여기에 나열합니다.

**Bluetooth, USB, 시리얼, NFC.** Web Bluetooth, WebUSB, Web Serial, WebNFC API는 컨테이너에서 사용할 수 없는 장치 액세스 및 시스템 서비스(BlueZ, udev)가 필요합니다.

**게임패드 및 MIDI.** Gamepad API는 `/dev/input/` 액세스가 필요합니다. Web MIDI는 ALSA 시퀀서 액세스가 필요합니다. 둘 다 전달되지 않습니다.

**PWA 설치.** 프로그레시브 웹 앱(PWA)은 컨테이너 내부에서 호스트 데스크톱에 설치할 수 없습니다.

**접근성.** AT-SPI를 통한 화면 리더 지원은 비활성화되어 있습니다(`NO_AT_BRIDGE=1`) — 컨테이너는 호스트의 접근성 버스에 연결되어 있지 않습니다. Web Speech API 합성은 작동합니다: `espeak-ng` 엔진을 사용하는 `speech-dispatcher`가 cage에 설치되어 있으며, 첫 사용 시 자동으로 시작되고 오디오는 공유 PulseAudio 소켓을 통해 라우팅됩니다.

## 호스트 구성

### 권장: overlay 스토리지

루트리스 Podman은 overlay 마운트 대신 전체 이미지 레이어를 복사하는 `vfs` 스토리지 드라이버로 대체될 수 있습니다. 이로 인해 빌드 후 컨테이너 시작이 훨씬 느려집니다. 현재 사용 중인 드라이버를 확인하세요:```sh
podman info --format '{{.Store.GraphDriverName}} {{.Store.GraphStatus}}'

overlayNative Overlay Diff:true는 빠른 경로이며 구성이 필요 없습니다. 커널 5.13 이상에서 ext4/xfs 백킹 파일시스템을 사용하는 경우, Podman은 권한 없는 overlayfs를 직접 사용합니다. 그렇게 표시된다면 할 일이 없으며, fuse-overlayfs를 설치해도 도움이 되지 않습니다.

vfs로 표시되는 경우에만(오래된 커널 또는 권한 없는 overlay를 지원하지 않는 백킹 파일시스템) fuse-overlayfs를 설치하고 ~/.config/containers/storage.conf에 추가하세요:```toml [storage] driver = "overlay"

[storage.options.overlay] mount_program = "/usr/bin/fuse-overlayfs"

이것은 대체 수단이지 업그레이드가 아닙니다. FUSE는 모든 파일시스템 작업을 사용자 공간을 통해 라우팅하므로 네이티브 오버레이보다 느립니다. 네이티브 오버레이를 지원하는 시스템에서 `mount_program`을 설정하면 상황이 더 나빠질 뿐입니다.

### foxcage를 기본 브라우저로 설정하기

먼저 `foxcage` 스크립트가 영구 위치(예: `~/bin/foxcage` 또는 `/usr/local/bin/foxcage`)에 있는지 확인하세요. 설치 명령은 스크립트의 현재 경로를 `.desktop` 파일에 기록하므로, 이후에 스크립트를 이동하면 실행기가 깨집니다.

그런 다음 실행하세요:```sh
foxcage --install

이렇게 하면 스크립트의 현재 위치를 가리키는 .desktop 파일이 생성되고, foxcage 아이콘이 설치되며, 데스크톱 및 아이콘 데이터베이스가 새로 고쳐집니다. 그러면 FoxCage가 애플리케이션 메뉴에 나타나야 합니다.

다른 애플리케이션에서 클릭한 링크가 foxcage에서 열리도록 foxcage를 기본 웹 브라우저로 설정하려면:```sh xdg-settings set default-web-browser foxcage.desktop

이미 케이지가 실행 중이면 URL은 기존 브라우저의 새 탭으로 열립니다.

실행 취소하려면:```sh
foxcage --uninstall

StartupNotify=true.desktop 파일에 설정되어 있으며, 이는 foxcage가 시작되는 동안 컴포지터가 스피너 커서를 표시하도록 지시합니다. 이미지 빌드가 필요할 때(몇 분이 걸릴 수 있음), foxcage는 데스크톱 알림을 보내 Firefox가 준비 중임을 알려줍니다. 조기 종료 오류(설정 오타, 누락된 의존성, 잘못된 cage 이름)도 데스크톱 알림으로 표시되어, 터미널 없이 데스크톱에서 실행한 사용자가 foxcage가 실패했을 때 아무것도 보지 못하고 멍하니 있지 않도록 합니다. 둘 다 notify-send(Debian/Ubuntu의 libnotify-bin에서 제공)가 필요합니다 — 설치되어 있지 않으면 알림은 조용히 건너뛰고 오류는 여전히 stderr로 전달됩니다.

수동 설정

.desktop 파일을 수동으로 만들고 싶다면 ~/.local/share/applications/foxcage.desktop를 생성하세요:```ini [Desktop Entry] Type=Application Name=FoxCage Comment=Firefox in a rootless Podman container Exec=/path/to/foxcage %u Icon=foxcage MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https; Terminal=false Categories=Network;WebBrowser; StartupNotify=true StartupWMClass=foxcage

`/path/to/foxcage`를 스크립트의 실제 경로로 바꾸세요. 등록하세요:```sh
update-desktop-database ~/.local/share/applications

테스트 실행

테스트 스위트는 pytest + pytest-cov를 사용하며, 이는 requirements-dev.txt에 개발 전용 의존성으로 선언되어 있습니다.``` pip install -r requirements-dev.txt pytest

테스트는 완전히 격리되어 있습니다 — podman 없음, 네트워크 없음, pytest의 `tmp_path` 외 실제 파일시스템 없음. 테스트 스위트는 **100% 라인 및 브랜치 커버리지**를 요구하며(`pytest.ini` 및 `.coveragerc`에 구성됨), 커버되지 않은 라인이나 조건문의 실행되지 않은 분기가 하나라도 있으면 실행이 실패합니다. CI는 `.gitlab-ci.yml`을 통해 모든 푸시에서 스위트를 실행합니다.

## 감사의 말

이 프로젝트는 Mike Cardwell이 개발했으며, Anthropic의 AI 코딩 도구인 [Claude Code](https://claude.ai/claude-code)의 도움을 받았습니다.

## 제 작업을 지원/감사히 여기기

- [Bitcoin](bitcoin:1PQLtWnjUi1itHLG6QCQeHM3Nxua8pRsq1): 1PQLtWnjUi1itHLG6QCQeHM3Nxua8pRsq1
- [Paypal](https://www.paypal.me/grepular)

카테고리