Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,36 @@ pip install pysnmp-lextudio paramiko cryptography textfsm aiofiles
pip install PyQt6 PyQt6-WebEngine
```

### Docker (Headless)

Run discovery without installing Python or any dependencies. The Docker image excludes PyQt6 (~200MB savings) and uses the library APIs directly to bypass interactive `getpass()` prompts.

```bash
cd docker
cp .env.template .env
# Edit .env with your seed IP, vault password, and SNMP/SSH credentials

docker compose build
docker compose up
```

Output files (map.json, devices.csv, topology.graphml) are written to `docker/output/`. The credential vault persists in a Docker volume between runs.

**Environment variables:**

| Variable | Required | Description |
|----------|----------|-------------|
| `SC_VAULT_PASSWORD` | Yes | Master password for the encrypted credential vault |
| `SC_SEED_IP` | Yes | IP of the seed device (core switch/router) |
| `SC_CRAWL_DEPTH` | No | Max hop depth (default: 10) |
| `SC_SNMPV2C_COMMUNITY` | No | SNMPv2c community string |
| `SC_SSH_USERNAME` | No | SSH username for fallback discovery |
| `SC_SSH_PASSWORD` | No | SSH password |
| `SC_EXCLUDE_STRING` | No | Comma-separated patterns to exclude from crawl |
| `SC_DOMAIN_SUFFIX` | No | Domain suffix(es) to strip from hostnames |

See `docker/.env.template` for the full list including SNMPv3 fields.

---

## Quick Start
Expand Down
57 changes: 57 additions & 0 deletions docker/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# =============================================================================
# Secure Cartography v2 - Docker Environment Configuration
# Copy this file to .env and fill in your values
# NEVER commit this file to version control with real credentials
# =============================================================================

# --- REQUIRED ---

# Master password for the encrypted credential vault
# Use a strong password - this protects all stored network credentials
SC_VAULT_PASSWORD=CHANGE_ME_STRONG_PASSWORD_HERE

# Seed device IP - the core switch or router to start discovery from
# Choose a device with the broadest CDP/LLDP neighbor visibility
SC_SEED_IP=10.1.1.1

# --- DISCOVERY SCOPE ---

# Maximum hop depth from seed device
SC_CRAWL_DEPTH=10

# Domain suffix(es) to strip from hostnames for cleaner diagram labels
# Comma-separated for multiple domains
# Example: "corp.example.com,example.lan"
SC_DOMAIN_SUFFIX=

# Comma-separated hostname patterns to exclude from CRAWLING (not discovery)
# Matched devices still appear in topology as leaf nodes, but their neighbors
# are not queried. This prevents crawling through phones, APs, servers, etc.
SC_EXCLUDE_STRING=phone,sep,wireless,ap,linux,camera,printer

# --- SNMPv2c CREDENTIALS ---
# Leave empty if not using SNMPv2c
SC_SNMPV2C_COMMUNITY=

# --- SNMPv3 CREDENTIALS ---
# Leave empty if not using SNMPv3
SC_SNMPV3_USERNAME=
SC_SNMPV3_AUTH_PROTOCOL=sha
SC_SNMPV3_AUTH_PASSWORD=
SC_SNMPV3_PRIV_PROTOCOL=aes128
SC_SNMPV3_PRIV_PASSWORD=

# --- SSH CREDENTIALS ---
# Used as fallback when SNMP cannot retrieve CDP/LLDP neighbor data
# Leave empty if SNMP is sufficient for your environment
SC_SSH_USERNAME=
SC_SSH_PASSWORD=

# --- DNS ---
# Your site's DNS servers (for Docker container hostname resolution)
SC_DNS_PRIMARY=8.8.8.8
SC_DNS_SECONDARY=8.8.4.4

# --- LOGGING ---
# Options: DEBUG, INFO, WARNING, ERROR
SC_LOG_LEVEL=INFO
5 changes: 5 additions & 0 deletions docker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Credentials - NEVER commit
.env

# Discovery output
output/
90 changes: 90 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# =============================================================================
# Secure Cartography v2 - Headless Docker Deployment
# Purpose: Automated network topology discovery without GUI dependencies
# Target: Cisco IOS/IOS-XE, NX-OS, ASA; Arista EOS; Juniper JUNOS
# Mode: CLI-only (no PyQt6) for scheduled/automated runs
# =============================================================================

FROM python:3.12-slim AS base

LABEL maintainer="Secure Cartography contributors"
LABEL description="Secure Cartography v2 - Headless Network Discovery"
LABEL version="2.0.0"

# Prevent interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Enable non-free repo for snmp-mibs-downloader (not in main)
RUN sed -i 's/Components: main/Components: main contrib non-free/' \
/etc/apt/sources.list.d/debian.sources

# Install system dependencies
# - graphviz: SVG/PNG diagram rendering
# - openssh-client: SSH key management utilities
# - snmp: SNMP CLI tools for manual troubleshooting inside container
# - tini: proper PID 1 signal handling
RUN apt-get update && apt-get install -y --no-install-recommends \
graphviz \
openssh-client \
snmp \
snmp-mibs-downloader \
tini \
&& rm -rf /var/lib/apt/lists/*

# Download standard MIBs (needed for SNMP OID resolution)
RUN download-mibs 2>/dev/null || true

# Create non-root user for security
RUN groupadd -r scart && useradd -r -g scart -m -s /bin/bash scart

# Install Secure Cartography v2 without PyQt6 GUI dependencies.
# The upstream package lists PyQt6 as a hard dependency, but we only use
# the headless discovery engine and credential vault APIs. Install with
# --no-deps and explicitly list the runtime dependencies we actually need.
RUN pip install --no-cache-dir --no-deps "secure-cartography>=2.0.0" \
&& pip install --no-cache-dir \
"pysnmp>=6.1" \
"pyasn1>=0.6" \
"pysmi>=1.3" \
paramiko \
cryptography \
textfsm \
aiofiles \
pyyaml \
networkx \
pydot \
tfsm-fire

# Create directory structure
RUN mkdir -p /app/output \
/app/vault \
/app/logs \
&& chown -R scart:scart /app

# Copy entrypoint and discovery runner
COPY --chown=scart:scart entrypoint.sh /app/entrypoint.sh
COPY --chown=scart:scart discovery_runner.py /app/discovery_runner.py
RUN chmod +x /app/entrypoint.sh

# Set working directory
WORKDIR /app

# Switch to non-root user
USER scart

# Environment variables with sane defaults
# Override these at runtime via docker-compose.yml or docker run -e
ENV SC_VAULT_PATH="/app/vault/credentials.db"
ENV SC_OUTPUT_DIR="/app/output"
ENV SC_LOG_LEVEL="INFO"
ENV SC_CRAWL_DEPTH="10"
ENV SC_DOMAIN_SUFFIX=""
ENV SC_EXCLUDE_STRING="phone,sep,wireless,ap,linux"
ENV SC_SEED_IP=""
ENV SC_VAULT_PASSWORD=""

# Use tini as init system for proper signal handling
ENTRYPOINT ["tini", "--"]

# Default command - run discovery via entrypoint script
CMD ["/app/entrypoint.sh"]
Loading