Skip to main content

CIDR-aware IP anonymizer with prefix-preserving permutation

Project description

ipanon

A deterministic, CIDR-aware IP anonymizer for log sanitization. Replaces IPv4 and IPv6 addresses in text while maintaining CIDR prefix relationships and respecting reserved/private range boundaries.

Features

  • Prefix-preserving permutation — IPs sharing a subnet stay grouped after anonymization
  • Three-tier classification — private ranges stay private, loopback/multicast pass through unchanged, public IPs get fully anonymized
  • Deterministic — same salt always produces the same output, enabling cross-log correlation
  • IPv4 + IPv6 — full support for both protocols including CIDR notation
  • Streaming — reads stdin, writes stdout; works in pipelines

Installation

pip install .

Or with uv:

uv pip install .

Quick Start

# Anonymize a log file (auto-generated random salt printed to stderr)
cat access.log | ipanon > anonymized.log

# Reproducible anonymization with a fixed salt
ipanon --salt mysecret input.log output.log

# Save the IP mapping for later analysis
ipanon --salt mysecret -m mapping.json < input.log > output.log

How It Works

Every IP address is classified into one of three categories:

Category Behavior Examples
A — Range-preserved Prefix bits locked, remaining bits permuted. 10.x.y.z stays in 10.0.0.0/8. 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, fe80::/10
B — Pass-through Returned unchanged. 127.0.0.1, 0.0.0.0/8, 224.0.0.0/4, ::1, ff00::/8
C — Public Fully anonymized with first-octet permutation + prefix-preserving lower bits. 8.8.8.8, 1.1.1.1, 2001:db8::1

Public IPs are guaranteed never to land in Category A or B address space after anonymization.

CLI Reference

ipanon [OPTIONS] [INPUT] [OUTPUT]
Option Description
-s, --salt SALT Reproducible anonymization salt (random if omitted)
--salt-env ENVNAME Read salt from environment variable
--remap MIXED=TARGET Map public IPs from a mixed first-octet to a pure-public target. Can repeat
--pass-through CIDR Don't anonymize IPs matching CIDR prefix. Can repeat
--allow-pt-collisions Downgrade pass-through collision errors to warnings
--ignore-subnets Treat sub-/8 IPv4 private ranges as public (only 10.0.0.0/8 stays Cat A)
--ignore-reserved Remove ALL reserved range handling; every IP gets fully anonymized
-m, --mapping FILE Write JSON mapping of original-to-anonymized IPs to FILE
-v, --verbose Print stats to stderr. -vv also prints all mappings
-q, --quiet Suppress all warnings

Examples

# Remap 172.x public IPs to the 42.x range
ipanon --salt s --remap 172=42 < input.log

# Don't anonymize your monitoring subnet
ipanon --salt s --pass-through 10.0.0.0/8 < input.log

# Treat all IPs as public (ignore private/reserved ranges)
ipanon --salt s --ignore-reserved < input.log

# Salt from environment variable
export ANON_SALT="my-secret-salt"
ipanon --salt-env ANON_SALT < input.log

Use Cases

Log Sanitization

Anonymize IP addresses in log files before sharing with vendors or publishing:

# Nginx access logs
ipanon --salt "$SECRET" < /var/log/nginx/access.log > sanitized-access.log

# Batch-process multiple log files with the same salt for cross-log correlation
for f in /var/log/app/*.log; do
    ipanon --salt "$SECRET" "$f" "sanitized/$(basename "$f")"
done

# Pipe from journald
journalctl -u myservice --no-pager | ipanon --salt "$SECRET" > sanitized.log

# Apache combined log format — structure is preserved, only IPs change
# Before: 203.0.113.50 - - [10/Oct/2024:13:55:36 -0700] "GET /index.html HTTP/1.1" 200 2326
# After:  71.142.89.50 - - [10/Oct/2024:13:55:36 -0700] "GET /index.html HTTP/1.1" 200 2326

# Save the mapping so you can look up the original IP if needed
ipanon --salt "$SECRET" -m mapping.json < access.log > sanitized.log
cat mapping.json
# {"203.0.113.50": "71.142.89.50", "10.0.1.5": "10.218.94.5", ...}

Config File Sanitization

Scrub IP addresses from configuration files before committing or sharing:

# Sanitize firewall rules
ipanon --salt cfg < iptables-rules.txt > iptables-rules.sanitized.txt

# Sanitize network configs, keeping internal structure visible
# --pass-through keeps your well-known subnets readable
ipanon --salt cfg --pass-through 10.0.0.0/8 < network.conf > network.sanitized.conf

# Sanitize DNS zone files
ipanon --salt cfg < db.example.com > db.example.sanitized.com

# Sanitize Kubernetes manifests or Terraform state
ipanon --salt cfg < terraform.tfstate > terraform.sanitized.tfstate

# Sanitize Ansible inventories
ipanon --salt cfg < hosts.ini > hosts.sanitized.ini

Sharing Diagnostic Output

Clean up diagnostic output before pasting into bug reports or support tickets:

# Sanitize traceroute output
traceroute example.com | ipanon --salt diag

# Sanitize tcpdump captures (text output)
tcpdump -nn -r capture.pcap | ipanon --salt diag > sanitized-capture.txt

# Sanitize `ss` or `netstat` output
ss -tunap | ipanon --salt diag

# Sanitize `ip addr` output
ip addr show | ipanon --salt diag

CI/CD Pipelines

# Set salt once per pipeline run for consistent anonymization across steps
export ANON_SALT="$(openssl rand -hex 16)"

# Anonymize test artifacts before uploading
ipanon --salt-env ANON_SALT < test-output.log > sanitized-output.log
ipanon --salt-env ANON_SALT < network-diag.txt > sanitized-diag.txt

Python API

See API.md for complete API documentation.

from ipanon import Anonymizer, scan_and_replace

# Single IP anonymization
anon = Anonymizer(salt="mysecret")
anon.anonymize("8.8.8.8")        # → "143.57.192.12" (deterministic)
anon.anonymize("10.1.2.3")       # → "10.187.42.5" (stays in 10.0.0.0/8)
anon.anonymize("127.0.0.1")      # → "127.0.0.1" (pass-through)

# Bulk text replacement
text = "Server 8.8.8.8 connected to 10.0.1.5 via 192.168.1.1"
scan_and_replace(text, anon)
# → "Server 143.57.192.12 connected to 10.187.42.5 via 192.168.78.201"

Requirements

  • Python >= 3.9
  • No external dependencies

License

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

ipanon-0.2.1.tar.gz (27.5 kB view details)

Uploaded Source

Built Distribution

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

ipanon-0.2.1-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file ipanon-0.2.1.tar.gz.

File metadata

  • Download URL: ipanon-0.2.1.tar.gz
  • Upload date:
  • Size: 27.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ipanon-0.2.1.tar.gz
Algorithm Hash digest
SHA256 06d038991e5f19ec1d7d88a80ea7d5415dda691c76366441df5962773302f67d
MD5 2e0d09633b6107b9b101e244af6b3892
BLAKE2b-256 e1701a64fcc389e75a90d55dcfff50bf84a5c750ee4e0ce02d550ba624470ee1

See more details on using hashes here.

Provenance

The following attestation bundles were made for ipanon-0.2.1.tar.gz:

Publisher: publish.yml on dg-i/ipanon

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

File details

Details for the file ipanon-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: ipanon-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ipanon-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0e6f623bacf3fc9f63a5862d9339847ea9420214c7db5221af66f06b2978be2c
MD5 1657c36125540f1ba0d1f8e80fa1f293
BLAKE2b-256 3aaa9bd0274f4a30ffb4d37906ffa341fb1e8af3282421b526ccfc474f591ed3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ipanon-0.2.1-py3-none-any.whl:

Publisher: publish.yml on dg-i/ipanon

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