Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

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"
}
# 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 ClientAliveInterval setting

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

  1. Use key-based authentication - Never use passwords
  2. Protect private keys - Use encrypted keys, proper permissions
  3. Verify host keys - Enable strict checking in production
  4. Limit bastion access - Restrict who can SSH to bastion
  5. Audit connections - Enable logging on bastion host
  6. Rotate keys regularly - Use short-lived certificates when possible

Next Steps