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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06d038991e5f19ec1d7d88a80ea7d5415dda691c76366441df5962773302f67d
|
|
| MD5 |
2e0d09633b6107b9b101e244af6b3892
|
|
| BLAKE2b-256 |
e1701a64fcc389e75a90d55dcfff50bf84a5c750ee4e0ce02d550ba624470ee1
|
Provenance
The following attestation bundles were made for ipanon-0.2.1.tar.gz:
Publisher:
publish.yml on dg-i/ipanon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ipanon-0.2.1.tar.gz -
Subject digest:
06d038991e5f19ec1d7d88a80ea7d5415dda691c76366441df5962773302f67d - Sigstore transparency entry: 1182360237
- Sigstore integration time:
-
Permalink:
dg-i/ipanon@ebec1ec8cbe9c144b8b7469e7765127bdaaa27ef -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/dg-i
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ebec1ec8cbe9c144b8b7469e7765127bdaaa27ef -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e6f623bacf3fc9f63a5862d9339847ea9420214c7db5221af66f06b2978be2c
|
|
| MD5 |
1657c36125540f1ba0d1f8e80fa1f293
|
|
| BLAKE2b-256 |
3aaa9bd0274f4a30ffb4d37906ffa341fb1e8af3282421b526ccfc474f591ed3
|
Provenance
The following attestation bundles were made for ipanon-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on dg-i/ipanon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ipanon-0.2.1-py3-none-any.whl -
Subject digest:
0e6f623bacf3fc9f63a5862d9339847ea9420214c7db5221af66f06b2978be2c - Sigstore transparency entry: 1182360246
- Sigstore integration time:
-
Permalink:
dg-i/ipanon@ebec1ec8cbe9c144b8b7469e7765127bdaaa27ef -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/dg-i
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ebec1ec8cbe9c144b8b7469e7765127bdaaa27ef -
Trigger Event:
release
-
Statement type: