Skip to main content

Enterprise-grade tool to redact secrets and anonymise identifiers in pfSense/Netgate config.xml exports. Preserves network topology while removing passwords, VPN keys, certificates, public IPs, domains, and MACs for safe sharing with support teams, consultants, AI tools, and forums.

Project description

pfSense XML Configuration Redactor

PyPI version Python Versions License: MIT Tests Downloads

Safely redact sensitive fields in a pfSense config.xml export so you can share it without exposing passwords, keys, or network identifiers.

Keywords: pfSense config.xml redactor, pfSense sanitiser, firewall config sanitisation, VPN config anonymisation, OpenVPN redaction, WireGuard key redaction, IPsec secret removal, network topology anonymiser, Netgate TAC sharing

pfsense-redactor redacts secrets and optionally anonymises identifiers in pfSense config.xml exports—so you can share them safely with support, vendors, auditors, forums, or AI tools.

Unlike generic XML redaction tools, pfsense-redactor understands pfSense-specific configuration structures and VPN formats.

Why pfsense-redactor?

  • pfSense-native – Understands pfSense XML structures (IPsec, OpenVPN, WireGuard, packages)
  • Zero dependencies – Pure Python stdlib, runs anywhere (Windows, macOS, Linux)
  • Well-tested – Extensively tested across multiple OSes and Python versions
  • Topology-preserving – Anonymise without breaking routing logic or firewall rules
  • Security-first – Built-in protections against path traversal and symlink attacks
  • CI/CD ready – Suitable for automated compliance workflows and GitOps

When should I use pfsense-redactor?

Use pfsense-redactor when you need to share a pfSense config.xml file outside the firewall (for example with vendors, consultants, forums, or AI tools) and want to remove secrets and/or anonymise network identifiers without breaking topology or routing logic.

Quick start (recommended)

1) Preserve private IPs (best for support / troubleshooting)

pfsense-redactor config.xml redacted.xml --keep-private-ips

Keeps internal addressing visible while removing secrets and redacting public identifiers.

2) Topology-preserving anonymisation (best for vendors / forums / AI)

pfsense-redactor config.xml redacted.xml --anonymise

Replaces identifiers with consistent placeholders so relationships remain clear while reducing disclosure.

For pfSense on-firewall sanitisation, see diag_sanitize.php below; pfsense-redactor is intended for off-box use and anonymisation.

See the Usage section and the Command-Line Flags Reference below for all available options.


Use Cases

Common command patterns depending on who you’re sharing with.

Sharing with Netgate TAC Support

# On the firewall (recommended for TAC)
/usr/local/sbin/diag_sanitize.php /conf/config.xml > /conf/config_sanitised.xml

# Off-box: additional anonymisation before sharing further
pfsense-redactor config_sanitised.xml support-safe.xml --keep-private-ips --no-redact-domains

Sharing with AI tools

Use --aggressive if you have third-party packages or aren’t sure where secrets live.

# Topology-preserving anonymisation (private IPs are kept visible by default with --anonymise)
pfsense-redactor config.xml ai-ready.xml --anonymise --aggressive
# Now safe to upload to AI tools for configuration analysis
# Anonymise everything (including private IPs)
pfsense-redactor config.xml ai-ready.xml --anonymise --no-keep-private-ips --aggressive
# Now safe to upload to AI tools for configuration analysis

Vendor/MSP Handoffs

# Option A: Preserve private IPs for troubleshooting context
pfsense-redactor config.xml vendor-share.xml --anonymise
# Option B: Anonymise everything (including private IPs) for stricter privacy
pfsense-redactor config.xml vendor-share.xml --anonymise --no-keep-private-ips

Security Audits

# Preview what will be redacted before sharing
pfsense-redactor config.xml --dry-run-verbose

Automated Compliance Workflows

# CI/CD integration for automated sanitisation
pfsense-redactor $INPUT_CONFIG $OUTPUT_CONFIG --aggressive --fail-on-warn

Relationship to pfSense built-in sanitisation

pfSense includes a built-in configuration sanitisation script:

/usr/local/sbin/diag_sanitize.php /conf/config.xml > /conf/config_sanitised.xml

This official tool runs on the firewall itself and is primarily intended for safely sharing configurations with Netgate support. It removes high-value secrets (password hashes, pre-shared keys, certificates, etc.) while preserving the original network topology.

pfsense-redactor is complementary, not a replacement.

Built-in diag_sanitize.php pfsense-redactor
Runs on pfSense only Runs anywhere (workstation, CI, automation)
PHP, internal to pfSense Python, standalone, MIT-licensed
Fixed sanitisation behaviour Configurable redaction and anonymisation
Removes secrets Removes secrets and can anonymise IPs, domains, MACs and URLs
Best suited to Netgate support (TAC) Best suited to vendors, consultants, AI tools and forums

pfsense-redactor exists to cover use cases where:

  • the configuration has already been exported,
  • you do not wish to run additional tooling on the firewall,
  • or you require privacy-preserving anonymisation in addition to basic secret removal.

Both tools share the same goal: preventing accidental disclosure of sensitive information when sharing pfSense configurations.


Comparison with Alternatives

Comparison with Alternatives
Feature pfsense-redactor Generic XML Tools Manual Redaction Built-in diag_sanitize.php
pfSense-aware structure ⚠️ Manual
Runs off-firewall ❌ (pfSense only)
Network anonymisation ⚠️ Error-prone
Topology preservation ⚠️ Error-prone
Configurable modes ✅ multiple modes N/A ❌ Fixed
CIDR allow-lists ⚠️ Error-prone
CI/CD integration ⚠️ ⚠️ Error-prone
Cross-platform ⚠️
WireGuard/IPsec aware ⚠️
Zero dependencies ⚠️ Varies

FAQ

What does pfsense-redactor redact by default?

pfsense-redactor removes secrets such as passwords, private keys, certificates, tokens, and shared secrets.
It also redacts public IP addresses, domains, MAC addresses, and URLs unless explicitly preserved.

Does pfsense-redactor anonymise or just remove data?

It supports both. Redaction removes sensitive values entirely, while anonymisation replaces identifiers with consistent placeholders so topology and relationships remain clear.

Will anonymisation break troubleshooting or topology analysis?

No. Deterministic anonymisation ensures the same identifier is always replaced with the same alias, preserving logical relationships and routing flow.

Can I safely share the output with vendors or AI tools?

Yes. pfsense-redactor is designed for sharing configurations externally without exposing secrets or identifiable network information.
Redacted output is for analysis only and must not be restored to pfSense.

Does this understand pfSense-specific configuration structures?

Yes. Unlike generic XML redaction tools, pfsense-redactor understands pfSense configuration layouts, including VPNs, interfaces, gateways, and common package XML structures.

When should I use aggressive mode?

Use --aggressive when sharing configurations publicly or when third-party packages may include unknown sensitive fields.

Aggressive mode broadens both secret detection and identifier rewriting. On top of the default behaviour it will:

  • Redact unrecognised high-entropy values (base64/hex/PEM-shaped) in any element, rather than reporting them
  • Redact free-text option blocks (custom_options, userparams, upsd_users, advanced, …) wholesale
  • Redact credential-shaped URL path segments, such as Slack/Discord webhook tokens
  • Apply IP/domain redaction to all element text, not just known fields

Can I restore the redacted file to pfSense?

No. Redacted output is for analysis/sharing only and must never be imported back into pfSense.


Installation

From PyPI (recommended)

pip install pfsense-redactor

Note: If you encounter an externally-managed-environment error (common on macOS and modern Linux distributions), use one of these alternatives:

Option 1: Install with pipx (recommended for CLI tools)

brew install pipx
pipx install pfsense-redactor

Option 2: Use a virtual environment

python3 -m venv venv
source venv/bin/activate
pip install pfsense-redactor

Option 3: Install in user space

pip install --user pfsense-redactor

From Source

git clone https://github.com/grounzero/pfsense-redactor.git
cd pfsense-redactor

Option 1: Development mode (recommended for contributing)

pip install -e .

Option 2: With virtual environment

python3 -m venv venv
source venv/bin/activate
pip install -e .

The tool preserves network architecture and routing logic whilst sanitising secrets and identifiers allowing safe troubleshooting and topology review without disclosing private data.

Keeps firewall and routing context
Removes passwords, keys, public IPs (optional), tokens, certs
Supports anonymisation for consistent placeholder mapping
Understands pfSense config structures, namespaces, VPNs, WireGuard, XML attributes, IPv6 zone IDs


Features

Protects real secrets

  • Passwords & encrypted passwords
  • Pre-shared keys (IPSec, OpenVPN, WireGuard)
  • TLS/OpenVPN static keys & certs
  • SNMP community strings
  • LDAP / RADIUS secrets
  • API keys & tokens
  • PEM blocks (RSA / EC / OpenSSH)

Preserves network logic

  • Subnets & masks (255.x.x.x always preserved)
  • Router topology
  • VLAN and VPN interfaces
  • Firewall rules and gateways

Smart redaction

Data Behaviour
Internal IPs Preserve with --keep-private-ips
Public IPs Mask or anonymise
Email addresses Mask or anonymise
URLs Preserve structure, mask hostname
MAC addresses Mask format-preserving
Certificates Collapse to [REDACTED_CERT_OR_KEY]

Operational modes

Mode Purpose
Default Safe redaction for sharing logs
--keep-private-ips Preserve private IPs (best for support/AI)
--anonymise Replace identifiers with consistent placeholders (IP_1, domain3.example)
--aggressive Scrub all fields (plugins/custom XML)
--redact-descriptions Also redact descriptions, hostnames and SSIDs (may contain personal names)

Requirements

  • Python 3.9+

Usage

Basic usage

# Output filename auto-generated as config-redacted.xml
pfsense-redactor config.xml

# Or specify output filename explicitly
pfsense-redactor config.xml redacted.xml

Preserve private IPs (recommended)

pfsense-redactor config.xml redacted.xml --keep-private-ips

Allow-list specific IPs and domains

# Preserve specific public services (never redact)
pfsense-redactor config.xml --allowlist-ip 8.8.8.8 --allowlist-domain time.nist.gov

# Preserve entire CIDR ranges
pfsense-redactor config.xml --allowlist-ip 203.0.113.0/24

# Use an allow-list file (supports IPs, CIDRs, and domains)
pfsense-redactor config.xml --allowlist-file my-allowlist.txt

Topology-safe anonymisation

pfsense-redactor config.xml redacted.xml --anonymise

Allow internal DNS names

pfsense-redactor config.xml redacted.xml --no-redact-domains --keep-private-ips

Aggressive mode

pfsense-redactor config.xml redacted.xml --aggressive

Dry run

# Show statistics only
pfsense-redactor config.xml --dry-run

# Show statistics with sample redactions (safely masked)
pfsense-redactor config.xml --dry-run-verbose

Output to STDOUT

pfsense-redactor config.xml --stdout > redacted.xml

In-place (danger)

pfsense-redactor config.xml --inplace --force

Command-Line Flags Reference

Version & Help

Flag Description
--version Show program version and exit
--check-version Check for updates from PyPI
-h, --help Show help message and exit

Input/Output

Flag Description
input Input pfSense config.xml file (positional argument)
output Output redacted config.xml file (positional argument, optional with --stdout/--dry-run/--inplace)
--stdout Write redacted XML to stdout instead of file
--inplace Overwrite input file with redacted output (use with caution)
--force Overwrite output file if it already exists
--allow-absolute-paths Allow absolute file paths (relative paths only by default for security)

Redaction Modes

Flag Description
--keep-private-ips Keep non-global IP addresses visible (RFC1918/ULA/loopback/link-local). Netmasks and unspecified addresses (0.0.0.0, ::) always preserved
--no-keep-private-ips When used with --anonymise, do NOT keep private IPs visible (mask all IPs)
--anonymise Use consistent aliases (IP_1, domain1.example) to preserve network topology. Implies --keep-private-ips unless --no-keep-private-ips specified
--aggressive Broaden secret detection (high-entropy values, free-text option blocks, URL path tokens) and apply IP/domain redaction to all element text
--no-redact-ips Do not redact IP addresses
--no-redact-domains Do not redact domain names
--redact-url-usernames Redact usernames in URLs (default: preserve usernames, always redact passwords)
--redact-descriptions Redact free-text descriptions and identifiers (descr, detail, hostname, ssid). Off by default as these aid troubleshooting
Allow-lists
  • Allow-lists let you preserve specific well-known IPs and domains that don't leak private information.
Flag Description
--allowlist-ip IP_OR_CIDR IP address or CIDR network to never redact (repeatable). Applies to text and URLs
--allowlist-domain DOMAIN Domain to never redact (repeatable, case-insensitive, supports suffix matching). Applies to bare FQDNs and URL hostnames
--allowlist-file PATH File containing IPs, CIDR networks, and domains to never redact (one per line)
--no-default-allowlist Do not load default allow-list files (.pfsense-allowlist in current dir or ~/.pfsense-allowlist)
Testing & Diagnostics
Flag Description
--dry-run Show statistics only, do not write output file
--dry-run-verbose Show statistics with sample redactions (safely masked to prevent leaks)
--fail-on-warn Exit with non-zero code if root tag is not 'pfsense' (useful in CI)

Output Control

Flag Description
-q, --quiet Suppress progress messages (show only warnings and errors)
-v, --verbose Show detailed debug information

Allow-lists

Allow-lists let you preserve specific well-known IPs and domains that don't leak private information.

Default allow-list files

The tool automatically loads allow-lists from these locations (if they exist):

  1. .pfsense-allowlist in current directory
  2. ~/.pfsense-allowlist in home directory

To disable: use --no-default-allowlist

Allow-list file format

Create .pfsense-allowlist or use --allowlist-file:

# Comments start with #
# One item per line (IP, CIDR, or domain)

# Public DNS servers
8.8.8.8
1.1.1.1

# Cloud provider ranges
203.0.113.0/24
198.51.100.0/24

# NTP servers (suffix matching: preserves time.nist.gov and *.time.nist.gov)
time.nist.gov
pool.ntp.org

# Wildcard domains (*.example.org preserves all subdomains)
*.pfsense.org

See allowlist.example for a complete template.

CLI allow-list flags

# Add specific IPs or CIDR ranges (repeatable)
--allowlist-ip 8.8.8.8 --allowlist-ip 203.0.113.0/24

# Add specific domains (repeatable, case-insensitive, supports suffix matching)
--allowlist-domain time.nist.gov --allowlist-domain pool.ntp.org

# Load from file (supports IPs, CIDRs, and domains)
--allowlist-file /path/to/allowlist.txt

# Disable default file loading
--no-default-allowlist

Features:

  • CIDR support: 203.0.113.0/24 preserves all IPs in that range
  • Suffix matching: example.org preserves sub.example.org, db.corp.example.org, etc.
  • Wildcard domains: *.example.org is equivalent to suffix matching on example.org
  • IDNA/punycode: Automatically handles internationalised domains (e.g., bücher.examplexn--bcher-kva.example)
  • Merged sources: All CLI flags, files, and default files are combined

Note: Items in allow-lists are never redacted in:

  • Raw text IP/domain references
  • URL hostnames
  • Bare FQDNs

Example

Input

<openvpn>
  <server>
    <local>192.168.10.1</local>
    <tlsauth>-----BEGIN OpenVPN Static key-----ABC123...</tlsauth>
    <remote>198.51.100.10</remote>
    <remote_port>443</remote_port>
  </server>
</openvpn>

Output (--keep-private-ips)

<openvpn>
  <server>
    <local>192.168.10.1</local>
    <tlsauth>[REDACTED]</tlsauth>
    <remote>XXX.XXX.XXX.XXX</remote>
    <remote_port>443</remote_port>
  </server>
</openvpn>

Output (--anonymise)

<openvpn>
  <server>
    <local>IP_1</local>
    <tlsauth>[REDACTED]</tlsauth>
    <remote>IP_2</remote>
    <remote_port>443</remote_port>
  </server>
</openvpn>

Security Notes

Never restore the redacted file to pfSense.

Redacted output is for analysis only, because:

  • CDATA and comments are removed by XML parser
  • PEM blocks and binary data are collapsed
  • Some optional metadata fields may be stripped

Always keep the original secure copy.

Path Security

The tool includes built-in protections against malicious file path operations:

Default behaviour (secure):

  • Only relative paths are allowed by default
  • Directory traversal (../../../etc/passwd) is blocked
  • Paths with null bytes are rejected
  • Writing to system directories (/etc, /sys, /proc, /Windows/System32, etc.) is blocked
  • Safe locations (home directory, current working directory, temp directories) are automatically allowed

Using --allow-absolute-paths:

  • Enables absolute paths for intentional use cases
  • Still blocks writes to sensitive system directories
  • Still blocks directory traversal attempts
  • Useful when you need to specify full paths explicitly

Examples:

# Safe: relative path (default)
pfsense-redactor config.xml output.xml

# Blocked: absolute path without flag
pfsense-redactor /etc/config.xml output.xml
# Error: Absolute paths not allowed (use --allow-absolute-paths)

# Blocked: directory traversal
pfsense-redactor ../../../etc/passwd output.xml
# Error: Path contains directory traversal components (..)

# Blocked: writing to system directory (even with flag)
pfsense-redactor config.xml /etc/output.xml --allow-absolute-paths
# Error: Cannot write to sensitive system directory

# Allowed: absolute path to safe location with flag
pfsense-redactor ~/config.xml ~/output.xml --allow-absolute-paths

# Blocked: in-place editing of system files
pfsense-redactor /etc/hosts --inplace --force --allow-absolute-paths
# Error: Cannot use --inplace with this file

Protected system directories:

  • Unix/Linux: /etc, /sys, /proc, /dev, /boot, /root, /bin, /sbin, /usr/bin, /usr/sbin, /lib, /lib64, /var/log, /var/run, /tmp, /run
  • Windows: C:\Windows, C:\Windows\System32, C:\Program Files, C:\ProgramData
  • Critical files: /etc/passwd, /etc/shadow, /etc/sudoers, etc.

Testing

Dry run summary

# Statistics only
pfsense-redactor config.xml --dry-run

# Statistics with sample redactions (safely masked to avoid leaks)
pfsense-redactor config.xml --dry-run-verbose

Sample output with --dry-run-verbose:

[+] Redaction summary:
    - Passwords/keys/secrets: 10
    - Certificates: 6
    - IP addresses: 26
    - Domain names: 47

[+] Samples of changes (limit N=5):
    IP: 198.51.***.42 → XXX.XXX.XXX.XXX
    IP: 2001:db8:*:****::1 → XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
    URL: https://198.51.***.42/admin → https://XXX.XXX.XXX.XXX/admin
    FQDN: db.***.example.org → example.com
    MAC: aa:bb:**:**:ee:ff → XX:XX:XX:XX:XX:XX
    Secret: p****************d (len=18) → [REDACTED]
    Cert/Key: PEM blob (len≈2048) → [REDACTED_CERT_OR_KEY]

Sample masking policy (prevents leaks in dry-run output):

  • IP: Keep first and last octet/segment, mask middle (e.g., 198.51.***.42)
  • URL: Show full URL but mask host as above
  • FQDN: Keep TLD and one left label, mask rest (e.g., db.***.example.org)
  • MAC: Mask middle octets (e.g., aa:bb:**:**:ee:ff)
  • Secret: Show length and first/last 2 chars only (e.g., p****************d (len=18))
  • Cert/Key: Just show placeholder with length (e.g., PEM blob (len≈2048))

Recommended test flags

Purpose Command
Support & AI review --keep-private-ips --no-redact-domains
Topology map w/o identifiers --anonymise
Nuke everything --aggressive

Stats example

[+] Redaction summary:
    - Passwords/keys/secrets: 4
    - Certificates: 2
    - IP addresses: 11
    - MAC addresses: 3
    - Domain names: 5
    - Email addresses: 1
    - URLs: 2

Contributing

Pull requests welcome. Particularly:

  • Additional pfSense element coverage
  • Plugin XML tag packs (WireGuard, pfBlockerNG, HAProxy, Snort, ACME, FRR)
  • Unit test configs

Licence

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pfsense_redactor-1.1.0.tar.gz (226.7 kB view details)

Uploaded Source

Built Distribution

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

pfsense_redactor-1.1.0-py3-none-any.whl (44.5 kB view details)

Uploaded Python 3

File details

Details for the file pfsense_redactor-1.1.0.tar.gz.

File metadata

  • Download URL: pfsense_redactor-1.1.0.tar.gz
  • Upload date:
  • Size: 226.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pfsense_redactor-1.1.0.tar.gz
Algorithm Hash digest
SHA256 cb7a1d3768eb23962d7930f42a855e03d77cb5062a16db7f68cbe8a9dbbf0892
MD5 b23a45b40c685683f93066fc3faaa8d6
BLAKE2b-256 47cc84b7d3e586241d192553eb516264e6f283984bed8974c56e29b73d936ab5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pfsense_redactor-1.1.0.tar.gz:

Publisher: python-publish.yml on grounzero/pfsense-redactor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pfsense_redactor-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pfsense_redactor-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 87c0ee1825641aaaa43fa00e919c0c822ac9052cf98bd7301f7fa39346add688
MD5 d31f7f1b3ac69547f67decbea44ebd81
BLAKE2b-256 71bb379711b3697c15ab70a98f3fcc662920eec87ebe8d203bdccf0225fe7c77

See more details on using hashes here.

Provenance

The following attestation bundles were made for pfsense_redactor-1.1.0-py3-none-any.whl:

Publisher: python-publish.yml on grounzero/pfsense-redactor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page