You’re still running a bastion host, aren’t you? That t3.micro sitting in a public subnet, port 22 open to… well, hopefully not 0.0.0.0/0, but let’s be honest — it’s probably close.

Stop it. AWS Systems Manager Session Manager exists, and it’s better in every way.

The Bastion Problem

Bastion hosts have been the standard for decades. Jump box in a public subnet, SSH through it to reach private instances. Simple enough.

But they come with baggage:

  • Another instance to patch — one more thing to maintain, update, secure
  • SSH keys to manage — who has access? Did you rotate them after Dave left?
  • Open ports — even locked down to your office IP, it’s attack surface
  • Logging gaps — you hope someone configured audit logs
  • Cost — small, but nonzero

Session Manager: The Better Way

SSM Session Manager flips the model. Instead of you connecting in, the agent connects out:

YourlaptopAWSConsole/CLISSMServiceSSMAgentoninstance

No inbound ports. No SSH keys. No bastion.

Setup (It’s Embarrassingly Easy)

  1. IAM role for your instances:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:UpdateInstanceInformation",
        "ssmmessages:CreateControlChannel",
        "ssmmessages:CreateDataChannel",
        "ssmmessages:OpenControlChannel",
        "ssmmessages:OpenDataChannel"
      ],
      "Resource": "*"
    }
  ]
}

Or just attach AmazonSSMManagedInstanceCore managed policy.

  1. SSM Agent installed — Amazon Linux 2/2023 and Ubuntu 18.04+ have it pre-installed. If not:
1
2
sudo yum install -y amazon-ssm-agent  # Amazon Linux
sudo snap install amazon-ssm-agent --classic  # Ubuntu
  1. Outbound HTTPS (443) — your instances need to reach SSM endpoints. If you’re in a locked-down VPC, add VPC endpoints for:
    • ssm
    • ssmmessages
    • ec2messages

That’s it. No security groups to punch holes in. No elastic IPs to assign.

Connect

Console:

EC2SelectinstanceConnectSessionManagerConnect

CLI:

1
aws ssm start-session --target i-1234567890abcdef0

SSH-style (with the plugin):

1
2
# Install Session Manager plugin first
ssh -o ProxyCommand="aws ssm start-session --target %h --document-name AWS-StartSSHSession" ec2-user@i-1234567890abcdef0

Why This is Actually Better

1. No Open Ports

Your instances can live in a fully private subnet with no inbound rules. Zero. None. The SSM agent initiates the connection outbound.

2. IAM-Based Access Control

Forget SSH key management. Access is controlled via IAM:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "Effect": "Allow",
  "Action": "ssm:StartSession",
  "Resource": "arn:aws:ec2:us-east-1:123456789012:instance/i-abc123",
  "Condition": {
    "StringLike": {
      "ssm:resourceTag/Environment": "dev"
    }
  }
}

Grant access by tag, instance ID, or whatever. Revoke by updating IAM — instant, no key rotation needed.

3. Full Audit Logging

Every session is logged to CloudWatch and/or S3. Every keystroke if you want:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
aws ssm update-document \
  --name "SSM-SessionManagerRunShell" \
  --document-version '$LATEST' \
  --content '{
    "schemaVersion": "1.0",
    "description": "Session with logging",
    "sessionType": "Standard_Stream",
    "inputs": {
      "cloudWatchLogGroupName": "/aws/ssm/sessions",
      "cloudWatchEncryptionEnabled": true,
      "s3BucketName": "my-session-logs",
      "s3EncryptionEnabled": true
    }
  }'

When security asks “who accessed what when” — you have receipts.

4. No Instance Metadata Required

You’re not storing SSH private keys on laptops, in repos, or in Slack DMs. There’s nothing to leak.

5. Works With Private Instances

The whole point. Your database server in a private subnet with no internet gateway? SSM reaches it through VPC endpoints. No NAT gateway needed for management traffic.

The Port Forwarding Trick

Need to access a service running on a private instance? RDS admin console, debugging a web server, whatever?

1
2
3
4
aws ssm start-session \
  --target i-1234567890abcdef0 \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["5432"],"localPortNumber":["5432"]}'

Now localhost:5432 tunnels to that instance’s PostgreSQL. No SSH tunnels, no bastion chains.

Remote host forwarding (access RDS through instance):

1
2
3
4
aws ssm start-session \
  --target i-1234567890abcdef0 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"host":["mydb.cluster-abc123.us-east-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5432"]}'

Your laptop → SSM → EC2 instance → RDS. Fully private path.

Cost

Session Manager itself is free. You pay only for:

  • VPC endpoints (~$7/month per endpoint per AZ)
  • CloudWatch Logs storage (if logging sessions)
  • S3 storage (if archiving)

Compare to a bastion host:

  • t3.micro: ~$7.50/month
  • EBS volume: ~$1/month
  • Elastic IP (if unused hours): variable
  • Your time managing it: priceless

Migration Checklist

  1. ✅ Add IAM role to all instances (or update existing roles)
  2. ✅ Verify SSM agent is running (systemctl status amazon-ssm-agent)
  3. ✅ Add VPC endpoints if instances lack internet access
  4. ✅ Update IAM policies for users/roles that need access
  5. ✅ Enable session logging to CloudWatch/S3
  6. ✅ Test access to critical instances
  7. ✅ Remove inbound SSH rules from security groups
  8. ✅ Terminate bastion host
  9. ✅ Delete the SSH keys from your laptop and feel the freedom

The Objections

“But I need SCP for file transfer!”

Use SSM with AWS-StartPortForwardingSession and SCP over the tunnel, or just use S3 as an intermediary. Or aws s3 cp from the instance directly.

“What if SSM is down?”

AWS has had like three SSM outages in its history. Your bastion’s uptime isn’t better. And when SSM is down, you’re probably having bigger problems.

“We need to connect from on-prem.”

Hybrid activations let you register on-prem servers with SSM. Or use AWS VPN/Direct Connect and reach VPC endpoints.


Your bastion host is a liability disguised as infrastructure. It’s another thing to patch, another attack surface, another SSH key to manage.

Kill it. Session Manager is free, more secure, and actually auditable.

Your security team will thank you. Your ops team will thank you. Dave from accounting who still has the SSH key from 2019 will finally stop being a risk.

🌍