SSH Tunnels
Connect to databases through SSH tunnels for secure access to private networks.
When to Use SSH Tunnels
- Database is in a private network (no direct access)
- Connecting through a bastion/jump host
- Security policy requires encrypted connections
- Accessing on-premises databases from cloud
Basic SSH Tunnel
from horizon_epoch import Config
config = Config(
metadata_url="postgresql://localhost/horizon_epoch"
).add_postgres(
name="production",
host="internal-db.private", # Internal hostname
database="production",
username="epoch_user",
password="secret",
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa"
}
)
Configuration Options
SSH Key Authentication
ssh_tunnel={
"host": "bastion.example.com",
"port": 22, # Default
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"key_passphrase": "${SSH_KEY_PASSPHRASE}" # If key is encrypted
}
SSH Agent
Use keys loaded in SSH agent:
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"use_agent": True
}
SSH Certificate Authentication
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"cert_file": "~/.ssh/id_rsa-cert.pub"
}
Jump Hosts (ProxyJump)
For multi-hop connections:
ssh_tunnel={
"host": "internal-bastion.private",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"jump_host": {
"host": "external-bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa"
}
}
CLI Configuration
# Environment variables
export EPOCH_SSH_HOST="bastion.example.com"
export EPOCH_SSH_USER="ubuntu"
export EPOCH_SSH_KEY_FILE="~/.ssh/id_rsa"
# epoch.toml
[storage.postgres.production]
host = "internal-db.private"
database = "production"
username = "epoch_user"
password_file = "/run/secrets/db-password"
[storage.postgres.production.ssh_tunnel]
host = "bastion.example.com"
user = "ubuntu"
key_file = "~/.ssh/id_rsa"
Host Key Verification
Strict (Recommended)
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"known_hosts_file": "~/.ssh/known_hosts",
"strict_host_key_checking": True
}
Accept on First Use
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"strict_host_key_checking": "accept-new"
}
Disable (Not Recommended)
# Only for testing/development
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"strict_host_key_checking": False
}
Connection Management
Keep-Alive
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"keepalive_interval": 60, # Send keepalive every 60 seconds
"keepalive_count_max": 3 # Disconnect after 3 missed keepalives
}
Auto-Reconnection
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"auto_reconnect": True,
"reconnect_delay": 5, # Wait 5 seconds between attempts
"max_reconnect_attempts": 3
}
Connection Timeout
ssh_tunnel={
"host": "bastion.example.com",
"user": "ubuntu",
"key_file": "~/.ssh/id_rsa",
"connect_timeout": 30 # 30 second timeout
}
Multiple Tunnels
For accessing multiple databases:
config.add_postgres(
name="prod_users",
host="users-db.private",
database="users",
ssh_tunnel=bastion_config
).add_postgres(
name="prod_orders",
host="orders-db.private",
database="orders",
ssh_tunnel=bastion_config # Same tunnel reused
)
Troubleshooting
Connection Refused
Error: ssh: connect to host bastion.example.com port 22: Connection refused
- Verify bastion host is running
- Check security groups/firewall allow port 22
- Verify DNS resolution
Authentication Failed
Error: ssh: handshake failed: ssh: unable to authenticate
- Verify SSH key is correct
- Check key has correct permissions:
chmod 600 ~/.ssh/id_rsa - Verify user exists on bastion
- Try connecting manually:
ssh -i ~/.ssh/id_rsa ubuntu@bastion.example.com
Host Key Verification Failed
Error: Host key verification failed
- Add host to known_hosts:
ssh-keyscan bastion.example.com >> ~/.ssh/known_hosts - Or use
strict_host_key_checking: "accept-new"
Tunnel Connection Lost
Error: SSH tunnel disconnected
- Enable keepalive settings
- Enable auto-reconnect
- Check bastion stability
- Review SSH server
ClientAliveIntervalsetting
Slow Connection
- Use connection pooling (tunnel is reused)
- Consider running Horizon Epoch closer to the bastion
- Check network latency between client and bastion
Security Best Practices
- Use key-based authentication - Never use passwords
- Protect private keys - Use encrypted keys, proper permissions
- Verify host keys - Enable strict checking in production
- Limit bastion access - Restrict who can SSH to bastion
- Audit connections - Enable logging on bastion host
- Rotate keys regularly - Use short-lived certificates when possible
Next Steps
- mTLS Authentication - Certificate-based database auth
- Vault Integration - SSH key management with Vault
- Configuration Reference - Full options