SSH bastion setup

Elglide connects to your source database through your SSH bastion — a jump host you already control. You open one inbound rule on the bastion: SSH, from our static IPs only. We initiate every session, it closes when the extract finishes, and the key can reach only the one database host and port you allow.

Nothing runs on your side. You don't start a tunnel — we dial out at the start of each extract and disconnect when it finishes. You set up your normal SSH server and one line in authorized_keys. That's all.
Prefer not to run a bastion? Direct connection — IP allowlist is simpler if your database is already reachable from the internet. Need source data to never leave your network at all? See Hybrid Agent.

Architecture

One trust boundary: your firewall. Our connector dials your bastion; the bastion forwards that single connection to your source database.

Elglide SSH bastion architecture Elglide cloud on the left with the connector; your network on the right behind your firewall, containing the SSH bastion and source database. The connector dials outbound SSH to the bastion (dashed), which local-forwards the connection to the database (solid); extracted rows return over the same tunnel. SSH bastion — Elglide dials outbound into your network Elglide cloud Your network 🔒 firewall inbound: SSH, our IPs only Connectoroutbound SSH dialer SSH bastionkey-only · permitopen-scoped Source databasereachable only from the bastion 1. outbound SSH — per-extract session 2. local forward, permitopen-scoped rows return over the same tunnel SSH tunnel — closed after each extract database rows

When to use it

  • You already run a bastion, or your security policy requires all access through one entry point.
  • You don't want to install Elglide software inside your network.
  • If source data must never leave your network, use Hybrid Agent instead.

Setup

1. Bastion sizing

A small VM is fine — 1 vCPU, 1 GB RAM, your distro of choice. Each extract opens one SSH session and closes it on completion. We never keep persistent tunnels.

2. Allow inbound SSH from us

Allow inbound TCP 22 (or whatever non-default port you've chosen for SSH) from our static egress IPs. That's the only inbound rule we need.

3. Generate the keypair (in our portal)

From Account → SSH Tunnels (sign-in required) click + New SSH tunnel. Fill in the bastion host, port and SSH user, then Generate keypair — we show you the public key to install in step 5.

Keep Ed25519 unless your bastion rejects it; then use RSA-4096.

The Configure new SSH tunnel dialog A dialog titled "Configure new SSH tunnel" with fields for Name (optional), Bastion host and Port, SSH user, and a Key type dropdown set to Ed25519 (recommended). Buttons at the bottom read Cancel and Generate keypair. Configure new SSH tunnel × Name defaults to user@host Bastion host * bastion.example.com Port 22 SSH user * ec2-user · elglide · ubuntu Key type Ed25519 (recommended) Cancel Generate keypair

4. Find your DB_HOST:DB_PORT

Use the address that reaches the database from the bastion. If the database runs on the bastion itself, use 127.0.0.1. Confirm on the bastion:

getent hosts DB_HOST        # resolves here?
nc -vz DB_HOST DB_PORT      # reachable from here?
This name is resolved on the bastion, never by us. Getting it wrong is the most common mistake on this page.

5. Create the user & install the key by platform

Replace <your-public-key> with the key from step 3 and DB_HOST:DB_PORT with the value from step 4. The line is the same on every platform:

restrict,port-forwarding,permitopen="DB_HOST:DB_PORT" <your-public-key>
restrict denies everything, port-forwarding re-enables forwarding only, and permitopen limits it to that one address — so this key cannot reach anything else on your network. Needs OpenSSH 7.2+ (check ssh -V); on older servers use:
no-pty,no-agent-forwarding,no-X11-forwarding,no-user-rc,permitopen="DB_HOST:DB_PORT" <your-public-key>
  1. Create the user:
    sudo useradd -m -s /usr/sbin/nologin elglide
    sudo passwd -l elglide
    sudo mkdir -p /home/elglide/.ssh && sudo chmod 700 /home/elglide/.ssh
    sudo touch /home/elglide/.ssh/authorized_keys
    sudo chmod 600 /home/elglide/.ssh/authorized_keys
    sudo chown -R elglide:elglide /home/elglide/.ssh
  2. Install the key:
    echo 'restrict,port-forwarding,permitopen="DB_HOST:DB_PORT" <your-public-key>' \
      | sudo tee -a /home/elglide/.ssh/authorized_keys
  3. Check forwarding is allowed and sshd is running:
    sudo sshd -T | grep -i allowtcpforwarding    # expect: yes
    sudo systemctl enable --now ssh              # sshd on RHEL-family
A nologin shell is fine — forwarding doesn't need one. If allowtcpforwarding is no, set AllowTcpForwarding yes in /etc/ssh/sshd_config and reload; permitopen can't override a server-wide no.
Dedicated dscl service accounts on macOS are fiddly. For an evaluation, use an account you already log in with. For anything long-lived, a small Linux VM is easier.
  1. Enable Remote Login:
    sudo systemsetup -setremotelogin on
    sudo systemsetup -getremotelogin        # expect: Remote Login: On
    Then restrict it: System Settings → General → Sharing → Remote Login → Only these users.
  2. Install the key:
    mkdir -p ~/.ssh && chmod 700 ~/.ssh
    printf '%s\n' 'restrict,port-forwarding,permitopen="DB_HOST:DB_PORT" <your-public-key>' \
      >> ~/.ssh/authorized_keys
    chmod 600 ~/.ssh/authorized_keys
  3. Check forwarding:
    sudo sshd -T | grep -i allowtcpforwarding    # expect: yes
A sleeping Mac is an unreachable bastion. While asleep sshd doesn't answer and our dial times out — which looks like a firewall problem. Overnight extracts are the usual casualty. Leave sudo caffeinate -s running, or disable sleep in System Settings → Energy Saver / Battery.

Windows has a native OpenSSH Server — an optional feature, already on the box.

  1. Install and start it (elevated PowerShell):
    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    Set-Service -Name sshd -StartupType Automatic
    Start-Service sshd
    Get-Service sshd | Select-Object Status, StartType   # expect: Running, Automatic
  2. Check which authorized_keys file sshd reads:
    Select-String -Path C:\ProgramData\ssh\sshd_config -Pattern '^\s*Match|AuthorizedKeysFile'
    The shipped config sends all administrators to one shared file:
    Match Group administrators
        AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
    So an admin user's key goes in C:\ProgramData\ssh\administrators_authorized_keys and C:\Users\<user>\.ssh\authorized_keys is ignored. A non-admin user uses the per-user file.
    Editing the wrong file gives a bare Permission denied (publickey) — no hint that the file you edited was never read.
  3. Create a non-admin user (recommended — keeps your key out of the shared admin file):
    $pw = Read-Host -AsSecureString "Password for elglide-tunnel"
    New-LocalUser -Name elglide-tunnel -Password $pw -PasswordNeverExpires -AccountNeverExpires
    # Do NOT add this user to Administrators.
    The password is never used — we authenticate with the key — but Windows requires one.
  4. Install the key with a strict ACL:
    $dir  = "C:\Users\elglide-tunnel\.ssh"
    $path = "$dir\authorized_keys"
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
    
    Set-Content -Path $path -Encoding ascii `
      -Value 'restrict,port-forwarding,permitopen="DB_HOST:DB_PORT" <your-public-key>'
    
    icacls $path /inheritance:r
    icacls $path /grant "elglide-tunnel:R"
    icacls $path /grant "SYSTEM:F"
    icacls $path /setowner elglide-tunnel
    C:\Users\elglide-tunnel won't exist until that account first logs in — creating the folder yourself, as above, is fine.
  5. Verify the ACL:
    Get-Content $path
    This command failing with Access … is denied from your admin shell is the correct result — it's what OpenSSH StrictModes requires. If it prints the key, re-run the icacls lines or sshd will reject it.
  6. Restart after any sshd_config edit:
    Restart-Service sshd
    Windows doesn't reload sshd_config on its own. authorized_keys is re-read per connection, so key changes need no restart.

If your tunnel user is an administrator, skip steps 3–5 and append the same line to C:\ProgramData\ssh\administrators_authorized_keys.

6. Test it yourself first

Before clicking Diagnose, reproduce what we do — an SSH session plus a local forward — from any machine that can reach the bastion. In one terminal:

ssh -N -L 15432:DB_HOST:DB_PORT BASTION_USER@BASTION_HOST -p BASTION_PORT

In a second, connect through it:

# PostgreSQL
psql -h 127.0.0.1 -p 15432 -U <db-user> -d <db-name> -c 'select 1'

# MySQL / MariaDB
mysql -h 127.0.0.1 -P 15432 -u <db-user> -p -e 'select 1'

# SQL Server
sqlcmd -S 127.0.0.1,15432 -U <db-user> -Q "select 1"
If select 1 returns, your bastion, firewall, AllowTcpForwarding, DB_HOST:DB_PORT and database credentials are all correct. You're authenticating as yourself here, so this doesn't test our key line — if this passes but Diagnose still fails on the SSH step, the problem is the key line, not your network.

7. Configure the source DB connection

From Connections (sign-in required) → Edit / Create. Pick SSH Tunnel in the Connectivity section and select the tunnel you just made. Click Diagnose to run the 4-step test (network → SSH → database → permissions).

8. Start, stop, revoke

There's no Elglide process to manage — only your SSH service.

PlatformStart / enable at bootStopStatus
Linux sudo systemctl enable --now ssh sudo systemctl stop ssh systemctl status ssh
macOS sudo systemsetup -setremotelogin on sudo systemsetup -setremotelogin off sudo systemsetup -getremotelogin
Windows Start-Service sshd Stop-Service sshd Get-Service sshd
To revoke our access, delete the elglide-tenant-<N> line — don't stop sshd. Removing the line takes effect on our next attempt, no restart needed. Stopping sshd is usually how you reach the machine too, and on a cloud host with no console it locks you out.

9. If your bastion's address changes

ngrok, Cloudflare Tunnel quick tunnels and similar free tiers hand out a new host and port on every restart, so the values you saved in step 3 go stale.

Symptom: it worked yesterday, and today Diagnose fails on network reachability only — the later steps never run.
Fix: Account → SSH Tunnels → pencil icon → update Bastion host and Port → Save → re-run Diagnose.

Don't rotate the key for this. The key is bound to your SSH server, not the address — it's still valid. Rotating breaks every Connection sharing this tunnel until you reinstall the new key on the bastion.
Past evaluation, give the bastion a stable DNS name or reserved address.

10. Replacing the public key (rotation)

We rotate keys periodically (or on demand) — Account → SSH Tunnels → the rotate icon. Choose Rotate the shared key to swap one key across every Connection that uses this tunnel, or Detach a specific source to give just one Connection its own fresh keypair while the others keep their existing one.

After clicking, we show you the new public key + the install line. Until you replace the old line in authorized_keys on the bastion (or the Windows equivalent from the Windows tab in step 5), every sync that uses this tunnel will fail with Permission denied (publickey).

Every key we issue ends with a comment of the form elglide-tenant-<N>, where <N> is your tenant id (visible in the portal). That comment is how you identify which line to replace.

Replace <bastion-user> with the user you created in step 5, <your-new-public-key> with the freshly shown key, and DB_HOST:DB_PORT with the value from step 4:

sudo -u <bastion-user> sed -i '/elglide-tenant-<N>/d' ~<bastion-user>/.ssh/authorized_keys
echo 'restrict,port-forwarding,permitopen="DB_HOST:DB_PORT" <your-new-public-key>' \
  | sudo -u <bastion-user> tee -a ~<bastion-user>/.ssh/authorized_keys
sudo -u <bastion-user> chmod 600 ~<bastion-user>/.ssh/authorized_keys

Then re-run the Diagnose modal on the Connection — the SSH step should turn green within seconds. No bastion restart needed; sshd re-reads authorized_keys on every connection.


Already set up?

Check your key line for permitlisten. If it's there, change it to permitopen. permitlisten only applies to ssh -R; we use ssh -L, so it does nothing — the key can currently forward anywhere your bastion can reach. Edit the elglide-tenant-<N> line in authorized_keys. No rotation, no downtime.

Troubleshooting

Diagnose step shows… Most likely cause Fix
✗ Network reachability — TCP refused Inbound firewall blocks our IPs Allow our static IPs on the bastion's SSH port
✗ Network reachability — timeout Wrong bastion hostname or no public DNS Use the bastion's public IP or a resolvable DNS name
✗ Network reachability — timeout, and it worked yesterday Bastion address changed (ngrok / Cloudflare free tier reassigns on restart) Update Bastion host and Port on the tunnel row (step 9). Don't rotate the key.
✗ Network reachability — timeout on a macOS bastion The Mac is asleep sudo caffeinate -s, or disable sleep
✗ SSH tunnel — Permission denied (publickey) Public key not installed, wrong username, or authorized_keys file permissions wrong Verify the user exists, the public key matches, and file perms are 600 (the dir 700)
✗ Port forwarding — every earlier step green AllowTcpForwarding no, or restrict without port-forwarding after it Set AllowTcpForwarding yes and restart sshd; confirm with sudo sshd -T | grep -i allowtcpforwarding
✗ SSH tunnel — administratively prohibited: open failed permitopen doesn't match the address we forward to — or the line still says permitlisten, which does nothing here Set permitopen="DB_HOST:DB_PORT" to exactly the step 4 value. db.internal:5432 and 10.0.1.7:5432 are different to OpenSSH.
✗ Database access — login failed DB credentials are wrong (everything before this passed, so the bastion is fine) Update the username/password on the Connection
✗ Permissions check — SELECT denied The DB user can connect but lacks SELECT on INFORMATION_SCHEMA / information_schema Grant SELECT on the metadata views (or use a role with schema-discovery privileges)
✗ SSH tunnel — Permission denied (publickey) after a rotation Old elglide-tenant-<N> line still in authorized_keys; sshd is matching the old key, not the new one Delete the old line; keep only the freshly-shown one. Step 10 above.
✗ SSH tunnel — Permission denied (publickey) on a Windows bastion Wrong authorized_keys file edited (per-user vs admin), OR the ACL still grants Administrators access Confirm which file your sshd reads (Windows tab, step 5), and verify Get-Content on it fails from an admin shell
Any step — a Windows sshd_config change had no effect Windows doesn't reload sshd_config automatically Restart-Service sshd

Still stuck? Open the failed step in the Diagnose modal and click Show details — the technical detail there includes the exact driver error, which is what to share when filing a support ticket.