AWS Systems Manager replaces SSH bastions and open RDP ports, but the same features that let administrators reach every instance without a network path also give anyone holding the right IAM permission a ready-made channel to run commands across the whole fleet. This guide covers the security of Systems Manager's access and execution features: how its IAM model works, which permissions amount to administrator access on every managed node, how instance credentials are issued, how to log sessions and commands, and how to store secrets safely in Parameter Store. It is written for cloud security engineers, platform teams and SOC analysts who run Systems Manager on EC2 and hybrid servers. The wider case for moving away from VPNs and bastions is made in the Secure Remote Access Beyond VPN Guide; this page is about running Systems Manager safely once you have chosen it.
Key takeaways
- Session Manager and Run Command are remote execution as a service:
ssm:StartSessionandssm:SendCommandon*are equivalent to root or SYSTEM on every managed node, so scope them by node tag and document. - Managed nodes are machine identities: each node gets credentials from an instance profile, Default Host Management Configuration or a hybrid activation, and those credentials are what an attacker on the node can steal.
- Logging is opt-in and incomplete: CloudTrail records that a session or command started, but session content needs S3 or CloudWatch Logs logging, and port forwarding and SSH-over-Session-Manager sessions are not logged at all.
- Use a customer managed KMS key for SecureString: anyone with
ssm:GetParameterin the account can decrypt parameters encrypted with the AWS managedaws/ssmkey. - Move from standing access to just-in-time access: just-in-time node access lets users request time-bound, approved access instead of holding permanent session rights.
How identity and access work in AWS Systems Manager
Systems Manager has two identities in every interaction. The caller (a person through IAM Identity Center or a pipeline role) needs IAM permission for the API, such as ssm:StartSession or ssm:SendCommand, on both the target node and the SSM document that defines what will run. The managed node runs SSM Agent, which authenticates to Systems Manager with its own AWS credentials and executes the document locally, normally as root on Linux or SYSTEM on Windows. No inbound port is needed: the agent makes outbound HTTPS connections, which is why Systems Manager works in private subnets through VPC endpoints.
| Capability | What it does | Key IAM actions | Security significance |
|---|---|---|---|
| Session Manager | Interactive shell, interactive commands or port forwarding to a node | ssm:StartSession, ssm:TerminateSession, ssm:ResumeSession | Replaces SSH and RDP; shell access as a privileged OS user unless Run As is configured |
| Run Command | Runs an SSM document on one or many nodes | ssm:SendCommand, ssm:GetCommandInvocation | Fleet-wide command execution; ATT&CK T1651 Cloud Administration Command |
| Automation | Runbooks that call AWS APIs and run steps on nodes | ssm:StartAutomationExecution, iam:PassRole for the assume role | Runbooks act with their assume role's permissions, which may exceed the caller's |
| State Manager | Associations that apply documents on a schedule | ssm:CreateAssociation | Persistent execution on every new matching node |
| Parameter Store | Configuration and secrets, including SecureString | ssm:GetParameter, ssm:PutParameter, kms:Decrypt | Secrets readable by anyone with get rights if the AWS managed key is used |
| Patch Manager and Inventory | Patching and software inventory | Associations and Run Command under the hood | A trusted path that also runs code on every node |
How managed nodes get credentials
| Method | How it works | Choose it when | Watch out for |
|---|---|---|---|
| Instance profile | An IAM role attached to each EC2 instance, typically with AmazonSSMManagedInstanceCore | Instances already need a role for application access | Application and SSM permissions mix in one role that anyone on the instance can use |
| Default Host Management Configuration (DHMC) | Account and Region setting; SSM issues node credentials using AWSSystemsManagerDefaultEC2InstanceManagementRole with AmazonSSMManagedEC2InstanceDefaultPolicy | You want every EC2 instance managed without editing instance profiles | Requires IMDSv2 and SSM Agent 3.2.582.0 or later; enabled per Region; any change to the role affects every managed instance |
| Hybrid activation | Activation code and ID register an on-premises or other-cloud server with a service role | Servers outside EC2 | Activation codes are credentials; set short expiry and registration limits, and install with ssm-setup-cli, which AWS says adds protections for non-EC2 machines |
DHMC has a precedence rule worth knowing: SSM Agent tries instance profile permissions first, and if the instance profile allows ssm:UpdateInstanceInformation the instance will not use DHMC. Separating node management credentials from application credentials (DHMC for management, a narrow instance profile for the application) reduces what a compromised application can do with Systems Manager. Because DHMC depends on IMDSv2, it pairs naturally with the EC2 Instance Metadata Service Hardening Guide.
Admin roles and privileged access
Treat the following permissions as privileged access to every node they can reach. The Lateral Movement Guide explains why a cloud control plane that can run commands on hosts is an attractive path between workloads.
| Permission | Why it is privileged | Scoping control |
|---|---|---|
ssm:StartSession on * | Shell on any managed node | Restrict Resource to instance ARNs with ssm:resourceTag/<key> conditions, and to named session documents |
ssm:SendCommand with AWS-RunShellScript or AWS-RunPowerShellScript | Arbitrary commands across many nodes at once | Allow only specific approved documents with allowedValues or allowedPattern parameters; restrict target tags |
ssm:CreateDocument, ssm:UpdateDocument | A custom document can contain any commands and be run by others | Pipeline only; review documents as code |
ssm:ModifyDocumentPermission | Shares documents with other accounts or publicly | Turn on block public sharing in Documents preferences; deny via SCP |
ssm:CreateAssociation | Persistent execution on current and future nodes | Pipeline only; alert on creation |
ssm:StartAutomationExecution with iam:PassRole | Runbook acts as a possibly more powerful role | Restrict which roles can be passed; prefer temporary permissions granted inside the runbook |
ssm:UpdateServiceSetting | Changes DHMC role or other account-wide behaviour | Security administration role only |
ssm:UpdateDocument on the Session Manager preferences document | Can switch off session logging or encryption | Deny via SCP outside the platform role |
- Interactive commands, not full shells: AWS recommends using session documents of the interactive commands type so users can only run a specific command or sequence, for example restarting a service or tailing a log.
- Run As: on Linux and macOS, Session Manager can start sessions as a named OS user (from the IAM principal's tag or a default) instead of the default privileged account, which gives per-user OS accountability.
- Own-session controls: allow
ssm:TerminateSessionandssm:ResumeSessiononly onarn:aws:ssm:*:*:session/${aws:userid}-*so users can manage only their own sessions. - Just-in-time node access: with the unified Systems Manager console, users request access to a node with a reason; auto-approval, manual approval and organisation-wide deny-access policies decide the outcome, and users need no standing session permissions. AWS notes that JIT sessions are not terminated automatically at the end of the window, so set the maximum session duration and idle timeout preferences. Requests are retained for one year. See the Privileged Session Management Guide.
Integrations, API tokens and service accounts
- Pipelines and automation: CI/CD roles that call
SendCommandfor deployments should be limited to the deployment documents and the node tags of their own application. Federate them with OIDC rather than storing access keys. See the AWS STS and AssumeRole Guide. - Parameter Store secrets: store sensitive values as SecureString with a customer managed KMS key. AWS documents that all users in the account can use the AWS managed
aws/ssmkey, so with it anyone withssm:GetParametercan read the plaintext by passingWithDecryption. With a customer managed key, readers also needkms:Decrypt, and writers needkms:Encryptandkms:GenerateDataKey. Scope parameter access by path, for exampleparameter/prod/payments/*. See the AWS KMS Security Guide. - Parameter Store or Secrets Manager: Parameter Store suits configuration and simple secrets. Choose Secrets Manager when you need managed rotation, cross-account resource policies or secret versioning stages. See the Cloud Secrets Managers Guide.
- Secrets in sessions and commands: session logs capture typed input and output, and Run Command output can go to S3 or CloudWatch. Never pass secrets as command parameters; have the script fetch them from Parameter Store or Secrets Manager at run time. AWS suggests disabling echo when a password must be typed in a logged session.
- Replacing SSH keys: Session Manager removes the need to distribute SSH keys. If you still tunnel SSH through Session Manager, the content of those sessions is not logged by Session Manager, and the SSH keys remain a credential to manage. See the SSH Key Management Guide.
- Proxies: when SSM Agent goes through a proxy, add the instance metadata IP address to
no_proxyso agent calls do not take on the proxy's identity. - Hybrid nodes: treat activation codes like passwords, register with the least privileged service role, and remove deregistered nodes promptly.
Logging and detection
| Source | What it shows | What it misses |
|---|---|---|
| CloudTrail management events | Who called StartSession, SendCommand, CreateAssociation, StartAutomationExecution, GetParameter, when and from where | What was typed or what the command output was |
| Session logging to S3 or CloudWatch Logs | Commands and output of shell sessions; streaming to CloudWatch for near real time | Port forwarding and SSH sessions; Linux logs can be truncated without screen |
| Run Command output to S3 or CloudWatch Logs | Output of each invocation | Nothing, if output destinations are not configured |
| Node OS logs via CloudWatch agent | Process and authentication activity on the node | Depends on agent configuration |
| EventBridge | Systems Manager events, including JIT access request status changes and failures | Content |
Encrypt session data with a symmetric KMS key through the Session Manager preferences (Enable KMS encryption); both the user starting the session and the managed node need permission to use the key, and the node needs SSM Agent 2.3.539.0 or later. Session data is already protected in transit by TLS; KMS adds a separate key-controlled layer.
Detections worth building
- New executor:
SendCommandorStartSessionby a principal that has never used it, or from a new source IP (T1651 Cloud Administration Command). - Broad targeting:
SendCommandwithAWS-RunShellScriptorAWS-RunPowerShellScriptagainst many instances or a wildcard tag outside a change window. - Persistence: new or modified associations and documents, especially documents shared outside the account.
- Logging tampering: changes to the Session Manager preferences document that disable S3 or CloudWatch logging or KMS encryption.
- Secret harvesting: bursts of
GetParameterorGetParametersByPathwith decryption from one principal (T1552 Unsecured Credentials). - Instance credential misuse: GuardDuty credential exfiltration findings for node roles. See the Amazon GuardDuty Guide.
Metrics
- Standing access: number of principals with
ssm:StartSessionorssm:SendCommandon*(target zero outside break-glass). - Logging coverage: percentage of accounts and Regions where session logging to S3 or CloudWatch is enabled and encrypted.
- Agent currency: percentage of managed nodes on an SSM Agent version less than 30 days old (AWS suggests checking at least every two weeks).
- Secret protection: percentage of SecureString parameters using a customer managed key.
- Bastion retirement: count of instances still reachable on TCP 22 or 3389 from outside the VPC.
Hardening baseline
| Area | Baseline |
|---|---|
| Network | Interface VPC endpoints for ssm, ssmmessages and ec2messages (plus KMS, S3 and CloudWatch Logs where used); no public IPs needed; close inbound 22 and 3389 |
| Node credentials | DHMC with IMDSv2 for management; separate narrow instance profiles for applications |
| Session access | Tag-scoped StartSession; approved session documents only; Run As per user on Linux; JIT node access for production |
| Command execution | Approved documents with constrained parameters; target tags; no generic shell document for routine roles |
| Documents | Block public sharing on; documents in source control with review |
| Logging | Session logs to S3 and CloudWatch Logs with KMS encryption; Run Command output to S3; CloudTrail organisation trail |
| Session limits | Idle session timeout and maximum session duration set in preferences |
| Parameter Store | SecureString with customer managed keys; path-scoped IAM; secrets needing rotation in Secrets Manager |
| Agents and tools | SSM Agent, Session Manager plugin and CLI updated at least every two weeks; agent signature verified |
Rollout in phases
- Inventory: list managed nodes by credential method, every principal holding session, command or document permissions, and current logging settings per Region.
- Turn on logging first: enable encrypted session logging and Run Command output before tightening access, so you see current use.
- Introduce DHMC and IMDSv2: enable DHMC in a pilot account, confirm nodes register within the 30-minute window, then remove SSM permissions from application instance profiles. Rollback is resetting the service setting, provided an instance profile still grants SSM access.
- Scope permissions: replace
*with tag-based conditions and approved documents team by team; watch CloudTrail forAccessDeniedfrom legitimate users. - Adopt JIT access: start with manual approval for production databases and auto-approval for stateless tiers, then remove standing session permissions.
- Retire bastions: close inbound SSH and RDP once all teams use Session Manager, keeping one documented break-glass path.
Decision guidance
- Session Manager or bastion with SSH: Session Manager removes inbound ports, key distribution and a host to patch, and ties access to IAM. A bastion may remain for tools that need native SSH features, but then session content is outside Session Manager's logging.
- Small or large estate: a small team can start with tag-scoped IAM and logging. A large organisation should add JIT access, SCPs that protect logging preferences and document sharing, and central log collection in a log archive account. See the AWS Multi-Account Security Reference Architecture Guide.
Framework mapping
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 | CIS Controls v8.1 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|---|
| Remote administrative access | PR.AA-05 | AC-17, MA-4, AC-6 | 6.5, 12.7 | A.8.2, A.8.5 |
| Session and command logging | PR.PS-04, DE.CM-03 | AU-2, AU-12 | 8.2 | A.8.15 |
| Patching and inventory | ID.AM-08, PR.PS-01 | SI-2, CM-8 | 7.3, 1.1 | A.8.8, A.5.9 |
| Secrets in Parameter Store | PR.DS-01 | IA-5, SC-28 | 3.11 | A.8.24 |
Common misconfigurations
- Granting
AmazonSSMFullAccessorssm:*to developers, which gives every developer command execution on every node, including production. - Combining SSM and application permissions in one instance profile, so a compromised application can use its credentials to reach Systems Manager APIs, and a stolen role works for both.
- Assuming sessions are recorded: logging is off until preferences are set, and port forwarding and SSH tunnels are never logged.
- SecureString with the AWS managed key, which means
ssm:GetParameteralone reveals the secret. - Publicly shared documents: custom documents shared publicly can expose internal scripts, hostnames or embedded credentials.
- Leaving bastions in place after migration, so the old path with SSH keys and open ports remains as a bypass.
Practitioner checklist
- Inventory managed nodes by credential method and every principal with session, command, document or association rights.
- Enable KMS-encrypted session logging to S3 and CloudWatch Logs and Run Command output in every account and Region.
- Deny changes to the Session Manager preferences document and document sharing settings via SCP outside the platform role.
- Scope
ssm:StartSessionandssm:SendCommandby node tag and approved documents; remove*grants. - Turn on block public sharing for documents and keep documents in reviewed source control.
- Use DHMC with IMDSv2 for node management and keep application instance profiles narrow.
- Store secrets as SecureString with customer managed KMS keys, scoped by parameter path.
- Adopt just-in-time node access for production and set idle timeout and maximum session duration.
- Alert on new executors, broad
SendCommandtargeting, new associations and bulk parameter reads. - Close inbound SSH and RDP after migration and update SSM Agent at least every two weeks.
Standards and references
- AWS: Security best practices for Systems Manager
- AWS: Sample IAM policies for Session Manager
- AWS: Logging session activity
- AWS: Enable KMS key encryption of session data
- AWS: Managing EC2 instances with Default Host Management Configuration
- AWS: Just-in-time node access using Systems Manager
- AWS: SecureString parameters and AWS KMS encryption
- MITRE ATT&CK: Cloud Administration Command
Related NHI Mgmt Group resources: Secure Remote Access Beyond VPN Guide · AWS Security Guide · Privileged Session Management Guide · Lateral Movement Guide