nftables-based egress firewalling for Podman containers
Project description
terok-shield
Default-deny egress firewall for rootless Podman containers.
terok-shield enforces default-deny outbound network filtering on Podman containers using nftables. Containers can only reach explicitly allowed destinations — everything else is rejected with an ICMP error and a per-packet audit entry.
Where it sits in the stack
terok-shield is the firewall layer of the terok ecosystem. The hardened-Podman runtime (terok-sandbox) installs the OCI hooks at setup time; the operator-in-the-loop verdict service (terok-clearance) mutates the live ruleset on Allow / Deny decisions. The shield itself is independent of all of these — it works on any rootless Podman container, with or without the rest of terok.
Features
- Default-deny egress with curated allowlists (domains and IPs)
- Dynamic DNS allowlisting — per-container dnsmasq with
--nftsetauto-populates allow sets on every DNS resolution, handling IP rotation at runtime; falls back to static pre-start resolution viadigorgetentwhen dnsmasq is unavailable - Live allow/deny at runtime for individual containers
- Per-container isolation — each container gets its own state bundle, hooks, and audit log
- Connection audit logging (JSON-lines lifecycle logs + kernel-level per-packet nftables logs)
- Fail-closed — hook failure prevents the container from starting
Requirements
- Linux with nftables (
nftbinary) — tested on Fedora 43, Debian 12 and 13, and Ubuntu 24.04, also works on other modern Linux distros - Podman (rootless, recommended ≥ 5.6.0, untested < 4.3.1)
- Python 3.12+
dnsmasq(recommended) for dynamic DNS-based egress control;dig(dnsutils/bind-utils) as fallback
Installation
pip install terok-shield
Quick start
1. Choose your allowlists
terok-shield ships with several bundled profiles (see Allowlist Profiles):
| Profile | Domains |
|---|---|
base |
DNS roots, NTP, OCSP, OS package repos |
dev-standard |
GitHub, Docker Hub, PyPI, npm, crates.io, Go |
dev-python |
Conda, Read the Docs, Python docs |
dev-node |
Yarn, jsDelivr, unpkg |
nvidia-hpc |
CUDA, NGC, NVIDIA drivers |
The default profile is dev-standard. To add a custom allowlist,
create a .txt file in ~/.config/terok/shield/profiles with one domain or IP per line:
e.g. ~/.config/terok/shield/profiles/my-project.txt
api.example.com
cdn.example.com
203.0.113.10
2. Start a container with the shield
terok-shield run my-container -- alpine:latest sh
This resolves DNS, installs OCI hooks, and launches the container
with a default-deny firewall — only destinations in the
dev-standard profile are reachable. To use custom profiles:
terok-shield run my-container --profiles dev-standard my-project -- alpine:latest sh
3. Allow a domain at runtime
terok-shield allow my-container example.com
# Allowed example.com -> <resolved-ip> for my-container
terok-shield deny my-container example.com # revoke later
License
Apache-2.0 — see LICENSES/Apache-2.0.txt.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file terok_shield-0.7.0.tar.gz.
File metadata
- Download URL: terok_shield-0.7.0.tar.gz
- Upload date:
- Size: 113.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
660133de84140e8fd79215e35c804a6e247951df9a73860361194cad8fa93808
|
|
| MD5 |
32d0bdc24c22ea6ad59621676a278a15
|
|
| BLAKE2b-256 |
601e88b52f78b1641cd4df334f87efc4cc8876961494d4b648e7d3ea52c1ae00
|
Provenance
The following attestation bundles were made for terok_shield-0.7.0.tar.gz:
Publisher:
release.yml on terok-ai/terok-shield
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
terok_shield-0.7.0.tar.gz -
Subject digest:
660133de84140e8fd79215e35c804a6e247951df9a73860361194cad8fa93808 - Sigstore transparency entry: 1711847029
- Sigstore integration time:
-
Permalink:
terok-ai/terok-shield@24a411b62016acffc4ed4f3c0d83c6e80b46d3d1 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/terok-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24a411b62016acffc4ed4f3c0d83c6e80b46d3d1 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file terok_shield-0.7.0-py3-none-any.whl.
File metadata
- Download URL: terok_shield-0.7.0-py3-none-any.whl
- Upload date:
- Size: 145.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f246e405a3c060af556b63f71fddc5ab80beb1e88f51d251cfd0267bf6ac8438
|
|
| MD5 |
4d2ce6331e1969bec726e20018e470a5
|
|
| BLAKE2b-256 |
1e47bd0daa6e2e75ac0a6d46bd68cf76cf39559ca92505988b74e714d745f31f
|
Provenance
The following attestation bundles were made for terok_shield-0.7.0-py3-none-any.whl:
Publisher:
release.yml on terok-ai/terok-shield
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
terok_shield-0.7.0-py3-none-any.whl -
Subject digest:
f246e405a3c060af556b63f71fddc5ab80beb1e88f51d251cfd0267bf6ac8438 - Sigstore transparency entry: 1711847070
- Sigstore integration time:
-
Permalink:
terok-ai/terok-shield@24a411b62016acffc4ed4f3c0d83c6e80b46d3d1 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/terok-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24a411b62016acffc4ed4f3c0d83c6e80b46d3d1 -
Trigger Event:
workflow_dispatch
-
Statement type: