provisioning/docs/book/user/SSH_TEMPORAL_KEYS_USER_GUIDE.html
Jesús Pérez 6a59d34bb1
chore: update provisioning configuration and documentation
Update configuration files, templates, and internal documentation
for the provisioning repository system.

Configuration Updates:
- KMS configuration modernization
- Plugin system settings
- Service port mappings
- Test cluster topologies
- Installation configuration examples
- VM configuration defaults
- Cedar authorization policies

Documentation Updates:
- Library module documentation
- Extension API guides
- AI system documentation
- Service management guides
- Test environment setup
- Plugin usage guides
- Validator configuration documentation

All changes are backward compatible.
2025-12-11 21:50:42 +00:00

699 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>SSH Temporal Keys User Guide - Provisioning Platform Documentation</title>
<!-- Custom HTML head -->
<meta name="description" content="Complete documentation for the Provisioning Platform - Infrastructure automation with Nushell, KCL, and Rust">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "ayu";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('ayu')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Provisioning Platform Documentation</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/provisioning/provisioning-platform" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/provisioning/provisioning-platform/edit/main/provisioning/docs/src/user/SSH_TEMPORAL_KEYS_USER_GUIDE.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="ssh-temporal-keys---user-guide"><a class="header" href="#ssh-temporal-keys---user-guide">SSH Temporal Keys - User Guide</a></h1>
<h2 id="quick-start"><a class="header" href="#quick-start">Quick Start</a></h2>
<h3 id="generate-and-connect-with-temporary-key"><a class="header" href="#generate-and-connect-with-temporary-key">Generate and Connect with Temporary Key</a></h3>
<p>The fastest way to use temporal SSH keys:</p>
<pre><code class="language-bash"># Auto-generate, deploy, and connect (key auto-revoked after disconnect)
ssh connect server.example.com
# Connect with custom user and TTL
ssh connect server.example.com --user deploy --ttl 30min
# Keep key active after disconnect
ssh connect server.example.com --keep
</code></pre>
<h3 id="manual-key-management"><a class="header" href="#manual-key-management">Manual Key Management</a></h3>
<p>For more control over the key lifecycle:</p>
<pre><code class="language-bash"># 1. Generate key
ssh generate-key server.example.com --user root --ttl 1hr
# Output:
# ✓ SSH key generated successfully
# Key ID: abc-123-def-456
# Type: dynamickeypair
# User: root
# Server: server.example.com
# Expires: 2024-01-01T13:00:00Z
# Fingerprint: SHA256:...
#
# Private Key (save securely):
# -----BEGIN OPENSSH PRIVATE KEY-----
# ...
# -----END OPENSSH PRIVATE KEY-----
# 2. Deploy key to server
ssh deploy-key abc-123-def-456
# 3. Use the private key to connect
ssh -i /path/to/private/key root@server.example.com
# 4. Revoke when done
ssh revoke-key abc-123-def-456
</code></pre>
<h2 id="key-features"><a class="header" href="#key-features">Key Features</a></h2>
<h3 id="automatic-expiration"><a class="header" href="#automatic-expiration">Automatic Expiration</a></h3>
<p>All keys expire automatically after their TTL:</p>
<ul>
<li><strong>Default TTL</strong>: 1 hour</li>
<li><strong>Configurable</strong>: From 5 minutes to 24 hours</li>
<li><strong>Background Cleanup</strong>: Automatic removal from servers every 5 minutes</li>
</ul>
<h3 id="multiple-key-types"><a class="header" href="#multiple-key-types">Multiple Key Types</a></h3>
<p>Choose the right key type for your use case:</p>
<div class="table-wrapper"><table><thead><tr><th>Type</th><th>Description</th><th>Use Case</th></tr></thead><tbody>
<tr><td><strong>dynamic</strong> (default)</td><td>Generated Ed25519 keys</td><td>Quick SSH access</td></tr>
<tr><td><strong>ca</strong></td><td>Vault CA-signed certificate</td><td>Enterprise with SSH CA</td></tr>
<tr><td><strong>otp</strong></td><td>Vault one-time password</td><td>Single-use access</td></tr>
</tbody></table>
</div>
<h3 id="security-benefits"><a class="header" href="#security-benefits">Security Benefits</a></h3>
<p>✅ No static SSH keys to manage
✅ Short-lived credentials (1 hour default)
✅ Automatic cleanup on expiration
✅ Audit trail for all operations
✅ Private keys never stored on disk</p>
<h2 id="common-usage-patterns"><a class="header" href="#common-usage-patterns">Common Usage Patterns</a></h2>
<h3 id="development-workflow"><a class="header" href="#development-workflow">Development Workflow</a></h3>
<pre><code class="language-bash"># Quick SSH for debugging
ssh connect dev-server.local --ttl 30min
# Execute commands
ssh root@dev-server.local "systemctl status nginx"
# Connection closes, key auto-revokes
</code></pre>
<h3 id="production-deployment"><a class="header" href="#production-deployment">Production Deployment</a></h3>
<pre><code class="language-bash"># Generate key with longer TTL for deployment
ssh generate-key prod-server.example.com --ttl 2hr
# Deploy to server
ssh deploy-key &lt;key-id&gt;
# Run deployment script
ssh -i /tmp/deploy-key root@prod-server.example.com &lt; deploy.sh
# Manual revoke when done
ssh revoke-key &lt;key-id&gt;
</code></pre>
<h3 id="multi-server-access"><a class="header" href="#multi-server-access">Multi-Server Access</a></h3>
<pre><code class="language-bash"># Generate one key
ssh generate-key server01.example.com --ttl 1hr
# Use the same private key for multiple servers (if you have provisioning access)
# Note: Currently each key is server-specific, multi-server support coming soon
</code></pre>
<h2 id="command-reference"><a class="header" href="#command-reference">Command Reference</a></h2>
<h3 id="ssh-generate-key"><a class="header" href="#ssh-generate-key">ssh generate-key</a></h3>
<p>Generate a new temporal SSH key.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh generate-key &lt;server&gt; [options]
</code></pre>
<p><strong>Options</strong>:</p>
<ul>
<li><code>--user &lt;name&gt;</code>: SSH user (default: root)</li>
<li><code>--ttl &lt;duration&gt;</code>: Key lifetime (default: 1hr)</li>
<li><code>--type &lt;ca|otp|dynamic&gt;</code>: Key type (default: dynamic)</li>
<li><code>--ip &lt;address&gt;</code>: Allowed IP (OTP mode only)</li>
<li><code>--principal &lt;name&gt;</code>: Principal (CA mode only)</li>
</ul>
<p><strong>Examples</strong>:</p>
<pre><code class="language-bash"># Basic usage
ssh generate-key server.example.com
# Custom user and TTL
ssh generate-key server.example.com --user deploy --ttl 30min
# Vault CA mode
ssh generate-key server.example.com --type ca --principal admin
</code></pre>
<h3 id="ssh-deploy-key"><a class="header" href="#ssh-deploy-key">ssh deploy-key</a></h3>
<p>Deploy a generated key to the target server.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh deploy-key &lt;key-id&gt;
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="language-bash">ssh deploy-key abc-123-def-456
</code></pre>
<h3 id="ssh-list-keys"><a class="header" href="#ssh-list-keys">ssh list-keys</a></h3>
<p>List all active SSH keys.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh list-keys [--expired]
</code></pre>
<p><strong>Examples</strong>:</p>
<pre><code class="language-bash"># List active keys
ssh list-keys
# Show only deployed keys
ssh list-keys | where deployed == true
# Include expired keys
ssh list-keys --expired
</code></pre>
<h3 id="ssh-get-key"><a class="header" href="#ssh-get-key">ssh get-key</a></h3>
<p>Get detailed information about a specific key.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh get-key &lt;key-id&gt;
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="language-bash">ssh get-key abc-123-def-456
</code></pre>
<h3 id="ssh-revoke-key"><a class="header" href="#ssh-revoke-key">ssh revoke-key</a></h3>
<p>Immediately revoke a key (removes from server and tracking).</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh revoke-key &lt;key-id&gt;
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="language-bash">ssh revoke-key abc-123-def-456
</code></pre>
<h3 id="ssh-connect"><a class="header" href="#ssh-connect">ssh connect</a></h3>
<p>Auto-generate, deploy, connect, and revoke (all-in-one).</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh connect &lt;server&gt; [options]
</code></pre>
<p><strong>Options</strong>:</p>
<ul>
<li><code>--user &lt;name&gt;</code>: SSH user (default: root)</li>
<li><code>--ttl &lt;duration&gt;</code>: Key lifetime (default: 1hr)</li>
<li><code>--type &lt;ca|otp|dynamic&gt;</code>: Key type (default: dynamic)</li>
<li><code>--keep</code>: Dont revoke after disconnect</li>
</ul>
<p><strong>Examples</strong>:</p>
<pre><code class="language-bash"># Quick connection
ssh connect server.example.com
# Custom user
ssh connect server.example.com --user deploy
# Keep key active after disconnect
ssh connect server.example.com --keep
</code></pre>
<h3 id="ssh-stats"><a class="header" href="#ssh-stats">ssh stats</a></h3>
<p>Show SSH key statistics.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh stats
</code></pre>
<p><strong>Example Output</strong>:</p>
<pre><code>SSH Key Statistics:
Total generated: 42
Active keys: 10
Expired keys: 32
Keys by type:
dynamic: 35
otp: 5
certificate: 2
Last cleanup: 2024-01-01T12:00:00Z
Cleaned keys: 5
</code></pre>
<h3 id="ssh-cleanup"><a class="header" href="#ssh-cleanup">ssh cleanup</a></h3>
<p>Manually trigger cleanup of expired keys.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh cleanup
</code></pre>
<h3 id="ssh-test"><a class="header" href="#ssh-test">ssh test</a></h3>
<p>Run a quick test of the SSH key system.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh test &lt;server&gt; [--user &lt;name&gt;]
</code></pre>
<p><strong>Example</strong>:</p>
<pre><code class="language-bash">ssh test server.example.com --user root
</code></pre>
<h3 id="ssh-help"><a class="header" href="#ssh-help">ssh help</a></h3>
<p>Show help information.</p>
<p><strong>Syntax</strong>:</p>
<pre><code class="language-bash">ssh help
</code></pre>
<h2 id="duration-formats"><a class="header" href="#duration-formats">Duration Formats</a></h2>
<p>The <code>--ttl</code> option accepts various duration formats:</p>
<div class="table-wrapper"><table><thead><tr><th>Format</th><th>Example</th><th>Meaning</th></tr></thead><tbody>
<tr><td>Minutes</td><td><code>30min</code></td><td>30 minutes</td></tr>
<tr><td>Hours</td><td><code>2hr</code></td><td>2 hours</td></tr>
<tr><td>Mixed</td><td><code>1hr 30min</code></td><td>1.5 hours</td></tr>
<tr><td>Seconds</td><td><code>3600sec</code></td><td>1 hour</td></tr>
</tbody></table>
</div>
<h2 id="working-with-private-keys"><a class="header" href="#working-with-private-keys">Working with Private Keys</a></h2>
<h3 id="saving-private-keys"><a class="header" href="#saving-private-keys">Saving Private Keys</a></h3>
<p>When you generate a key, save the private key immediately:</p>
<pre><code class="language-bash"># Generate and save to file
ssh generate-key server.example.com | get private_key | save -f ~/.ssh/temp_key
chmod 600 ~/.ssh/temp_key
# Use the key
ssh -i ~/.ssh/temp_key root@server.example.com
# Cleanup
rm ~/.ssh/temp_key
</code></pre>
<h3 id="using-ssh-agent"><a class="header" href="#using-ssh-agent">Using SSH Agent</a></h3>
<p>Add the temporary key to your SSH agent:</p>
<pre><code class="language-bash"># Generate key and extract private key
ssh generate-key server.example.com | get private_key | save -f /tmp/temp_key
chmod 600 /tmp/temp_key
# Add to agent
ssh-add /tmp/temp_key
# Connect (agent provides the key automatically)
ssh root@server.example.com
# Remove from agent
ssh-add -d /tmp/temp_key
rm /tmp/temp_key
</code></pre>
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
<h3 id="key-deployment-fails"><a class="header" href="#key-deployment-fails">Key Deployment Fails</a></h3>
<p><strong>Problem</strong>: <code>ssh deploy-key</code> returns error</p>
<p><strong>Solutions</strong>:</p>
<ol>
<li>
<p>Check SSH connectivity to server:</p>
<pre><code class="language-bash">ssh root@server.example.com
</code></pre>
</li>
<li>
<p>Verify provisioning key is configured:</p>
<pre><code class="language-bash">echo $PROVISIONING_SSH_KEY
</code></pre>
</li>
<li>
<p>Check server SSH daemon:</p>
<pre><code class="language-bash">ssh root@server.example.com "systemctl status sshd"
</code></pre>
</li>
</ol>
<h3 id="private-key-not-working"><a class="header" href="#private-key-not-working">Private Key Not Working</a></h3>
<p><strong>Problem</strong>: SSH connection fails with “Permission denied (publickey)”</p>
<p><strong>Solutions</strong>:</p>
<ol>
<li>
<p>Verify key was deployed:</p>
<pre><code class="language-bash">ssh list-keys | where id == "&lt;key-id&gt;"
</code></pre>
</li>
<li>
<p>Check key hasnt expired:</p>
<pre><code class="language-bash">ssh get-key &lt;key-id&gt; | get expires_at
</code></pre>
</li>
<li>
<p>Verify private key permissions:</p>
<pre><code class="language-bash">chmod 600 /path/to/private/key
</code></pre>
</li>
</ol>
<h3 id="cleanup-not-running"><a class="header" href="#cleanup-not-running">Cleanup Not Running</a></h3>
<p><strong>Problem</strong>: Expired keys not being removed</p>
<p><strong>Solutions</strong>:</p>
<ol>
<li>
<p>Check orchestrator is running:</p>
<pre><code class="language-bash">curl http://localhost:9090/health
</code></pre>
</li>
<li>
<p>Trigger manual cleanup:</p>
<pre><code class="language-bash">ssh cleanup
</code></pre>
</li>
<li>
<p>Check orchestrator logs:</p>
<pre><code class="language-bash">tail -f ./data/orchestrator.log | grep SSH
</code></pre>
</li>
</ol>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="security"><a class="header" href="#security">Security</a></h3>
<ol>
<li>
<p><strong>Short TTLs</strong>: Use the shortest TTL that works for your task</p>
<pre><code class="language-bash">ssh connect server.example.com --ttl 30min
</code></pre>
</li>
<li>
<p><strong>Immediate Revocation</strong>: Revoke keys when youre done</p>
<pre><code class="language-bash">ssh revoke-key &lt;key-id&gt;
</code></pre>
</li>
<li>
<p><strong>Private Key Handling</strong>: Never share or commit private keys</p>
<pre><code class="language-bash"># Save to temp location, delete after use
ssh generate-key server.example.com | get private_key | save -f /tmp/key
# ... use key ...
rm /tmp/key
</code></pre>
</li>
</ol>
<h3 id="workflow-integration"><a class="header" href="#workflow-integration">Workflow Integration</a></h3>
<ol>
<li>
<p><strong>Automated Deployments</strong>: Generate key in CI/CD</p>
<pre><code class="language-bash">#!/bin/bash
KEY_ID=$(ssh generate-key prod.example.com --ttl 1hr | get id)
ssh deploy-key $KEY_ID
# Run deployment
ansible-playbook deploy.yml
ssh revoke-key $KEY_ID
</code></pre>
</li>
<li>
<p><strong>Interactive Use</strong>: Use <code>ssh connect</code> for quick access</p>
<pre><code class="language-bash">ssh connect dev.example.com
</code></pre>
</li>
<li>
<p><strong>Monitoring</strong>: Check statistics regularly</p>
<pre><code class="language-bash">ssh stats
</code></pre>
</li>
</ol>
<h2 id="advanced-usage"><a class="header" href="#advanced-usage">Advanced Usage</a></h2>
<h3 id="vault-integration"><a class="header" href="#vault-integration">Vault Integration</a></h3>
<p>If your organization uses HashiCorp Vault:</p>
<h4 id="ca-mode-recommended"><a class="header" href="#ca-mode-recommended">CA Mode (Recommended)</a></h4>
<pre><code class="language-bash"># Generate CA-signed certificate
ssh generate-key server.example.com --type ca --principal admin --ttl 1hr
# Vault signs your public key
# Server must trust Vault CA certificate
</code></pre>
<p><strong>Setup</strong> (one-time):</p>
<pre><code class="language-bash"># On servers, add to /etc/ssh/sshd_config:
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
# Get Vault CA public key:
vault read -field=public_key ssh/config/ca | \
sudo tee /etc/ssh/trusted-user-ca-keys.pem
# Restart SSH:
sudo systemctl restart sshd
</code></pre>
<h4 id="otp-mode"><a class="header" href="#otp-mode">OTP Mode</a></h4>
<pre><code class="language-bash"># Generate one-time password
ssh generate-key server.example.com --type otp --ip 192.168.1.100
# Use the OTP to connect (single use only)
</code></pre>
<h3 id="scripting"><a class="header" href="#scripting">Scripting</a></h3>
<p>Use in scripts for automated operations:</p>
<pre><code class="language-nushell"># deploy.nu
def deploy [target: string] {
let key = (ssh generate-key $target --ttl 1hr)
ssh deploy-key $key.id
# Run deployment
try {
ssh $"root@($target)" "bash /path/to/deploy.sh"
} catch {
print "Deployment failed"
}
# Always cleanup
ssh revoke-key $key.id
}
</code></pre>
<h2 id="api-integration"><a class="header" href="#api-integration">API Integration</a></h2>
<p>For programmatic access, use the REST API:</p>
<pre><code class="language-bash"># Generate key
curl -X POST http://localhost:9090/api/v1/ssh/generate \
-H "Content-Type: application/json" \
-d '{
"key_type": "dynamickeypair",
"user": "root",
"target_server": "server.example.com",
"ttl_seconds": 3600
}'
# Deploy key
curl -X POST http://localhost:9090/api/v1/ssh/{key_id}/deploy
# List keys
curl http://localhost:9090/api/v1/ssh/keys
# Get stats
curl http://localhost:9090/api/v1/ssh/stats
</code></pre>
<h2 id="faq"><a class="header" href="#faq">FAQ</a></h2>
<p><strong>Q: Can I use the same key for multiple servers?</strong>
A: Currently, each key is tied to a specific server. Multi-server support is planned.</p>
<p><strong>Q: What happens if the orchestrator crashes?</strong>
A: Keys in memory are lost, but keys already deployed to servers remain until their expiration time.</p>
<p><strong>Q: Can I extend the TTL of an existing key?</strong>
A: No, you must generate a new key. This is by design for security.</p>
<p><strong>Q: Whats the maximum TTL?</strong>
A: Configurable by admin, default maximum is 24 hours.</p>
<p><strong>Q: Are private keys stored anywhere?</strong>
A: Private keys exist only in memory during generation and are shown once to the user. They are never written to disk by the system.</p>
<p><strong>Q: What happens if cleanup fails?</strong>
A: The key remains in authorized_keys until the next cleanup run. You can trigger manual cleanup with <code>ssh cleanup</code>.</p>
<p><strong>Q: Can I use this with non-root users?</strong>
A: Yes, use <code>--user &lt;username&gt;</code> when generating the key.</p>
<p><strong>Q: How do I know when my key will expire?</strong>
A: Use <code>ssh get-key &lt;key-id&gt;</code> to see the exact expiration timestamp.</p>
<h2 id="support"><a class="header" href="#support">Support</a></h2>
<p>For issues or questions:</p>
<ol>
<li>Check orchestrator logs: <code>tail -f ./data/orchestrator.log</code></li>
<li>Run diagnostics: <code>ssh stats</code></li>
<li>Test connectivity: <code>ssh test server.example.com</code></li>
<li>Review documentation: <code>SSH_KEY_MANAGEMENT.md</code></li>
</ol>
<h2 id="see-also"><a class="header" href="#see-also">See Also</a></h2>
<ul>
<li><strong>Architecture</strong>: <code>SSH_KEY_MANAGEMENT.md</code></li>
<li><strong>Implementation</strong>: <code>SSH_IMPLEMENTATION_SUMMARY.md</code></li>
<li><strong>Configuration</strong>: <code>config/ssh-config.toml.example</code></li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../user/DYNAMIC_SECRETS_QUICK_REFERENCE.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../user/RUSTYVAULT_KMS_GUIDE.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../user/DYNAMIC_SECRETS_QUICK_REFERENCE.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../user/RUSTYVAULT_KMS_GUIDE.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>