AWS Systems Manager β Session Manager
AWS Systems Manager Session Manager (SSM) is a native AWS service that allows access to EC2 instances without opening network ports, without managing SSH keys, and with full auditing of every session. It replaces the traditional bastion host model with a more secure, auditable approach at no additional infrastructure cost.
The Bastion Host Problemβ
The traditional server access model on AWS works like this: you create an EC2 instance called a bastion host (or jump box) in a public subnet, open port 22 (SSH) in the security group, and distribute SSH keys to engineers. To access any machine in the private subnet, the engineer first connects to the bastion and from there makes a second SSH connection to the target server.
This model works, but creates several operational and security problems:
- Exposed attack surface: The bastion host needs to be accessible from the internet (port 22 open). Any vulnerability in it compromises the entire internal network.
- SSH key management: Distributing, rotating, and revoking SSH keys for dozens or hundreds of engineers is an operational nightmare.
- Lack of granular auditing: Knowing who accessed which server and what they did during the session requires additional logging configurations.
- Operational cost: The bastion host needs to be maintained, updated, and monitored β it's another server to manage.
- Standing access: Once the engineer has the SSH key, they have continuous access until the key is manually revoked.
How SSM Worksβ
SSM works through an agent installed on the EC2 instance (the SSM Agent) that communicates with the AWS service via HTTPS. Instead of opening a direct SSH connection, the engineer starts a session via the AWS console, CLI, or SDK, and the SSM Agent on the instance creates a secure communication channel.
The flow in practice:
- The engineer runs
aws ssm start-session --target i-0abc123def(or clicks "Connect" in the console). - The SSM service verifies whether the IAM user has permission to start sessions on that instance.
- The SSM Agent on the EC2 receives the instruction and opens a secure channel.
- The engineer receives an interactive shell on the machine β no SSH, no key, no open port.
- The entire session can be automatically recorded in S3 or CloudWatch Logs.
Prerequisitesβ
For SSM to work on an EC2 instance, three components are required:
1. SSM Agent Installed and Activeβ
The SSM Agent is the software that runs inside the EC2 and communicates with the AWS Systems Manager service. It's responsible for receiving commands, starting sessions, and reporting instance status.
- Amazon Linux 2/2023, Ubuntu 20.04+: The SSM Agent comes pre-installed by default. In most cases, no action is needed.
- Other AMIs: Manual installation may be required via
yum install amazon-ssm-agentorsnap install amazon-ssm-agent. - Check the status with:
systemctl status amazon-ssm-agent.
2. IAM Role on the Instance (Instance Profile)β
An EC2 instance cannot have an IAM role directly associated with it. For the instance to assume a role, AWS uses an intermediary resource called Instance Profile. The Instance Profile is a "container" that encapsulates an IAM role and allows the EC2 to assume that identity. It's what appears in the instance launch configuration (in the "IAM Instance Profile" field). When you associate an Instance Profile with an EC2, any process running inside the instance (including the SSM Agent) can automatically assume the role and obtain temporary credentials via the metadata service, without needing hardcoded access keys.
In the AWS console, when creating or editing an EC2, the field appears as "IAM role", but under the hood, AWS is creating and associating an Instance Profile automatically. Via CLI or Terraform, the distinction is more visible: you create the role, create the Instance Profile, associate the role with the Instance Profile, and then associate the Instance Profile with the instance.
The EC2 instance needs an IAM role (via Instance Profile) that allows the SSM Agent to communicate with the AWS service. The managed policy AmazonSSMManagedInstanceCore contains all necessary permissions.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:UpdateInstanceInformation",
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
}
In practice, just attach the managed policy
AmazonSSMManagedInstanceCoreto the instance role. There's no need to create a custom policy, as the managed policy already includes all permissions listed above and is maintained by AWS.
3. Connectivity to the SSM Serviceβ
The SSM Agent needs to reach the Systems Manager service endpoints via HTTPS (port 443). There are two ways:
| Method | When to use |
|---|---|
| Internet (NAT Gateway) | The private subnet has internet access via NAT. The Agent communicates directly with SSM's public endpoints. |
| VPC Endpoints (PrivateLink) | The subnet has no internet access. You create VPC endpoints for ssm, ssmmessages, and ec2messages, and traffic stays within the AWS network. |
For environments requiring zero internet access on instances, VPC Endpoints are mandatory. You'll need three Interface Endpoints:
com.amazonaws.<region>.ssmcom.amazonaws.<region>.ssmmessagescom.amazonaws.<region>.ec2messages
For more details on endpoint types, private DNS, costs, and configuration, see the dedicated article on VPC Endpoints.
User Permissionsβ
In addition to the instance configuration, the user who starts the session also needs IAM permissions. The minimum required permission is:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:<region>:<account>:instance/i-0abc123def"
]
},
{
"Effect": "Allow",
"Action": [
"ssm:TerminateSession",
"ssm:ResumeSession"
],
"Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
}
]
}
This policy allows the user to start sessions only on specific instances (defined by ARN) and manage only their own sessions. This is where the principle of least privilege applies: instead of granting access to all instances, you control exactly which machines each user can access.
SSM Advantages over Bastion Hostβ
| Aspect | Bastion Host | SSM |
|---|---|---|
| Network ports | Port 22 open (SSH) | No inbound ports |
| SSH keys | Required and hard to manage | No SSH keys used |
| Auditing | Requires additional configuration | Native (CloudTrail + session recording) |
| Cost | Dedicated EC2 instance | No additional cost (included service) |
| Access control | Key and IP-based | IAM permission-based (granular) |
| Maintenance | Patches, updates, monitoring | Zero infrastructure maintenance |
SSM is the foundation upon which Just-in-Time access management tools like Apono operate β controlling who receives the ssm:StartSession permission and for how long, automating the granting and revocation cycle that previously depended on manual action.
Security Considerations: Port Forwardingβ
SSM Session Manager offers a port forwarding feature that allows redirecting ports from an EC2 instance to the engineer's local machine. While useful in legitimate scenarios (such as accessing an RDS database or an internal web interface), this feature introduces security risks that need to be evaluated.
The Problemβ
In the traditional bastion host model, a common hardening practice is to restrict file transfers between the local machine and the bastion, blocking SCP, SFTP, and SSH tunnels. This limits data exfiltration and prevents unauthorized files from being sent to the internal environment.
With SSM, port forwarding can reintroduce this risk. An engineer with port forwarding permission can:
- Redirect database ports to their local machine, accessing data directly without going through control layers.
- Create tunnels to internal services, exposing applications that should not be accessible outside the VPC.
- Facilitate data exfiltration, transferring sensitive information through the established tunnel.
# Port forwarding example via SSM β redirects port 5432 (PostgreSQL) from EC2 to localhost
aws ssm start-session \
--target i-0abc123def \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["5432"],"localPortNumber":["5432"]}'
How to Mitigateβ
AWS allows controlling port forwarding via IAM policies and SSM Session Documents. The recommended strategy is to deny by default and only allow it for those who truly need it.
1. Block Port Forwarding via IAMβ
This policy must be applied to the user's IAM (or the role assumed by them, as in the case of SSO), and not to the EC2 instance role. This is because the user is the one executing the ssm:StartSession action and the instance only receives the connection. Therefore, the control over who can or cannot use port forwarding lies on the side of whoever initiates the session:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyPortForwardingFromSSOToAponoInstances",
"Effect": "Deny",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ssm:*:*:document/AWS-StartPortForwardingSession",
"arn:aws:ssm:*:*:document/AWS-StartPortForwardingSessionToRemoteHost"
],
}
]
}
The
AWS-StartPortForwardingSessiondocument redirects ports from the instance itself, whileAWS-StartPortForwardingSessionToRemoteHostallows redirecting ports from other hosts accessible by the instance (such as an RDS in another subnet). Both should be blocked if the goal is to prevent any type of tunnel.
This policy can be applied as a Service Control Policy (SCP) in AWS Organizations, ensuring that the port forwarding block applies to all accounts and EC2 instances in the organization, regardless of individual account permissions. Since SCPs function as denial guardrails, even if an account administrator grants port forwarding permission, the SCP prevents execution.
2. Monitor with CloudTrailβ
Every SSM session, including port forwarding, is recorded in CloudTrail. Configure alerts to detect the use of port forwarding documents:
- Monitor
StartSessionevents where theDocumentNamecontainsPortForwarding. - Create CloudWatch alarms to notify the security team when port forwarding is used.
- Keep session logs enabled in S3 or CloudWatch Logs for complete auditing.
When to Allow Port Forwardingβ
Port forwarding is not inherently bad, as it replaces even riskier practices like exposing services directly to the internet. Scenarios where it makes sense to allow it:
- Database access in development: Engineers need to connect local tools (such as DBeaver or pgAdmin) to an RDS in a private subnet.
- Internal application debugging: Temporarily accessing internal monitoring interfaces or dashboards.
- Non-production environments: In development and staging environments, the risk is lower and productivity is a priority.
The recommendation is to apply the principle of least privilege: block port forwarding by default in production and grant access on demand with Just-in-Time access tools like Apono.