Instance roles and SSM Session Manager
Kill standing SSH keys on bastions.
In short. Long-lived SSH keys on bastions are standing credentials with poor attribution.
Long-lived SSH keys on bastions are standing credentials with poor attribution. IAM instance profiles give EC2 temporary credentials automatically; Systems Manager Session Manager gives humans interactive shells through the AWS API — audited, with no inbound port 22 required — when the SSM agent and network path to endpoints exist.
Prefer task/pod/instance roles scoped to the app over copying access keys onto disks. Prefer SSM over SSH. Prefer private subnets plus VPC endpoints for ssm, ssmmessages, and ec2messages.
Instance profile + SSM
aws iam create-role --role-name ec2-ssm --assume-role-policy-document file://ec2-trust.jsonaws iam attach-role-policy --role-name ec2-ssm --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCoreaws ssm start-session --target i-0123456789abcdef0
Starting session with SessionId: …sh-5.2$
Hardening notes
Disable SSH or restrict to break-glass only. Encrypt EBS with CMKs. Require IMDSv2 (HttpTokens=required) so SSRF cannot easily mint role credentials. Keep roles thin — the instance role is the blast radius of every app compromise on that box.
Going deeper
Session logging to S3/CloudWatch with encryption gives forensics after a malicious insider uses SSM. Turn it on before you need it. Restrict who can read those logs.
Patch Manager and Inventory complement SSM access. An open SSM path to an unpatched host is still a risk. Keep agents updated; broken agents force people back to SSH exceptions.
Bastions, if you still need them, should be ephemeral, SSM-only, and auto-rotated AMIs — not pet machines with decade-old keys in authorized_keys.
Combine SSM with IAM Conditions on ssm:resourceTag so operators can only start sessions on instances tagged for their team. Untagged instances should be unreachable and non-compliant.
Document the break-glass SSH path if it exists at all: who approves, how keys are issued for hours not months, and how the hole is closed. Ideally the answer is "there is no SSH path."
Disable unnecessary instance metadata hops and consider host management via autoscaling refresh instead of long-lived SSH pets. Cattle, not snowflakes, shrink key inventory.
Rotate host management AMI golden images on a cadence and require new launches from approved AMIs via SCPs or launch constraints.
If containers share the instance role, assume compromise of one container compromises the role; push workloads toward IRSA or task roles as part of the same hardening program that adopts SSM.
Try this
In a lab VPC, attach AmazonSSMManagedInstanceCore to an instance role, open a session, and confirm security group has no inbound 22.
aws ec2 describe-instances --instance-ids i-0123456789abcdef0 \--query 'Reservations[].Instances[].SecurityGroups'aws ec2 describe-instances --instance-ids i-0123456789abcdef0 \--query 'Reservations[].Instances[].MetadataOptions'
… GroupName=… …HttpTokens: required
Takeaway
Instance roles + SSM replace standing SSH keys and baked AKIA keys. Tighten IMDS, encrypt disks, and keep roles least privilege.
Next: incident response when those controls fail and a key still leaks.