UNPKG

@cloud-copilot/iam-collect

Version:

Collect IAM information from AWS Accounts

236 lines 9.58 kB
import { GetBucketEncryptionCommand, GetBucketPolicyCommand, GetBucketTaggingCommand, GetPublicAccessBlockCommand, ListBucketsCommand, ListDirectoryBucketsCommand, S3Client } from '@aws-sdk/client-s3'; import {} from '../../aws/coreAuth.js'; import { AbstractClient } from '../../customClients/AbstractClient.js'; import { ResourceNotFoundException } from '../../customClients/ResourceNotFoundException.js'; import { stringifyIfPresent } from '../../utils/json.js'; import { awsConfigCommand } from '../AwsConfigClientContext.js'; import { executeConfigQuery, parseConfigItem, resourceStatusWhereClause } from '../awsConfigUtils.js'; /** * AWS Config-based S3 client implementation */ export class AwsConfigS3Client extends AbstractClient { constructor(options, customContext) { super(options, customContext); } registerCommands() { this.registerCommand(AwsConfigGetBucketPolicyCommand); this.registerCommand(AwsConfigGetBucketEncryptionCommand); this.registerCommand(AwsConfigGetBucketTaggingCommand); this.registerCommand(AwsConfigGetPublicAccessBlockCommand); this.registerCommand(AwsConfigListBucketsCommand); this.registerCommand(AwsConfigListDirectoryBucketsCommand); } } AwsConfigS3Client.clientName = S3Client.name; /** * Config-based implementation of S3 ListBucketsCommand * Returns bucket listing from AWS Config inventory data */ const AwsConfigListBucketsCommand = awsConfigCommand({ command: ListBucketsCommand, execute: async (input, context) => { // Query Config for S3 buckets const query = ` SELECT arn, resourceName, configuration.name, configuration.creationDate, supplementaryConfiguration.BucketPolicy, supplementaryConfiguration.ServerSideEncryptionConfiguration, supplementaryConfiguration.PublicAccessBlockConfiguration, tags WHERE resourceType = 'AWS::S3::Bucket' AND awsRegion = '${input.BucketRegion}' AND accountId = '${context.accountId}' AND ${resourceStatusWhereClause} `; const configResults = await executeConfigQuery(query, context); const buckets = configResults.map((resultString) => { const { configItem, configuration, supplementaryConfiguration, tags } = parseConfigItem(resultString); context.putCache(configItem.resourceName, 'policy', supplementaryConfiguration?.BucketPolicy.policyText); context.putCache(configItem.resourceName, 'encryption', supplementaryConfiguration?.ServerSideEncryptionConfiguration); context.putCache(configItem.resourceName, 'tags', tags); context.putCache(configItem.resourceName, 'publicAccessBlock', supplementaryConfiguration?.PublicAccessBlockConfiguration); return { Name: configuration?.name || configItem.resourceName, CreationDate: configuration?.creationDate ? new Date(configuration.creationDate) : undefined }; }); return { Buckets: buckets }; } }); /** * Config-based implementation of S3 ListDirectoryBucketsCommand * Uses AWS::S3Express::DirectoryBucket resource type */ const AwsConfigListDirectoryBucketsCommand = awsConfigCommand({ command: ListDirectoryBucketsCommand, execute: async (input, context) => { const query = ` SELECT arn, resourceId, resourceName, configuration.BucketEncryption, tags WHERE resourceType = 'AWS::S3Express::DirectoryBucket' AND awsRegion = '${context.region}' AND accountId = '${context.accountId}' AND ${resourceStatusWhereClause} `; const results = await executeConfigQuery(query, context); const buckets = results?.map((resultString) => { const { configItem, configuration } = parseConfigItem(resultString); context.putCache(configItem.resourceName, 'policy', configuration?.PolicyDocument); context.putCache(configItem.resourceName, 'encryption', configuration?.BucketEncryption); return { Name: configItem.resourceName }; }) || []; return { Buckets: buckets }; } }); /** * Config-based implementation of S3 GetBucketPolicyCommand * Returns bucket policy from AWS Config supplementary data */ const AwsConfigGetBucketPolicyCommand = awsConfigCommand({ command: GetBucketPolicyCommand, execute: async (input, context) => { const { Bucket } = input; if (!Bucket) { throw new Error('Bucket parameter is required'); } if (isDirectoryBucket(Bucket)) { const query = ` SELECT resourceName, configuration.PolicyDocument WHERE resourceType = 'AWS::S3Express::BucketPolicy' AND resourceName = '${Bucket}' AND accountId = '${context.accountId}' AND awsRegion = '${context.region}' AND ${resourceStatusWhereClause} `; const configResults = await executeConfigQuery(query, context); if (configResults.length === 0) { throw new ResourceNotFoundException(`Bucket '${Bucket}' not found`); } const { configuration } = parseConfigItem(configResults[0]); return { Policy: JSON.stringify(configuration?.PolicyDocument) }; } const cachedPolicy = context.getCache(Bucket, 'policy'); return { Policy: stringifyIfPresent(cachedPolicy) }; } }); /** * Config-based implementation of S3 GetBucketEncryptionCommand * Returns bucket encryption configuration from AWS Config supplementary data */ const AwsConfigGetBucketEncryptionCommand = awsConfigCommand({ command: GetBucketEncryptionCommand, execute: async (input, context) => { const { Bucket } = input; if (!Bucket) { throw new Error('Bucket parameter is required'); } if (isDirectoryBucket(Bucket)) { const encryptionConfig = context.getCache(Bucket, 'encryption'); const seeConfig = encryptionConfig?.ServerSideEncryptionConfiguration?.at(0); if (!seeConfig) { // Return an empty result to indicate no encryption configuration is set return {}; } return { ServerSideEncryptionConfiguration: { Rules: [ { ApplyServerSideEncryptionByDefault: { SSEAlgorithm: seeConfig.ServerSideEncryptionByDefault?.SSEAlgorithm, KMSMasterKeyID: seeConfig.ServerSideEncryptionByDefault?.KMSMasterKeyID }, BucketKeyEnabled: seeConfig.BucketKeyEnabled } ] } }; } const serverSideEncryption = context.getCache(Bucket, 'encryption'); if (!serverSideEncryption) { // Return undefined to indicate no encryption configuration is set return undefined; } // Convert Config encryption format to S3 ServerSideEncryptionConfiguration format const rules = serverSideEncryption.rules?.map((rule) => ({ ApplyServerSideEncryptionByDefault: { SSEAlgorithm: rule.applyServerSideEncryptionByDefault?.sseAlgorithm, KMSMasterKeyID: rule.applyServerSideEncryptionByDefault?.kmsMasterKeyID }, BucketKeyEnabled: rule.bucketKeyEnabled })) || []; return { ServerSideEncryptionConfiguration: { Rules: rules } }; } }); /** * Config-based implementation of S3 GetBucketTaggingCommand * Returns bucket tags from AWS Config tags data */ const AwsConfigGetBucketTaggingCommand = awsConfigCommand({ command: GetBucketTaggingCommand, execute: async (input, context) => { const { Bucket } = input; if (!Bucket) { throw new Error('Bucket parameter is required'); } const tags = context.getCache(Bucket, 'tags'); return { TagSet: tags }; } }); /** * Config-based implementation of S3 GetPublicAccessBlockCommand * Returns public access block configuration from AWS Config supplementary data */ const AwsConfigGetPublicAccessBlockCommand = awsConfigCommand({ command: GetPublicAccessBlockCommand, execute: async (input, context) => { const { Bucket } = input; if (!Bucket) { throw new Error('Bucket parameter is required'); } const publicAccessBlock = context.getCache(Bucket, 'publicAccessBlock'); if (!publicAccessBlock) { // Return undefined to indicate no public access block configuration is set return undefined; } return { PublicAccessBlockConfiguration: { BlockPublicAcls: publicAccessBlock.blockPublicAcls, BlockPublicPolicy: publicAccessBlock.blockPublicPolicy, IgnorePublicAcls: publicAccessBlock.ignorePublicAcls, RestrictPublicBuckets: publicAccessBlock.restrictPublicBuckets } }; } }); function isDirectoryBucket(bucketName) { return bucketName.endsWith('-x-s3'); } //# sourceMappingURL=AwsConfigS3Client.js.map