Jarrod E. Brown
← All projects

Solution Architecture Document — OFAC Deny List

Project: OFAC-Deny-List — Dynamic Firewall Rules for EdgeRouter Author: Jarrod E. Brown Status: Working Repository: github.com/jarrodebrown/OFAC-Deny-List (private)


1. Overview

OFAC Deny List is an automated pipeline that fetches U.S. Treasury OFAC sanctions data, resolves sanctioned countries and entities to IP CIDR blocks, and generates dynamic firewall deny rules for a Ubiquiti EdgeRouter (Vyatta CLI). It converts a compliance/threat-reduction intent — "don't talk to sanctioned networks" — into enforceable, auditable, reversible firewall configuration.

The pipeline bridges three distinct data domains — sanctions policy, IP geolocation, and edge-firewall configuration — into a single automated workflow, keeping deny lists current and safe to deploy without manual intervention.

2. Problem & Context

Sanctions and high-risk geographies change over time, but most edge firewalls carry static, hand-maintained block lists that quickly go stale. Manually translating OFAC designations into accurate, current IP ranges — without accidentally blocking legitimate CDN/cloud traffic that geolocates to those regions — is tedious and error-prone.

The core challenge is three-fold. First, the OFAC SDN list uses country codes and entity names, not IP addresses — translation to network-level enforcement requires resolving through RIR allocations and BGP routing tables. Second, major CDN and cloud providers announce prefixes that geolocate to sanctioned regions but serve global legitimate traffic; blocking them creates collateral damage. Third, both sanctions designations and IP allocations change over time, so any static approach degrades silently.

This pipeline keeps the deny list current, avoids CDN/cloud collateral blocking, and provides an auditable, reversible deployment mechanism.

3. Goals & Requirements

Functional

  • Download and parse the OFAC Specially Designated Nationals (SDN) list.
  • Resolve comprehensively sanctioned countries to current IP CIDR blocks via RIR delegation data.
  • Resolve sanctioned-region entities/ISPs to BGP-announced prefixes via ASN lookup.
  • Exclude major CDN/cloud prefixes to avoid collateral blocking.
  • Aggregate and deduplicate CIDR blocks to minimize rule-set size.
  • Generate EdgeRouter (Vyatta CLI) network-groups and inbound/outbound drop rules.
  • Support dry-run, deploy, and full rollback.

Non-functional

  • Auditable: maintain an append-only removal/audit ledger with timestamped entries.
  • Safe by default: dry-run first; reversible deployment with one-command rollback.
  • Configurable country/entity scope via JSON config.
  • Freshness: deny lists reflect sanctions and IP allocation changes within the configured update cadence.

4. Decision Rationale

Why Python over shell scripting? The pipeline requires HTTP fetching, SDN XML/CSV parsing, CIDR aggregation (netaddr), and SSH-based router management (paramiko). Shell scripting could handle individual steps, but orchestrating error handling, retry logic, and structured data manipulation across the full chain is cleaner in Python. The shell wrapper (run_ofac_update.sh) remains for the operator-facing interface — it handles argument parsing and environment setup, then delegates to Python for the heavy lifting.

Why ipdeny.com over direct RIR queries? The five Regional Internet Registries (AFRINIC, APNIC, ARIN, LACNIC, RIPE NCC) each publish delegation files in different formats and update cadences. ipdeny.com aggregates all five into per-country CIDR lists updated daily. The tradeoff is an additional dependency, but it eliminates the complexity of parsing five heterogeneous data sources and normalizing their outputs. If ipdeny becomes unavailable, the pipeline can fall back to direct RIR delegation files — the parsing logic is isolated in a single module.

Why Team Cymru for ASN resolution? Sanctioned entities often operate under specific Autonomous System Numbers. Team Cymru's IP-to-ASN mapping service is the standard reference for BGP-announced prefix resolution, used by threat-intelligence platforms and network-security tooling industry-wide. The whois-based interface is lightweight and avoids the complexity of maintaining a local BGP routing table.

Why EdgeRouter/Vyatta CLI as the enforcement target? The Ubiquiti EdgeRouter is the deployed edge device on the target network. The Vyatta CLI provides a structured, scriptable interface for firewall configuration — network-groups can be created, populated, and referenced by drop rules in a single SSH session. The alternative — iptables directly — would bypass the router's configuration management, making rollback and audit significantly harder.

Why CDN whitelist subtraction over prefix-level allow-listing? The pipeline subtracts known CDN/cloud prefixes from the country-level deny lists rather than maintaining a separate allow list. This approach ensures that new CDN prefixes added by providers are not blocked by default — the whitelist is refreshed from authoritative provider-published ranges on each pipeline run. The alternative (a static allow list) would require manual updates whenever a CDN expands its infrastructure.

5. Architecture Overview

OFAC Deny List Data Flow Diagram
Data Flow Diagram — full resolution chain from Treasury to EdgeRouter

The pipeline follows a four-stage linear architecture: Fetch → Resolve → Filter → Deploy. Each stage produces intermediate artifacts that are inspectable and auditable before the next stage consumes them.

  1. Fetch — download the OFAC SDN list from Treasury.gov and per-country CIDR blocks from ipdeny.com.
  2. Resolve — map sanctioned countries to their IP allocations; resolve sanctioned entities/ASNs to BGP-announced prefixes via Team Cymru.
  3. Filter — subtract CDN/cloud provider prefixes (fetched from each provider's published IP range endpoint) from the combined deny set.
  4. Deploy — generate Vyatta CLI commands, apply via SSH in dry-run or commit mode, and record changes in the audit ledger.

6. Components

# Component Responsibility
1 ofac_deny_list.py Core pipeline: fetches OFAC SDN data, resolves countries (ipdeny.com) and entities/ASNs (Team Cymru) to CIDRs, applies CDN whitelist subtraction, writes per-country, per-entity, and combined deny-list files.
2 deploy_rules.py Generates Vyatta CLI commands for network-groups and drop rules; manages SSH connection to EdgeRouter; handles dry-run, commit, and rollback modes.
3 run_ofac_update.sh Operator-facing wrapper: argument parsing (--deploy, --rollback, --dry-run), environment validation, pipeline orchestration.
4 config.json Configuration: router IP, SSH credentials path, rule number ranges, enabled countries, extended watchlist, CDN whitelist sources, update cadence.
5 ofac_removal_audit.py Audit subsystem: compares current deny-list run against previous snapshot, records additions and removals with timestamps in the append-only ledger.
6 deny_lists/ Generated output directory: combined.txt, per-country files (KP.txt, IR.txt, etc.), entity_prefixes.txt, and whitelist_removed.txt (CIDRs excluded by the CDN filter).
7 audit_ledger.jsonl Append-only audit log: one JSON object per line, each recording a timestamped change (addition, removal, or rollback) with source attribution.

7. Data Flow

  1. OFAC SDN fetch. The pipeline downloads the current SDN list from Treasury.gov (XML format). The parser extracts country codes from comprehensively sanctioned programs and entity identifiers from targeted sanctions entries.
  2. Country-to-CIDR resolution. For each sanctioned country code, the pipeline fetches the corresponding aggregated CIDR list from ipdeny.com. These lists are sourced from RIR (Regional Internet Registry) delegation data and updated daily by ipdeny. The pipeline aggregates overlapping prefixes using netaddr's cidr_merge to minimize the resulting rule count.
  3. Entity/ASN resolution. Sanctioned entities with known ASNs are resolved to their BGP-announced prefixes via Team Cymru's whois service. This captures network blocks operated by sanctioned ISPs or organizations that may not fall within a sanctioned country's allocation.
  4. CDN/cloud whitelist subtraction. The pipeline fetches current IP ranges from each whitelisted provider's published endpoint (Cloudflare, AWS, Google, Azure, Akamai, Fastly). These prefixes are subtracted from the combined deny set. Removed CIDRs are logged to whitelist_removed.txt for audit visibility.
  5. Deny-list generation. The filtered CIDR sets are written to deny_lists/ as combined, per-country, and entity files. A diff against the previous run's output feeds the audit ledger.
  6. Rule generation and deployment. deploy_rules.py reads the deny lists and generates Vyatta CLI commands: set firewall group network-group entries and set firewall name <chain> rule <n> action drop rules for both inbound and outbound. In dry-run mode, commands are printed for review. In deploy mode, they are applied via SSH and committed. The previous configuration is captured for rollback.

8. Data Model

SDN parsed output — structured records extracted from the Treasury XML: entity name, entity type (individual, organization, vessel), associated country codes, and any known network identifiers (ASNs, IP ranges published in the SDN itself).

CIDR deny sets — per-country and per-entity sets of IPv4 CIDR blocks, stored as newline-delimited text files. Each file represents the aggregated, deduplicated prefix list for a single source. The combined file is the union of all per-country and per-entity sets minus the CDN whitelist.

CDN whitelist — a merged set of CIDR blocks from all whitelisted providers, refreshed on each pipeline run. The whitelist is not persisted between runs; it is always fetched fresh to capture provider infrastructure changes.

Audit ledger (audit_ledger.jsonl) — append-only, one JSON object per line. Each entry contains: timestamp (ISO 8601), action (add | remove | rollback), cidr (the affected prefix), source (country code, entity name, or "whitelist"), run_id (unique identifier for the pipeline execution), and operator (the SSH user who initiated the run). Retention: indefinite; the ledger is append-only and never truncated.

EdgeRouter configuration snapshot — a full Vyatta configuration export captured before each deploy, stored as pre_deploy_config_<timestamp>.txt. Used by the rollback mechanism to restore the previous firewall state.

9. External Interfaces

Source Endpoint / Method Data Format Refresh Strategy Auth
OFAC SDN List treasury.gov/ofac/downloads/sdn.xml XML Fetched each pipeline run; Treasury publishes updates as sanctions change (typically weekly) Public, no key
ipdeny.com ipdeny.com/ipblocks/data/aggregated/<cc>-aggregated.zone Newline-delimited CIDRs Fetched each pipeline run; ipdeny aggregates RIR data daily Public, no key
Team Cymru whois -h whois.cymru.com (bulk mode) Text (ASN → prefix mapping) Queried each pipeline run for configured entity ASNs Public, no key
Cloudflare IPs cloudflare.com/ips-v4 Newline-delimited CIDRs Fetched each pipeline run Public, no key
AWS IP Ranges ip-ranges.amazonaws.com/ip-ranges.json JSON Fetched each pipeline run Public, no key
Google Cloud IPs gstatic.com/ipranges/cloud.json JSON Fetched each pipeline run Public, no key
Azure IP Ranges Microsoft download center (weekly JSON) JSON Fetched each pipeline run Public, no key
Akamai IPs Published prefix list Newline-delimited CIDRs Fetched each pipeline run Public, no key
Fastly IPs api.fastly.com/public-ip-list JSON Fetched each pipeline run Public, no key
EdgeRouter SSH (paramiko) → Vyatta CLI CLI commands On deploy/rollback SSH key auth

10. Error Handling & Resilience

OFAC SDN fetch failure. If the Treasury endpoint is unreachable or returns an unexpected format, the pipeline aborts the current run with a non-zero exit code and logs the error. It does not fall back to a cached SDN list — deploying against stale sanctions data is worse than skipping a run. The operator is expected to re-run when connectivity is restored.

ipdeny.com unavailability. If a per-country CIDR file cannot be fetched, the pipeline logs a warning and excludes that country from the current run's deny set. The combined output reflects the partial data, and the audit ledger records which countries were skipped. The pipeline does not abort entirely — partial coverage is preferable to no update at all, provided the operator reviews the log.

Team Cymru query failure. If the ASN-to-prefix lookup fails (timeout, connection refused), entity-level prefixes are omitted from the deny set for that run. Country-level blocks are unaffected. The failure is logged and the entity section of the audit ledger notes the gap.

CDN whitelist fetch failure. If any provider's IP-range endpoint is unreachable, the pipeline aborts. This is a deliberate safety decision: deploying deny rules without subtracting CDN prefixes risks blocking legitimate traffic. The abort is logged with the specific provider that failed. The operator can override with a --skip-whitelist-check flag (documented as unsafe).

OFAC list format changes. The SDN parser validates expected XML structure (root element, namespace, required fields) before processing. If the format has changed — new namespace, restructured elements, missing required fields — the parser raises a structured error identifying what changed, rather than silently producing incorrect output. This is the primary defense against silent data corruption from upstream format evolution.

EdgeRouter SSH failure. If the SSH connection to the router fails during deploy, no commands are issued. If the connection drops mid-deploy (partial commit), the pipeline captures the error state and logs which commands were applied. The rollback mechanism can restore from the pre-deploy snapshot regardless of how far the partial deploy progressed.

Rule-set capacity limits. The EdgeRouter has finite capacity for network-group entries and firewall rules. The pipeline tracks the total CIDR count after aggregation and logs a warning if it exceeds a configurable threshold (default: 15,000 prefixes). If the count exceeds a hard ceiling (default: 25,000), the pipeline aborts with an error rather than attempting a deploy that may destabilize the router's forwarding plane. Current typical run sizes: ~8,000–12,000 aggregated prefixes across all sanctioned countries.

Rollback safety. The --rollback flag restores the pre-deploy configuration snapshot via SSH. Rollback is atomic at the Vyatta CLI level — the entire previous firewall configuration is loaded and committed in a single session. If rollback itself fails (SSH error), the operator is directed to the saved snapshot file for manual recovery.

11. Non-Functional Requirements (Measured)

NFR Target Current Basis
Deny-list freshness ≤ 24 hours from source update Operator-triggered (manual cadence) ipdeny updates daily; OFAC updates as sanctions change
CIDR aggregation ratio ≥ 3:1 reduction ~4:1 typical netaddr cidr_merge; measured across sanctioned-country sets
Total prefix count (post-aggregation) < 15,000 (warn) / < 25,000 (abort) ~8,000–12,000 typical EdgeRouter network-group capacity; measured each run
CDN whitelist coverage 6 major providers 6 providers (Cloudflare, AWS, Google, Azure, Akamai, Fastly) Provider-published ranges; refreshed each run
Deploy/rollback time < 60 seconds ~15–30 seconds typical SSH session to EdgeRouter; measured end-to-end
Audit ledger completeness 100% of changes recorded 100% (enforced by pipeline) Every add, remove, and rollback logged with timestamp and source
Dry-run fidelity Identical command set to deploy Verified by diffing dry-run output against deploy commands Same code path; only SSH commit is gated

12. Tech Stack

Layer Technology Role
Pipeline core Python 3.x SDN parsing, CIDR resolution, aggregation, whitelist subtraction, deny-list generation
CIDR manipulation netaddr IP network arithmetic, prefix aggregation (cidr_merge), set operations (subtraction)
SSH / router management paramiko SSH sessions to EdgeRouter for deploy, rollback, and config snapshot capture
XML parsing xml.etree.ElementTree OFAC SDN list parsing (Treasury XML format)
ASN resolution whois (subprocess) Team Cymru bulk ASN-to-prefix lookups
HTTP fetching requests SDN list, ipdeny zones, CDN IP ranges
Pipeline wrapper Bash (run_ofac_update.sh) Argument parsing, environment validation, operator interface
Configuration JSON (config.json) Router target, SSH credentials path, country scope, rule numbers, thresholds
Audit persistence JSONL (audit_ledger.jsonl) Append-only structured change log
Enforcement target Ubiquiti EdgeRouter / Vyatta CLI Firewall network-groups and drop rules (inbound + outbound)

13. Security & Compliance

CDN whitelist as a safety control. The whitelist subtraction is the primary defense against over-blocking. By fetching provider-published IP ranges fresh on each run, the pipeline adapts to CDN infrastructure changes without manual intervention. The whitelist_removed.txt output provides audit visibility into exactly which prefixes were excluded and why.

Dry-run-first deployment model. Every deploy is preceded by a dry-run that generates the identical command set without applying it. The operator reviews the generated commands in deny_lists/ before committing. This two-step model prevents accidental deployment of incorrect or overly broad deny rules.

Append-only audit ledger. The audit ledger (audit_ledger.jsonl) provides a tamper-evident change history. Each entry records what changed, when, why (source attribution), and who initiated the run. The ledger supports compliance audits for sanctions-motivated network controls and incident reconstruction if a legitimate service is inadvertently blocked.

SSH key authentication. Router access uses SSH key authentication; no passwords are stored in configuration files. The SSH key path is referenced in config.json but the key itself lives outside the repository. The pipeline validates key accessibility before attempting a connection.

No outbound data exfiltration. The pipeline only reads from external sources (Treasury, ipdeny, Team Cymru, CDN providers) and writes to the local filesystem and the EdgeRouter. No data is sent to external services. DNS, HTTP, and whois queries contain only country codes, ASNs, or provider URLs — no sensitive or PII-bearing data.

14. Deployment & Operations

OFAC Deny List Deployment Diagram
Deployment Diagram — infrastructure topology and data flow
OFAC Deny List State Diagram
State Diagram — deploy/rollback lifecycle

Initial setup. Clone the repository, install Python dependencies (pip install -r requirements.txt), configure config.json with the EdgeRouter's IP, SSH key path, rule number ranges, and desired country scope. Verify SSH connectivity to the router.

Standard update cycle. Run run_ofac_update.sh (defaults to dry-run). Review generated commands and deny lists in deny_lists/. Deploy with run_ofac_update.sh --deploy. The pipeline fetches fresh data from all sources, generates updated deny rules, captures a pre-deploy configuration snapshot, applies rules via SSH, and records all changes in the audit ledger.

Rollback. Run run_ofac_update.sh --rollback --deploy to restore the pre-deploy configuration snapshot. Rollback is atomic — the full previous firewall configuration is loaded and committed in a single Vyatta CLI session.

Monitoring. Review audit_ledger.jsonl for change history. Check pipeline exit codes and log output for fetch failures or capacity warnings. The pipeline logs total prefix counts, countries processed, entities resolved, and whitelist exclusions on each run.

Update cadence. The pipeline is designed for operator-triggered execution. A recommended cadence is weekly for routine updates, with ad-hoc runs when Treasury announces significant sanctions changes. The pipeline can be scheduled via cron for automated execution, though the dry-run-first model is recommended for attended operation.

15. Cross-Project Context

Network Threat Pipeline (shared infrastructure). The OFAC Deny List and the Network Threat Pipeline both operate on the same Ubiquiti EdgeRouter and share a common operational context: network-edge security for a home/small-office environment. The Network Threat Pipeline captures and analyzes WAN traffic — the OFAC Deny List shapes what traffic is permitted to reach the edge in the first place. Together, they form a two-layer defense: the OFAC deny rules block known-bad networks at the firewall level, while the threat pipeline detects suspicious behavior in the traffic that passes through.

The two projects share SSH access patterns to the EdgeRouter (paramiko, key-based auth) and could share configuration for the router target. Currently they are independent repositories with no code-level dependency, but a future integration point is feeding the threat pipeline's detected malicious source IPs back into the OFAC deny list as a supplementary blocklist — extending the deny set beyond sanctions-motivated blocking to include threat-intelligence-motivated blocking.

16. Risks, Assumptions & Limitations

  • IP-to-geography mapping is approximate. RIR allocations and BGP announcements do not map perfectly to physical geography. The CDN whitelist mitigates the most impactful false positives (legitimate services hosted on CDN infrastructure geolocating to sanctioned regions), but edge cases remain — particularly for smaller hosting providers with presence in sanctioned countries.
  • Source availability and rate limits. ipdeny.com and Team Cymru are community/research services without SLA guarantees. Extended outages would prevent deny-list updates. The pipeline's partial-failure handling (skip unavailable countries, abort on CDN whitelist failure) balances coverage against safety.
  • EdgeRouter capacity ceiling. The ER-X supports approximately 25,000 network-group entries before forwarding-plane performance degrades. Current typical runs produce 8,000–12,000 aggregated prefixes, leaving headroom, but adding the full extended watchlist could approach the ceiling. The configurable threshold and hard abort protect against exceeding capacity.
  • OFAC list format stability. The SDN XML format has been stable for years, but Treasury provides no schema versioning or change notification. A format change would cause the parser to abort (by design), but the failure mode is a missed update — not incorrect data.
  • Single-target deployment. The current implementation targets a single EdgeRouter. Multi-router environments would require extending deploy_rules.py to manage multiple SSH targets and per-router configuration — not architecturally complex, but not yet implemented.
  • No real-time sanctions monitoring. The pipeline is batch-oriented. Between runs, newly sanctioned networks are not blocked. For most sanctions changes, the lag is acceptable (sanctions are announced publicly before enforcement begins), but the system does not provide real-time response to emergency designations.

17. Roadmap

Phase 1 — Core Pipeline (current). Operator-triggered pipeline covering comprehensively sanctioned countries, entity/ASN resolution, CDN whitelist subtraction, and EdgeRouter deployment with dry-run, commit, and rollback. Append-only audit ledger for change tracking.

Phase 2 — Automated Scheduling & Alerting. Add cron-based scheduling with configurable cadence (daily/weekly). Integrate alert notifications (email or webhook) for pipeline failures, capacity warnings, and significant deny-list changes (>10% delta from previous run). Implement OFAC SDN change detection to trigger ad-hoc runs when Treasury publishes updates.

Phase 3 — Threat Pipeline Integration. Feed Network Threat Pipeline detections back into the deny list as a supplementary blocklist. Detected C2 servers, scanning sources, and DNS-tunneling endpoints would be added to a threat-intel network-group alongside the sanctions-based deny set. This extends the enforcement model from compliance-motivated blocking to threat-intelligence-motivated blocking.

Phase 4 — Multi-Router & IPv6. Extend deployment to support multiple EdgeRouter targets with per-router configuration profiles. Add IPv6 prefix handling (ipdeny and RIRs publish IPv6 delegation data; Team Cymru supports IPv6 ASN lookups). The aggregation and whitelist logic generalizes cleanly; the primary work is in the Vyatta CLI generation and testing.


Diagrams: Data Flow Diagram · Deployment Diagram · State Diagram