aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.71k stars 3.93k forks source link

Proposed way to use iam access and secretKey in cdk #4661

Closed konstantinj closed 1 year ago

konstantinj commented 5 years ago

:question: General Issue

There are a lot of application out there that still require an access/secretKey pair and do not work with roles. What's the ideal/secure way to use those in cdk without creating resources manually beforehand.

The Question

There is the parameter store (SSM) and the secrets manager (SM) available. In SSM I can only create "normal" strings from cdk/cloudformation - not secure strings. In SM I can only get a new secret but not store my own one.

So the only option currently seems to be this:

const user = new iam.User(this, 'User', {
    userName: this.getConfig('bucketName'),
})

const accessKey = new iam.CfnAccessKey(this, 'AccessKey', {
    userName: user.userName,
})

const ssmSecretKey = new ssm.StringParameter(this, 'SsmSecretKey', {
    parameterName: '/' + [this.account, this.stackName, 'secretKey'].join('/'),
    stringValue: accessKey.attrSecretAccessKey,
})

const bucket = new s3.Bucket(this, 'Bucket', {
    bucketName: this.getConfig('bucketName'),
    versioned: false,
    removalPolicy: RemovalPolicy.RETAIN,
    publicReadAccess: false,
    blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
})

bucket.grantReadWrite(user)

and then of course using it later in my case in ecs:

const loadBalancedEcsService = new ecsPatterns.ApplicationLoadBalancedEc2Service(this, 'Service', {
    cluster: cluster,
    memoryLimitMiB: 1024,
    taskImageOptions: {
        image: ecs.ContainerImage.fromEcrRepository(asset.repository),
        environment: {
            AWS_ACCESS_KEY_ID: accessKey.ref,
            AWS_DEFAULT_REGION: this.region,
        },
        secrets: {
            AWS_SECRET_ACCESS_KEY: ecs.Secret.fromSsmParameter(ssmSecretKey),
        },
        containerPort: 4873,
    },
    desiredCount: 1,
    domainName: this.getConfig('domainName'),
    protocol: ApplicationProtocol.HTTPS,
    domainZone: domainZone,
    healthCheckGracePeriod: cdk.Duration.seconds(60),
})

Other information

1857 #3520

rix0rrr commented 4 years ago

Unfortunately we are a little bound by what CloudFormation supports in this area. I think a Custom Resource that creates the keypair and stores it in SecretsManager is going to be your best bet.

sblackstone commented 4 years ago

I'd like to add that I'm also running into this exact scenario where I want to create an IAM user with keys at stack creation and store the secret key that's created for later use.....

rix0rrr commented 4 years ago

I'm going to classify this as a feature request, even though there is nothing that CDK can do for you.

Someone out there could make a Custom Resource that does this for you.

hoegertn commented 4 years ago

This is exactly what is on my roadmap. A custom resource to create AccessKeys and store them in secrets manager. Regarding your example, why are you using AccessKey/SecretKey as env instead of an IAM role for the Fargate task?

konstantinj commented 4 years ago

@hoegertn because the taskrole's accessKey expires and I need to handle that in the application. If the application is not made by you that's often not possible.

hoegertn commented 4 years ago

What is the application doing that needs a "static" key? The only thing I can currently imagine is creating pre-signed URLs for S3.

konstantinj commented 4 years ago

The application just needs access- and secret-key and I can't modify the application to include logic to fetch it differently.

hoegertn commented 4 years ago

Normally the applications will use an AWS SDK and it will fetch it automatically but we are getting too off-topic I think.

swe-ds commented 4 years ago

I am having the same need.

I would like to create a new IAM user with Access/Secret keys and store them in the SecretsManager protected by a customer managed KMS key. To finish out the pattern, I want to schedule automatic rotation of this API key.

The only ways I can see to accomplish this with CDK is via a hack or possibly with a lower level contruct.

My use case is to establish a principal identity to be used in sts:AssumeRole across account boundaries.

Doug

liamor commented 4 years ago

My use case for this is when a Java Lambda needs to access an AppSync GraphQL endpoint that is more secure than just using the API Key.

BryanPan342 commented 4 years ago

@liamor I would suggest looking into auth type AWS_IAM for your AppSync GraphQL Api. There is an example in the readme on how to use a grant to accomplish your use case.

binarythinktank commented 4 years ago

just musing here, but what if CDK could support an encrypted "credentials" file. This should not be committed to the git, its encrypted, and developers can add key-value pairs to it for sensitive data such as passwords. Then provide developers a means to reference a key in this file from anywhere in the code such as lambda params or ssm params (which is supported by cfm: https://aws.amazon.com/about-aws/whats-new/2018/08/aws-cloudformation-introduces-dynamic-references-to-support-aws-/). Such values should also be added as params in CFM with NoEcho enabled to avoid them appearing in describe stack.

if multiple developers are working on a project, then they can be given the same keys to include in the cdk project that they clone IF they need it. If a referenced key is missing, then do not deploy it to avoid overwriting a valid value in the cloud with a null/empty value. this should not throw an error, or perhaps only a warning as there are plenty of cases where only the lead dev would be managing the actual secrets but other devs don't need access.

sblackstone commented 4 years ago

Ideally when creating a user, you would be able to just specify a key and it would populate that key in your secrets manager on your behalf... Then you'd never need to actually see the key in the first place and it would programatically be available to your app.

binarythinktank commented 4 years ago

That only works if you need a new key generated, it would be useful to have a mechanism for dealing with existing keys/secrets/passwords/etc. too. Also SM charged per secret and really only worth it if you want automatic rotation (e.g. for RDS), SSM Params is free and suits most cases.

flochaz commented 4 years ago

Unfortunately we are a little bound by what CloudFormation supports in this area. I think a Custom Resource that creates the keypair and stores it in SecretsManager is going to be your best bet.

Here is an example using awsCustomResource to store accessKeyId and secretAccessKey in

const adminPassword = new secretsmanager.Secret(this, "AdminUserPassword");

    const mainAdminUser = new iam.User(this, "admin", {
      userName: "admin",
      password: adminPassword.secretValue,
    });

    const accessKey = new iam.CfnAccessKey(this, "AdminUserAccessKey", {
      userName: mainAdminUser.userName,
    });

    new cr.AwsCustomResource(this, 
      "AdminAccessKeyIdSecret", 
      {
        onCreate: {
          service: 'SecretsManager',
          action: 'createSecret',
          physicalResourceId: cr.PhysicalResourceId.fromResponse('Name'),
          parameters:
            {
              Name: "AdminAccessKeyId", 
              SecretString: accessKey.ref
            }
        },
        installLatestAwsSdk: false, 
        policy: cr.AwsCustomResourcePolicy.fromSdkCalls(
          {
            resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE
          }
        ),
        logRetention: RetentionDays.ONE_DAY
      }
    );

    new cr.AwsCustomResource(this, 
      "AdminSecretAccessKeySecret", 
      {
        onCreate: {
          service: 'SecretsManager',
          action: 'createSecret',
          physicalResourceId: cr.PhysicalResourceId.fromResponse('Name'),
          parameters:
            {
              Name: "AdminSecretAccessKey", 
              SecretString: accessKey.attrSecretAccessKey
            }
        },
        installLatestAwsSdk: false, 
        policy: cr.AwsCustomResourcePolicy.fromSdkCalls(
          {
            resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE
          }
        ),
        logRetention: RetentionDays.ONE_DAY
      }
    );

But custom resource lambda logs will contains your secret for 24h (might have a way to force delete the logStream though)

peterwoodworth commented 2 years ago

If you would like to see native CDK support for this instead of with custom resources, I recommend checking out the Cloudformation coverage roadmap to see if they are planning to implement it. Feel free to open a request there if this feature isn't currently being tracked 🙂

laurelmay commented 2 years ago

I believe that this can be accomplished natively today.

const user = new iam.User(this, "User");
const accessKey = new iam.AccessKey(this, "AccessKey", { user });
const secret = new secrets.Secret(this, "Secret", {
  secretObjectValue: {
    accessKeyId: cdk.SecretValue.resourceAttribute(accessKey.accessKeyId),
    secretAccessKey: accessKey.secretAccessKey,
  }
});

I think that between #18180 and #21091, this has been resolved. I'd highly recommend adopting the @aws-cdk/core:checkSecretUsage feature flag before going down this path.

comcalvi commented 1 year ago

Closing as this appears to have been resolved.

github-actions[bot] commented 1 year ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.