How to set up SSH bastion access into a private subnet
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:
- Create two security groups: one for the bastion, one for the private instances
- Launch the bastion on the private network and give it the single floating IP this pattern needs
- Launch private instances with no floating IP
- Configure SSH
ProxyJumpon your workstation to reach the private instances through the bastion - Verify the connection and tear the pattern down
Prerequisites
- ConsoleLogged in to the Quake AI console
- CLIOpenStack CLI installed and authenticated (
clouds.yamloropenrcsourced)
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 toPublicStatic)
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 bastionSet 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:
ssh app-1To connect without editing ~/.ssh/config, pass the jump host inline with -J:
ssh -J ubuntu@FLOATING_IP_ADDRESS [email protected]Verify the connection#
Connect to the private instance through the bastion:
ssh app-1On 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:
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-sgscoped to your administrators' source range. Do not widen port 22 to0.0.0.0/0. - Disable password authentication and root login in
/etc/ssh/sshd_config(PasswordAuthentication no,PermitRootLogin no), then reloadsshd. - Restrict who can log in with
AllowUsersorAllowGroupsso 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
See Also
Networks
Prerequisite
How to deploy a multi-tier application with Terraform
Shares: Security, Floating IPs
How to front a Quake AI workload with a web application firewall
Shares: Security, Floating IPs
How to deploy VMs with Terraform (simple example)
Shares: Security, Networks
How to Create a Virtual Machine Instance
Shares: Security, Networks