A comprehensive system for managing and tracking OpenPubkey SSH (opkssh) deployments across multiple Linux hosts.
[!note] Disclaimer This project is not affiliated with the official opkssh or pocket-id projects. It is an independent tool designed to facilitate the deployment and management of opkssh installations.
This system consists of:
- Tracking Server - A centralized FastAPI server that tracks opkssh deployments via HTTP API
- Deployment Scripts - Automated bash scripts for deploying opkssh to remote Linux hosts
- Client Utilities - Tools for reporting status and generating SSH configurations
This deployment tracker depends on two awesome open-source projects:
opkssh is a SSH authentication system that eliminates the need for traditional SSH keys by leveraging OpenID Connect (OIDC) for authentication. Instead of managing SSH key pairs, users authenticate with their existing OIDC provider (like Google, Microsoft, or self-hosted solutions), and opkssh automatically generates cryptographic proofs that authorize SSH access.
Why opkssh?
- ✅ No SSH key management - No more lost keys, key rotation headaches, or distributing public keys
- ✅ Centralized identity - Use your existing OIDC provider for authentication
- ✅ Enhanced security - Cryptographic proofs based on modern OIDC standards
- ✅ Audit trail - Every SSH connection is tied to a verified identity
- ✅ Easy revocation - Disable access by removing OIDC permissions, no need to update authorized_keys files
Learn more: github.com/openpubkey/opkssh
Pocket ID is a lightweight, self-hosted OpenID Connect (OIDC) provider that gives you complete control over your authentication infrastructure. Perfect for homelab enthusiasts, small businesses, and anyone who wants to avoid vendor lock-in with cloud identity providers.
Why Pocket ID?
- ✅ Self-hosted - Full control over your identity provider, no third-party dependencies
- ✅ Privacy-focused - Your authentication data stays on your infrastructure
- ✅ Easy setup - Docker-based deployment with minimal configuration
- ✅ Standards-compliant - Full OIDC implementation compatible with any OIDC-aware application
- ✅ Passkey Only - Use Passkeys for authentication instead of Passwords
- ✅ Perfect for opkssh - Designed to work seamlessly with modern authentication systems
- ✅ No vendor lock-in - Own your identity infrastructure
Learn more: github.com/pocket-id/pocket-id
This deployment tracker integrates these technologies to provide a complete SSH management solution:
- Pocket ID (or any OIDC provider) handles user authentication
- opkssh uses OIDC tokens to authorize SSH connections without traditional keys
- This tracker helps you deploy and manage opkssh across your infrastructure
The result? Secure, modern SSH authentication without the hassle of key management!
- About the Required Projects
- Server Setup
- Web Dashboard
- Client Scripts Usage
- Configuration
- OIDC Authentication Setup
- Upgrading from Previous Versions
- API Endpoints
- Troubleshooting
Latest changes can be found in the Changelog
The tracking server runs as a Docker container using a pre-built image.
- Docker and Docker Compose installed
- A Linux host or server to run the tracker
- Ports: 8080 (or your chosen port) available
- OIDC Provider for authentication (e.g. Pocket ID, Auth0, Keycloak)
Required OIDC details:
- Issuer URL
- Client ID
- Expiry duration
- Account on your OIDC provider for authentication
-
Checkout this repository:
git clone git@github.com:Basti-Fantasti/opkssh-deployment-tracker.git cd opkssh-deployment-tracker -
Create configuration file:
cp config.toml.example config.toml
-
Edit the configuration:
nano config.toml # or use your preferred editorImportant: Change the default password!
[auth] mode = "basic" # Authentication mode: "none", "basic", or "oidc" username = "admin" password = "your-secure-password-here" # CHANGE THIS!
-
Create data directory:
mkdir -p data
-
Start the server:
docker-compose up -d
-
Verify the server is running:
docker-compose logs -f
Or check the health endpoint:
curl http://localhost:8080/health
-
Access the web dashboard: Open your browser and navigate to:
http://your-server-ip:8080
View logs:
docker-compose logs -fStop the server:
docker-compose downRestart the server:
docker-compose restartUpdate to latest version:
docker-compose pull
docker-compose up -dThe opkssh Deployment Tracker provides a comprehensive web-based dashboard for managing your deployments. Access it at http://your-server-ip:8080 after starting the server.
The main dashboard provides:
- Deployment Statistics - Quick overview showing total deployments, successful deployments, and failed deployments
- Deployment Table - Comprehensive view of all tracked hosts with columns for:
- Hostname (clickable to view deployment history)
- Alias (friendly name for SSH config)
- IP Address
- SSH User
- Group (colored badge, clickable for inline editing)
- SSH-Agent forwarding status (toggle on/off per host)
- Deployment Status (success/incomplete/failed)
- Timestamp of last report
- opkssh Version installed
- Operating System information
- Actions (Edit/Delete buttons)
- Group Filter Dropdown - Filter deployments by group
- Manage Groups Button - Access group management modal
- Generate Bootstrap Command button for one-command deployments
- SSH Config Generator with hostname/IP mode selection
- API Endpoints quick access links
Once you have deployments tracked, the dashboard shows all deployment details in an organized table. The hostname is clickable to view the complete deployment history timeline for that host.
New in v0.7.0
Organize your deployments into logical groups with visual distinction and filtering capabilities.
- Single group per deployment - Each deployment can belong to one group (optional, defaults to "Ungrouped")
- Implicit group creation - Simply type a new group name to auto-create it
- Bootstrap token support - Pre-assign group when generating bootstrap commands
- Inline editing - Click the group badge in the table to quickly change a deployment's group
Access via the "Manage Groups" button in the dashboard header to:
- View all groups with deployment counts
- Rename groups (updates all associated deployments automatically)
- Delete groups (moves deployments to "Ungrouped")
- Customize group colors using the color picker
- Real-time updates without page reload
- Group selector dropdown in the dashboard header
- Filter deployments by specific group or view all
- Statistics cards update to show filtered counts
- Smooth transitions when switching between groups
When downloading SSH config:
- Select a specific group to generate config for only those deployments
- Group comments are included in the generated SSH config file
- API support:
GET /ssh-config?group=production
The bootstrap deployment feature enables one-command opkssh installation without manual script distribution or configuration files.
Click the "Generate Bootstrap Command" button to open the bootstrap modal where you can:
- View the tracker URL (automatically populated)
- Select a Group for the deployment (with autocomplete from existing groups)
- Enable Non-interactive mode to pre-configure all deployment values for fully automated installation
- Generate a time-limited deployment token
After clicking "Generate", you receive:
- A ready-to-use
curl | bashcommand - Token expiry countdown (default: 1 hour)
- One-click "Copy to Clipboard" functionality
- Option to regenerate the token
- Reusable token - use the same command on multiple servers within the expiry window
On a fresh system without opkssh:
The bootstrap script automatically:
- Detects that opkssh is not installed
- Checks the latest available version
- Presents a clean menu with options to install or cancel
On a system with existing opkssh installation:
The smart bootstrap script:
- Detects the installed opkssh version
- Checks for available updates
- Finds existing configuration in
/etc/opk - Offers context-aware options:
- Reconfigure existing installation
- Report current status to tracker
- Cancel
Installation and Configuration Process:
The bootstrap script handles the complete deployment process:
- Downloads and installs opkssh
- Configures OpenID provider settings
- Sets up user authorization
- Configures SSHD for opkssh authentication
- Sets up SSH agent forwarding (with multi-shell support: bash, zsh, fish)
- Reports deployment status to the tracker
- All with detailed progress feedback and error handling
Reporting to Tracker:
After installation or when using the "Report status" option, the script:
- Collects hostname, alias, IP, user, and configuration details
- Reports deployment status to the tracker server
- Confirms successful registration
Click the "Edit" button on any deployment to modify:
- Hostname
- Alias (friendly name)
- IP Address
- SSH User
- Changes are saved immediately with visual feedback
Each deployment row has a "Delete" button that:
- Shows a confirmation dialog before deletion
- Immediately removes the deployment from the tracker
- Updates the dashboard statistics
The "SSH-Agent" column contains checkboxes that allow you to:
- Enable/disable SSH agent forwarding per host
- Changes are saved automatically via API
- Affects the generated SSH config (
ForwardAgent yes/no)
The tracker can generate OpenSSH configuration for all successful deployments.
Using IP Address Mode:
Select "Use IP Address" to generate SSH config with:
- IP address in the
HostNamefield - Hostname shown as a comment
- Perfect for environments where DNS is unreliable
Using Hostname Mode:
Select "Use Hostname" to generate SSH config with:
- Hostname in the
HostNamefield - IP address shown as a comment
- Ideal when you have proper DNS resolution
Both modes include:
- User configuration
- Identity file path
- SSH agent forwarding setting (when enabled)
- Timestamp of generation
- Group filtering option (generate config for specific group or all)
Click "Download SSH Config" to download the configuration file, which can be:
- Copied directly into
~/.ssh/config - Used with the
update-ssh-config.shscript for automatic integration
The dashboard provides access to interactive API documentation at /openapi.json, showing all available endpoints:
- POST /report - Submit deployment reports
- GET /reports - Retrieve all deployment reports
- DELETE /reports - Clear all reports
- GET /reports/{hostname} - Get specific deployment
- DELETE /reports/{hostname} - Delete specific deployment
- PATCH /reports/{hostname} - Update deployment (e.g., SSH agent setting)
- GET /ssh-config - Generate SSH configuration
- GET /health - Health check endpoint
- POST /api/bootstrap-token - Create bootstrap deployment token
- GET /bootstrap - Download bootstrap installation script
- GET /api/scripts/{script_name} - Download deployment scripts
- GET /api/latest-opkssh-version - Get latest opkssh version from GitHub
- GET /api/deployment-history/{hostname} - View deployment timeline
Each endpoint shows authentication requirements (lock icon) and supports the OpenAPI/Swagger specification.
Edit config.toml to customize:
[server]
host = "0.0.0.0" # Bind to all interfaces
port = 8080 # Default port
[auth]
mode = "basic" # Auth mode: "none", "basic", or "oidc"
username = "admin" # Basic auth username
password = "change-me" # Basic auth password
[ssh_config]
default_use_hostname = true # Use hostname (true) or IP (false) in SSH configAfter changing configuration, restart the server:
docker-compose restartThe client scripts are used on your local machine to deploy opkssh to remote hosts and manage SSH configurations.
All scripts can be found in the scripts/ directory.
- Bash shell
curlandwgetinstalledsudoaccess (for deployment script)- SSH or physical/direct access to target hosts
The deploy-opkssh.sh script installs and configures opkssh on a Debian-based Linux host.
-
Create your .env file:
cp .env.example .env
-
Configure your environment:
nano .env # or use your preferred editorEdit the following variables:
# Tracker server URL TRACKER_URL="http://your-tracker-server:8080" # Tracker authentication TRACKER_USER="admin" TRACKER_PASS="your-password" # Default SSH principal DEFAULT_PRINCIPAL="root" # OpenID Provider Configuration PROVIDER_ISSUER="https://auth.yourdomain.com" PROVIDER_CLIENT_ID="your-client-id" PROVIDER_EXPIRY="24h" USER_EMAIL="your-email@yourdomain.com"
Interactive deployment (prompts for username):
ssh user@target-host
sudo ./deploy-opkssh.shNon-interactive deployment (automated):
ssh root@target-host
sudo ./deploy-opkssh.sh --user root --alias webserver-01With custom alias:
sudo ./deploy-opkssh.sh --alias db-server-prodOptions:
--user USERNAME- Local username/principal for SSH access (default: root)--alias ALIAS- Friendly alias for SSH config (default: hostname)--tracker-url URL- Override tracker URL from .env--tracker-user USER- Override tracker username from .env--tracker-pass PASS- Override tracker password from .env--help- Show help message
The report-opkssh.sh script analyzes an existing opkssh installation and reports it to the tracker.
Usage:
./report-opkssh.shWith options:
./report-opkssh.sh --alias production-db --status successDry run (show what would be reported):
./report-opkssh.sh --dry-runOptions:
--alias ALIAS- Set a friendly alias--status STATUS- Override detected status (success/incomplete/failed)--tracker-url URL- Override tracker URL from .env--tracker-user USER- Override tracker username--tracker-pass PASS- Override tracker password--dry-run- Show report without sending--help- Show help message
The update-ssh-config.sh script fetches deployment data from the tracker and updates your ~/.ssh/config file.
Usage:
Basic usage:
./update-ssh-config.shWith custom prefix:
./update-ssh-config.sh --prefix "opk-"This creates SSH hosts like: opk-webserver-01
With custom identity file:
./update-ssh-config.sh --identity-file ~/.ssh/my_opk_keyDry run (preview changes):
./update-ssh-config.sh --dry-runOptions:
--prefix PREFIX- Add prefix to host aliases (e.g., "opk-")--identity-file FILE- Path to SSH identity file (default: ~/.ssh/id_ecdsa)--config FILE- SSH config file to update (default: ~/.ssh/config)--tracker-url URL- Override tracker URL from .env--tracker-user USER- Override tracker username--tracker-pass PASS- Override tracker password--no-backup- Don't create backup of existing config--dry-run- Preview changes without applying--help- Show help message
After updating, SSH to your hosts:
ssh root@webserver-01
# or with prefix:
ssh root@opk-webserver-01Copy .env.example to .env and configure:
# Tracker Server
TRACKER_URL="http://your-tracker-server:8080"
TRACKER_USER="admin"
TRACKER_PASS="your-password"
# Default Settings
DEFAULT_PRINCIPAL="root"
# OpenID Provider (required for deploy-opkssh.sh)
PROVIDER_ISSUER="https://auth.yourdomain.com"
PROVIDER_CLIENT_ID="your-client-id-here"
PROVIDER_EXPIRY="24h"
USER_EMAIL="your-email@yourdomain.com"[server]
host = "0.0.0.0"
port = 8080
[auth]
mode = "basic" # Auth mode: "none", "basic", or "oidc"
username = "admin"
password = "secure-password"
[ssh_config]
default_use_hostname = trueThe tracker supports three authentication modes:
No authentication required. Use only for development.
HTTP Basic Auth with username/password (default). Session-based authentication enables proper logout functionality - users authenticate once and receive a session cookie.
How Basic Auth Logout Works:
- On login, credentials are verified and a session is created
- Session cookie (
opkssh_session) is used for subsequent requests - Logout invalidates the session and redirects to a "Logged Out" page
- Browser credential cache is cleared via realm change technique
OpenID Connect with your SSO provider using PKCE (Proof Key for Code Exchange).
Note: The tracker reuses the same OIDC provider you configured for opkssh deployments in the
[deployment.provider]section. This means you only need to configure one SSO provider, and it will be used for both SSH authentication (via opkssh) and tracker dashboard access.
How OIDC Works:
- User clicks "Login" and is redirected to SSO provider
- Tracker uses OIDC Discovery to fetch endpoints from
{issuer}/.well-known/openid-configuration - User authenticates with SSO provider (using PKCE for security)
- SSO redirects back with authorization code
- Tracker exchanges code for tokens using PKCE code verifier
- User email is verified against
allowed_emailslist - Session is created with secure cookie
OIDC Discovery: Only the issuer URL from [deployment.provider] is needed - all other endpoints (authorization, token, logout) are automatically discovered from the provider's well-known configuration.
- Configure config.toml:
[auth]
mode = "oidc"
[auth.oidc]
# Redirect URI must match exactly what's registered with SSO provider
redirect_uri = "https://your-tracker-domain/auth/callback"
# Only these emails can access the dashboard
allowed_emails = [
"admin@example.com",
"user@example.com"
]
# Session persistence (required for Docker)
session_file = "/data/sessions.json"
# Cookie security - set to false only for local development without HTTPS
secure_cookies = true- Configure your OIDC Provider:
Add the tracker's URLs to your existing opkssh OIDC client application. Since the tracker reuses the same client configured in [deployment.provider], you just need to add additional redirect URIs.
Client ID: Use the same client ID from [deployment.provider].client_id
Add these Redirect URIs to your OIDC client:
| URL Type | Production | Local Development |
|---|---|---|
| Callback URL | https://your-tracker-domain/auth/callback |
http://localhost:8080/auth/callback |
| Post-Logout URL | https://your-tracker-domain/auth/logged-out |
http://localhost:8080/auth/logged-out |
Important: The
redirect_uriin yourconfig.tomlmust exactly match what's registered in your OIDC provider (including protocol, domain, and path).
Example OIDC Provider Configuration (Pocket ID / Keycloak / etc.):
Client ID: your-opkssh-client-id
Client Type: Public (no secret)
PKCE: Enabled (S256)
Grant Type: Authorization Code
Redirect URIs:
- https://your-tracker-domain/auth/callback
- http://localhost:8080/auth/callback (for development)
Post-Logout Redirect URIs:
- https://your-tracker-domain/auth/logged-out
- http://localhost:8080/auth/logged-out (for development)
Scopes: openid, email, profile
- SSO Logout (RP-Initiated Logout)
The tracker supports proper SSO logout using RP-Initiated Logout:
- When user clicks "Logout", they are redirected to SSO provider's
end_session_endpoint - SSO provider terminates the session
- User is redirected back to tracker's
/auth/logged-outpage - This prevents auto-login after logout
Required SSO Provider Settings:
- Enable RP-Initiated Logout (most providers support this)
- Register post-logout redirect URI:
https://your-tracker-domain/auth/logged-out
- Docker Network Requirements
For OIDC authentication to work in Docker, the container must be able to reach your SSO provider:
# If using a custom network without internet access (e.g., for reverse proxy)
# Add a second network with IP masquerade enabled:
services:
opkssh-tracker:
networks:
- your-internal-network # For reverse proxy
- internet-access # For OIDC provider access
networks:
your-internal-network:
external: true
internet-access:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_ip_masquerade: "true"Verify connectivity:
docker exec -it opkssh-tracker python -c "import httpx; print(httpx.get('https://your-sso-provider/.well-known/openid-configuration').status_code)"Version 0.8.0 introduces a new authentication configuration format. If you're upgrading from an older version, follow this guide.
[auth]
enabled = true # REMOVED - no longer used
username = "admin"
password = "secret"[auth]
mode = "basic" # NEW - replaces "enabled" (options: "none", "basic", "oidc")
username = "admin"
password = "secret"
# NEW - Required only for mode = "oidc"
[auth.oidc]
redirect_uri = "https://your-tracker-domain/auth/callback"
allowed_emails = ["admin@example.com"]
session_file = "/data/sessions.json"
secure_cookies = true-
Open your
config.toml -
Replace the
[auth]section:Old Setting New Setting Notes enabled = truemode = "basic"For HTTP Basic Auth enabled = falsemode = "none"No authentication (new) mode = "oidc"For SSO login -
For Basic Auth users (most common):
# Before (v0.7.x) [auth] enabled = true username = "admin" password = "your-password" # After (v0.8.x) [auth] mode = "basic" username = "admin" password = "your-password"
-
For OIDC users (new feature):
[auth] mode = "oidc" [auth.oidc] redirect_uri = "https://your-tracker-domain/auth/callback" allowed_emails = ["your-email@example.com"] session_file = "/data/sessions.json" secure_cookies = true
-
Restart the container:
docker-compose restart
| Change | Action Required |
|---|---|
enabled = true/false removed |
Replace with mode = "basic" or mode = "none" |
New mode setting |
Add mode = "basic", "oidc", or "none" |
New [auth.oidc] section |
Add only if using OIDC authentication |
| Logout now works properly | No action needed - automatic improvement |
After updating, verify the server starts correctly:
docker-compose logs -f | head -50Look for:
Auth mode: basicorAuth mode: oidc- confirms new config is loaded- No errors about missing configuration keys
The tracker server provides the following REST API endpoints:
Submit a deployment report.
Requires authentication if enabled.
Request body:
{
"hostname": "webserver-01",
"alias": "web-prod",
"ip": "192.168.1.100",
"user": "root",
"group": "production",
"status": "success",
"opkssh_version": "0.3.0",
"os_info": "Debian 12",
"error": ""
}Get all deployment reports.
Requires authentication if enabled.
Query parameters:
group- Filter by group name (optional)
Response:
[
{
"hostname": "webserver-01",
"alias": "web-prod",
"ip": "192.168.1.100",
"user": "root",
"group": "production",
"status": "success",
"opkssh_version": "0.3.0",
"os_info": "Debian 12",
"timestamp": "2025-01-15T10:30:00Z",
"error": ""
}
]Get a specific deployment report by hostname.
Requires authentication if enabled.
Delete a deployment report.
Requires authentication if enabled.
Generate SSH config for all successful deployments.
Requires authentication if enabled.
Query parameters:
identity_file- SSH identity file path (default: ~/.ssh/id_ecdsa)prefix- Prefix for host aliases (optional)use_hostname- Use hostname instead of IP (default: true)group- Filter by group name (optional)
Response: OpenSSH config format text
List all groups with deployment counts and colors.
Requires authentication if enabled.
Response:
[
{
"name": "production",
"color": "#4CAF50",
"description": "",
"count": 5
}
]Rename a group or update its color/description.
Requires authentication if enabled.
Request body:
{
"new_name": "prod-servers",
"color": "#2196F3",
"description": "Production servers"
}Delete a group. Associated deployments become "Ungrouped".
Requires authentication if enabled.
Health check endpoint (no authentication required).
Web dashboard showing all deployments with statistics.
Initiates authentication flow.
- Basic mode: Shows login form or processes credentials
- OIDC mode: Redirects to SSO provider's authorization endpoint
OIDC callback endpoint (OIDC mode only).
- Receives authorization code from SSO provider
- Exchanges code for tokens using PKCE
- Creates session and redirects to dashboard
Terminates user session.
- Basic mode: Invalidates session, shows logged-out page
- OIDC mode: Redirects to SSO provider's
end_session_endpointfor full logout
Post-logout landing page. Displayed after successful logout from SSO provider.
Container won't start:
# Check logs
docker-compose logs
# Check if port is already in use
netstat -tulpn | grep 8080
# Check config file syntax
cat config.tomlCan't access web dashboard:
- Verify the server is running:
docker-compose ps - Check firewall rules:
sudo ufw status - Verify port binding:
netstat -tulpn | grep 8080
Authentication failures:
- Verify credentials in
server/config.toml - Verify credentials in
.envmatch server config
"Missing required configuration variables":
- Ensure
.envfile exists - Verify all required variables are set in
.env - Check variable names match exactly (case-sensitive)
"opkssh is not installed":
- The deployment script installs opkssh automatically
- For report script, opkssh must already be installed
- Verify with:
which opkssh
SSH config not updating:
- Check tracker URL is accessible:
curl http://tracker:8080/health - Verify authentication credentials
- Try with
--dry-runfirst to preview changes - Check
~/.ssh/configpermissions:ls -la ~/.ssh/config
"Authentication failed":
- Verify
TRACKER_USERandTRACKER_PASSin.env - Verify they match
server/config.toml - Check auth mode in config:
[auth] mode = "basic"or"oidc"
"Connect timeout" or "Connection refused" during login:
- Container cannot reach SSO provider
- Check Docker network configuration (see Docker Network Requirements)
- Verify DNS resolution:
docker exec opkssh-tracker nslookup your-sso-provider - Test connectivity:
docker exec opkssh-tracker curl -I https://your-sso-provider
"Access denied for email@example.com":
- Email not in
allowed_emailslist in config.toml - Add the email to the list and restart the container
"Invalid state parameter" or "Invalid code verifier":
- PKCE verification failed - usually means cookies expired
- Clear browser cookies and try again
- Check that
redirect_urimatches exactly (including trailing slashes)
Logout redirects back to logged-in state:
- SSO provider session still active
- Ensure RP-Initiated Logout is configured (see SSO Logout)
- Register post-logout redirect URI with your SSO provider
"No matching key found in JWKS":
- Token signing key not found in provider's JWKS
- Clear the OIDC cache by restarting the container
- Check if your SSO provider rotated keys recently
Can't reach tracker from clients:
# Test connectivity
curl http://your-tracker-server:8080/health
# With authentication
curl -u admin:password http://your-tracker-server:8080/health
# Check DNS resolution
nslookup your-tracker-server
# Check network route
traceroute your-tracker-server- Change default passwords - Always change the default password in
server/config.toml - Use HTTPS - Consider putting the tracker behind a reverse proxy with SSL/TLS
- Firewall rules - Restrict access to port 8080 to known client IPs
- Keep .env private - Never commit
.envto version control - Regular updates - Update the Docker image regularly:
docker-compose pull && docker-compose up -d
For issues, questions, or contributions, open an issue in this repo.
See LICENSE file for details.