SSHMap is an agentless SSH exposure management CLI. It discovers SSH services, collects read-only configuration evidence, analyzes security risks, builds a directed access graph, and exposes inventory through a command-line interface, REST API, and React dashboard.
Current release: 1.2.0
License: GNU General Public License v3.0 or later (GPL-3.0-or-later)
| Developer | Cuma Kurt |
| cumakurt@gmail.com | |
| cuma-kurt-34414917 | |
| GitHub | cumakurt/sshmap |
Run sshmap -a -t <targets> for a full audit in one command, or sshmap --help for the complete reference. Use sshmap <command> --help for detailed flag documentation on any subcommand.
- What SSHMap Does
- Feature Reference
- Safety
- Installation
- Quick Start
- Quick Workflow (
-a/--all) - Configuration
- Live Progress
- Command Examples
- Web Server, API, and Dashboard
- End-to-End Workflow
- Documentation Index
- License
SSHMap answers four practical questions for infrastructure and security teams:
- Where is SSH exposed? — TCP discovery finds open SSH ports and banners without authentication.
- What SSH-related configuration exists? — Authenticated scans and imports collect
sshd_config,authorized_keys, sudoers, PAM/nsswitch,known_hosts,/etc/hosts, and clientssh_configevidence. - What is risky? — A built-in risk engine flags weak daemon settings, unrestricted keys, key reuse, dangerous sudo rules, Match-block overrides, certificate expiry, PAM weaknesses, combined escalation paths, stale keys, known OpenSSH CVEs, and server host key drift.
- How can access spread? — A directed graph models users, keys, hosts, sudo relationships, and SSH CA trust for path, blast-radius, and key-compromise simulation.
All findings are stored in a local SQLite database. You can query them from the CLI, export reports, compare baselines over time, or serve them through a read-only API.
Each section below explains what a feature does, when to use it, and what it produces.
| Command | Purpose |
|---|---|
sshmap init |
Creates a new SQLite database with the current schema. Use this at the start of every engagement or customer project. |
sshmap db migrate |
Applies pending schema migrations to an existing database. Safe to run after upgrades. |
sshmap db stats |
Prints row counts for hosts, users, keys, risks, raw evidence, graph edges, aliases, data quality findings, baselines, and exceptions. Use to verify that collection and analysis completed. |
The database is the single source of truth. Discovery, scans, imports, and analysis all write into it; reports, graph tools, and the API read from it.
sshmap doctor validates the local environment before you run discovery or scans:
- OpenSSH client availability and version
- SSH agent socket when
--agentis configured - ControlMaster / multiplexing support for connection reuse
- Writable control socket directory
- Known-hosts file permissions when strict host key checking is enabled
- Optional scope file readability
Use doctor in CI, onboarding scripts, or when scan failures suggest a local tooling problem rather than a remote host issue.
Purpose: Find which targets expose SSH without logging in.
Discovery performs concurrent TCP checks against IP addresses, CIDR ranges, hostnames, or a target file. For each open port it records:
- Target host and port
- Whether SSH appears open
- SSH banner text when available
- Timestamp and source (
discover)
When to use: Network sweeps, asset inventory, or the first phase of an audit before you have credentials.
Output: Host rows in the hosts table with ssh_open and optional ssh_banner. No user accounts or keys are collected at this stage.
sshmap discover --targets 10.10.0.0/24 --ports 22,2222 --concurrency 100 --db sshmap.dbLive progress (host:port and completion percentage) is printed to stderr when stderr is a terminal. See Live Progress.
Purpose: Collect read-only SSH security evidence from live hosts using an audit SSH key or agent.
After connecting, SSHMap runs a fixed set of remote commands (passwd, groups, authorized_keys, sshd_config, effective sshd -T, sudoers, known_hosts, ssh client config, /etc/hosts, /etc/os-release, hostname, etc.). Evidence is stored as raw text in raw_evidence and linked to host rows.
| Option | What it does |
|---|---|
--user / --key |
SSH identity for authentication |
--users-file |
Repeat collection for multiple SSH usernames with the same key or agent |
--agent |
Use SSH_AUTH_SOCK instead of a key file |
--sudo |
Prefix commands that need root-readable paths with non-interactive sudo |
--transport openssh |
Use the system ssh client (default); supports ControlMaster connection reuse |
--transport native |
Use the in-process russh client when OpenSSH is unavailable |
--proxy-jump / -J |
Reach targets through one or more bastion hops (OpenSSH and native) |
--strict-host-key |
Control host key verification (yes, no, accept-new) |
--no-connection-reuse |
Disable OpenSSH ControlMaster per-host multiplexing |
--dry-run |
Print planned read-only remote commands per target without connecting or writing evidence |
--progress |
Force live progress on stderr (enabled by default on terminals) |
When to use: Authorized assessments where you have SSH access and want live, complete evidence.
Output: Raw evidence rows per host. Run sshmap analyze afterward to parse and score findings.
Private key file contents are never stored. Sensitive patterns in collected output (private keys, secret assignments) are redacted before persistence.
Purpose: Audit the machine where SSHMap runs without SSH.
Local scan executes the same read-only collection commands locally, including /etc/hosts, effective sshd -T, and OS metadata collection. Useful for bastions, CI runners, or air-gapped analysis workstations.
Use --sudo when passwordless sudo is required to read /etc/ssh, /etc/sudoers, and user home directories.
Live progress reports each evidence type as it is collected (see Live Progress).
Output: Same raw evidence pipeline as remote scan; host source is recorded as a local collection.
Purpose: Load inventory or evidence files when live SSH is impossible or undesirable.
| Import type | Input | What it adds |
|---|---|---|
ansible |
Ansible INI inventory | Hostname/IP rows |
nmap |
Nmap XML | Discovered SSH hosts |
csv |
Custom CSV mapping | Host inventory |
known-hosts |
known_hosts file |
Client trust relationships |
hosts-file |
/etc/hosts style file |
Host/IP aliases and inventory hints |
sshd-config |
sshd_config snippet |
Daemon configuration evidence |
ssh-config |
SSH client config | Client jump/forward settings |
authorized-keys |
authorized_keys file |
Key-to-user bindings (requires --user) |
sudoers |
sudoers fragment | Privilege escalation rules |
json |
Prior SSHMap JSON report | Host inventory from export |
ssh-audit |
ssh-audit JSON report | External SSH daemon findings |
lynis |
Lynis report/dat file | External hardening warnings and suggestions |
auto |
Any supported evidence or inventory file | Parser is selected from filename and content |
bundle |
Directory of evidence files | Imports every auto-detected supported file |
Imports create or update host rows and insert raw evidence. IPv6 targets are supported in bracketed form, e.g. --host [2001:db8::1]:2222.
auto and bundle are useful when evidence comes from mixed file drops. Host-scoped evidence such as sshd_config, ssh_config, authorized_keys, or sudoers still needs --host; authorized_keys also needs --user when it cannot be inferred.
When to use: Offline forensics, vendor file drops, or combining scanner output with later analysis.
Purpose: Turn raw evidence into structured tables, risk findings, and the access graph.
The analyzer:
- Parses passwd, groups, authorized_keys, sshd_config, effective
sshd -T, sudoers, known_hosts,/etc/hosts, OS metadata, and ssh_config - Normalizes users, keys, sudo rules, host metadata, and client config entries
- Runs the risk engine (with optional YAML policy overrides)
- Applies stored risk exceptions
- Rebuilds graph edges between hosts, users, keys, and sudo rules
- Refreshes host aliases and data quality findings
- Records analysis timestamp for incremental mode
| Flag | What it does |
|---|---|
--only risks |
Regenerate risks only (skip graph rebuild) |
--only graph |
Rebuild graph only (skip risk regeneration) |
--risk-policy |
YAML file to disable rules or change severity thresholds |
--incremental --only graph |
Skip graph rebuild when no new evidence since last run |
Phase progress (loading evidence, parsing, generating risks, persisting) is printed to stderr on terminals. See Live Progress.
When to use: After every discovery, scan, or import batch.
Output: Populated users, public_keys, authorized_keys, risks, graph_edges, host_aliases, data_quality_findings, host metadata, and related tables.
Purpose: Run a complete engagement from targets to reports in one command — no subcommands required.
sshmap -a chains discovery (port 22), authenticated OpenSSH scan, analysis, reverse DNS enrichment, baseline creation, and full artifact export into a timestamped session directory. An authorization notice is printed before collection begins.
| Flag | What it does |
|---|---|
-a / --all |
Enable the quick workflow (requires -t or -f) |
-t / --target |
Inline targets: IPs, CIDRs, hostnames, comma-separated lists |
-f / --file |
Target file (mixed formats; see below) |
--user |
SSH username (defaults to scan.user in config or the current OS user) |
--key |
SSH private key path (OpenSSH defaults and agent are used when omitted) |
--sudo |
Prefix protected collection commands with non-interactive sudo |
--session |
Custom session name suffix in the output directory |
--reports-dir |
Output root directory (default: reports) |
--timeout |
Per-target timeout in seconds (default: 10) |
--concurrency |
Maximum concurrent operations (default: 100) |
--max-targets |
Cap the number of expanded targets |
--serve-listen |
Listen address printed for the post-run serve command (default: 127.0.0.1:8080) |
Target file formats (-f): one target per line, comma/space/semicolon-separated values, /etc/hosts entries, CIDR blocks, IPv4 ranges (192.168.1.10-20), wildcard IPv4 (192.168.1.*), host:port / [IPv6]:port, key=value lines, and ssh://user@host:port URLs. Expanded targets are scanned on port 22.
Session output (reports/<timestamp-session>/):
| Artifact | Description |
|---|---|
sshmap.db |
SQLite inventory database |
report.html, report.json, csv/ |
Human and machine-readable reports |
graph.json, graph.dot, graph-cytoscape.json |
Access graph exports |
summary.json, risks.json, risks.ndjson |
Risk and inventory summaries |
hosts.json, hosts.csv, known-hosts.*, ssh-config.* |
Entity exports |
compliance.json, hardening.json |
Compliance and hardening scores |
risks.sarif.json |
SARIF for CI and code scanning |
remediation.sh, remediation.yml |
Remediation snippets |
evidence-bundle.zip |
Audit bundle with raw evidence |
At completion, SSHMap prints a ready-to-run sshmap serve command for the session database.
sshmap -a -t 192.168.0.0/24
sshmap -a -f /etc/hosts --user audit --key ~/.ssh/audit_ed25519 --sudo
sshmap -a -f targets.txt --session branch-office --reports-dir reports --max-targets 500When to use: First-time audits, branch-office sweeps, or any engagement where you want discover → scan → analyze → export without scripting individual phases.
Compared to workflow run: The quick workflow always uses OpenSSH, writes a self-contained session directory with all exports, creates a named baseline, and prints the dashboard serve command. workflow run is better when you need native transport, proxy jumps, repeat scheduling, or a single shared database path.
Purpose: Run the standard audit chain in one command with full transport and scheduling options.
sshmap workflow run executes discovery, authenticated scan, analysis, and optional DNS enrichment. It is a wrapper around existing read-only phases, so each phase still records normal scan run history. Phase banners and per-target progress are shown on stderr when running on a terminal.
sshmap workflow run --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 \
--sudo --enrich-dns --reverse-dns --db sshmap.dbFor lightweight scheduled audits, use --repeat-every-seconds with --repeat-count.
Purpose: Inspect recorded discovery, scan, local-scan, and import executions.
| Command | Purpose |
|---|---|
sshmap scan-runs list |
Show recent run mode, status, timestamps, operator, and summary |
sshmap scan-runs show <id-or-uuid> |
Show one run with audit events and JSON summaries |
Purpose: Improve host identity resolution and surface inventory consistency problems.
| Command | Purpose |
|---|---|
sshmap enrich dns |
Resolve known hostnames and aliases into IP aliases |
sshmap enrich dns --reverse |
Also attempt reverse DNS through getent hosts |
sshmap enrich cloud --file tags.json |
Apply cloud/CMDB tags to hosts (environment, criticality, provider metadata) |
Host aliases come from /etc/hosts, DNS enrichment, and parsed evidence. Low-confidence loopback or special-use aliases are retained as evidence but are not used to create inventory rows automatically.
Data quality findings flag issues that can make analysis ambiguous, such as unnamed hosts, SSH-open hosts without users, or conflicting aliases that point to multiple addresses. These findings are exposed in the dashboard Quality view and the REST API.
Purpose: Surface actionable SSH exposure findings with severity, evidence, and remediation text.
Example risk categories:
- Weak
sshd_config(password auth, root login, forwarding) - Unrestricted
authorized_keysentries - SSH key reuse across hosts or users
- Dangerous sudo rules (NOPASSWD, broad commands)
- Combined critical paths (reused key plus passwordless sudo)
- Risky SSH client config (
StrictHostKeyChecking no,ProxyJumpchains) - Effective SSH daemon drift (
sshd_configvssshd -T) - Weak SSH key material (
ssh-dss, legacyssh-rsa, RSA below 2048 bits) - Risky daemon directives (
MaxAuthTries,PermitUserEnvironment,X11Forwarding) - Wildcard sudoers command patterns
- SSH certificate expiry and Match-block policy overrides
- PAM stack weaknesses (
nullok, password-backed sshd PAM) - Short sudo paths to root (
SUDO_PATH_TO_ROOT)
| Command | Purpose |
|---|---|
sshmap risks list |
Filter by severity or risk code |
sshmap risks show <id> |
Full detail, evidence, and recommendation |
sshmap exceptions add |
Suppress accepted findings (optional expiry, host, user, or key scope) |
sshmap exceptions list |
Review active suppressions |
sshmap exceptions remove |
Delete an exception |
Exceptions are applied during analysis, not at display time, so suppressed risks do not reappear until the exception expires or is removed.
Purpose: Browse normalized SSH identity and access data.
| Command | What you get |
|---|---|
host list / host show |
Hosts with SSH state, user counts, aliases, linked risks |
user list / user show |
Cross-host user presence, authorized keys, sudo rules, risks |
keys list |
All public keys with usage counts |
keys reuse |
Keys appearing on multiple hosts or users |
keys show |
Key fingerprint, locations, and linked risks |
Use inventory commands for triage before diving into graph path analysis.
Purpose: Model and query how SSH access can flow through your estate.
The graph contains nodes for hosts, users, public keys, and sudo rules. Edges describe relationships such as:
HOST_HAS_USER
USER_ON_HOST
PUBLIC_KEY_CAN_LOGIN_TO_USER
PUBLIC_KEY_REUSED_ON_HOST
USER_HAS_SUDO_RULE
SUDO_RULE_APPLIES_TO_HOST
USER_HAS_PASSWORDLESS_SUDO
CLIENT_CONFIG_PROXY_JUMP
SSH_CA_SIGNED_PUBLIC_KEY
SSH_CA_GRANTS_USER_ACCESS
| Command | Purpose |
|---|---|
graph export |
Export JSON, Graphviz DOT, or Cytoscape JSON for visualization |
path --from ... --to ... |
Shortest directed path between two graph nodes |
path --weighted --from ... --to ... |
Weighted shortest path (NOPASSWD sudo and key edges prioritized) |
paths --from ... --to ... |
Enumerate multiple directed paths (limit configurable) |
blast-radius --user ... |
All hosts, keys, and passwordless-sudo targets reachable from a username |
key-blast-radius --fingerprint SHA256:... |
Compromise simulation from a public key fingerprint |
--full-graph on path / paths / blast-radius / key-blast-radius |
Raise the analysis edge cap from 10,000 to 100,000 for large inventories |
Node references use type:value syntax, e.g. host:web01, user:deploy@web01, key:SHA256:....
Graph path and blast-radius analysis load up to 10,000 edges by default (CLI and API). Responses include edges_truncated when the inventory exceeds the cap. Use --full-graph on CLI commands or set SSHMAP_GRAPH_EDGE_LIMIT on the server for larger graphs.
When to use: Lateral movement analysis, key compromise impact, or explaining access chains to stakeholders.
Purpose: Track how risk posture changes over time.
| Command | Purpose |
|---|---|
baseline create --name <name> |
Snapshot current risks (signatures, severity, targets) |
baseline list |
List saved baselines |
diff --from <name> --to latest |
New, resolved, and unchanged risks since baseline |
diff --from <a> --to <b> |
Compare any two baselines |
diff --evidence --host <target> |
Compare raw evidence drift between scan runs |
Use baselines after initial audit and after remediation sprints to prove progress. Use evidence drift to detect configuration changes before new risks appear.
During sshmap analyze, severity and score are adjusted using host environment, criticality, ssh_open, and OS metadata. Production-exposed hosts escalate findings such as password authentication.
Discovery records SSH server host key fingerprints (when ssh-keyscan is available) and flags key changes or cross-host key conflicts.
Discovery banners are parsed for OpenSSH versions and matched against an embedded offline CVE rule set (SSH_OPENSSH_KNOWN_CVE).
Stale and never-rotated widely deployed keys produce SSH_KEY_STALE and SSH_KEY_NEVER_ROTATED findings. Tune age threshold with SSH_KEY_STALE.high_threshold in risk policy.
Maps open risk codes to CIS and STIG SSH control catalogs. Use all to include every framework, or filter with CIS or STIG.
sshmap compliance report --framework CIS --db sshmap.db
sshmap compliance report --framework all --json --db sshmap.dbThe REST API exposes the same report at GET /api/compliance?framework=CIS. Framework values are length-limited and restricted to alphanumeric characters, hyphen, and underscore.
| Feature | Command / API | Purpose |
|---|---|---|
| Webhook alerting | sshmap watch --webhook-url URL |
Periodic analyze cycles with optional baseline drift in webhook payload; webhook failures are logged and do not stop the watch loop |
| SARIF export | sshmap export sarif |
SARIF 2.1.0 for GitHub Code Scanning and CI gates |
| Remediation export | sshmap export remediation --format ansible|shell |
Bulk Ansible playbook or shell script snippets from open risks |
| Evidence audit bundle | sshmap export bundle --output audit.zip |
ZIP manifest with hosts, risks, optional raw evidence |
| Host hardening score | sshmap hardening report / GET /api/hardening |
Per-host 0–100 score from risks and compliance |
| PAM / nsswitch collection | Included in scan |
Collects /etc/pam.d/sshd, common-auth, nsswitch.conf |
| Match block risks | Automatic in analyze |
Flags risky Match overrides (root login, password auth) |
| Certificate expiry risks | Automatic in analyze |
SSH_CERTIFICATE_EXPIRED / SSH_CERTIFICATE_EXPIRING_SOON |
| Sudo path-to-root | Automatic in analyze |
SUDO_PATH_TO_ROOT for NOPASSWD shell escalation binaries |
| Bastion reachability edges | Automatic when scanning via --proxy-jump |
BASTION_REACHABILITY graph edges |
| Scoped API tokens | serve --read-token read:... --write-token write:... |
Separate read vs write API credentials |
| Read-only manifest CI test | cargo test remote_command_manifest_is_read_only |
Guards remote command allowlist in unit tests |
| Graph edge limits | --full-graph / SSHMAP_GRAPH_EDGE_LIMIT |
Bounded path and blast-radius analysis (default 10,000 edges) |
| Dependency audit | cargo audit in CI |
Tracks advisories; see scripts/check-rustsec-rsa.sh for native transport |
sshmap watch --interval 3600 --webhook-url https://hooks.example.com/sshmap --baseline weekly --db sshmap.db
sshmap export sarif --output findings.sarif.json --db sshmap.db
sshmap export remediation --format ansible --output remediation.yml --db sshmap.db
sshmap export bundle --output audit-bundle.zip --include-raw-evidence --db sshmap.db
sshmap hardening report --json --db sshmap.db
sshmap serve --read-token read:secret --write-token write:secret --allow-write-api --db sshmap.dbsshmap merge --from region-a.db --from region-b.db --output central.dbPurpose: Deliver findings to humans and automation.
| Command | Output |
|---|---|
report create --format json |
Single JSON document with hosts, users, keys, risks, graph |
report create --format html |
Self-contained HTML report |
report create --format csv |
Directory of CSV files per entity type |
export summary |
Compact JSON stats for dashboards |
export risks |
JSON or NDJSON risk stream |
export hosts / known-hosts / ssh-config |
Filtered CSV or JSON slices |
export sarif |
SARIF 2.1.0 JSON for CI and code scanning platforms |
export remediation |
Ansible playbook or shell remediation script |
export bundle |
ZIP audit bundle (manifest, hosts, risks, optional raw evidence) |
Purpose: Measure analyze, report, and graph performance on a seeded database; enforce CI regression thresholds.
sshmap bench --seed --hosts 25 --iterations 3 --thresholds benchmarks/ci-thresholds.json --db bench.dbUse in release pipelines to catch performance regressions.
Purpose: Expose the SQLite inventory over HTTP for dashboards and integrations.
- Opens the database in read-only mode
- Serves JSON REST endpoints under
/api/* - Optional embedded HTML dashboard or React build from
dashboard/dist - Optional
--token,--read-token, or--write-tokenauthentication (X-SSHMap-Tokenheader, constant-time compare); required on non-loopback binds - Set
SSHMAP_REQUIRE_TOKEN=1or pass--require-tokento require tokens even on loopback - Read endpoints use a SQLite read-only connection pool; write routes open the database for mutations
- Rate limiting on
/api/*(20 req/s, burst 40 per client IP) --allow-write-apienables baseline/exception writes; write routes require the write-scoped token when tokens are split
See Web Server, API, and Dashboard for endpoint list. Security boundaries are documented in SECURITY.md.
Generates bash or zsh completion scripts for faster CLI usage:
sshmap completion --shell bash > ~/.local/share/bash-completion/completions/sshmapOnly use SSHMap against systems you own or are explicitly authorized to assess.
SSHMap supports read-only inspection through the system OpenSSH client or the built-in native transport. It does not brute force, exploit, or attempt password login. An authorization notice is printed before discovery, scan, and local-scan commands.
Webhook URLs are validated (HTTPS required except loopback HTTP), DNS is resolved and pinned per request, redirects are disabled, and URL credentials are stripped before outbound delivery. Import host identifiers are validated before evidence is stored. The watch command logs webhook delivery failures and continues scheduled analysis cycles.
See SECURITY.md for API authentication, native transport dependency notes, and reporting security issues.
The repository includes an install.sh installer with a guided, step-by-step setup:
- Detect operating system and package manager
- Check required dependencies (OpenSSH, curl, compiler, SQLite, Rust)
- Install only missing OS packages for your distribution
- Install Rust via rustup when
cargois not present - Build the release binary and install it to
~/.local/bin - Update your shell
PATHsosshmapis available immediately
./install.sh # interactive install
./install.sh -y # non-interactive (package managers)
./install.sh --dry-runSupported package managers:
apt, dnf, yum, pacman, zypper, apk, brew
After installation, open a new terminal or source ~/.bashrc / source ~/.zshrc, then:
sshmap doctor
sshmap init --db sshmap.db
sshmap doctor --db sshmap.db --config examples/sshmap.yaml --scope examples/hosts.txtSee docs/doctor.md for the full check list.
Fastest path — one command from targets to reports and dashboard:
sshmap -a -t 192.168.0.0/24
sshmap -a -f /etc/hostsWith credentials and a named session:
sshmap -a -t 10.10.0.0/24 --user audit --key ~/.ssh/audit_ed25519 --sudo
sshmap -a -f hosts.txt --session branch-office --reports-dir reportsThen open the dashboard using the serve command printed at the end, or browse artifacts under reports/<timestamp-session>/. See Quick Workflow (-a/--all) for the full flag and output reference.
Manual workflow:
sshmap init --db sshmap.db
sshmap doctor
sshmap db stats --db sshmap.db
sshmap discover --targets 127.0.0.1 --ports 22 --db sshmap.db
sshmap analyze --db sshmap.db
sshmap risks list --db sshmap.db
sshmap graph export --format dot --output graph.dot --db sshmap.db
sshmap baseline create --name initial --db sshmap.db# Inline targets
sshmap -a -t 10.10.0.0/24,192.168.1.0/24
# Mixed-format target file
sshmap -a -f targets.txt --user audit --key ~/.ssh/audit_ed25519 --sudo
# Cap targets and customize session output
sshmap -a -f /etc/hosts --session datacenter --max-targets 250 --timeout 15
# After completion, serve the session database (command is also printed by SSHMap)
sshmap serve --read-only --db reports/20260612T120000Z-datacenter/sshmap.db --listen 127.0.0.1:8080Load shared defaults from YAML:
sshmap --config examples/sshmap.yaml scan --file hosts.txt --db sshmap.dbSee examples/sshmap.yaml for scan, discover, serve, and database defaults.
Long-running commands print live status to stderr when stderr is an interactive terminal (stdout stays clean for piping and scripts).
| Command | What you see |
|---|---|
discover |
discover: 42/100 (42%) 10.0.0.5:22 — updates on the same line |
scan |
scan: 15/50 (30%) web01.example.com:22 |
-a / --all |
Discovery and scan progress, then analysis phase steps |
workflow run |
Phase banners (discovery, scan, analyze) plus per-target progress in each phase |
analyze |
Phase steps: loading evidence, parsing, generating risks, persisting |
local-scan |
Current evidence type (passwd, sshd_config, …) |
watch |
Analyze phase progress on each scheduled cycle; webhook errors are logged without stopping the loop |
Defaults: Progress is on when stderr is a TTY. Disable globally with --no-progress. Force on in non-TTY environments (CI logs, redirected stderr) with --progress on discover, scan, or workflow run.
# Default: live progress on an interactive terminal
sshmap discover --targets 10.10.0.0/24 --db sshmap.db
# Disable progress (quiet batch jobs)
sshmap --no-progress scan --file hosts.txt --user audit --key ~/.ssh/id_ed25519 --db sshmap.db
# Force progress in CI or when stderr is redirected
sshmap scan --progress --file hosts.txt --user audit --key ~/.ssh/id_ed25519 --db sshmap.db 2>scan.logProgress uses carriage-return line updates on terminals and periodic line logs when stderr is not a TTY.
sshmap -a -t 192.168.0.0/24
sshmap -a -f /etc/hosts --user audit --key ~/.ssh/audit_ed25519 --sudo
sshmap -a -f targets.txt --session site-a --reports-dir reports --concurrency 50sshmap discover --targets 10.10.0.0/24 --ports 22 --concurrency 100 --db sshmap.db
sshmap discover --file examples/hosts.txt --ports 22,2222 --progress --db sshmap.dbsshmap scan --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 --db sshmap.db
sshmap scan --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 \
--proxy-jump bastion.example.com --transport native --db sshmap.db
sshmap scan --targets 10.10.0.0/24 --user audituser --key ~/.ssh/audit_ed25519 --sudo --db sshmap.db
sshmap scan --file examples/hosts.txt --users-file audit-users.txt --agent --db sshmap.db
sshmap scan --dry-run --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 --db sshmap.dbsshmap workflow run --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 \
--sudo --enrich-dns --reverse-dns --db sshmap.db
sshmap scan-runs list --db sshmap.db
sshmap scan-runs show 1 --db sshmap.dbsshmap import ansible --file inventory.ini --db sshmap.db
sshmap import hosts-file --file /etc/hosts --db sshmap.db
sshmap import auto --file evidence/sshd_config --host web01 --db sshmap.db
sshmap import bundle --dir evidence-drop --host web01 --user deploy --db sshmap.db
sshmap import sshd-config --file sshd_config --host web01 --db sshmap.db
sshmap import authorized-keys --file authorized_keys --host web01 --user deploy --db sshmap.db
sshmap import ssh-audit --file ssh-audit.json --host web01 --db sshmap.db
sshmap import lynis --file lynis-report.dat --host web01 --db sshmap.db
sshmap analyze --db sshmap.dbsshmap enrich dns --db sshmap.db
sshmap enrich dns --reverse --limit 500 --db sshmap.db
sshmap enrich cloud --file examples/cloud-tags.json --db sshmap.db
sshmap db stats --db sshmap.dbsshmap analyze --db sshmap.db --risk-policy examples/risk-policy.yaml
sshmap analyze --only risks --db sshmap.db
sshmap analyze --incremental --only graph --db sshmap.db
sshmap risks list --severity critical --db sshmap.db
sshmap exceptions add --code SSH_PASSWORD_AUTH_ENABLED --host-id 1 --reason "legacy" --db sshmap.dbsshmap keys list --db sshmap.db
sshmap path --from key:SHA256:exampleFingerprint --to host:web01 --db sshmap.db
sshmap paths --from user:deploy --to host:db01 --limit 5 --db sshmap.db
sshmap key-blast-radius --fingerprint SHA256:exampleFingerprint --db sshmap.db
sshmap blast-radius --user deploy --db sshmap.db
sshmap diff --evidence --host web01 --db sshmap.db
sshmap compliance report --framework CIS --db sshmap.db
sshmap merge --from region-a.db --from region-b.db --output central.dbsshmap report create --format html --output report.html --db sshmap.db
sshmap export summary --output summary.json --db sshmap.db
sshmap export sarif --output findings.sarif.json --db sshmap.db
sshmap export remediation --format ansible --output remediation.yml --db sshmap.db
sshmap export bundle --output audit-bundle.zip --include-raw-evidence --db sshmap.dbsshmap hardening report --json --db sshmap.db
sshmap watch --interval 3600 --webhook-url https://hooks.example.com/sshmap --baseline weekly --db sshmap.dbEmbedded dashboard:
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-onlyReact dashboard (detail pages, filters, Quality view, Operations metrics, baseline risk trend, compliance summary, graph canvas with limit and edge filters):
cd dashboard && npm ci && npm run build
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-only --dashboard dashboard/distDashboard tests (Vitest unit tests + shell E2E smoke against the built bundle):
cd dashboard && npm test && npm run test:e2eOptional browser E2E with Playwright: npm run test:e2e:playwright (requires Chromium).
API token (required on non-loopback addresses; optional on loopback unless SSHMAP_REQUIRE_TOKEN=1):
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-only --token "$SSHMAP_TOKEN"
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-only --require-token --token "$SSHMAP_TOKEN"Send the token in the X-SSHMap-Token header. The React dashboard stores it in browser local storage from the Tools page. /health is unauthenticated; /api/* routes are rate-limited per client IP. Graph analysis endpoints load up to 10,000 edges by default (same as CLI); override with SSHMAP_GRAPH_EDGE_LIMIT on the server or --full-graph on CLI path commands.
Write API endpoints are disabled by default. To create baselines or exceptions through HTTP, start the server with a write-capable token and --allow-write-api:
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-only \
--token "$SSHMAP_TOKEN" --allow-write-apiSplit read and write credentials when integrations should not mutate inventory:
sshmap serve --db sshmap.db --listen 127.0.0.1:8080 --read-only \
--read-token "read:$SSHMAP_READ_TOKEN" \
--write-token "write:$SSHMAP_WRITE_TOKEN" \
--allow-write-apiCore API endpoints:
GET /api/summary
GET /api/hosts?ssh_open=&source=&q=&limit=
GET /api/hosts/{id}
GET /api/users?q=&min_hosts=&min_risks=&limit=
GET /api/users/{username}
GET /api/keys
GET /api/keys/reuse
GET /api/keys/{target}
GET /api/risks?severity=&code=&limit=
GET /api/risks/{id}
GET /api/graph?limit=
GET /api/path?from=...&to=...
GET /api/blast-radius?user=...
GET /api/scan-runs?limit=
GET /api/scan-runs/{id-or-uuid}
GET /api/baselines
GET /api/diff?from=...&to=latest
GET /api/exceptions
GET /api/known-hosts
GET /api/ssh-config
GET /api/host-aliases
GET /api/data-quality
GET /api/remediation/{code}
GET /api/compliance?framework=
GET /api/operations-metrics
GET /api/paths?from=...&to=...&limit=
GET /api/key-blast-radius?fingerprint=
GET /api/hardening
POST /api/baselines
POST /api/exceptions
DELETE /api/exceptions/{id}
See docs/api.md and docs/dashboard.md for full reference.
Breaking response shape changes for dashboard and automation clients:
GET /api/graphreturns{ edges, truncated, total_edges, edge_limit }instead of a bare edge array.GET /api/hardeningreturns{ hosts, summary, control_count }instead of a bare host score array.- Path and blast-radius analysis responses include
edges_truncatedwhen the graph edge cap is hit.
See CHANGELOG.md for details.
Graph analysis uses a default cap of 10,000 edges (CLI and API). Use --full-graph on path, paths, blast-radius, and key-blast-radius for up to 100,000 edges, or set SSHMAP_GRAPH_EDGE_LIMIT on the server.
One-command session (recommended for new engagements):
sshmap -a -f examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 --sudoManual step-by-step workflow:
sshmap init --db customer.db
sshmap discover --file examples/hosts.txt --ports 22 --concurrency 100 --db customer.db
sshmap scan --file examples/hosts.txt --user audituser --key ~/.ssh/audit_ed25519 \
--sudo --timeout 10 --concurrency 20 --db customer.db
# Live progress appears on stderr during discover and scan when running in a terminal
sshmap scan-runs list --db customer.db
sshmap analyze --db customer.db
sshmap enrich dns --reverse --db customer.db
sshmap risks list --severity critical --db customer.db
sshmap keys reuse --db customer.db
sshmap graph export --format dot --output customer-graph.dot --db customer.db
sshmap path --from key:SHA256:exampleFingerprint --to host:web01 --db customer.db
sshmap path --from user:deploy@web01 --to host:web02 --full-graph --db customer.db
sshmap baseline create --name customer-initial --db customer.db
sshmap diff --from customer-initial --to latest --db customer.db
sshmap report create --format html --output customer-report.html --db customer.dbCHANGELOG.md
RELEASE_NOTES.md
SECURITY.md
docs/getting-started.md
docs/scope.md
docs/discovery.md
docs/authenticated-scan.md
docs/local-scan.md
docs/importers.md
docs/reports.md
docs/remediation.md
docs/integrations.md
docs/packaging.md
docs/doctor.md
docs/dashboard.md
docs/api.md
docs/benchmarks.md
docs/architecture.md
docs/development/
Start with docs/getting-started.md and docs/architecture.md. API breaking changes in v1.2.0 are summarized in CHANGELOG.md and the API migration section above.
The SQLite layer lives under src/db/ (pool, migrations, graph, store modules). Run cargo test, cargo clippy --all-targets -- -D warnings, and cargo audit before releases.
SSHMap is free software licensed under the GNU General Public License v3.0 or later.
Copyright (C) 2026 Cuma Kurt
You may redistribute and modify SSHMap under the terms of the GPL. See LICENSE for the full notice.
