UNPKG

@k9securityio/k9-cdk

Version:

Provision strong AWS security policies easily using the AWS CDK.

328 lines (290 loc) 11.9 kB
#!/usr/bin/env node import * as cdk from "aws-cdk-lib"; import {RemovalPolicy, Tags} from "aws-cdk-lib"; // import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; // import * as cforigins from "aws-cdk-lib/aws-cloudfront-origins"; import * as dynamodb from "aws-cdk-lib/aws-dynamodb"; import * as kms from "aws-cdk-lib/aws-kms"; import * as s3 from "aws-cdk-lib/aws-s3"; import {BlockPublicAccess, BucketEncryption} from "aws-cdk-lib/aws-s3"; import * as events from 'aws-cdk-lib/aws-events'; import * as sqs from 'aws-cdk-lib/aws-sqs'; import * as k9 from "../lib"; import {K9EventBusResourcePolicyProps} from "../src/events"; import {K9SQSResourcePolicyProps} from "../src/sqs"; const administerResourceArns = [ // for development "arn:aws:iam::139710491120:user/ci", "arn:aws:iam::139710491120:user/skuenzli", "arn:aws:iam::139710491120:role/k9-dev-appeng", "arn:aws:iam::139710491120:role/cdk-hnb659fds-cfn-exec-role-139710491120-us-east-1" ]; const readConfigArns = administerResourceArns.concat( [ "arn:aws:iam::139710491120:role/k9-auditor", // for audit "arn:aws:iam::139710491120:role/k9-backend-dev" // for integration tests ] ); const readWriteDataArns = [ "arn:aws:iam::123456789012:role/app-backend", "arn:aws:iam::139710491120:role/k9-dev-appeng", "arn:aws:sts::139710491120:assumed-role/k9-dev-appeng/console" ]; const readDataArns = [ "arn:aws:iam::123456789012:role/customer-service" ]; const app = new cdk.App( // Can configure features with an AppProps: // https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.AppProps.html ); const stack = new cdk.Stack(app, 'K9PolicyLibV2IntegrationTest'); const bucket = new s3.Bucket(stack, 'TestBucket', { bucketName: 'k9-cdk-v2-internal-bucket-test', removalPolicy: RemovalPolicy.DESTROY, }); const k9BucketPolicyProps: k9.s3.K9BucketPolicyProps = { bucket: bucket, k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns, }, { accessCapabilities: [ k9.k9policy.AccessCapability.READ_DATA, k9.k9policy.AccessCapability.WRITE_DATA ], allowPrincipalArns: readWriteDataArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_DATA, allowPrincipalArns: readDataArns, restrictToPrincipalOrgIDs: ['o-y2fdpt5ftt'], } // omit access spec for delete-data because it is unneeded ) }; k9.s3.grantAccessViaResourcePolicy(stack, "S3Bucket", k9BucketPolicyProps); const websiteBucket = new s3.Bucket(stack, 'WebsiteBucket', { bucketName: 'k9-cdk-v2-public-website-test', removalPolicy: RemovalPolicy.DESTROY, encryption: BucketEncryption.S3_MANAGED, blockPublicAccess: new BlockPublicAccess({ blockPublicPolicy: false }) }); const websiteK9BucketPolicyProps: k9.s3.K9BucketPolicyProps = { bucket: websiteBucket, k9DesiredAccess: k9BucketPolicyProps.k9DesiredAccess.concat([]), publicReadAccess: true, encryption: BucketEncryption.S3_MANAGED, }; k9.s3.grantAccessViaResourcePolicy(stack, "S3PublicWebsite", websiteK9BucketPolicyProps); const autoDeleteBucket = new s3.Bucket(stack, 'AutoDeleteBucket', { bucketName: 'k9-cdk-v2-auto-delete-test', removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, }); console.log(`original autoDeleteBucket.policy: ${autoDeleteBucket.policy}`); const k9AutoDeleteBucketPolicyProps: k9.s3.K9BucketPolicyProps = { bucket: autoDeleteBucket, k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns, }, { accessCapabilities: k9.k9policy.AccessCapability.WRITE_DATA, allowPrincipalArns: readWriteDataArns, } ) }; k9.s3.grantAccessViaResourcePolicy(stack, 'AutoDeleteBucket', k9AutoDeleteBucketPolicyProps); console.log(`k9 autoDeleteBucket.policy: ${autoDeleteBucket.policy}`); // Now create a Key policy that grants access the same access const k9KeyPolicyProps: k9.kms.K9KeyPolicyProps = { k9DesiredAccess: k9BucketPolicyProps.k9DesiredAccess, //trustAccountIdentities: true // the effective default trustAccountIdentities: false }; const keyPolicy = k9.kms.makeKeyPolicy(k9KeyPolicyProps); // Set CDK preference @aws-cdk/aws-kms:defaultKeyPolicies to true in cdk.json const key = new kms.Key(stack, 'KMSKey', { alias: 'k9-cdk-v2-integration-test', policy: keyPolicy, }); // Created test distribution manually because I haven't fully sorted through // https://github.com/aws/aws-cdk/issues/21771 // // let cloudfrontDistribution = new cloudfront.Distribution(stack, 'oac-bucket-dist', { // comment: 'k9-cdk integration test distribution for CloudFront OAC', // defaultBehavior: { // origin: new cforigins.S3Origin(cloudfrontOACBucket, { // // }), // }, // }); let cloudfrontDistributionId = 'E1OHGXOERP1X0D' let cloudfrontDistributionArn = `arn:aws:cloudfront::${stack.account}:distribution/${cloudfrontDistributionId}` // let cloudfrontDistributionArn = `arn:aws:cloudfront::${stack.account}:distribution/${cloudfrontDistribution.distributionId}` const cloudfrontOACk9KeyPolicyProps: k9.kms.K9KeyPolicyProps = { k9DesiredAccess: k9BucketPolicyProps.k9DesiredAccess, trustAccountIdentities: false, awsServiceAccessGenerators: new Array<k9.k9policy.IAWSServiceAccessGenerator>( new k9.kms.CloudFrontOACReadAccessGenerator(cloudfrontDistributionArn), ) }; const cloudfrontOACKeyPolicy = k9.kms.makeKeyPolicy(cloudfrontOACk9KeyPolicyProps); const cloudfrontOACKey = new kms.Key(stack, 'CloudFrontOACKMSKey', { alias: 'k9-cdk-v2-cloudfront-oac-test', policy: cloudfrontOACKeyPolicy, }); const cloudfrontOACBucket = new s3.Bucket(stack, 'CloudFrontOACBucket', { bucketName: 'k9-cdk-v2-cloudfront-oac-test', removalPolicy: RemovalPolicy.DESTROY, encryption: BucketEncryption.KMS, encryptionKey: cloudfrontOACKey }); const cloudfrontOACBucketPolicyProps: k9.s3.K9BucketPolicyProps = { bucket: cloudfrontOACBucket, k9DesiredAccess: k9BucketPolicyProps.k9DesiredAccess.concat([]), encryption: BucketEncryption.KMS_MANAGED, awsServiceAccessGenerators: new Array<k9.k9policy.IAWSServiceAccessGenerator>( new k9.s3.CloudFrontOACReadAccessGenerator(cloudfrontOACBucket, cloudfrontDistributionArn), ) }; k9.s3.grantAccessViaResourcePolicy(stack, "CloudFrontOACBucket", cloudfrontOACBucketPolicyProps); // Demonstrate generating and applying a DynamoDB resource policy const ddbResourcePolicyProps: k9.dynamodb.K9DynamoDBResourcePolicyProps = { k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns.concat([ "arn:aws:iam::139710491120:role/aws-service-role/access-analyzer.amazonaws.com/AWSServiceRoleForAccessAnalyzer" ]), }, { accessCapabilities: k9.k9policy.AccessCapability.READ_DATA, allowPrincipalArns: readWriteDataArns, }, { accessCapabilities: k9.k9policy.AccessCapability.WRITE_DATA, allowPrincipalArns: readWriteDataArns, restrictToPrincipalOrgIDs: ['o-y2fdpt5ftt'], }, { accessCapabilities: k9.k9policy.AccessCapability.DELETE_DATA, allowPrincipalArns: readWriteDataArns, }, ) }; const ddbResourcePolicy = k9.dynamodb.makeResourcePolicy(ddbResourcePolicyProps); const table = new dynamodb.TableV2(stack, 'k9-cdk-v2-int-test', { partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, removalPolicy: cdk.RemovalPolicy.DESTROY, resourcePolicy: ddbResourcePolicy }); const queue = new sqs.Queue(stack, 'k9-cdk-v2-int-test-queue', { queueName: 'k9-cdk-v2-int-test', }) const k9SQSResourcePolicyProps: K9SQSResourcePolicyProps = { queue: queue, k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns.concat([ "arn:aws:iam::139710491120:role/aws-service-role/access-analyzer.amazonaws.com/AWSServiceRoleForAccessAnalyzer", ]), }, { accessCapabilities: k9.k9policy.AccessCapability.READ_DATA, allowPrincipalArns: readWriteDataArns, }, { accessCapabilities: k9.k9policy.AccessCapability.WRITE_DATA, allowPrincipalArns: readWriteDataArns, restrictToPrincipalOrgIDs: ['o-y2fdpt5ftt'], }, { accessCapabilities: k9.k9policy.AccessCapability.DELETE_DATA, allowPrincipalArns: readWriteDataArns, }, ) } k9.sqs.grantAccessViaResourcePolicy(k9SQSResourcePolicyProps); // Demonstrate generating and applying an EventBridge Bus resource policy const bus = new events.EventBus(stack, 'k9-cdk-v2-int-test-bus', { eventBusName: 'k9-cdk-v2-int-test', }); const k9EventBusResourcePolicyProps: K9EventBusResourcePolicyProps = { bus: bus, k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns, }, { accessCapabilities: k9.k9policy.AccessCapability.WRITE_DATA, allowPrincipalArns: readWriteDataArns, restrictToPrincipalOrgIDs: ['o-y2fdpt5ftt'], }, ) }; k9.events.grantAccessViaResourcePolicy(k9EventBusResourcePolicyProps); // Test wildcard + org constraint pattern (no DenyEveryoneElse) const orgBus = new events.EventBus(stack, 'k9-cdk-v2-int-test-org-bus', { eventBusName: 'k9-cdk-v2-int-test-org', }); const k9OrgBusResourcePolicyProps: K9EventBusResourcePolicyProps = { bus: orgBus, k9DesiredAccess: new Array<k9.k9policy.IAccessSpec>( { accessCapabilities: k9.k9policy.AccessCapability.ADMINISTER_RESOURCE, allowPrincipalArns: administerResourceArns, }, { accessCapabilities: k9.k9policy.AccessCapability.READ_CONFIG, allowPrincipalArns: readConfigArns, }, { accessCapabilities: k9.k9policy.AccessCapability.WRITE_DATA, allowPrincipalArns: ['*'], restrictToPrincipalOrgIDs: ['o-y2fdpt5ftt'], }, ) }; k9.events.grantAccessViaResourcePolicy(k9OrgBusResourcePolicyProps); for (let construct of [bucket, websiteBucket, autoDeleteBucket, key, // cloudfrontDistribution, cloudfrontOACBucket, cloudfrontOACKey, table, queue, bus, orgBus, ]) { Tags.of(construct).add('k9security:analysis', 'include'); }