Skip to content

Ephemeral Keys

Provide just-in-time (JIT) SSH access with automatically expiring credentials for enhanced security.

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.

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 key
ScenarioBenefit
CI/CD PipelinesTime-limited deploy access
Emergency AccessBreak-glass scenarios with audit
Third-Party AccessContractor/vendor access
Privileged OperationsJIT elevation for admins
ComplianceNo permanent standing access
import { QcClient } from '@qcecuring/ssh-sdk';
const client = new QcClient({ apiKey: process.env.QC_API_KEY });
// Request ephemeral credentials
const 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);
Terminal window
# Request ephemeral key
qcecuring ssh ephemeral request \
--host production-server.example.com \
--user deploy \
--ttl 5m \
--reason "Deploy v2.1.0"
# Output saved to ~/.ssh/ephemeral_key
ssh -i ~/.ssh/ephemeral_key deploy@production-server.example.com
  1. Navigate to Access → Request Ephemeral Key
  2. Select target host and username
  3. Set TTL and provide reason
  4. Download private key or copy to clipboard

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']
}
});

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'
}
}
});
Access TypeRecommended TTLMax TTL
CI/CD Pipeline5-15 minutes30 minutes
Manual Deploy15-30 minutes1 hour
Debugging30-60 minutes2 hours
Emergency15 minutes30 minutes
Audit/Review1-2 hours4 hours

Keys are automatically removed after TTL:

// Agent-side cleanup runs every minute
// Keys removed within 60 seconds of expiration
// Verify key was removed
const keyStatus = await client.ssh.getEphemeralStatus(credentials.id);
// { status: 'expired', removedAt: '2025-01-15T14:35:00Z' }

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
});

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']
});

All ephemeral access is logged:

// Query ephemeral access logs
const 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`);
});
# GitHub Actions example
jobs:
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_key
// Emergency access with approval
const emergency = await client.ssh.requestEmergencyAccess({
host: 'critical-server.example.com',
username: 'root',
reason: 'Production incident INC-5678',
severity: 'critical'
});
// Returns immediately with pending approval
console.log(`Request ID: ${emergency.requestId}`);
console.log(`Status: ${emergency.status}`); // 'pending_approval'
// Poll for approval
const approved = await client.ssh.waitForApproval(emergency.requestId, {
timeout: 900000 // 15 minutes
});
if (approved.status === 'approved') {
console.log('Access granted:', approved.credentials.privateKey);
}

Issue: Ephemeral key rejected by host

Solutions:

  1. Verify key hasn’t expired: check expiresAt
  2. Confirm agent deployed the key
  3. Check source IP restrictions
  4. Verify SSH daemon accepts key algorithm

Issue: Public key still in authorized_keys after TTL

Solutions:

  1. Check agent is running and connected
  2. Verify agent has write access to authorized_keys
  3. Review agent logs for errors
  4. Manually remove and investigate