awwall
Egress allowlist that fails closed: declare what a workload may reach, watch everything else fail with the rule that denied it.
What It Does
awwall is a Python package that provides an egress allowlist policy engine. It works by:
- Failing closed by default — an empty policy denies all outbound connections
- Allowing only what you declare — add rules for hosts you trust
- Explaining denials — when a connection is blocked, you see exactly which rule (or lack thereof) caused it
- Multiple output formats — emit policy as JSON,
/etc/hosts, or shell commands for iptables
Rules come in three types:
- exact —
example.commatches onlyexample.com - domain —
example.commatchesexample.com,api.example.com,v1.api.example.com, etc. - glob —
*.example.commatches any subdomain
Installation
pip install awwall
The Adoption Guarantee
Block one outbound host for one workload and watch the call fail closed with the rule that denied it.
# 1. Create an empty policy (denies everything)
$ awwall list
No rules defined (default: deny everything)
# 2. Check if google.com is allowed
$ awwall check google.com
$ echo $?
1 # Denied!
# 3. Explain why
$ awwall explain google.com
DENIED: google.com
Reason: Policy is empty (default deny)
# 4. Allow one host
$ awwall allow github.com --type domain --description "GitHub repositories"
Added: github.com (domain)
# 5. Check again
$ awwall check github.com
$ echo $?
0 # Allowed!
$ awwall check api.github.com
$ echo $?
0 # Subdomains allowed too (domain rule)
# 6. But google.com is still blocked
$ awwall check google.com
$ echo $?
1 # Still denied
$ awwall explain google.com
DENIED: google.com
Reason: No rule matched (checked 1 rule(s))
CLI Commands
awwall allow <host>
Add a host to the allowlist.
awwall allow api.example.com # Inferred as exact match
awwall allow example.com --type domain # Explicit domain rule (allows subdomains)
awwall allow *.cdn.com --type glob # Glob pattern
awwall allow example.com --description "Prod API" --type exact
awwall check <host>
Check if a host is allowed (exit 0 = allowed, exit 1 = denied).
awwall check example.com # Silent
awwall check example.com -v # Verbose output
awwall explain <host>
Explain why a host is allowed or denied.
$ awwall explain api.example.com
ALLOWED: api.example.com
Matched rule: example.com (type: domain)
Description: Production API
awwall emit --format <format>
Emit policy in different formats.
awwall emit --format json # Print as JSON
awwall emit --format hosts # /etc/hosts format
awwall emit --format iptables # Shell script with iptables rules
awwall emit --format json --output policy.json # Save to file
awwall list
List all rules in the policy.
$ awwall list
Policy rules (2 total):
1. api.example.com [exact] - Production API
2. example.com [domain] - All subdomains
awwall --self-test
Run self-tests to verify the policy engine.
$ awwall --self-test
Running awwall self-tests...
[PASS] Empty policy denies all
[PASS] Exact match works
[PASS] Exact match rejects subdomains
[PASS] Domain match includes subdomains
[PASS] Domain match rejects different domain
[PASS] Glob pattern works
[PASS] Glob rejects non-matching
[PASS] Case insensitive matching
[PASS] Whitespace trimming works
[PASS] Rejects malformed policy
[PASS] Missing policy file defaults to deny-all
All self-tests passed!
Policy File Format
By default, policies are stored in ~/.awwall/policy.json:
{
"rules": [
{
"pattern": "api.example.com",
"rule_type": "exact",
"description": "Production API"
},
{
"pattern": "example.com",
"rule_type": "domain",
"description": "All example.com subdomains"
},
{
"pattern": "*.cdn.com",
"rule_type": "glob",
"description": "CDN patterns"
}
]
}
Specify a different file with --policy-file:
awwall --policy-file /etc/awwall/prod.json check example.com
Exit Codes
- 0 — Success (check: host allowed, command worked)
- 1 — Denied (check: host blocked) or command failed
- 2 — Cannot judge (malformed policy, missing file in strict mode)
A policy file that cannot be parsed exits with code 2 (cannot judge), never 0. This prevents silent failures.
Python API
from awwall import Policy, AllowRule
# Create a policy
policy = Policy([
AllowRule("example.com", "exact"),
AllowRule("api.other.com", "domain"),
])
# Check a host
allowed, matching_rule = policy.check("api.other.com")
if allowed:
print(f"Allowed by rule: {matching_rule.pattern}")
else:
print("Denied: no rule matched")
# Load from file
policy = Policy.from_file("/path/to/policy.json")
# Load from dict
policy = Policy.from_dict({"rules": [...]})
# Export
print(policy.to_hosts_format())
print(policy.to_iptables_format())
Testing
pytest tests/test_awwall.py -v
The test suite includes:
- Default deny verification — empty policy blocks everything
- Rule type tests — exact, domain, and glob matching
- Negative tests — verify rules DON'T match when they shouldn't
- Fail-closed proofs — malformed policy is treated as empty (deny all)
- Roundtrip tests — export and reimport preserves semantics
Design Principles
- Fail closed by default — empty policy denies all, malformed policy denies all
- Transparent denials — every denied connection names the rule that caused it
- Simple rules — exact, domain suffix, and glob patterns cover 99% of real use cases
- No magic — no attempt to detect "safe" IPs or make assumptions
- Exportable — policy can be rendered for other tools (hosts file, iptables, etc.)
License
MIT
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 awwall-0.1.0.tar.gz.
File metadata
- Download URL: awwall-0.1.0.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b875a7bf69a549310b580b92201a677a8cfd9c37a9580bad8c2ddb99f5461ce
|
|
| MD5 |
001e8b6be6805e87b62a6cc1f4555055
|
|
| BLAKE2b-256 |
43b5bd709d36008ed9517f5ea4df98e31efe3e6509e23a0868de58c93ee206ad
|
File details
Details for the file awwall-0.1.0-py3-none-any.whl.
File metadata
- Download URL: awwall-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2022ae6726ce6b36dd456137796da3e65b836cd5119495741e47bd4465226cb5
|
|
| MD5 |
a836fd5108cf69977415c3ba195b4762
|
|
| BLAKE2b-256 |
f41250ba110929feb6ab31205826a321b990102a9b2c07c22b7ad0da240eb0c9
|