Skip to main content

UFAYA

Unified Firewall Abstraction laYer for Automation

CI PyPI version Python versions License: MIT

UFAYA is a Python SDK that provides a single, consistent interface for interacting with firewalls from multiple vendors. Instead of writing separate automation scripts for each firewall platform, UFAYA exposes a unified abstraction layer that normalizes firewall operations across different systems.

The design follows the same architectural principle used by tools like NAPALM, which provide a unified API to interact with devices from different vendors through an abstraction layer.

Supported Vendors

Vendor Driver Status
Juniper SRX juniper_srx Read-only XML ingestion + firewall-rule JSON export with live policy hit counts + XML-first NAT JSON export
Palo Alto paloalto Planned
Fortinet fortinet Planned
Cisco cisco Planned

Drivers can also be contributed out-of-tree and registered via the ufaya.drivers entry-point group; see CONTRIBUTING.md.

Juniper SRX exports

JuniperSRXDriver.export_rules_json(output_dir, mode=...) writes a context-grouped JSON document for parsed security policies.

  • Export modes remain minimal, enriched, and debug.
  • Export payloads now use schema_version: 3.
  • Each exported rule includes a canonical hit_count field.
  • In live mode, UFAYA fetches show security policies hit-count | display xml | no-more and populates hit_count when that operational snapshot is available.
  • The live hit-count parser supports both older policy-information responses and newer Junos operational XML variants such as multi-routing-engine-results with policy-hit-count-entry records.
  • In file mode, or when the live hit-count snapshot cannot be collected, rules still include hit_count: null.
  • Live exports that successfully collect hit counts also include a top-level hit_counts_collected_at UTC timestamp.
  • Hit-count parser maintenance notes live in JUNIPER_HIT_COUNTS.md.

JuniperSRXDriver.export_nat_json(output_dir, mode=...) writes a context-grouped JSON document for parsed Junos NAT rules.

  • NAT export is XML-first in both modes:
    • live mode fetches show configuration | display xml | no-more
    • file mode reads the XML file passed via config_path
  • NAT parsing walks <security><nat><source>, <destination>, and <static> from configuration XML.
  • NAT export modes are also minimal, enriched, and debug.
  • NAT payloads use schema_version: 2.
  • Exported NAT rules use a vendor-agnostic, rule-centric shape with explicit conditions (traffic match) and mapping (before/after rewrite) blocks.
  • conditions describes which packets the rule selects; mapping describes what field is rewritten, from which addresses/ports, to which addresses/ports.
  • Each mapping step includes a human-readable summary, original/translated sides, mapping_kind (fixed/pool/interface_address), determinism (exact/set_based/dynamic), and resolution_status (resolved/unresolved).
  • Static NAT exports both forward (inbound destination rewrite) and reverse (outbound source rewrite) mapping steps.
  • Unconstrained NAT address selectors export explicitly as ["any"] in conditions.
  • NAT application references are resolved into canonical protocol/port condition fields while preserving raw application names.
  • Enriched and debug NAT exports also include referenced translation pools under supporting_objects.translation_pools.
  • supporting_objects.translation_pools remains scoped to pools actually referenced by exported rules, not the full device inventory.
  • Referenced translation pools export the same normalized address/port values used by rule-level mapping targets, including supported address-range forms.
  • NAT lookup metadata records Juniper precedence as static, then destination, then source.

Installation

pip install ufaya

Command line

Export security policies or NAT rules directly from a saved Junos XML configuration:

ufaya export rules \
  --vendor juniper_srx \
  --config srx-prod.xml \
  --out exports/ \
  --mode enriched

ufaya export nat \
  --vendor juniper_srx \
  --config srx-prod.xml \
  --out exports/ \
  --mode enriched

--mode accepts minimal, enriched, or debug and defaults to enriched. On success, the command prints the path of the generated JSON file. Invalid vendors, missing or malformed configuration files, unsupported capabilities, and unwritable output locations produce a concise error and a nonzero exit.

Usage

import ufaya

# File mode: parse a saved Junos XML config.
driver = ufaya.get_firewall_driver(
    "juniper_srx",
    config_path="srx-prod.xml",
)
rules = driver.get_rules()

# Live mode: SSH to the device. Use as a context manager so a single SSH
# session is shared across get_rules() and get_nat_rules() (and any future
# operational reads).
driver = ufaya.get_firewall_driver(
    "juniper_srx",
    host="srx-prod.example.com",
    username="readonly",
    password="...",
    read_timeout=120,  # Optional per-command timeout in seconds (default: 60).
)
with driver:
    rules = driver.get_rules()
    nat_rules = driver.get_nat_rules()

# Capability discovery: ask what the driver can do, rather than calling and
# catching NotImplementedError.
from ufaya.firewall.base import NatReader, FirewallWriter
assert isinstance(driver, NatReader)        # supports NAT reads
assert not isinstance(driver, FirewallWriter)  # read-only

Juniper live reads default to a 60-second timeout per configuration or hit-count command. Set read_timeout on JuniperSRXDriver or through get_firewall_driver() for slower devices or larger configurations. The value must be finite and greater than zero; it does not change SSH connection or authentication timeouts and is unused in file mode.

If the hit-count command raises, the driver closes that session and reconnects before reading configuration. Successful configuration reads still return rules with hit_count: null when counters are unavailable. Inside with driver:, the replacement session is reused until the block exits. Configuration or reconnect failures still raise ConnectionError; a session whose command read failed is closed before another call can reuse it.

Out-of-tree drivers can register themselves via ufaya.register_driver(...) or by declaring a [project.entry-points."ufaya.drivers"] entry in their own pyproject.toml. See CONTRIBUTING.md.

JSON Schemas

The schemas/ directory publishes the canonical contract for each export type:

Both are strict (additionalProperties: false) Draft 2020-12 schemas. Downstream consumers can use them with any JSON Schema validator. The repo's CI fails any change that violates the contract; see CONTRIBUTING.md for the development flow.

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT

Metadata

Release files for ufaya 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ufaya 0.8.0
File Size Uploaded
ufaya-0.8.0.tar.gz 81.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ufaya 0.8.0
File Interpreter ABI Platform
ufaya-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 115.0 kB

Release files / ufaya-0.8.0.tar.gz

Download URL ufaya-0.8.0.tar.gz
Size 81.3 kB
Tags Source
SHA-256 checksum
How to use checksums
200539159e4e625e41db175c026014a7a2620a92ec82856043d72365455bff69
BLAKE2b-256 checksum
How to use checksums
82daf48d2c3511095826d948e90e184b633e4a6ec5bb0fee0ac9b65fb24acff1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / ufaya-0.8.0-py3-none-any.whl

Download URL ufaya-0.8.0-py3-none-any.whl
Size 33.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ff20a225b17d54f58458c8cdd9c77cf7fd731b7a63f088ca7c6d9ddf7d26fa16
BLAKE2b-256 checksum
How to use checksums
44a982027fab6f707a21b3684ffb3f789dc60467a4442145940604dfc330f9c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release 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