diff --git a/Makefile b/Makefile index 6605b8b96..1bdaf935d 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ deploy-image: docker push ${account}.dkr.ecr.${region}.amazonaws.com/${repo}:${image-tag} assume-role: - aws sts assume-role --role-arn "arn:aws:iam::${REMOTE_ACCOUNT_ID}:role/${REMOTE_ROLE}" --role-session-name "session1" >.assume_role_json + aws sts assume-role --role-arn "arn:aws:iam::${REMOTE_ACCOUNT_ID}:role/${REMOTE_ROLE}" --external-id ${EXTERNAL_ID} --role-session-name "session1" >.assume_role_json echo "export AWS_ACCESS_KEY_ID=$$(cat .assume_role_json | jq '.Credentials.AccessKeyId' -r)" >.env.assumed_role echo "export AWS_SECRET_ACCESS_KEY=$$(cat .assume_role_json | jq '.Credentials.SecretAccessKey' -r)" >>.env.assumed_role echo "export AWS_SESSION_TOKEN=$$(cat .assume_role_json | jq '.Credentials.SessionToken' -r)" >>.env.assumed_role diff --git a/deploy/stacks/backend_stack.py b/deploy/stacks/backend_stack.py index 0dd9f4350..66f0e6d64 100644 --- a/deploy/stacks/backend_stack.py +++ b/deploy/stacks/backend_stack.py @@ -25,6 +25,7 @@ from .sqs import SqsStack from .trigger_function_stack import TriggerFunctionStack from .vpc import VpcStack +from .iam_utils import get_tooling_account_external_id class BackendStack(Stack): @@ -138,6 +139,7 @@ def __init__( f'{resource_prefix}-{envname}-frontend-config-role', role_name=f'{resource_prefix}-{envname}-frontend-config-role', assumed_by=iam.AccountPrincipal(tooling_account_id), + external_ids=[get_tooling_account_external_id(self.account)], ) cross_account_frontend_config_role.add_to_policy( iam.PolicyStatement( diff --git a/deploy/stacks/cloudfront.py b/deploy/stacks/cloudfront.py index 6b07ba8e1..72798a85c 100644 --- a/deploy/stacks/cloudfront.py +++ b/deploy/stacks/cloudfront.py @@ -23,6 +23,7 @@ from .pyNestedStack import pyNestedClass from .solution_bundling import SolutionBundling from .waf_rules import get_waf_rules +from .iam_utils import get_tooling_account_external_id class CloudfrontDistro(pyNestedClass): @@ -227,6 +228,7 @@ def __init__( f'S3DeploymentRole{envname}', role_name=f'{resource_prefix}-{envname}-S3DeploymentRole', assumed_by=iam.AccountPrincipal(tooling_account_id), + external_ids=[get_tooling_account_external_id(self.account)], ) resources_for_cross_account = [] resources_for_cross_account.append(f'{cloudfront_bucket.bucket_arn}/*') diff --git a/deploy/stacks/cognito.py b/deploy/stacks/cognito.py index 661dc87a0..e0123a9f6 100644 --- a/deploy/stacks/cognito.py +++ b/deploy/stacks/cognito.py @@ -21,6 +21,7 @@ from .pyNestedStack import pyNestedClass from .solution_bundling import SolutionBundling from .waf_rules import get_waf_rules +from .iam_utils import get_tooling_account_external_id class IdpStack(pyNestedClass): @@ -198,6 +199,7 @@ def __init__( f'{resource_prefix}-{envname}-cognito-config-role', role_name=f'{resource_prefix}-{envname}-cognito-config-role', assumed_by=iam.AccountPrincipal(tooling_account_id), + external_ids=[get_tooling_account_external_id(self.account)], ) cross_account_frontend_config_role.add_to_policy( iam.PolicyStatement( diff --git a/deploy/stacks/container.py b/deploy/stacks/container.py index 5150e08b1..745e4c01d 100644 --- a/deploy/stacks/container.py +++ b/deploy/stacks/container.py @@ -15,6 +15,7 @@ from .pyNestedStack import pyNestedClass from .run_if import run_if from .deploy_config import deploy_config +from .iam_utils import get_tooling_account_external_id class ContainerStack(pyNestedClass): @@ -562,10 +563,15 @@ def create_cicd_stacks_updater_role(self, envname, resource_prefix, tooling_acco self, id=f'StackUpdaterCBRole{envname}', role_name=f'{resource_prefix}-{envname}-cb-stackupdater-role', - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('codebuild.amazonaws.com'), - iam.AccountPrincipal(tooling_account_id), - ), + assumed_by=iam.AccountPrincipal(tooling_account_id), + external_ids=[get_tooling_account_external_id(self.account)], + ) + cicd_stacks_updater_role.assume_role_policy.add_statements( + iam.PolicyStatement( + effect=iam.Effect.ALLOW, + principals=[iam.ServicePrincipal('codebuild.amazonaws.com')], + actions=['sts:AssumeRole'], + ) ) cicd_stacks_updater_role.add_to_policy( iam.PolicyStatement( diff --git a/deploy/stacks/iam_utils.py b/deploy/stacks/iam_utils.py new file mode 100644 index 000000000..8c63179d2 --- /dev/null +++ b/deploy/stacks/iam_utils.py @@ -0,0 +1,5 @@ +import hashlib + + +def get_tooling_account_external_id(account: str): + return hashlib.sha256(str(account).encode('UTF-8')).hexdigest()[:12] diff --git a/deploy/stacks/pipeline.py b/deploy/stacks/pipeline.py index 804792923..5411e337e 100644 --- a/deploy/stacks/pipeline.py +++ b/deploy/stacks/pipeline.py @@ -20,6 +20,7 @@ from .codeartifact import CodeArtifactStack from .ecr_stage import ECRStage from .vpc import VpcStack +from .iam_utils import get_tooling_account_external_id class PipelineStack(Stack): @@ -683,6 +684,7 @@ def set_approval_tests_stage( 'echo "[profile buildprofile]" > ~/.aws/config', f'echo "role_arn = {frontend_deployment_role_arn}" >> ~/.aws/config', 'echo "credential_source = EcsContainer" >> ~/.aws/config', + f'echo "external_id = {get_tooling_account_external_id(target_env["account"])}" >> ~/.aws/config', 'aws sts get-caller-identity --profile buildprofile', f'export COGNITO_CLIENT=$(aws ssm get-parameter --name /dataall/{target_env["envname"]}/cognito/appclient --profile buildprofile --output text --query "Parameter.Value")', f'export API_ENDPOINT=$(aws ssm get-parameter --name /dataall/{target_env["envname"]}/apiGateway/backendUrl --profile buildprofile --output text --query "Parameter.Value")', @@ -728,6 +730,7 @@ def set_stacks_updater_stage( 'echo "[profile buildprofile]" > ~/.aws/config', f'echo "role_arn = arn:aws:iam::{target_env["account"]}:role/{self.resource_prefix}-{target_env["envname"]}-cb-stackupdater-role" >> ~/.aws/config', 'echo "credential_source = EcsContainer" >> ~/.aws/config', + f'echo "external_id = {get_tooling_account_external_id(target_env["account"])}" >> ~/.aws/config', 'aws sts get-caller-identity --profile buildprofile', f"export cluster_name=$(aws ssm get-parameter --name /dataall/{target_env['envname']}/ecs/cluster/name --profile buildprofile --output text --query 'Parameter.Value')", f"export private_subnets=$(aws ssm get-parameter --name /dataall/{target_env['envname']}/ecs/private_subnets --profile buildprofile --output text --query 'Parameter.Value')", @@ -788,6 +791,7 @@ def set_cloudfront_stage(self, target_env): 'echo "[profile buildprofile]" > ~/.aws/config', f'echo "role_arn = arn:aws:iam::{target_env["account"]}:role/{self.resource_prefix}-{target_env["envname"]}-S3DeploymentRole" >> ~/.aws/config', 'echo "credential_source = EcsContainer" >> ~/.aws/config', + f'echo "external_id = {get_tooling_account_external_id(target_env["account"])}" >> ~/.aws/config', 'aws sts get-caller-identity --profile buildprofile', 'export AWS_PROFILE=buildprofile', 'pip install boto3==1.34.35', @@ -826,7 +830,7 @@ def set_cloudfront_stage(self, target_env): ), commands=[ f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', - f"make assume-role REMOTE_ACCOUNT_ID={target_env['account']} REMOTE_ROLE={self.resource_prefix}-{target_env['envname']}-S3DeploymentRole", + f"make assume-role REMOTE_ACCOUNT_ID={target_env['account']} REMOTE_ROLE={self.resource_prefix}-{target_env['envname']}-S3DeploymentRole EXTERNAL_ID={get_tooling_account_external_id(target_env['account'])}", '. ./.env.assumed_role', 'aws sts get-caller-identity', 'export AWS_DEFAULT_REGION=us-east-1', @@ -927,6 +931,7 @@ def set_albfront_stage(self, target_env, repository_name): 'echo "[profile buildprofile]" > ~/.aws/config', f'echo "role_arn = {frontend_deployment_role_arn}" >> ~/.aws/config', 'echo "credential_source = EcsContainer" >> ~/.aws/config', + f'echo "external_id = {get_tooling_account_external_id(target_env["account"])}" >> ~/.aws/config', 'aws sts get-caller-identity --profile buildprofile', 'export AWS_PROFILE=buildprofile', 'pip install boto3==1.34.35',