Skip to main content

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:

  1. Failing closed by default — an empty policy denies all outbound connections
  2. Allowing only what you declare — add rules for hosts you trust
  3. Explaining denials — when a connection is blocked, you see exactly which rule (or lack thereof) caused it
  4. Multiple output formats — emit policy as JSON, /etc/hosts, or shell commands for iptables

Rules come in three types:

  • exactexample.com matches only example.com
  • domainexample.com matches example.com, api.example.com, v1.api.example.com, etc.
  • glob*.example.com matches 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

  1. Fail closed by default — empty policy denies all, malformed policy denies all
  2. Transparent denials — every denied connection names the rule that caused it
  3. Simple rules — exact, domain suffix, and glob patterns cover 99% of real use cases
  4. No magic — no attempt to detect "safe" IPs or make assumptions
  5. 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

awwall-0.1.0.tar.gz (18.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

awwall-0.1.0-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

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

Hashes for awwall-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8b875a7bf69a549310b580b92201a677a8cfd9c37a9580bad8c2ddb99f5461ce
MD5 001e8b6be6805e87b62a6cc1f4555055
BLAKE2b-256 43b5bd709d36008ed9517f5ea4df98e31efe3e6509e23a0868de58c93ee206ad

See more details on using hashes here.

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

Hashes for awwall-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2022ae6726ce6b36dd456137796da3e65b836cd5119495741e47bd4465226cb5
MD5 a836fd5108cf69977415c3ba195b4762
BLAKE2b-256 f41250ba110929feb6ab31205826a321b990102a9b2c07c22b7ad0da240eb0c9

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page