-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Applying permissions boundary to aws-cdk globally #3242
Comments
We use aspects to achieve this. import cdk = require('@aws-cdk/core');
import iam = require('@aws-cdk/aws-iam');
export class PermissionsBoundary implements cdk.IAspect {
private readonly permissionsBoundaryArn: string;
constructor(permissionBoundaryArn: string) {
this.permissionsBoundaryArn = permissionBoundaryArn;
}
public visit(node: cdk.IConstruct): void {
if (node instanceof iam.Role) {
const roleResource = node.node.findChild('Resource') as iam.CfnRole;
roleResource.addPropertyOverride('PermissionsBoundary', this.permissionsBoundaryArn);
}
}
} Apply as follows: import { PermissionsBoundary } from '@cultureamp/cdk-common'
const service = new ServiceStack(app, `${projectName}`)
service.node.apply(new PermissionsBoundary('arn:aws:iam::${cdk.Aws.accountId}:policy/hello-world-service-permission-boundary')) |
Thanks @matthewtapper. Few minor changes in service.node.applyAspect(new PermissionsBoundary('arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:policy/hello-world-service-permission-boundary')) Still, it would be nice for this to be done on the aws-cdk level. :) P.S. Also, just putting this here for other folks... |
+1 for this, the IAM Roles that I create need to follow a permission boundary. |
@mattaschmann I already have a pending PR to attach permissions boundary to an IAM role created through cdk #2919. Hopefully we’ll see it merged in soon. |
Thanks @robertd to raise the PR. I faced the same issue to add permission boundary policy with Second, with your PR #2919 I found you add Inline policy directly (hard coded) But in our cases, the permission boundary policy denies us to add any inline policies. Only managed policy allowed. Third, we need apply another managed policy |
+1 for this issue. I work with security team that have created permission boundary for us to work within and providing this capability will be really nice. 👍 |
Here's a solution in Python for CDK 1.4.0 inspired by @matthewtapper's code Needless to say it's very ugly, since python CDK does not provide construct objects in aspects. We have to dig deep into JSII to resolve the objects. Hope it helps someone. from jsii._reference_map import _refs
from jsii._utils import Singleton
import jsii
@jsii.implements(core.IAspect)
class PermissionBoundaryAspect:
def __init__(self, permission_boundary: Union[aws_iam.ManagedPolicy, str]) -> None:
"""
:param permission_boundary: Either aws_iam.ManagedPolicy object or managed policy's ARN as string
"""
self.permission_boundary = permission_boundary
def visit(self, construct_ref: core.IConstruct) -> None:
"""
construct_ref only contains a string reference to an object. To get the actual object, we need to resolve it using JSII mapping.
:param construct_ref: ObjRef object with string reference to the actual object.
:return: None
"""
kernel = Singleton._instances[jsii._kernel.Kernel]
resolve = _refs.resolve(kernel, construct_ref)
def _walk(obj):
if isinstance(obj, aws_iam.Role):
cfn_role = obj.node.find_child('Resource')
policy_arn = self.permission_boundary if isinstance(self.permission_boundary, str) else self.permission_boundary.managed_policy_arn
cfn_role.add_property_override('PermissionsBoundary', policy_arn)
else:
if hasattr(obj, 'permissions_node'):
for c in obj.permissions_node.children:
_walk(c)
if obj.node.children:
for c in obj.node.children:
_walk(c)
_walk(resolve) Usage: stack.node.apply_aspect(PermissionBoundaryAspect(managed_policy_arn)) |
Huge shoutout for @pepastach for this. I was pulling my hair out trying to get Aspects to work in Python. |
Great stuff! So that gets me past applying permissions boundary. The permissions boundary I am applying requires that the custom roles and policies have the prefix ‘cust-‘. Is there a way using cdk to specify a prefix for the custom roles and policies being auto generated by cdk? |
@tarunaroraonline yeah, use the Aspect and apply it to the Stack, it will apply whatever custom logic you want to each element. |
I am a python newbee ... The code seems to fail with a generic exception ... I am using Python 3.6.5 32-bit and cdk 1.6.1 Exception has occurred: NameError
name 'Union' is not defined
File "C:\Users\xxx\source\repos\xxx\infra.app.xxx\infra\custom.py", line 13, in PermissionBoundaryAspect
def __init__(self, permission_boundary: Union[aws_iam.ManagedPolicy, str]) -> None:
File "C:\Users\xxx\source\repos\xxx\infra.app.xxx\infra\custom.py", line 10, in <module>
@jsii.implements(core.IAspect) The custom.py class ... from aws_cdk import (
core,
aws_iam
)
from jsii._reference_map import _refs
from jsii._utils import Singleton
import jsii
@jsii.implements(core.IAspect)
class PermissionBoundaryAspect:
def __init__(self, permission_boundary: Union[aws_iam.ManagedPolicy, str]) -> None:
"""
:param permission_boundary: Either aws_iam.ManagedPolicy object or managed policy's ARN as string
"""
self.permission_boundary = permission_boundary
def visit(self, construct_ref: core.IConstruct) -> None:
"""
construct_ref only contains a string reference to an object. To get the actual object, we need to resolve it using JSII mapping.
:param construct_ref: ObjRef object with string reference to the actual object.
:return: None
"""
kernel = Singleton._instances[jsii._kernel.Kernel]
resolve = _refs.resolve(kernel, construct_ref)
def _walk(obj):
if isinstance(obj, aws_iam.Role):
cfn_role = obj.node.find_child('Resource')
policy_arn = self.permission_boundary if isinstance(self.permission_boundary, str) else self.permission_boundary.managed_policy_arn
cfn_role.add_property_override('PermissionsBoundary', policy_arn)
else:
if hasattr(obj, 'permissions_node'):
for c in obj.permissions_node.children:
_walk(c)
if obj.node.children:
for c in obj.node.children:
_walk(c)
_walk(resolve) Any idea what I am missing? |
@tarunaroraonline You're trying to use |
I had to make small tweak to @pepastach 's version to make it work (had to add a check because not all obj have node attr), here is code:
|
@pepastach @jacques- @matthewtapper this seems to be broken in aws-cdk 1.16.x with the introduction of changes to jsii 0.20.0... Anyone else got it to work with the changes? aws/jsii#980 |
Hi everybody, here's a fix that should work with CDK based on JSII pre-0.20 as well as the newest ones. https://gitlab.com/josef.stach/aws-cdk-permission-boundary-aspect from typing import Union
import jsii
from aws_cdk import aws_iam, core
from jsii._reference_map import _refs
from jsii._utils import Singleton
@jsii.implements(core.IAspect)
class PermissionBoundaryAspect:
"""
This aspect finds all aws_iam.Role objects in a node (ie. CDK stack) and sets permission boundary to the given ARN.
"""
def __init__(self, permission_boundary: Union[aws_iam.ManagedPolicy, str]) -> None:
"""
:param permission_boundary: Either aws_iam.ManagedPolicy object or managed policy's ARN string
"""
self.permission_boundary = permission_boundary
def visit(self, construct_ref: core.IConstruct) -> None:
"""
construct_ref only contains a string reference to an object. To get the actual object, we need to resolve it using JSII mapping.
:param construct_ref: ObjRef object with string reference to the actual object.
:return: None
"""
if isinstance(construct_ref, jsii._kernel.ObjRef) and hasattr(construct_ref, 'ref'):
kernel = Singleton._instances[jsii._kernel.Kernel] # The same object is available as: jsii.kernel
resolve = _refs.resolve(kernel, construct_ref)
else:
resolve = construct_ref
def _walk(obj):
if isinstance(obj, aws_iam.Role):
cfn_role = obj.node.find_child('Resource')
policy_arn = self.permission_boundary if isinstance(self.permission_boundary, str) else self.permission_boundary.managed_policy_arn
cfn_role.add_property_override('PermissionsBoundary', policy_arn)
else:
if hasattr(obj, 'permissions_node'):
for c in obj.permissions_node.children:
_walk(c)
if hasattr(obj, 'node') and obj.node.children:
for c in obj.node.children:
_walk(c)
_walk(resolve) |
We used the aspect solution to implement 'global' permissionBoundary as outlined by @matthewtapper above (THANKS) but did find the following code more reliable, as import cdk = require('@aws-cdk/core');
export class PermissionsBoundary implements cdk.IAspect {
private readonly permissionsBoundaryArn: string;
constructor(permissionBoundaryArn: string) {
this.permissionsBoundaryArn = permissionBoundaryArn;
}
public visit(node: cdk.IConstruct): void {
if (cdk.CfnResource.isCfnResource(node) && node.cfnResourceType === 'AWS::IAM::Role') {
node.addPropertyOverride('PermissionsBoundary', this.permissionsBoundaryArn);
}
}
} |
Works like a charm, many thanks! Still would be nice to have the functionality cleanly embedded in CDK. |
Apply an aspect to each CDK template (backend and client) which adds a permission boundary to any IAM role. This makes it easier to delegate deploy permissions without requiring unbounded access to manage IAM roles. Reference: aws/aws-cdk#3242
I've been using the aspect in the code snippets above in all our code without trouble till one of our stacks (with dozens of nested stacks) grew a bit too complicated. Now I'm hitting the following during the _walk:
Unfortunately it is hard to reproduce without sharing lots of internal code. I don't think this is a circular recursion because I can make the stack simpler by reducing the number of similar nested stacks slightly, and then it still works. I'd be grateful for any hints or suggestions on where to look next. |
Allow configuring Permissions Boundaries for an entire subtree using Aspects, add a sample policy which can be used to reduce future misconfiguration risk for untrusted CodeBuild projects as an example. Fixes #3242.
Allow configuring Permissions Boundaries for an entire subtree using Aspects, add a sample policy which can be used to reduce future misconfiguration risk for untrusted CodeBuild projects as an example. Addresses one part of aws/aws-cdk-rfcs#5. Fixes #3242. ALSO IN THIS COMMIT: Fix a bug in the `assert` library, where `haveResource()` would *never* match any resource that didn't have a `Properties` block (even if we tested for no property in particular, or the absence of properties). This fix caused two ECS tests to fail, which were asserting the wrong thing anyway (both were asserting `notTo(haveResource(...))` where they actually meant to assert `to(haveResource())`. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
|
Allow configuring Permissions Boundaries for an entire subtree using Aspects, add a sample policy which can be used to reduce future misconfiguration risk for untrusted CodeBuild projects as an example. Addresses one part of aws/aws-cdk-rfcs#5. Fixes aws#3242. ALSO IN THIS COMMIT: Fix a bug in the `assert` library, where `haveResource()` would *never* match any resource that didn't have a `Properties` block (even if we tested for no property in particular, or the absence of properties). This fix caused two ECS tests to fail, which were asserting the wrong thing anyway (both were asserting `notTo(haveResource(...))` where they actually meant to assert `to(haveResource())`. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
For CDK version 1.9.0 + I needed to adjust the Python code from @pepastach to get it to work, first added and extra
And the new implementation API for the stack is:
|
https://aws.amazon.com/blogs/devops/secure-cdk-deployments-with-iam-permission-boundaries/ In {
"context": {
"@aws-cdk/core:permissionsBoundary": {
"name": "developer-policy"
}
}
} |
Note: for support questions, please first reference our documentation, then use Stackoverflow. This repository's issues are intended for feature requests and bug reports.
I'm submitting a ...
What is the current behavior?
It’s not possible to set permissions boundary globally for cdk, or anything similar along those lines
What is the expected behavior (or behavior of feature suggested)?
My Gitlab CI/CD pipeline runner uses an IAM role that has permissions boundary set (only what our devops team is willing to let us do on our own). However, cdk tries to automagically create or alter roles/permissions/security groups/etc between resources that don’t fall under rules set in our permissions boundary policy (i.e. any customer role must have /custom/ path, etc) . We end up with bunch of errors (cdk trying to create something that permissions boundary on IAM role for the gitlab runner doesn’t allow). What I’m asking is… it would be great if there a way to tell cdk to somehow use permissions boundary policy whenever it tries to automagically create relationships (IAM roles, policies, etc) between the resources.
We would be able to limit cdk based on pre-created permission boundaries created by our ops team.
Please tell us about your environment:
Other information (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. associated pull-request, stackoverflow, gitter, etc)
Conversation w/ @eladb on Gitter.
The text was updated successfully, but these errors were encountered: