Skip to content

How to set up SSH bastion access into a private subnet

How-to · Updated Jun 2026
Before this

How to set up SSH bastion access into a private subnet

Reach instances on a private subnet through a single hardened jump host, so the private instances never need a public address.

In this guide you will:

  1. Create two security groups: one for the bastion, one for the private instances
  2. Launch the bastion on the private network and give it the single floating IP this pattern needs
  3. Launch private instances with no floating IP
  4. Configure SSH ProxyJump on your workstation to reach the private instances through the bastion
  5. Verify the connection and tear the pattern down
Prerequisites

Windows: CLI examples use bash. Set up a Linux CLI environment on Windows before proceeding.

  • An SSH key pair uploaded to your account
  • A private network, subnet, and router already in place. Follow How to create a VM on a private network to build the network, subnet, and router used below (my-private-net, my-private-subnet, attached to PublicStatic)

Step 1. Create the security groups#

Create two security groups. The bastion group allows SSH only from your administrators' source addresses. The private group allows SSH only from the bastion group, so the private instances accept connections from the jump host and nothing else.

Replace YOUR_ADMIN_CIDR with the public address range your administrators connect from, for example 203.0.113.10/32 for a single static address.

Step 2. Launch the bastion and assign one floating IP#

Launch a small instance on the private network with the bastion-sg security group, then associate the single floating IP. A shared flavor such as s1a.small is enough: the bastion forwards connections, it does not run application load.

Step 3. Launch the private instances#

Launch each application instance on the same private network with the private-sg security group and no floating IP. With no floating IP and a security group that only admits the bastion, these instances are reachable from the bastion and not from the public internet.

Step 4. Configure SSH ProxyJump on your workstation#

ProxyJump tells your SSH client to open a connection to the bastion first, then tunnel through it to the private instance in a single command. Your private key stays on your workstation: the bastion forwards an encrypted channel and never sees the key material.

Add a block to your ~/.ssh/config, replacing the floating IP and private address with the values from Steps 2 and 3:

Host bastion
    HostName FLOATING_IP_ADDRESS
    User ubuntu
    IdentityFile ~/.ssh/my-keypair

Host app-1
    HostName 192.168.200.0
    User ubuntu
    IdentityFile ~/.ssh/my-keypair
    ProxyJump bastion

Set HostName for app-1 to the private address that openstack server show app-1 reported. With this config, one command reaches the private instance through the bastion:

bash
ssh app-1

To connect without editing ~/.ssh/config, pass the jump host inline with -J:

bash
ssh -J ubuntu@FLOATING_IP_ADDRESS [email protected]

Verify the connection#

Connect to the private instance through the bastion:

bash
ssh app-1

On first connection your SSH client prompts you to accept the host key for both the bastion and the private instance. Type yes for each. Your prompt changes to ubuntu@app-1, which confirms the session terminated on the private instance after transiting the bastion.

Confirm the private instance is not reachable directly. A connection straight to its private address from your workstation, without the jump, times out because no floating IP and no public route exist:

bash
ssh -o ConnectTimeout=5 [email protected]

Type logout to end the session.

Harden the bastion#

The bastion is the one host in this pattern exposed to the internet, so treat it as a security boundary. Beyond the full security hardening checklist, apply these bastion-specific controls:

  • Keep bastion-sg scoped to your administrators' source range. Do not widen port 22 to 0.0.0.0/0.
  • Disable password authentication and root login in /etc/ssh/sshd_config (PasswordAuthentication no, PermitRootLogin no), then reload sshd.
  • Restrict who can log in with AllowUsers or AllowGroups so only operator accounts reach the bastion.
  • Apply operating system security updates on a schedule. A jump host that lags on patches becomes the weakest link.
  • Run nothing else on the bastion. A single-purpose host has a smaller attack surface than one that also serves an application.

Tear down#

Remove the pattern in reverse order. The single floating IP is released in the second step.

Leave the private network, subnet, and router in place if other workloads use them. To remove them as well, follow the teardown steps in How to create a VM on a private network.

See also#

Usage Guidelines

The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.

For the full policy, see Usage Guidelines.

Last validated: 25.06.2026

Was this page helpful?