Skip to content

Commit

Permalink
- Added ECR Repository field to IASG for automated permissions.
Browse files Browse the repository at this point in the history
- Fixed IAM Policy name generation to support 127 characters, was only 64 before.
  • Loading branch information
gitwater committed Jul 27, 2021
1 parent 42b33f6 commit e9d6d65
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 102 deletions.
15 changes: 10 additions & 5 deletions src/paco/cftemplates/asg.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(
self.ec2_manager_cache_id = ec2_manager_cache_id
super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_NAMED_IAM"])
self.set_aws_name('ASG', self.resource_group_name, self.resource_name)
self.instance_iam_role_name = self.paco_ctx.get_ref(asg_config.paco_ref + '.instance_iam_role.name')

# Troposphere
self.init_template('AutoScalingGroup: ' + self.ec2_manager_cache_id)
Expand Down Expand Up @@ -335,7 +336,13 @@ def __init__(
self.script_manager_ecr_deploy(asg_config.script_manager.ecr_deploy, asg_dict, asg_config, template)
if asg_config.script_manager.ecs:
self.script_manager_ecs(asg_config.script_manager.ecs, asg_dict, asg_config, template)

# ECR Repository access
self.set_ecr_repositories_statements(
asg_config.ecr,
template,
'ECRAccess',
[self.instance_iam_role_name]
)
asg_res = troposphere.autoscaling.AutoScalingGroup.from_dict(
'ASG',
asg_dict
Expand Down Expand Up @@ -550,14 +557,13 @@ def script_manager_ecs(self, ecs_group, asg_dict, asg_config, template):

idx += 1

role_name = self.paco_ctx.get_ref(asg_config.paco_ref + '.instance_iam_role.name')
script_manager_ecs_policy_res = troposphere.iam.ManagedPolicy(
title='ScriptManagerECS',
PolicyDocument=PolicyDocument(
Version="2012-10-17",
Statement=policy_statements
),
Roles=[role_name]
Roles=[self.instance_iam_role_name]
)
template.add_resource(script_manager_ecs_policy_res)

Expand Down Expand Up @@ -762,13 +768,12 @@ def script_manager_ecr_deploy(self, ecr_deploy_group, asg_dict, asg_config, temp
Resource=[ '*' ]
)
)
role_name = self.paco_ctx.get_ref(asg_config.paco_ref + '.instance_iam_role.name')
ecs_release_phase_project_policy_res = troposphere.iam.ManagedPolicy(
title='ECSReleasePhase',
PolicyDocument=PolicyDocument(
Version="2012-10-17",
Statement=policy_statements
),
Roles=[role_name]
Roles=[self.instance_iam_role_name]
)
template.add_resource(ecs_release_phase_project_policy_res)
118 changes: 107 additions & 11 deletions src/paco/cftemplates/cftemplates.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from awacs.aws import Allow, Statement, Policy, PolicyDocument, Principal, Action, Condition, StringEquals, StringLike
from paco import utils
from paco.core.exception import StackException, PacoErrorCode, PacoException
from paco.models import schemas
from paco.models.references import Reference
from paco.models.references import Reference, get_model_obj_from_ref
from paco.models.locations import get_parent_by_interface
from paco.stack import StackOutputParam, Stack
from paco.utils import md5sum, big_join
Expand Down Expand Up @@ -351,8 +352,12 @@ def resource_name_filter(self, name, filter_id, hash_long_names):
if max_name_len != None and hash_long_names == True:
message = None
name_hash = md5sum(str_data=name)[:8].upper()
name = name_hash + '-' + name[((max_name_len-9)*-1):]

# Legacy logic. If this changes it will break IAM role names everywhere.
if filter_id in ['IAM.Role.RoleName', 'IAM.Policy.PolicyName']:
name_hash_len = len(name_hash+'-')+1
name = name_hash + '-' + name[-(max_name_len-name_hash_len):]
else:
name = name_hash + '-' + name[((max_name_len-9)*-1):]

if message != None:
raise StackException(
Expand Down Expand Up @@ -587,21 +592,22 @@ def gen_output(self, name, value):

# Role and Policy names must not be longer than 64 charcters
def create_iam_resource_name(self, name_list, filter_id=None):
role_name = self.create_resource_name_join(
iam_name = self.create_resource_name_join(
name_list=name_list,
separator='-',
camel_case=True,
hash_long_names=True,
filter_id=filter_id
)
if len(role_name) > 64:
name_hash = md5sum(str_data=role_name)[:8].upper()
# len('AABBCCDD-')
name_hash_len = len(name_hash+'-')+1
max_role_name_len = 64
# if len(role_name) > 64:
# name_hash = md5sum(str_data=role_name)[:8].upper()
# # len('AABBCCDD-')
# name_hash_len = len(name_hash+'-')+1
# max_role_name_len = 64

role_name = name_hash + '-' + role_name[-(max_role_name_len-name_hash_len):]
# role_name = name_hash + '-' + role_name[-(max_role_name_len-name_hash_len):]

return role_name
return iam_name

def set_aws_name(self, template_name, first_id=None, second_id=None, third_id=None, fourth_id=None):
if isinstance(first_id, list):
Expand All @@ -626,3 +632,93 @@ def set_aws_name(self, template_name, first_id=None, second_id=None, third_id=No
none_value_ok=True
)
self.aws_name.replace('_', '-')

def set_ecr_repositories_statements(self, ecr_repositories, template, policy_name_prefix, roles):
if ecr_repositories == None or len(ecr_repositories) == 0:
return
index = 0
pull_actions = [
Action('ecr', 'GetDownloadUrlForLayer'),
Action('ecr', 'BatchGetImage'),
]
push_actions = [
Action('ecr', 'GetDownloadUrlForLayer'),
Action('ecr', 'BatchCheckLayerAvailability'),
Action('ecr', 'PutImage'),
Action('ecr', 'InitiateLayerUpload'),
Action('ecr', 'UploadLayerPart'),
Action('ecr', 'CompleteLayerUpload'),
]
push_pull_actions = pull_actions + push_actions
ecr_params = {}
for ecr_permission in ecr_repositories:
ecr_repo = get_model_obj_from_ref(ecr_permission.repository, self.paco_ctx.project)
if ecr_repo.paco_ref not in ecr_params:
param_name = ecr_repo.create_cfn_logical_id()
ecr_repo_name_param = self.create_cfn_parameter(
param_type='String',
name=f'{param_name}ARN',
description='The ARN of the ECR repository',
value=ecr_repo.paco_ref + '.arn',
)
ecr_params[ecr_repo.paco_ref] = ecr_repo_name_param
for ecr_permission in ecr_repositories:
perm_name = f'PacoEcr{index}'
policy_name = self.create_resource_name_join(
name_list=[policy_name_prefix, perm_name],
separator='-',
filter_id='IAM.Policy.PolicyName',
hash_long_names=True,
camel_case=True
)
statement_list = [
Statement(
Effect='Allow',
Action=[
Action('ecr', 'GetAuthorizationToken'),
],
Resource=['*'],
),
]
ecr_repo = get_model_obj_from_ref(ecr_permission.repository, self.paco_ctx.project)
if ecr_permission.permission == 'Pull':
statement_list.append(
Statement(
Effect='Allow',
Action=pull_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)
elif ecr_permission.permission == 'Push':
statement_list.append(
Statement(
Effect='Allow',
Action=push_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)
elif ecr_permission.permission == 'PushAndPull':
statement_list.append(
Statement(
Effect='Allow',
Action=push_pull_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)

troposphere.iam.PolicyType(
title=self.create_cfn_logical_id('CodeBuildProjectPolicy' + perm_name, camel_case=True),
template=template,
PolicyName=policy_name,
PolicyDocument=PolicyDocument(
Statement=statement_list,
),
Roles=roles
)
index += 1
93 changes: 7 additions & 86 deletions src/paco/cftemplates/codebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def create_codebuild_cfn(
name_list=[self.res_name_prefix, 'CodeBuild-Project'],
filter_id='IAM.Role.RoleName'
)

# codecommit_repo_users ManagedPolicies
managed_policy_arns = []
for user_ref in action_config.codecommit_repo_users:
Expand Down Expand Up @@ -376,92 +377,12 @@ def create_codebuild_cfn(
)

# ECR Permission Policies
index = 0
pull_actions = [
Action('ecr', 'GetDownloadUrlForLayer'),
Action('ecr', 'BatchGetImage'),
]
push_actions = [
Action('ecr', 'GetDownloadUrlForLayer'),
Action('ecr', 'BatchCheckLayerAvailability'),
Action('ecr', 'PutImage'),
Action('ecr', 'InitiateLayerUpload'),
Action('ecr', 'UploadLayerPart'),
Action('ecr', 'CompleteLayerUpload'),
]
push_pull_actions = pull_actions + push_actions
ecr_params = {}
for ecr_permission in action_config.ecr_repositories:
ecr_repo = get_model_obj_from_ref(ecr_permission.repository, self.paco_ctx.project)
if ecr_repo.paco_ref not in ecr_params:
param_name = ecr_repo.create_cfn_logical_id()
ecr_repo_name_param = self.create_cfn_parameter(
param_type='String',
name=f'{param_name}ARN',
description='The ARN of the ECR repository',
value=ecr_repo.paco_ref + '.arn',
)
ecr_params[ecr_repo.paco_ref] = ecr_repo_name_param
for ecr_permission in action_config.ecr_repositories:
perm_name = f'PacoEcr{index}'
policy_name = self.create_resource_name_join(
name_list=[self.res_name_prefix, 'CodeBuild-Project', perm_name],
separator='-',
filter_id='IAM.Policy.PolicyName',
hash_long_names=True,
camel_case=True
)
statement_list = [
Statement(
Effect='Allow',
Action=[
Action('ecr', 'GetAuthorizationToken'),
],
Resource=['*'],
),
]
ecr_repo = get_model_obj_from_ref(ecr_permission.repository, self.paco_ctx.project)
if ecr_permission.permission == 'Pull':
statement_list.append(
Statement(
Effect='Allow',
Action=pull_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)
elif ecr_permission.permission == 'Push':
statement_list.append(
Statement(
Effect='Allow',
Action=push_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)
elif ecr_permission.permission == 'PushAndPull':
statement_list.append(
Statement(
Effect='Allow',
Action=push_pull_actions,
Resource=[
troposphere.Ref(ecr_params[ecr_repo.paco_ref])
],
)
)

troposphere.iam.PolicyType(
title=self.create_cfn_logical_id('CodeBuildProjectPolicy' + perm_name, camel_case=True),
template=template,
PolicyName=policy_name,
PolicyDocument=PolicyDocument(
Statement=statement_list,
),
Roles=[troposphere.Ref(project_role_res)]
)
index += 1
self.set_ecr_repositories_statements(
action_config.ecr_repositories,
template,
f'{self.res_name_prefix}-CodeBuild-Project',
[troposphere.Ref(project_role_res)]
)

# CodeBuild Project Resource
timeout_mins_param = self.create_cfn_parameter(
Expand Down

0 comments on commit e9d6d65

Please sign in to comment.