SCA stands for SSH Central Agent - a sophisticated SSH gateway system that provides secure, multi-level SSH access to remote hosts through a centralized jump host infrastructure.
The SSH Central Agent (SCA) is a gateway system that:
- Centralizes SSH agent management - Combines local and remote SSH keys into a single, unified agent
- Provides multi-level access control - Routes connections through security-level-based jump hosts (levels 0-3)
- Enables seamless authentication - Allows you to use keys from different sources (local YubiKey, remote server agents, etc.) simultaneously
- Manages secure connections - Handles SSH agent forwarding and multiplexing automatically
The sca command creates a subshell environment where your SSH client can access both local and remote SSH keys through a single agent socket, making it easy to connect to hosts across different security zones without manually managing multiple SSH agents.
A common enterprise deployment pattern involves:
- SSH Gateway Hosts: Multi-level jump hosts (
sca-jmp-level1,sca-jmp-level2,sca-jmp-level3, etc.) that route connections to protected infrastructure based on security levels - Central Servers with YubiKeys: Each jump host (level 1 and above) can have one or more YubiKeys physically attached and managed by the SSH agent. Level 0 typically does not use YubiKeys.
- Local Agent Access: Users connect to the appropriate gateway based on their security level, which forwards the remote SSH agent socket (containing YubiKey keys) to their local machine
When you run sca, it:
- Connects to the SSH gateway host corresponding to your security level (level 1-x) using your local credentials
- Establishes an SSH agent forwarding connection to the jump host's agent (which has access to one or more YubiKeys)
- Creates a local socket (
~/.ssh/scadev-agent.sock) that proxies authentication requests to the remote YubiKey(s) - Multiplexes this remote agent with your local agent, giving you access to both sets of keys seamlessly
Multi-YubiKey Support: Each jump host can have multiple YubiKeys attached, and all keys from all YubiKeys on that host become available through the forwarded agent socket. This allows for redundancy, different key types, or multiple security roles within the same level.
- Centralized Security: YubiKeys remain physically secured at the jump host servers, never leaving the controlled environment
- Multi-Level Architecture: Different security levels (1-x) can have their own YubiKey(s), allowing for granular access control and separation of concerns
- Scalable Key Management: Each level can have one or more YubiKeys, supporting redundancy, different key types, or multiple security roles
- No Physical Access Required: Users don't need direct physical access to the YubiKeys - authentication happens remotely through the gateway
- Seamless Experience: Once connected via
sca, you can use YubiKey-protected keys just like local keys - no manual intervention needed - Multi-User Access: Multiple authorized users at the same security level can access the same secure keys simultaneously through their own gateway connections
- Audit Trail: All authentication attempts go through the centralized gateway, providing better security monitoring and logging
- Key Protection: Private keys never leave the secure server - only signing operations are forwarded, keeping the actual key material safe
This architecture is ideal for organizations that need to provide secure SSH access to sensitive infrastructure while maintaining strict control over hardware security keys across multiple security levels.
Detailed documentation for setting up the sca-jmp (jump host) and sca-key (YubiKey server) infrastructure will be published in a future release. This will include:
- Jump host configuration and deployment
- YubiKey server setup and agent configuration
- Multi-level security architecture implementation
- Best practices for production deployments
Interested in Enterprise Deployment?
If your company is interested in implementing this SSH gateway architecture with centralized YubiKey management, we can help with setup and deployment. Please contact us for assistance with:
- Architecture design and planning
- Jump host and YubiKey server configuration
- Security level implementation
- Custom deployment requirements
- Generate an SSH key (if you don't have one):
# Standard key
ssh-keygen -t ed25519 -C your_email@example.com -f ~/.ssh/id_ed25519 -N yourpassword
# Or with YubiKey
ssh-keygen -t ecdsa-sk -O no-touch-required -C "your_email@example.com (notouch+passphrase)" -f ~/.ssh/id_your_sk
# Recommended: Create a dedicated key for SCA connections
ssh-keygen -t ed25519 -C "sca-connection" -f ~/.ssh/id_ed25519_sca -N yourpassword- Ensure your SSH agent is running (optional):
# Check if agent is running
ssh-add -l
# If not, start it
eval $(ssh-agent)
# Add your key
ssh-add ~/.ssh/id_ed25519Note: If you don't have a local SSH agent running, sca will automatically detect and use identity files (like ~/.ssh/id_ed25519) by creating a temporary agent. This ensures you only need to enter your passphrase once.
Recommended location: Check out the repository in ~/.ssh/sca. The playbook will adapt to any location, but ~/.ssh/sca is the recommended path.
First-time setup:
-
Clone the repository:
git clone https://github.com/styliteag/ssh-central-agent ~/.ssh/sca # or git clone https://github.com/styliteag/ssh-central-agent ~/.ssh/sca
-
Create the hosts directory: After checkout, create a
hostsdirectory. This can be:- A symlink to another private repository (recommended for team setups)
- An empty directory (if you don't have access to the private hosts repo yet)
- A separate private git repository
# Option 1: Create empty directory mkdir hosts # Option 2: Clone private repo as hosts directory git clone <private-hosts-repo-url> hosts
-
Configure your settings (optional):
# Copy the example file cp localvars-example.yml localvars.yml # Edit localvars.yml and set: # - sca_key_server: IP or hostname of your SCA key server # - sca_key_server_port: Port number # - remote_username: Your username on remote servers # - my_ssh_key: (optional) Custom key for SCA connections, e.g., ~/.ssh/id_ed25519_sca
-
Run the Ansible playbook:
ansible-playbook playbook.yml
This will:
- Generate the Python entrypoint
scaand the legacy bash entrypointsca.sh - Create SSH configuration files (
config_singlefor Host blocks,config_matchfor Match blocks) - Add
Includelines to~/.ssh/config(load order:config_single→hosts/*→config_match) - Set up shell integration (.bashrc/.zshrc)
Note: The playbook automatically adapts to wherever you've checked out the repository. See SETUP.md for detailed setup instructions.
The project now ships two parallel entrypoints:
sca→ Python implementation (generated fromtemplates/sca.py.j2)sca.sh→ Legacy bash implementation (generated fromtemplates/sca.sh.j2)
Use sca by default. Keep sca.sh only for legacy workflows or comparison.
Start a shell with access to remote keys using the SSH Central Agent:
scaOr use eval \sca -e`to set the SSH agent in your current shell (similar tossh-agent -s`):
eval `sca -e --key=local`
# Now SSH_AUTH_SOCK is set in your current shell
ssh-add -l # List available keysShow version information:
sca -v # Show version and socket paths
sca --version # Same as above
sca --help # Show help messageDirect SSH Connection:
Positional arguments are automatically treated as SSH arguments - sca is an SSH tool by default:
sca user@hostname # Connect using remote agent (default)
sca --key=local user@hostname # Connect using local agent
sca --key=mux user@hostname # Connect using multiplexed agentWhen using SSH options that start with - (like -p, -o), use -- to separate SCA options from SSH arguments:
sca --key=remote -- -p9922 root@10.20.3.254 hostname
sca --key=local -- -o StrictHostKeyChecking=no user@host
sca --key=mux -- -p9922 -v user@hostnameAll arguments after -- are passed directly to ssh, so you can use any SSH options:
sca --key=local -- -p9922 -v user@hostname
sca --key=mux -- user@hostname "command to run"Subshell Mode:
Use -s or --shell to open a subshell with the MUX agent environment set, instead of connecting to a host:
sca -s # Open subshell with MUX agent environment
sca --shell # Same as above
sca --shell --key=local # Subshell with local agentBasic Usage:
sca --key=local # Start subshell with local key only
sca --key=remote # Start subshell with remote key only (default)
sca --key=mux # Start subshell with multiplexed agent (local + remote)Host Management:
sca --list # List all configured hosts
sca --find hostname # Find and display information about a specific host
sca --add hostname # Add a new host to the configurationDirect SSH Connection (default):
sca user@host # Connect using remote agent (default)
sca --key=local user@host # Connect using local agent
sca --key=mux user@host # Connect using multiplexed agent
sca --key=remote -- -p9922 root@10.20.3.254 hostname # Use -- for SSH options starting with -
sca --key=mux -- -p9922 user@host # Connect using multiplexed agent with optionsSubshell Mode:
sca -s # Open subshell with MUX agent environment
sca --shell # Same as above
sca --shell --key=local # Subshell with local agent onlyAdvanced Options:
sca -s # Open a subshell with the MUX agent environment set
sca -e --key=local # Output env vars for eval (use with eval \`sca -e --key=local\`)
sca --wait # Run in background and monitor connection, restart if needed
sca --kill # Kill all agents and connections, remove socket files
sca -d # Enable debug mode (verbose output)
sca -r # Reverse the order of agents when multiplexing
sca -l 2 # Set security level (0-3, default: auto-detect)
sca -v, --version # Show version information and exit
sca -h, --help # Show help message and exitCombined Options:
sca -dr --key=local # Debug mode + reverse agent order
sca --wait --key=local # Background monitoring modeThe system uses the built-in Python multiplexer (sshagentmux.py) to combine local and remote SSH agents into a single socket. No external multiplexer is supported.
Add to your ~/.zshrc or ~/.bashrc:
# SCA-KEY subshell integration (SSH Central Agent)
if [ -n "$SCA_SUBSHELL" ] ; then
echo "SCA_SUBSHELL: $SCA_SUBSHELL"
PS1="$PS1($SCA_SUBSHELL) "
if [ -n "$MUX_SSH_AUTH_SOCK" ] ; then
export SSH_AUTH_SOCK=$MUX_SSH_AUTH_SOCK
elif [ -n "$SCA_SSH_AUTH_SOCK" ] ; then
export SSH_AUTH_SOCK=$SCA_SSH_AUTH_SOCK
fi
else
export LOCAL_SSH_AUTH_SOCK=$SSH_AUTH_SOCK
fiCheck agent status:
ssh-add -lView socket files:
ls -la ~/.ssh/scadev-*Kill and restart:
sca --kill
scaSee AGENTS.md for detailed documentation including:
- Architecture overview
- Configuration management
- Debugging guide
- Multiplexer details (Python only)
For macOS, you may need to use /usr/local/bin/ssh-agent instead of the built-in one:
which ssh
# Should show: /usr/local/bin/ssh
ssh -V
# Should show: OpenSSH_8.8p1 or laterIf needed:
pkill ssh-agent
eval $(/usr/local/bin/ssh-agent)
ssh-add ~/.ssh/id_xx_sk