Skip to content

ContainerSigningInfrastructure

Bases: Construct

AWS CDK construct for container image signing infrastructure.

Creates signing infrastructure for container images using AWS Signer, KMS asymmetric keys, and IAM roles. Enables cryptographic signing for supply chain security and image integrity verification.

Resources created:

  • KMS asymmetric key (ECC_NIST_P256) for digital signatures
  • AWS Signer profile for Notation-OCI format
  • IAM role for CodeBuild with signing permissions
  • SSM parameters for resource ARN storage
Example
        signing_infra = ContainerSigningInfrastructure(
            self, 
            "ContainerSigning",
            app_helper=app_helper,
            enable_key_rotation=True,
            key_removal_policy=RemovalPolicy.RETAIN
        )

        # Use the signing role in CodeBuild
        build_project = codebuild.Project(
            self, "SigningBuild",
            role=signing_infra.signing_role,
            # ... other configuration
        )
Source code in mare_aws_common_lib/constructs/container_signing_infrastructure.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
class ContainerSigningInfrastructure(Construct):
    """AWS CDK construct for container image signing infrastructure.

    Creates signing infrastructure for container images using AWS Signer, KMS
    asymmetric keys, and IAM roles. Enables cryptographic signing for supply
    chain security and image integrity verification.

    Resources created:

    - KMS asymmetric key (ECC_NIST_P256) for digital signatures
    - AWS Signer profile for Notation-OCI format
    - IAM role for CodeBuild with signing permissions
    - SSM parameters for resource ARN storage

    Example:
        ```python
                signing_infra = ContainerSigningInfrastructure(
                    self, 
                    "ContainerSigning",
                    app_helper=app_helper,
                    enable_key_rotation=True,
                    key_removal_policy=RemovalPolicy.RETAIN
                )

                # Use the signing role in CodeBuild
                build_project = codebuild.Project(
                    self, "SigningBuild",
                    role=signing_infra.signing_role,
                    # ... other configuration
                )
        ```
    """

    BASE_NAME = "container-signing"

    def __init__(self, scope: Construct, id: str, app_helper: ApplicationHelper, *, enable_key_rotation: bool = False,
        key_removal_policy: RemovalPolicy = RemovalPolicy.RETAIN, signing_platform: str = "Notation-OCI-SHA384-ECDSA", **kwargs):
        """Initialize container signing infrastructure.

        Creates KMS key, AWS Signer profile, and IAM role for container signing
        operations. Stores resource ARNs in SSM Parameter Store for cross-stack access.

        Args:
            scope: CDK construct scope for resource creation
            id: Unique identifier for this construct
            app_helper: Application helper for environment configuration and naming
            enable_key_rotation: Enable automatic KMS key rotation. Defaults to False
                to maintain signature compatibility.
            key_removal_policy: CDK removal policy for KMS key. Defaults to RETAIN
                to prevent accidental deletion.
            signing_platform: AWS Signer platform identifier. Defaults to 
                "Notation-OCI-SHA384-ECDSA" for OCI container signing.
            **kwargs: Additional arguments passed to parent Construct

        Stores SSM Parameters:

        - SIGNING_KEY_ARN: KMS key ARN
        - SIGNING_PROFILE_ARN: Signer profile ARN
        - SIGNING_PROFILE_NAME: Signer profile name
        """
        super().__init__(scope, id, **kwargs)

        # Create KMS Key
        key_name, _ =  ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.KMS, 
                                                            resource_name=f"{self.BASE_NAME}-key", max_length=AWSResourceNameLength.KMS_ALIAS.value)
        signing_key = kms.Key(
            self,
            ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.KMS, resource_type_suffix=f"{self.BASE_NAME}-key"),
            description="KMS key for container image signing with AWS Signer",
            alias=f"alias/{key_name}",
            enable_key_rotation=enable_key_rotation,
            removal_policy=key_removal_policy,
            key_spec=kms.KeySpec.ECC_NIST_P256,
            key_usage=kms.KeyUsage.SIGN_VERIFY,
        )
        app_helper.store_parameter(self, "SIGNING_KEY_ARN", signing_key.key_arn)

        # Create Signing Profile
        # profile_name, _ = ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.SIGNER, resource_name=f"{self.BASE_NAME}-profile", 
        #                                                             max_length=AWSResourceNameLength.SIGNING_PROFILE.value)
        signing_profile = signer.CfnSigningProfile(
            self,
            ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.SIGNER, resource_type_suffix=f"{self.BASE_NAME}-profile"),
            platform_id=signing_platform
            # No signing_material needed - AWS Signer will use KMS directly
        )
        app_helper.store_parameter(self, "SIGNING_PROFILE_ARN", signing_profile.attr_arn)
        app_helper.store_parameter(self, "SIGNING_PROFILE_NAME", signing_profile.attr_profile_name)

        signing_role = self._create_signing_role(app_helper, signing_key.key_arn, signing_profile.attr_arn)

        signing_key.grant_sign_verify(signing_role)

    def _create_signing_role(self, app_helper: ApplicationHelper, signing_key_arn: str, signing_profile_arn) -> iam.Role:
        """Create IAM role for container signing operations.

        Creates role with permissions for CodeBuild to sign container images using
        KMS and AWS Signer. Includes access to ECR, VPC networking, SSM parameters,
        and CloudWatch Logs.

        Permission sets included:

        - VPC Access: Network interface management for VPC-enabled builds
        - ECR Access: Container image pull, push, and metadata operations
        - KMS Signing: Cryptographic signing with the specified key
        - AWS Signer: Profile usage and job management
        - SSM Parameters: Read signing configuration
        - CloudWatch Logs: Write build and signing logs

        Args:
            app_helper: Application helper for configuration and naming
            signing_key_arn: ARN of KMS key to grant signing permissions
            signing_profile_arn: ARN of Signer profile for scoped access

        Returns:
            IAM role configured for CodeBuild signing operations
        """
        region = app_helper.get_from_common("region")
        account_id = app_helper.get_from_env("account_id")
        role_name, _ = ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.SIGNER, resource_name=self.BASE_NAME, 
                                                         max_length=AWSResourceNameLength.ROLE.value)
        return iam.Role(
            self, ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.SIGNER, resource_type_suffix=f"{self.BASE_NAME}-role"),
            role_name=role_name,
            assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
            description="Role for CodeBuild to sign containers with KMS and AWS Signer",
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name("AWSCodeBuildDeveloperAccess")
            ],
            inline_policies={
                "ContainerSigningWithKMSPolicy": iam.PolicyDocument(
                    statements=[
                         # S3 permissions for source artifacts  <-- ADD THIS
                        iam.PolicyStatement(
                            sid="S3ArtifactsAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "s3:GetObject",
                                "s3:GetObjectVersion",
                                "s3:ListBucket",
                                "s3:PutObject",
                                "s3:GetBucketLocation",
                                "s3:GetBucketVersioning"
                            ],
                            resources=["*"]
                        ),
                        # KMS permissions for S3 bucket encryption  <-- ADD THIS
                        iam.PolicyStatement(
                            sid="KMSS3EncryptionAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "kms:Decrypt",
                                "kms:GenerateDataKey",
                                "kms:DescribeKey"
                            ],
                            resources=["*"]
                        ),
                        # EC2 permissions
                        iam.PolicyStatement(
                            sid="VPCAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "ec2:CreateNetworkInterface",
                                "ec2:DescribeNetworkInterfaces",
                                "ec2:DeleteNetworkInterface",
                                "ec2:DescribeSubnets",
                                "ec2:DescribeSecurityGroups",
                                "ec2:DescribeDhcpOptions",
                                "ec2:DescribeVpcs",
                                "ec2:CreateNetworkInterfacePermission"
                            ],
                            resources=["*"]
                        ),
                        # ECR permissions
                        iam.PolicyStatement(
                            sid="ECRAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "ecr:GetAuthorizationToken",
                                "ecr:BatchCheckLayerAvailability",
                                "ecr:GetDownloadUrlForLayer",
                                "ecr:BatchGetImage",
                                "ecr:PutImage",
                                "ecr:InitiateLayerUpload",
                                "ecr:UploadLayerPart",
                                "ecr:CompleteLayerUpload",
                                "ecr:DescribeImages",
                                "ecr:ListImages",
                            ],
                            resources=["*"],
                        ),
                        # KMS permissions for signing
                        iam.PolicyStatement(
                            sid="KMSSigningAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "kms:Sign",
                                "kms:Verify",
                                "kms:DescribeKey",
                                "kms:GetPublicKey",
                            ],
                            resources=[signing_key_arn],
                        ),
                        # AWS Signer permissions
                        iam.PolicyStatement(
                            sid="SignerAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "signer:StartSigningJob",
                                "signer:DescribeSigningJob",
                                "signer:GetSigningProfile",
                                "signer:ListSigningJobs",
                                "signer:GetSigningPlatform",
                                "signer:PutSigningProfile",
                                "signer:SignPayload", 
                                "signer:GetRevocationStatus"
                            ],
                            resources=[
                                signing_profile_arn,
                                f"arn:aws:signer:{region}:{account_id}:/signing-jobs/*",
                            ],
                        ),
                        # SSM Parameter Store
                        iam.PolicyStatement(
                            sid="SSMParameterAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "ssm:GetParameter",
                                "ssm:GetParameters",
                                "ssm:GetParametersByPath",
                            ],
                            resources=[
                                f"arn:aws:ssm:{region}:{account_id}:parameter/{self.BASE_NAME}/*"
                            ],
                        ),
                        # CloudWatch Logs
                        iam.PolicyStatement(
                            sid="CloudWatchLogsAccess",
                            effect=iam.Effect.ALLOW,
                            actions=[
                                "logs:CreateLogGroup",
                                "logs:CreateLogStream",
                                "logs:PutLogEvents",
                            ],
                            resources=[
                                f"arn:aws:logs:{region}:{account_id}:log-group:/aws/codebuild/*"
                            ],
                        )
                    ]
                )
            }
        )

Attributes

BASE_NAME = 'container-signing' class-attribute instance-attribute

Functions

__init__(scope, id, app_helper, *, enable_key_rotation=False, key_removal_policy=RemovalPolicy.RETAIN, signing_platform='Notation-OCI-SHA384-ECDSA', **kwargs)

Initialize container signing infrastructure.

Creates KMS key, AWS Signer profile, and IAM role for container signing operations. Stores resource ARNs in SSM Parameter Store for cross-stack access.

Parameters:

Name Type Description Default
scope Construct

CDK construct scope for resource creation

required
id str

Unique identifier for this construct

required
app_helper ApplicationHelper

Application helper for environment configuration and naming

required
enable_key_rotation bool

Enable automatic KMS key rotation. Defaults to False to maintain signature compatibility.

False
key_removal_policy RemovalPolicy

CDK removal policy for KMS key. Defaults to RETAIN to prevent accidental deletion.

RETAIN
signing_platform str

AWS Signer platform identifier. Defaults to "Notation-OCI-SHA384-ECDSA" for OCI container signing.

'Notation-OCI-SHA384-ECDSA'
**kwargs

Additional arguments passed to parent Construct

{}

Stores SSM Parameters:

  • SIGNING_KEY_ARN: KMS key ARN
  • SIGNING_PROFILE_ARN: Signer profile ARN
  • SIGNING_PROFILE_NAME: Signer profile name
Source code in mare_aws_common_lib/constructs/container_signing_infrastructure.py
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def __init__(self, scope: Construct, id: str, app_helper: ApplicationHelper, *, enable_key_rotation: bool = False,
    key_removal_policy: RemovalPolicy = RemovalPolicy.RETAIN, signing_platform: str = "Notation-OCI-SHA384-ECDSA", **kwargs):
    """Initialize container signing infrastructure.

    Creates KMS key, AWS Signer profile, and IAM role for container signing
    operations. Stores resource ARNs in SSM Parameter Store for cross-stack access.

    Args:
        scope: CDK construct scope for resource creation
        id: Unique identifier for this construct
        app_helper: Application helper for environment configuration and naming
        enable_key_rotation: Enable automatic KMS key rotation. Defaults to False
            to maintain signature compatibility.
        key_removal_policy: CDK removal policy for KMS key. Defaults to RETAIN
            to prevent accidental deletion.
        signing_platform: AWS Signer platform identifier. Defaults to 
            "Notation-OCI-SHA384-ECDSA" for OCI container signing.
        **kwargs: Additional arguments passed to parent Construct

    Stores SSM Parameters:

    - SIGNING_KEY_ARN: KMS key ARN
    - SIGNING_PROFILE_ARN: Signer profile ARN
    - SIGNING_PROFILE_NAME: Signer profile name
    """
    super().__init__(scope, id, **kwargs)

    # Create KMS Key
    key_name, _ =  ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.KMS, 
                                                        resource_name=f"{self.BASE_NAME}-key", max_length=AWSResourceNameLength.KMS_ALIAS.value)
    signing_key = kms.Key(
        self,
        ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.KMS, resource_type_suffix=f"{self.BASE_NAME}-key"),
        description="KMS key for container image signing with AWS Signer",
        alias=f"alias/{key_name}",
        enable_key_rotation=enable_key_rotation,
        removal_policy=key_removal_policy,
        key_spec=kms.KeySpec.ECC_NIST_P256,
        key_usage=kms.KeyUsage.SIGN_VERIFY,
    )
    app_helper.store_parameter(self, "SIGNING_KEY_ARN", signing_key.key_arn)

    # Create Signing Profile
    # profile_name, _ = ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.SIGNER, resource_name=f"{self.BASE_NAME}-profile", 
    #                                                             max_length=AWSResourceNameLength.SIGNING_PROFILE.value)
    signing_profile = signer.CfnSigningProfile(
        self,
        ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.SIGNER, resource_type_suffix=f"{self.BASE_NAME}-profile"),
        platform_id=signing_platform
        # No signing_material needed - AWS Signer will use KMS directly
    )
    app_helper.store_parameter(self, "SIGNING_PROFILE_ARN", signing_profile.attr_arn)
    app_helper.store_parameter(self, "SIGNING_PROFILE_NAME", signing_profile.attr_profile_name)

    signing_role = self._create_signing_role(app_helper, signing_key.key_arn, signing_profile.attr_arn)

    signing_key.grant_sign_verify(signing_role)

_create_signing_role(app_helper, signing_key_arn, signing_profile_arn)

Create IAM role for container signing operations.

Creates role with permissions for CodeBuild to sign container images using KMS and AWS Signer. Includes access to ECR, VPC networking, SSM parameters, and CloudWatch Logs.

Permission sets included:

  • VPC Access: Network interface management for VPC-enabled builds
  • ECR Access: Container image pull, push, and metadata operations
  • KMS Signing: Cryptographic signing with the specified key
  • AWS Signer: Profile usage and job management
  • SSM Parameters: Read signing configuration
  • CloudWatch Logs: Write build and signing logs

Parameters:

Name Type Description Default
app_helper ApplicationHelper

Application helper for configuration and naming

required
signing_key_arn str

ARN of KMS key to grant signing permissions

required
signing_profile_arn

ARN of Signer profile for scoped access

required

Returns:

Type Description
Role

IAM role configured for CodeBuild signing operations

Source code in mare_aws_common_lib/constructs/container_signing_infrastructure.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def _create_signing_role(self, app_helper: ApplicationHelper, signing_key_arn: str, signing_profile_arn) -> iam.Role:
    """Create IAM role for container signing operations.

    Creates role with permissions for CodeBuild to sign container images using
    KMS and AWS Signer. Includes access to ECR, VPC networking, SSM parameters,
    and CloudWatch Logs.

    Permission sets included:

    - VPC Access: Network interface management for VPC-enabled builds
    - ECR Access: Container image pull, push, and metadata operations
    - KMS Signing: Cryptographic signing with the specified key
    - AWS Signer: Profile usage and job management
    - SSM Parameters: Read signing configuration
    - CloudWatch Logs: Write build and signing logs

    Args:
        app_helper: Application helper for configuration and naming
        signing_key_arn: ARN of KMS key to grant signing permissions
        signing_profile_arn: ARN of Signer profile for scoped access

    Returns:
        IAM role configured for CodeBuild signing operations
    """
    region = app_helper.get_from_common("region")
    account_id = app_helper.get_from_env("account_id")
    role_name, _ = ResourceNaming.get_name_for_resource(app_helper, AWSResourceType.SIGNER, resource_name=self.BASE_NAME, 
                                                     max_length=AWSResourceNameLength.ROLE.value)
    return iam.Role(
        self, ResourceNaming.get_cfn_logical_id(app_helper, AWSResourceType.SIGNER, resource_type_suffix=f"{self.BASE_NAME}-role"),
        role_name=role_name,
        assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
        description="Role for CodeBuild to sign containers with KMS and AWS Signer",
        managed_policies=[
            iam.ManagedPolicy.from_aws_managed_policy_name("AWSCodeBuildDeveloperAccess")
        ],
        inline_policies={
            "ContainerSigningWithKMSPolicy": iam.PolicyDocument(
                statements=[
                     # S3 permissions for source artifacts  <-- ADD THIS
                    iam.PolicyStatement(
                        sid="S3ArtifactsAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "s3:GetObject",
                            "s3:GetObjectVersion",
                            "s3:ListBucket",
                            "s3:PutObject",
                            "s3:GetBucketLocation",
                            "s3:GetBucketVersioning"
                        ],
                        resources=["*"]
                    ),
                    # KMS permissions for S3 bucket encryption  <-- ADD THIS
                    iam.PolicyStatement(
                        sid="KMSS3EncryptionAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "kms:Decrypt",
                            "kms:GenerateDataKey",
                            "kms:DescribeKey"
                        ],
                        resources=["*"]
                    ),
                    # EC2 permissions
                    iam.PolicyStatement(
                        sid="VPCAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "ec2:CreateNetworkInterface",
                            "ec2:DescribeNetworkInterfaces",
                            "ec2:DeleteNetworkInterface",
                            "ec2:DescribeSubnets",
                            "ec2:DescribeSecurityGroups",
                            "ec2:DescribeDhcpOptions",
                            "ec2:DescribeVpcs",
                            "ec2:CreateNetworkInterfacePermission"
                        ],
                        resources=["*"]
                    ),
                    # ECR permissions
                    iam.PolicyStatement(
                        sid="ECRAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "ecr:GetAuthorizationToken",
                            "ecr:BatchCheckLayerAvailability",
                            "ecr:GetDownloadUrlForLayer",
                            "ecr:BatchGetImage",
                            "ecr:PutImage",
                            "ecr:InitiateLayerUpload",
                            "ecr:UploadLayerPart",
                            "ecr:CompleteLayerUpload",
                            "ecr:DescribeImages",
                            "ecr:ListImages",
                        ],
                        resources=["*"],
                    ),
                    # KMS permissions for signing
                    iam.PolicyStatement(
                        sid="KMSSigningAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "kms:Sign",
                            "kms:Verify",
                            "kms:DescribeKey",
                            "kms:GetPublicKey",
                        ],
                        resources=[signing_key_arn],
                    ),
                    # AWS Signer permissions
                    iam.PolicyStatement(
                        sid="SignerAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "signer:StartSigningJob",
                            "signer:DescribeSigningJob",
                            "signer:GetSigningProfile",
                            "signer:ListSigningJobs",
                            "signer:GetSigningPlatform",
                            "signer:PutSigningProfile",
                            "signer:SignPayload", 
                            "signer:GetRevocationStatus"
                        ],
                        resources=[
                            signing_profile_arn,
                            f"arn:aws:signer:{region}:{account_id}:/signing-jobs/*",
                        ],
                    ),
                    # SSM Parameter Store
                    iam.PolicyStatement(
                        sid="SSMParameterAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "ssm:GetParameter",
                            "ssm:GetParameters",
                            "ssm:GetParametersByPath",
                        ],
                        resources=[
                            f"arn:aws:ssm:{region}:{account_id}:parameter/{self.BASE_NAME}/*"
                        ],
                    ),
                    # CloudWatch Logs
                    iam.PolicyStatement(
                        sid="CloudWatchLogsAccess",
                        effect=iam.Effect.ALLOW,
                        actions=[
                            "logs:CreateLogGroup",
                            "logs:CreateLogStream",
                            "logs:PutLogEvents",
                        ],
                        resources=[
                            f"arn:aws:logs:{region}:{account_id}:log-group:/aws/codebuild/*"
                        ],
                    )
                ]
            )
        }
    )