AWS Break Glass Role
Create a break glass role for emergency use in order to limit production console access. Configure automatic alerts and logging of activities in the role to secure its use in production environments.
- Basic Implementation
- Login Alerts
- Log Actions
- Regions
- Break Glass Role Properties
- Break-Glass Deployer Permissions
- Available Constructs
- Architecture
- Security
- License
Break Glass Roles
Often when undergoing security reviews for applications in a full CI/CD deployment environment, it is recommended to restrict console access to the AWS account that hosts the production environment. A popular way to implement this recommendation is to remove all production write access completely from user and role policies.
In these cases it may be necessary to provision a user or role with elevated permissions to be used only in emergency cases. This type of role is typically called a "Break Glass Role" and is usually used in On Call situations or other circumstances when quick mitigating action is needed.
Since Break Glass roles aren't supposed to be assumed on a regular basis, it's a good idea to outfit the role with alerts when someone assumes it and/or logging of the actions performed using it. So the purpose of this package is to automate this entire process - from the creation of the role to the login alerts to the logging of activities. If the break glass role already exists, it can be passed in as an argument as well.
Basic Implementation
The only required property for the BreakGlassRole
Resource is usernames
. Usernames
is a list of user identities that will be assuming the break glass role. If federated access is used, the user identity for the role can be found in the assumed role ARN contained in the signin event. The format of the ARN will be <RoleArn>:role/<RoleName>/<UserIdentity>
.
new BreakGlassRole(stack, testName, {
usernames: ["myUser", "otherUser"]
});
If you don't pass a role
in, then the Break Glass Role
will be created for you. The default created role with no policies attached will be created with full Administrator Access, but keep in mind that full access is not recommended even for Break Glass roles.
Policies can be attached to the role in lieu of full Administrator Access like this:
new BreakGlassRole(stack, testName, {
usernames: ["myUser", "otherUser"],
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName(
'job-function/ViewOnlyAccess'
)
],
policyStatements: [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ['s3:Get*', 's3:PutObject'],
resources: ['*']
}),
new PolicyStatement({
effect: Effect.DENY,
actions: ['s3:PutObjectAcl'],
resources: ['*']
})
]
});
The Principal of the trust policy for an internally created Break-Glass Role
will be the Account Principal for the AWS account with conditions restricting the use of the role to the identities listed in the usernames
property:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEqualsIgnoreCase": {
"aws:username": ["myUser", "otherUser"]
}
}
}
You can alternately use the role
property to pass in an existing role to use as the Break Glass Role
:
new BreakGlassRole(stack, testName, {
usernames: ["myUser", "otherUser"],
role: Role.fromRoleName(stack, 'myImportedRole', 'myRole')
});
The BreakGlassRole
construct intends to automate as much of the process of outfitting a role for break-glass status as possible, but it also doesn't want to cause failures due to limited permissions.
So when you import a role to have it outfitted for break-glass status, it will only interact with the role directly if you set the canManageBreakGlassRole
property to true
.
So what does it mean for a role to take on "break-glass" status?
Login Alerts
The first way a role displays break-glass properties is by sending login alerts to the necessary parties whenever the role is assumed. This can be done by sending login alert emails, by logging the events to Amazon CloudWatch, or by doing both.
To send email alerts, pass a list of email addresses to the loginAlertEmails
property:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
loginAlertEmails: [
"myEmail@me.com",
"myEmail@example.com",
"myEmail@mail.com"
]
});
The above implementation imports an existing role and sends alerts to the emails in loginAlertEmails
whenever myUser
assumes the role in the default region. Future versions of this package will allow for SMS and push notification alerts as well.
This alerts are created using an Amazon EventBridge rule with an Amazon SNS target. The only manual step involved in activating this is that the recipients of the emails must confirm the SNS subscription before receiving alerts.
Messages
By default the Login Alerts will be sent as an unformatted JSON string of the Login event, but if you want to customize how the information is presented, you can use the message
field. Some of the fields in the event can be referred to in this custom message using the following field names:
Login Event Information
- username: The user identity that logged in.
- principal: A unique session identifier for the user. Sometimes the user name is appended using the format
<identifier>:<username>
- account: The account where the login took place.
- eventTime: The timestamp when the event took place.
- region: The AWS region where the login occurred.
- eventId: The unique id of the event.
- requestId: The unique id of the request, useful for AWS X-Ray traces.
- ip: The IP address of the event request
- userAgent: The User agent listed in the request
- result: The result of the request, either
Success
orFailure
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
loginAlertEmails: [
"myEmail@me.com",
"myEmail@example.com",
"myEmail@mail.com"
],
message: "User <principal> logged in at '<eventTime>'"
});
The alert that results from the above resource would say something like User AIDACKCEVSQ6C2EXAMPLE:myUser logged in at '2022-12-29T21:34:59Z'
Logging Login Alerts
Use the createLoginLogGroup
property to log the login alerts in Amazon CloudWatch:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
createLoginLogGroup: true
});
Log Actions
The second way a role displays break-glass properties is by keeping a log of user actions. You can log all user actions in the active regions by setting the logActions
property to true
, or you can specify which type of actions you want to log. For instance, if you only wanted to log write
actions:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
logActions: 'write'
});
Logging Actions On Specific Services
Logging user actions can become difficult to manage and expensive in many cases if left unchecked, so in addition to limiting logging to read
or write
actions, you can also limit by AWS service. Let's say you only need to monitor whether or not a user uses a Break Glass Role
to perform write actions in AWS CodePipeline or CloudFormation. In that case you could pass a list of AWS service prefixes in the logServices
property:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
logServices: ['codepipeline', 'cloudformation']
});
Then only events where the eventSource
is that AWS service will be logged.
Retention
Another way to conserve resources while logging Break Glass
actions is by setting a retention policy. Set the retention policy to delete logs after a certain period of time. This retention policy applies to all log groups created by the construct - for both log actions and login alerts:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
role: Role.fromRoleName('myRole'),
retention: RetentionDays.SIX_MONTHS
});
Regions
Since EventBridge rules are regional, in a default implementation alerts and log actions would only be activated when the user assumes the break-glass role in the default region. To ensure that alerts are sent cross-regionally, set the regions
property:
new BreakGlassRole(stack, testName, {
usernames: ["myUser"],
regions: ['us-east-2', 'us-west-2'],
role: Role.fromRoleName('myRole'),
loginAlertEmails: [
"myEmail@me.com",
"myEmail@example.com",
"myEmail@mail.com"
]
});
To send login alerts from all regions, set regions
to ['*']
. To reduce operational overhead, the full EventBridge rule is not replicated in every region. In regions other than the main region, a smaller rule targets an event bus that then reroutes the event to the main region for the alert to be sent.
Note: All log groups will be centrally created in the main region and events from other regions will be routed there and appear in those logs.
Break Glass Role Properties
-
role (optional): IRole
-
managedPolicies (optional): IManagedPolicy[]
-
policyStatements (optional): PolicyStatement[]
-
canManageBreakGlassRole (optional): boolean
-
loginAlertEmails (optional): string[]
-
regions (optional): string[]
-
eventBus (optional): EventBus | EventBusOptions
-
busRole (optional): IRole
-
retention (optional): RetentionDays
-
logServices (optional): string[]
-
message (optional): string
-
createLoginLogGroup (optional): boolean
Break-Glass Deployer Permissions
You can optionally use the deployer
property to set a username, IUser
, or Role
as the entity needing permissions to deploy the stacks and initiate the integration tests for the Break Glass Role
. If canManageBreakGlassRole
is set to true
, this role will also give the deployer permission to manage the BreakGlassRole
.
Setting deployer
will create a role with Least-Privilege permissions set for deploying the BreakGlassRole
stack contained herein. That role can then be delegated and used to signin to the AWS account programatically before running cdk deploy
. If the user or role that deployer is set to already exists in the account, set deployerExists
to user
or role
accordingly so that the construct knows to import them rather than try to create a new one.
Available Constructs
In addition to the main BreakGlassRole
, BreakGlassLoginAlert
, BreakGlassLogActions
, and BreakGlassDeployer
are all available as separate constructs. The major difference when using these constructs separately is that the BreakGlassRole
cannot be created internally and an IRole
must be passed to them as the BreakGlassRole
.
Architecture
Security
See CONTRIBUTING for more information.
License
This project is licensed under the Apache-2.0 License.