Esc
Type to search posts, tags, and more...
Skip to content

How to Sanitise Cisco IOS Configurations

Using Netconan and Python to strip passwords, IPs, and sensitive data from Cisco configs before sharing them.

Contents

Why Sanitise Configs?

Network configurations contain passwords, SNMP community strings, pre-shared keys, IP addressing schemes, and AS numbers. You need to strip all of that before sharing configs with vendors, customers, or posting them in a support ticket.

Doing this by hand is tedious and error-prone. You will miss a Type 7 password buried on line 847 or forget to scrub that TACACS key. This is a textbook automation use case.

Netconan

Netconan (Network Configuration Anonymizer) from Intentionet is the go-to tool for this. It is actively maintained (v0.15.0 as of early 2026), handles multiple vendor formats, and preserves network structure while anonymizing sensitive data.

Install

pip install netconan

Basic Usage: Anonymize Passwords

Set up a working directory with your original configs and an output folder:

mkdir -p original_configs clean_configs
# Copy your .cfg files into original_configs/

Run Netconan with password anonymization:

netconan -i original_configs/ -o clean_configs/ --anonymize-passwords

This catches and replaces:

  • Cisco Type 7 passwords
  • Salted MD5 hashes (Type 5)
  • Cisco Type 8/9 PBKDF2 and scrypt hashes
  • Juniper Type 9 passwords
  • SNMP community strings
  • TACACS/RADIUS keys

Each password is replaced with a format-compliant substitute, so the resulting config still looks structurally valid.

Add IP Anonymization

netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips

Netconan does prefix-preserving anonymization for both IPv4 and IPv6. Addresses that shared a common prefix before anonymization will still share the same prefix length afterward. This means your subnet relationships and routing topology remain readable in the output.

By default, private-use prefixes (RFC 1918) and the last 8 bits of each address are preserved. This keeps /30 point-to-point links and NAT pools intact. You can override this:

# Preserve a specific prefix but anonymize the rest
netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips \
    --preserve-prefixes 10.0.0.0/8

# Preserve specific addresses entirely
netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips \
    --preserve-addresses 192.168.1.1,10.0.0.0/16

# Keep all private addresses untouched
netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips \
    --preserve-private-addresses

Sensitive Words and AS Numbers

You can also scrub company names, location identifiers, and AS numbers:

netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips \
    --sensitive-words acme,london,dc01 \
    --as-numbers 65001,65002

Deterministic Anonymization with Salt

By default, Netconan generates a random salt each run. If you need consistent mappings across multiple runs (e.g., anonymizing configs from different devices that reference each other), provide a salt:

netconan -i original_configs/ -o clean_configs/ \
    --anonymize-passwords \
    --anonymize-ips \
    --salt "my-consistent-salt"

Same salt, same input, same output. You can even reverse the IP anonymization later:

netconan -i clean_configs/ -o restored_configs/ \
    --undo \
    --salt "my-consistent-salt"

Config File for Repeatable Runs

Instead of passing flags every time, use a config file:

# netconan.cfg
anonymize-ips=true
anonymize-passwords=true
sensitive-words=acme,london,dc01
as-numbers=65001,65002
salt=my-consistent-salt
preserve-private-addresses=true
netconan -i original_configs/ -o clean_configs/ -c netconan.cfg

Scripting It with Python

For bulk operations or integration into a pipeline, call Netconan from Python:

import subprocess
from pathlib import Path


def sanitise_configs(
    input_dir: Path,
    output_dir: Path,
    salt: str = "pipeline-salt",
) -> None:
    """Sanitise all configs in a directory using Netconan."""
    output_dir.mkdir(parents=True, exist_ok=True)

    cmd = [
        "netconan",
        "-i", str(input_dir),
        "-o", str(output_dir),
        "--anonymize-passwords",
        "--anonymize-ips",
        "--preserve-private-addresses",
        "--salt", salt,
        "--log-level", "WARNING",
    ]

    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Netconan failed: {result.stderr}")


sanitise_configs(
    input_dir=Path("original_configs"),
    output_dir=Path("clean_configs"),
)

Quick Regex-Based Scrubbing

Sometimes you need a lighter touch — maybe you just want to mask a few specific patterns without pulling in a dependency. A simple regex approach:

import re
from pathlib import Path

PATTERNS = {
    # Cisco Type 7 password
    r"(password\s+7\s+)\S+": r"\1REDACTED",
    # Cisco enable secret (Type 5, 8, 9)
    r"(enable secret \d\s+)\S+": r"\1REDACTED",
    # SNMP community strings
    r"(snmp-server community\s+)\S+": r"\1REDACTED",
    # TACACS/RADIUS keys
    r"(key\s+(?:7\s+)?)\S+": r"\1REDACTED",
    # Username passwords
    r"(username\s+\S+\s+(?:privilege\s+\d+\s+)?(?:secret|password)\s+\d\s+)\S+": r"\1REDACTED",
}


def scrub_config(text: str) -> str:
    for pattern, replacement in PATTERNS.items():
        text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
    return text


config = Path("router.cfg").read_text()
scrubbed = scrub_config(config)
Path("router_clean.cfg").write_text(scrubbed)

This is a quick and dirty approach. For anything you are sharing externally, use Netconan — it handles edge cases and vendor-specific formats that regex will miss.

CI Integration

Wire config sanitization into your pipeline so sanitised copies are always available:

# .github/workflows/sanitise-configs.yml
name: Sanitise Configs
on:
  push:
    paths: ['configs/**']
jobs:
  sanitise:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install netconan
      - run: |
          netconan \
            -i configs/ \
            -o sanitised/ \
            --anonymize-passwords \
            --anonymize-ips \
            --preserve-private-addresses \
            --salt "${{ secrets.NETCONAN_SALT }}"
      - uses: actions/upload-artifact@v4
        with:
          name: sanitised-configs
          path: sanitised/

Every time someone pushes a config change, the pipeline produces a sanitised copy as a build artifact. No one has to remember to run the tool manually.

Summary

Netconan handles the hard parts: vendor-specific password formats, prefix-preserving IP anonymization, deterministic salt-based mapping, and reversibility. For quick one-off scrubbing, regex works. For anything you are sharing with third parties, use the proper tool.

! Was this useful?