Ditch SSH: Secure EC2 Access with AWS Systems Manager Session Manager

Opening port 22 on an EC2 instance is one of the most common attack vectors in cloud environments — bots continuously scan for exposed SSH ports, and a single misconfigured security group can become a critical breach point. AWS Systems Manager Session Manager eliminates this risk entirely by providing browser-based and CLI shell access to EC2 instances with zero open inbound ports, zero SSH keys, and full audit logging.

TL;DR

ConcernTraditional SSHSSM Session Manager
Inbound port requiredPort 22 openNo inbound ports needed
AuthenticationSSH key pairsIAM identity & policies
Key managementManual (rotate, distribute)None — IAM handles it
Audit trailNone by defaultFull session logs via CloudWatch / S3
Access from browserNot supported nativelyNative AWS Console support
Bastion host neededOften yesNo

How It Works: The Architecture

Session Manager works through the SSM Agent installed on your EC2 instance. The agent initiates an outbound HTTPS connection (port 443) to the AWS Systems Manager service endpoint — meaning your instance calls out, not in. Your browser or CLI connects to SSM, and SSM proxies the session. No inbound firewall rules are required.

graph LR User(["👤 Operator Browser or CLI"]) -->|"IAM Auth"| SSM["AWS Systems Manager Session Manager"] SSM -->|"Proxied Shell Session"| Agent["SSM Agent on EC2"] Agent -->|"Outbound HTTPS 443 No inbound port 22"| SSM EC2["EC2 Instance Security Group: No port 22"] --- Agent SSM -->|"Session Logs"| CW["CloudWatch Logs"] SSM -->|"Session Logs"| S3["S3 Bucket"] SSM -->|"API Audit"| CT["CloudTrail"]
  1. User (Browser/CLI): Initiates a session via the AWS Console or aws ssm start-session CLI command, authenticated via IAM.
  2. AWS Systems Manager: Validates the IAM principal's permissions against the target instance resource.
  3. SSM Agent (on EC2): The agent, running on the instance, maintains a persistent outbound WebSocket connection over HTTPS (port 443) to the SSM service endpoint. SSM proxies the shell session through this channel.
  4. No inbound traffic: The EC2 security group requires no inbound rules — not even port 22.
  5. Session logs: All session activity is optionally streamed to CloudWatch Logs or an S3 bucket for compliance and auditing.
Analogy: Think of Session Manager like a corporate VPN reverse tunnel. Instead of the office (EC2) opening its front door (port 22) to the internet, the employee inside (SSM Agent) calls the corporate switchboard (SSM service) and says "I'm ready." When you need access, the switchboard connects you through that existing internal call — the front door never opens.

Prerequisites

Before you can use Session Manager, three conditions must be met on your EC2 instance:

  1. SSM Agent installed and running — Pre-installed on Amazon Linux 2, Amazon Linux 2023, and recent Windows Server AMIs. May need manual installation on Ubuntu or custom AMIs.
  2. IAM Instance Profile with SSM permissions — The EC2 instance must have an attached IAM role with the AmazonSSMManagedInstanceCore managed policy.
  3. Network connectivity to SSM endpoints — Either via a public NAT Gateway/Internet Gateway, or via VPC Interface Endpoints for fully private connectivity.

Step 1: Create the IAM Role for Your EC2 Instance

The instance needs an IAM role that allows it to communicate with the SSM service. The AWS-managed policy AmazonSSMManagedInstanceCore grants the minimum required permissions.

🔽 [Click to expand] — IAM Trust Policy & Role (AWS CLI)
# 1. Create the trust policy document
cat > ec2-trust-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# 2. Create the IAM role
aws iam create-role \
  --role-name EC2-SSM-SessionManager-Role \
  --assume-role-policy-document file://ec2-trust-policy.json

# 3. Attach the AWS managed policy for SSM core functionality
aws iam attach-role-policy \
  --role-name EC2-SSM-SessionManager-Role \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

# 4. Create an instance profile and attach the role
aws iam create-instance-profile \
  --instance-profile-name EC2-SSM-InstanceProfile

aws iam add-role-to-instance-profile \
  --instance-profile-name EC2-SSM-InstanceProfile \
  --role-name EC2-SSM-SessionManager-Role

# 5. Attach the instance profile to your EC2 instance
aws ec2 associate-iam-instance-profile \
  --instance-id i-0123456789abcdef0 \
  --iam-instance-profile Name=EC2-SSM-InstanceProfile

Step 2: Verify the Security Group (No Port 22 Needed)

Your EC2 security group should have no inbound rules for SSH. The only network requirement is that the instance can reach SSM service endpoints outbound on port 443.

# Confirm no port 22 inbound rule exists — this should return empty
aws ec2 describe-security-groups \
  --group-ids sg-0123456789abcdef0 \
  --query "SecurityGroups[0].IpPermissions[?FromPort==\`22\`]"

If you are in a private subnet with no internet access, you must create VPC Interface Endpoints for the following services in your VPC:

  • com.amazonaws.us-east-1.ssm
  • com.amazonaws.us-east-1.ssmmessages
  • com.amazonaws.us-east-1.ec2messages

Step 3: Grant IAM Users/Roles Permission to Start Sessions

The operator (the human or role initiating the session) also needs IAM permissions. Apply least privilege by restricting access to specific instance IDs.

🔽 [Click to expand] — Least-Privilege IAM Policy for Session Operator
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSSMSessionStart",
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession",
        "ssm:DescribeSessions",
        "ssm:GetConnectionStatus",
        "ssm:DescribeInstanceProperties",
        "ssm:DescribeInstanceInformation"
      ],
      "Resource": [
        "arn:aws:ec2:us-east-1:123456789012:instance/i-0123456789abcdef0",
        "arn:aws:ssm:us-east-1:123456789012:session/${aws:username}-*"
      ]
    },
    {
      "Sid": "AllowSessionTermination",
      "Effect": "Allow",
      "Action": "ssm:TerminateSession",
      "Resource": "arn:aws:ssm:us-east-1:123456789012:session/${aws:username}-*"
    }
  ]
}

Step 4: Connect — Browser & CLI

Option A: AWS Management Console (Browser)

  1. Navigate to AWS Systems Manager → Session Manager → Start Session.
  2. Select your target EC2 instance from the list (it will appear only if the SSM Agent is running and the instance profile is attached).
  3. Click Start Session. A browser-based terminal opens immediately.

Option B: AWS CLI

# Requires the Session Manager plugin for the AWS CLI
# Install: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

aws ssm start-session \
  --target i-0123456789abcdef0 \
  --region us-east-1

Option C: SSH Tunneling via Session Manager (Port Forwarding)

You can even tunnel SSH or local ports through Session Manager without opening port 22 — useful for tools that require native SSH.

# ~/.ssh/config entry to proxy SSH through SSM
Host i-* mi-*
  ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
  User ec2-user
  IdentityFile ~/.ssh/your-key.pem

Note: This SSH-over-SSM approach still requires the key file locally, but the EC2 security group still needs no inbound port 22 — the tunnel is proxied through SSM's outbound channel.

Step 5: Enable Session Logging for Compliance

Session Manager can log all session activity. Configure this in Systems Manager → Session Manager → Preferences.

sequenceDiagram participant Op as Operator participant SSM as SSM Service participant EC2 as EC2 SSM Agent participant CW as CloudWatch Logs participant S3 as S3 Bucket participant CT as CloudTrail Op->>SSM: StartSession API call CT-->>CT: Records StartSession event SSM->>EC2: Route session via outbound channel EC2-->>SSM: Shell I/O stream SSM-->>Op: Interactive terminal SSM->>CW: Stream session logs SSM->>S3: Archive session logs Op->>SSM: TerminateSession CT-->>CT: Records TerminateSession event
  1. Session starts via Console or CLI.
  2. All keystrokes and output are captured by the SSM service.
  3. Logs are streamed to CloudWatch Logs for real-time querying, and/or archived to an S3 bucket for long-term retention.
  4. CloudTrail records the ssm:StartSession API call, providing a full identity-level audit trail (who started the session, when, from which IP).
🔽 [Click to expand] — S3 Bucket Policy for Session Logs (Least Privilege)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SSMSessionManagerLogs",
      "Effect": "Allow",
      "Principal": {
        "Service": "ssm.amazonaws.com"
      },
      "Action": [
        "s3:PutObject",
        "s3:GetEncryptionConfiguration"
      ],
      "Resource": [
        "arn:aws:s3:::your-session-logs-bucket",
        "arn:aws:s3:::your-session-logs-bucket/*"
      ]
    }
  ]
}

Troubleshooting: Instance Not Appearing in Session Manager

SymptomRoot CauseFix
Instance not listedSSM Agent not runningRun sudo systemctl status amazon-ssm-agent and start it
Instance not listedNo IAM instance profile attachedAttach the role with AmazonSSMManagedInstanceCore
Instance not listedNo outbound HTTPS to SSM endpointsAdd NAT Gateway or create VPC Interface Endpoints
Session fails to startOperator lacks IAM permissionsAttach ssm:StartSession policy to the operator's role

Glossary

TermDefinition
SSM AgentA lightweight agent pre-installed on many AWS AMIs that communicates with the Systems Manager service over HTTPS.
Instance ProfileAn IAM container that passes an IAM role's credentials to an EC2 instance at launch.
VPC Interface EndpointAn AWS PrivateLink-powered endpoint that allows private subnet resources to reach AWS services without traversing the public internet.
Session DocumentAn SSM document (e.g., AWS-StartInteractiveCommand) that defines the type of session to initiate on the target instance.
AmazonSSMManagedInstanceCoreAn AWS-managed IAM policy granting the minimum permissions an EC2 instance needs to be managed by Systems Manager.

Next Steps

  • 📖 Official AWS Session Manager Documentation
  • 🔒 Enforce Session Manager as the only access method by using an SCP (Service Control Policy) to deny ec2:AuthorizeSecurityGroupIngress for port 22 across your AWS Organization.
  • 📊 Set up CloudWatch Alarms on Session Manager session start events via CloudTrail → EventBridge for real-time alerting on instance access.

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It