Skip to content

Key Discovery

Understand how SSH-KLM discovers, inventories, and classifies SSH keys across your infrastructure.

SSH Key Discovery is the foundation of SSH-KLM, providing complete visibility into all SSH keys in your environment. Discovery can run in agent-based or agentless modes.

The SSH-KLM agent runs on target hosts and provides deep visibility.

Advantages:

  • Real-time key change detection
  • Access to all user directories
  • Minimal network overhead
  • Works in air-gapped environments

Scanned Locations:

/home/*/.ssh/
/root/.ssh/
/etc/ssh/
~/.ssh/authorized_keys
~/.ssh/id_*
/etc/ssh/ssh_host_*

SSH-KLM connects via SSH to scan remote hosts.

Advantages:

  • No agent installation required
  • Quick deployment
  • Good for read-only environments

Limitations:

  • Requires SSH credentials
  • Limited to accessible directories
  • Higher network overhead
flowchart TD
A[Start Discovery] --> B{Discovery Mode}
B -->|Agent| C[Agent Scans Local FS]
B -->|Agentless| D[SSH Connection]
C --> E[Enumerate SSH Directories]
D --> E
E --> F[Parse Key Files]
F --> G[Extract Metadata]
G --> H[Calculate Risk Score]
H --> I[Store in Database]
I --> J[Generate Report]

For each discovered key, SSH-KLM extracts:

FieldDescription
FingerprintSHA256 fingerprint
AlgorithmRSA, ED25519, ECDSA, DSA
Key SizeBit length (e.g., 4096)
Created DateFile creation timestamp
Last UsedLast authentication time
OwnerUser/service account
LocationFile path
Authorized HostsWhere public key is authorized

SSH-KLM calculates a risk score (0-100) for each key:

FactorWeightHigh Risk Indicators
Key Age25%> 365 days old
Algorithm20%DSA, RSA < 2048 bits
No Passphrase15%Unprotected private key
Shared Key15%Same key on multiple hosts
Root Key10%Key grants root access
Orphaned10%Owner account deleted
No Rotation5%Never rotated
// Example risk score calculation
const riskScore = await client.ssh.calculateRisk({
keyId: 'KEY-12345'
});
console.log(riskScore);
// {
// score: 75,
// factors: [
// { name: 'keyAge', score: 25, reason: 'Key is 450 days old' },
// { name: 'algorithm', score: 20, reason: 'RSA 2048 (weak)' },
// { name: 'noPassphrase', score: 15, reason: 'Private key unprotected' },
// { name: 'rootAccess', score: 10, reason: 'Grants root access' },
// { name: 'noRotation', score: 5, reason: 'Never rotated' }
// ]
// }

SSH-KLM classifies discovered keys:

StatusDescriptionAction
ManagedKey enrolled in SSH-KLMAutomated rotation
UnmanagedDiscovered but not enrolledReview and enroll
OrphanedOwner account deletedRemove or reassign
RogueUnauthorized keyInvestigate and remove
ServiceUsed by applicationsSpecial rotation policy

Configure discovery frequency:

// Create discovery schedule
await client.ssh.createDiscoverySchedule({
name: 'Daily Full Scan',
schedule: '0 2 * * *', // 2 AM daily
scanType: 'full',
hosts: ['group:production']
});
// Incremental scans for changes
await client.ssh.createDiscoverySchedule({
name: 'Hourly Incremental',
schedule: '0 * * * *', // Every hour
scanType: 'incremental',
hosts: ['group:production']
});
import { QcClient } from '@qcecuring/ssh-sdk';
const client = new QcClient({ apiKey: process.env.QC_API_KEY });
// Start discovery for specific hosts
const scan = await client.ssh.startDiscovery({
hosts: ['server01.example.com', 'server02.example.com'],
scanType: 'full',
includeSystemKeys: true
});
// Wait for completion
const result = await client.ssh.waitForScan(scan.id, {
timeout: 300000 // 5 minutes
});
console.log(`Discovered ${result.keysFound} keys`);
console.log(`High risk: ${result.highRiskKeys}`);
// Get high-risk keys
const riskyKeys = await client.ssh.listKeys({
riskScoreMin: 70,
status: 'unmanaged',
algorithm: ['dsa', 'rsa-1024']
});
// Get orphaned keys
const orphanedKeys = await client.ssh.listKeys({
status: 'orphaned'
});

Issue: Scan completes but no keys found

Solutions:

  1. Verify agent has read permissions on SSH directories
  2. Check scan includes correct paths
  3. Review discovery logs for errors

Issue: Known keys not discovered

Solutions:

  1. Verify key file permissions (must be readable)
  2. Check for non-standard SSH directory locations
  3. Add custom scan paths if needed