Ephemeral Keys
Ephemeral SSH Keys
Section titled “Ephemeral SSH Keys”Provide just-in-time (JIT) SSH access with automatically expiring credentials for enhanced security.
Overview
Section titled “Overview”Ephemeral keys are short-lived SSH credentials that automatically expire after a configured time-to-live (TTL). This eliminates the risk of permanent key exposure and provides strong audit trails for access.
How Ephemeral Keys Work
Section titled “How Ephemeral Keys Work”sequenceDiagram participant User participant API as SSH-KLM API participant Agent as Target Agent participant Host as Target Host
User->>API: Request ephemeral access API->>API: Verify permissions API->>API: Generate key pair API->>Agent: Deploy public key with TTL Agent->>Host: Add to authorized_keys API-->>User: Return private key User->>Host: SSH with ephemeral key Note over Agent,Host: TTL expires Agent->>Host: Remove public keyUse Cases
Section titled “Use Cases”| Scenario | Benefit |
|---|---|
| CI/CD Pipelines | Time-limited deploy access |
| Emergency Access | Break-glass scenarios with audit |
| Third-Party Access | Contractor/vendor access |
| Privileged Operations | JIT elevation for admins |
| Compliance | No permanent standing access |
Requesting Ephemeral Access
Section titled “Requesting Ephemeral Access”Via SDK
Section titled “Via SDK”import { QcClient } from '@qcecuring/ssh-sdk';
const client = new QcClient({ apiKey: process.env.QC_API_KEY });
// Request ephemeral credentialsconst credentials = await client.ssh.requestEphemeral({ host: 'production-server.example.com', username: 'deploy', ttl: 300, // 5 minutes reason: 'Deploy application v2.1.0', ticketId: 'JIRA-1234' // Optional: link to ticket});
console.log('Private Key:', credentials.privateKey);console.log('Expires At:', credentials.expiresAt);Via CLI
Section titled “Via CLI”# Request ephemeral keyqcecuring ssh ephemeral request \ --host production-server.example.com \ --user deploy \ --ttl 5m \ --reason "Deploy v2.1.0"
# Output saved to ~/.ssh/ephemeral_keyssh -i ~/.ssh/ephemeral_key deploy@production-server.example.comVia UI
Section titled “Via UI”- Navigate to Access → Request Ephemeral Key
- Select target host and username
- Set TTL and provide reason
- Download private key or copy to clipboard
Configuration
Section titled “Configuration”Access Policies
Section titled “Access Policies”Define who can request ephemeral access:
await client.ssh.createAccessPolicy({ name: 'Developer Ephemeral Access',
// Who can request subjects: { groups: ['developers'], users: ['alice@example.com'] },
// What they can access resources: { hostGroups: ['staging', 'development'], users: ['deploy', 'app'] },
// Constraints constraints: { maxTtl: '1h', requireReason: true, requireApproval: false, allowedHours: { start: 6, end: 22 }, allowedDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'] }});Approval Workflows
Section titled “Approval Workflows”For sensitive access, require approval:
await client.ssh.createAccessPolicy({ name: 'Production Emergency Access',
subjects: { groups: ['sre-team'] },
resources: { hostGroups: ['production'], users: ['root'] },
constraints: { maxTtl: '30m', requireApproval: true, approvers: { groups: ['security-team'], minApprovals: 1, approvalTimeout: '15m' } }});TTL Guidelines
Section titled “TTL Guidelines”| Access Type | Recommended TTL | Max TTL |
|---|---|---|
| CI/CD Pipeline | 5-15 minutes | 30 minutes |
| Manual Deploy | 15-30 minutes | 1 hour |
| Debugging | 30-60 minutes | 2 hours |
| Emergency | 15 minutes | 30 minutes |
| Audit/Review | 1-2 hours | 4 hours |
Security Features
Section titled “Security Features”Automatic Expiration
Section titled “Automatic Expiration”Keys are automatically removed after TTL:
// Agent-side cleanup runs every minute// Keys removed within 60 seconds of expiration
// Verify key was removedconst keyStatus = await client.ssh.getEphemeralStatus(credentials.id);// { status: 'expired', removedAt: '2025-01-15T14:35:00Z' }Single-Use Keys
Section titled “Single-Use Keys”Configure keys that can only be used once:
const singleUse = await client.ssh.requestEphemeral({ host: 'secure-server.example.com', username: 'admin', ttl: 300, singleUse: true // Revoked after first use});IP Restrictions
Section titled “IP Restrictions”Limit which IPs can use the ephemeral key:
const restricted = await client.ssh.requestEphemeral({ host: 'server.example.com', username: 'deploy', ttl: 600, sourceIp: ['10.0.0.0/8', '192.168.1.100']});Audit Trail
Section titled “Audit Trail”All ephemeral access is logged:
// Query ephemeral access logsconst logs = await client.ssh.getAccessLogs({ type: 'ephemeral', host: 'production-server.example.com', dateRange: { start: '2025-01-01', end: '2025-01-31' }});
logs.forEach(log => { console.log(`${log.timestamp} - ${log.user} accessed ${log.host}`); console.log(` Reason: ${log.reason}`); console.log(` TTL: ${log.ttl}s, Used: ${log.duration}s`);});Examples
Section titled “Examples”CI/CD Integration
Section titled “CI/CD Integration”# GitHub Actions examplejobs: deploy: runs-on: ubuntu-latest steps: - name: Get ephemeral SSH key run: | KEY=$(curl -X POST https://ssh-klm.example.com/api/v1/ephemeral \ -H "Authorization: Bearer ${{ secrets.SSHKLM_TOKEN }}" \ -d '{ "host": "deploy.example.com", "username": "deploy", "ttl": 300, "reason": "GitHub Actions deploy ${{ github.sha }}" }' | jq -r '.privateKey') echo "$KEY" > /tmp/deploy_key chmod 600 /tmp/deploy_key
- name: Deploy run: | ssh -i /tmp/deploy_key deploy@deploy.example.com "./deploy.sh"
- name: Cleanup if: always() run: rm -f /tmp/deploy_keyBreak-Glass Access
Section titled “Break-Glass Access”// Emergency access with approvalconst emergency = await client.ssh.requestEmergencyAccess({ host: 'critical-server.example.com', username: 'root', reason: 'Production incident INC-5678', severity: 'critical'});
// Returns immediately with pending approvalconsole.log(`Request ID: ${emergency.requestId}`);console.log(`Status: ${emergency.status}`); // 'pending_approval'
// Poll for approvalconst approved = await client.ssh.waitForApproval(emergency.requestId, { timeout: 900000 // 15 minutes});
if (approved.status === 'approved') { console.log('Access granted:', approved.credentials.privateKey);}Troubleshooting
Section titled “Troubleshooting”Key Not Working
Section titled “Key Not Working”Issue: Ephemeral key rejected by host
Solutions:
- Verify key hasn’t expired: check
expiresAt - Confirm agent deployed the key
- Check source IP restrictions
- Verify SSH daemon accepts key algorithm
Key Not Removed After Expiry
Section titled “Key Not Removed After Expiry”Issue: Public key still in authorized_keys after TTL
Solutions:
- Check agent is running and connected
- Verify agent has write access to authorized_keys
- Review agent logs for errors
- Manually remove and investigate