SXAccessSXAccessDocs
Open dashboard

AWS EC2 and cloud VPCs

Reach a private VPC without a bastion, a VPN gateway or a public address, and survive the instance being replaced.

What you will achieve

Your team reaches databases and services inside a private VPC, from their laptops, with nothing exposed to the internet.

No bastion host to keep patched, no public address, and no inbound rule on the routing instance. By the end, a laptop in engineering opens an RDS database by name, even after the routing instance is replaced.

When to use this

Use this when what you need is inside a cloud network:

  • An RDS database developers open with a desktop client.
  • Private services with no public endpoint, reached today through a bastion.
  • An internal dashboard on an instance with no public address.
  • A VPC that people currently reach over a client VPN you pay for by the hour.

What you need

The rows marked Required are enough to reach one database.

WhatWhy
RequiredThe Owner or Admin role in the dashboardSteps 1 and 2 need it
RequiredThe AWS CLI (AWS CloudShell has it). Permission in AWS to create a secret, an IAM role, a security group, a launch template and an Auto Scaling group. Also permission to pass the role to EC2 (iam:PassRole), to store a new value in the secret (secretsmanager:PutSecretValue), to change the database's security group, to terminate an instance and to start a Session Manager sessionSteps 3 to 6, then Verify and Troubleshooting
RequiredPrivate subnets whose route table sends outbound traffic to a NAT gatewayThe agent only connects out. Nothing connects in
RequiredThe database's endpoint and port, such as an RDS writer endpoint on 5432It becomes a resource
RequiredA laptop with the SXAccess app or the CLI agent, signed inTo test from. See Which client do I need?
OptionalSSM Parameter Store instead of Secrets ManagerWorks the same; change the command that reads the key, and the role's permissions
AdvancedPrivate subnets in a second availability zoneRedundancy

Before you start

One instance speaks for the VPC.

How a private VPC is reachedA laptop connects over an encrypted tunnel to one small EC2 instance inside the VPC, which an Auto Scaling group replaces if it fails. That instance runs the agent and reaches RDS, caches and private hosts on the laptop’s behalf. The agent dials out, so the instance needs no inbound rule and no public address; only the database’s security group lets the instance in.AWS VPCNothing installedRDSElastiCachePrivate hostsLaptopSXAccess appEC2 instanceone small instance,replaced if it failsencrypted tunnelplain VPC trafficNo inbound rule on the instance, no public addressHow a private VPC is reachedA laptop connects over an encrypted tunnel to one small EC2 instance inside the VPC, which an Auto Scaling group replaces if it fails. That instance runs the agent and reaches RDS, caches and private hosts on the laptop’s behalf. The agent dials out, so the instance needs no inbound rule and no public address; only the database’s security group lets the instance in.AWS VPCNothing installedRDSElastiCachePrivate hostsLaptopSXAccess appEC2 instanceone small instance,replaced if it failsencrypted tunnelplain VPC trafficNo inbound rule on theinstance, no public address

The agent on the instance only connects out, and the tunnel is built from the inside. So the instance needs no public address and no inbound rule; only the database has to let the instance in.

Setup

  1. 1

    Give the tester the engineering group

    The rule in step 8 lets the group engineering in. Put the member who will test in it: People › Members › the member › Access. In Auto-assigned groups, type engineering, press Enter, then press Save Changes; the group is created when you save.

    With group propagation on, the default, this also moves a laptop the member has already signed in on: see Group propagation.

    You see: engineering on the member's Access tab.

  2. 2

    Create the enrollment key

    Access › Enrollment keys › Create enrollment key. Afterwards only its groups can change, so set it up for the Auto Scaling group now:

    • Name - vpc-routers.
    • Make this key reusable - on, for every instance the group launches.
    • Expires in - how long the launch template should work, such as 365 days; empty means never.
    • Usage limit - the enrolments you expect over the key's life, such as 50. Each new instance counts, and so does each one that enrols again after a long outage.
    • Temporary devices - on, so terminated instances leave Devices after 10 minutes offline. Step 5 brings back a removed instance that still runs.
    • Allow Extra DNS Labels - off.
    • Auto-assigned groups - type vpc-routers and press Enter. Step 9 attaches this group.

    Press Create enrollment key and copy the key now: it is not shown again.

  3. 3

    Store the key as a plaintext secret

    The block needs the AWS CLI; put your region in place of eu-central-1. Run the block in bash or zsh (macOS, Linux, WSL or AWS CloudShell). Paste the whole block and press Enter once. When Enrollment key: appears, paste the key, and only the key, and press Enter. The key is not shown, and an empty line asks again.

    The block stores the key as plain text; a secret made of key/value pairs would hand the instance JSON instead of a key.

    You see: the secret's ARN, for step 4.

    shell
    {  KEY=''  until [ -n "$KEY" ]; do printf 'Enrollment key: '; read -rs KEY || break; echo; done  [ -n "$KEY" ] && aws secretsmanager create-secret --region eu-central-1 \    --name sxaccess/enrollment-key --secret-string "$KEY" --query ARN --output text  unset KEY}
  4. 4

    Create the instance role

    Create an IAM role for EC2 with the permissions below, with the ARN from step 3 in Resource. The instance can then read this one secret and no other.

    Also attach AmazonSSMManagedInstanceCore, which AWS provides. It gives you a shell on the instance through Session Manager, with no public address; troubleshooting uses it.

    shell
    {  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": "secretsmanager:GetSecretValue",      "Resource": "arn:aws:secretsmanager:eu-central-1:111122223333:secret:sxaccess/enrollment-key-AbCdEf"    }  ]}
  5. 5

    Launch the routing instance from an Auto Scaling group

    Create a launch template, then an Auto Scaling group that uses it:

    • Launch template - Amazon Linux 2023, which has the AWS CLI, on a small instance type. Choose the role from step 4 as IAM instance profile, no public IP address and a new security group without inbound rules, and paste the script below as user data.
    • Auto Scaling group - the VPC's private subnets; desired, minimum and maximum capacity 1. It replaces an instance that fails its health check or is terminated.

    The script installs the agent and a systemd timer that runs an enrol script now, a minute after each boot and every 5 minutes. The enrol script does nothing while the instance is connected. If SXAccess removed the instance after more than 10 minutes offline, it enrols it again: EC2 would still call that instance healthy and never replace it.

    The timer also connects the instance again within 5 minutes of a manual sxaccess stop. For maintenance, stop the timer first with sudo systemctl disable --now sxaccess-enrol.timer, and turn it back on afterwards with sudo systemctl enable --now sxaccess-enrol.timer.

    You see: within a few minutes, a device such as vpc-router-ip-10-0-1-23 under Devices, in vpc-routers.

    shell
    #!/bin/sh# EC2 user data. Runs once, at first boot.set -eucurl -fsSL https://pkgs.sxaccess.com/install.sh | sh# The enrol script. Use your region.cat > /usr/local/sbin/sxaccess-enrol <<'EOF'#!/bin/shset -eusxaccess info | grep -qx Connected && exit 0KEY=$(aws secretsmanager get-secret-value --region eu-central-1 \  --secret-id sxaccess/enrollment-key --query SecretString --output text)SX_ENROLLMENT_KEY="$KEY" sxaccess start --hostname "vpc-router-$(hostname -s)"EOFchmod 700 /usr/local/sbin/sxaccess-enrol# Run it now, 1 minute after every boot, and every 5 minutes.cat > /etc/systemd/system/sxaccess-enrol.service <<'EOF'[Service]Type=oneshotExecStart=/usr/local/sbin/sxaccess-enrolEOFcat > /etc/systemd/system/sxaccess-enrol.timer <<'EOF'[Timer]OnBootSec=1minOnUnitActiveSec=5min[Install]WantedBy=timers.targetEOFsystemctl daemon-reloadsystemctl enable --now sxaccess-enrol.timersystemctl start sxaccess-enrol.service
  6. 6

    Let the routing instance into the database

    With Masquerade on (step 9), the database sees the routing instance's own address. On the database's security group, add an inbound rule for TCP 5432 from the instance's security group of step 5. It covers every replacement too, and it is the only inbound rule this setup needs.

  7. 7

    Create the network and the resource

    Networks › Internal networks › Add internal network. Set Network Name to the VPC and region, such as prod-vpc eu-central-1, and press Add Network. Add Resource opens next:

    • Name - orders-db, the name laptops see, for example in sxaccess reach list.
    • Address - the writer endpoint, such as my-db.cluster-abc123.eu-central-1.rds.amazonaws.com. As a domain, it follows the address when AWS changes it, and the routing instance resolves it.
    • Destination Groups - type vpc-databases and press Enter; a resource needs a group.

    Press Add Resource. Keep wildcards to your own endpoints, such as *.cluster-abc123.eu-central-1.rds.amazonaws.com; *.rds.amazonaws.com would route every RDS name a laptop looks up through your VPC.

  8. 8

    Let engineering reach the database

    The access rule form opens next, with orders-db already filled in as the destination.

    Allow engineering to reach orders-db over TCP on ports 5432.

    • Access rule name - replace the suggested orders-db access rule with the intent, such as Engineering reaches the orders database.
    • Allow - pick engineering.
    • over - choose TCP. A new rule starts at any protocol, which opens every port.
    • on ports - click the field, type 5432 and press Enter: the field shows 5432 instead of All. Left empty, it opens every port.

    Direction stays locked to One-way. Press Create access rule.

  9. 9

    Attach the routing instances as a group

    Add Routing Device opens last. Switch to Device group, pick vpc-routers and press Continue. The group takes in every instance the key enrols, replacements included; an entry naming one instance would stop working when it goes.

    Under Settings, keep Enable Routing Device and Masquerade on and Metric at 9999, and press Add Routing Device. With Masquerade on, the route tables and the source/destination check stay as they are. The database is now reachable.

    For each further database, open the network by its name under Networks › Internal networks and press Add Resource. Then press Add Access rule in that resource's Access rules column, fill it in as in step 8 and press Create access rule. Let the instance in as in step 6.

The Edit Resource panel: Name, Description, an Address field that takes a single IP, a CIDR block or a domain name, Destination Groups, and Enable Resource.
The resource form, here opened to edit an existing resource. A managed endpoint goes in as a domain, so it follows the address when AWS changes it.

Verify

Test from the tester's laptop, by endpoint name.

Expected result

The SXAccess app lists orders-db under Resources, and the database accepts a connection on 5432, for example from psql with the same endpoint it would use inside the VPC. The command below for your system checks the port: it must succeed, which PowerShell shows as TcpTestSucceeded : True.

With the CLI agent, sxaccess reach list shows orders-db with state: Selected, and sxaccess info -d lists a vpc-router device whose Reaches row names the endpoint.

Then check that the rule is what lets you in

A closed port proves nothing here, because the database's security group drops other ports anyway. Switch the rule off instead, in its Status column under Access › Access rules. Within seconds the laptop loses orders-db: sxaccess reach list prints There are no networks to show. and the connection fails. Switch the rule back on.

Then prove it survives a replacement

Terminate the routing instance in the EC2 console. The Auto Scaling group launches a new one, which enrols and joins vpc-routers, and access returns by itself within a few minutes. The old device leaves Devices about 10 minutes later.

shell - check from the laptop, on macOS
nc -vz -G 5 my-db.cluster-abc123.eu-central-1.rds.amazonaws.com 5432
shell - check from the laptop, on Linux
nc -vz -w 5 my-db.cluster-abc123.eu-central-1.rds.amazonaws.com 5432
PowerShell - check from the laptop, on Windows
Test-NetConnection my-db.cluster-abc123.eu-central-1.rds.amazonaws.com -Port 5432

Troubleshooting

The first four are specific to AWS and catch most of it.

SymptomLikely causeWhat to do
The instance never appears under DevicesNo outbound path, such as a private subnet without a NAT gateway. Or the instance could not read the key: check the role, the region and the secret's name.Fix the subnet's default route; add no inbound rule. In a Session Manager shell on the instance, read /var/log/cloud-init-output.log and journalctl -u sxaccess-enrol
Reaches the instance, not the databaseThe database's security group does not let the instance in, or IP forwarding was switched off after the agent turned it on.Step 6. In a Session Manager shell, sysctl net.ipv4.ip_forward must print 1
A domain resource does not resolveThe instance cannot resolve the endpoint, for example because the VPC has DNS resolution or DNS hostnames turned off.In a Session Manager shell, getent hosts <endpoint> must print a private address
New instances stop enrolling, or one stays offline after an outageThe key expired or used up its Usage limit, so a removed instance cannot enrol again. sxaccess info on it prints This device is not signed in (NeedsLogin).Replace the key as in Replacing the enrollment key. A key's settings cannot be edited
It worked, then stopped after a deployThe routing device entry names one instance, or the image carried enrolled state, so two machines claim one identity.Attach the group as a Device group (step 9). Never bake /var/lib/sxaccess/ into an AMI
Works from one place, not from anotherThe local network there uses addresses inside the VPC range, and the local network wins. Publishing single endpoints does not avoid this.Give new VPCs a range those networks do not use
Terminated instances pile up under DevicesThe key was created without Temporary devices, which cannot be switched on later.Replace the key with one that has it on, as in Replacing the enrollment key
Still stuck?Take the connection apart one stage at a time.Trace why a connection is blocked

Replacing the enrollment key

Create the new key as in step 2, and put your region in place of eu-central-1 in the block below. Run the block in bash or zsh (macOS, Linux, WSL or AWS CloudShell). Paste the whole block and press Enter once. When Enrollment key: appears, paste the key, and only the key, and press Enter. The key is not shown, and an empty line asks again.

You see: the ID of the secret's new version. Instances read the secret each time they enrol, so the next one uses the new key.

Then revoke the old key: under Access › Enrollment keys, choose Revoke in its row menu and confirm with Revoke key.

shell - where you ran step 3
{  KEY=''  until [ -n "$KEY" ]; do printf 'Enrollment key: '; read -rs KEY || break; echo; done  [ -n "$KEY" ] && aws secretsmanager put-secret-value --region eu-central-1 \    --secret-id sxaccess/enrollment-key --secret-string "$KEY" --query VersionId --output text  unset KEY}

Advanced configuration

Surviving an availability zone

Give the Auto Scaling group private subnets in two zones and a capacity of 2. All instances in the vpc-routers entry share one Metric; clients prefer one they are connected to and move on when it stops answering.

To prefer one zone, give each zone its own key, group and launch template, and attach each group as its own entry. Then give the preferred entry a Metric below 9999.

An instance with an Elastic IP

The agent already learns the public address it is seen from, through STUN, and offers it to other devices as a connection candidate. That covers most instances with an Elastic IP. If a direct path still does not form, sxaccess start has a support-level option, --external-ip-map (see the command reference), that announces the public address explicitly; use it together with support.

Publishing the VPC CIDR after all

Sometimes there are too many endpoints to name. Then add the VPC range as one resource, check it first against the networks people work from, and remember it grants everything in the VPC, including what is created next month.

Other clouds

Azure and GCP work the same way: one small instance inside the network, outbound only, endpoints published as domain resources.

Related