Score:1

Safely store AWS IAM User Keys (Access and Secret) created by IaC

de flag

I've the following setup:

  1. Infrastructure is setup using AWS CDK;
  2. I've one Stack/Environment (Production, Staging...);
  3. Each Stack has a different S3 Bucket (used for website hosting);
  4. I've a Stack that creates an IAM User (used by CI/CD);
  5. CI/CD in this case is GitHub Actions (deploy every time a merge to main happens);
  6. The IAM User has only put rights to all the Buckets (deploy means put the assets in the Bucket);

What is the best way to store/handle the keys for that user?

I started printing it in the Outputs but it is not secure. Everyone can see it (if they've access to the logs of CI/CD for example).

I've been suggested to store them in SSM: it works but you can't create it as SecureString so it would be just a String.

I've also taken a look into Secrets Manager: it also works and seems to be more secure (not sure if my feeling here is valid though).

Any ideas/opinions here?

Thanks!


In the code it looks something like:

// Production Stack
const bucket = new Bucket(this, "Bucket", {
  bucketName: "production",
});

// Staging Stack
const bucket = new Bucket(this, "Bucket", {
  bucketName: "staging",
});

// IAM Stack
const user = new User(this, "User", {
  userName: "ci-cd-user",
});
const userAccessKey = new AccessKey(this, "UserAccessKey", { user });

// This is just an example, I go through all the available Buckets
bucketProduction.grantPut(user);
bucketStaging.grantPut(user);
Tim avatar
gp flag
Tim
What CI/CD tool do you use? Is it in AWS? Please update your question with more detail, then reply here so I can see it's been updated. The answer is different depending on this information.
viniciuskneves avatar
de flag
Hey @Tim, I've just updated with more information: CI/CD is GitHub Actions and deploy means putting assets in the Buckets. Does it help or do you need more details?
Tim avatar
gp flag
Tim
Personally I would create the IAM user or at least their access key manually then transfer credentials to where they're needed in Github, rather than storing them inside AWS. If you must store them inside AWS then parameter store or secrets manager would be my pick, either is fine so long as they're encrypted. I would probably restrict who can view the credentials wherever you decide to store them.
viniciuskneves avatar
de flag
Hummm.. Got it. I was trying to automate as much as possible the process but indeed manually tackling the keys makes sense. Would Roles, through federation, be the best solution? What I mean: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services
Tim avatar
gp flag
Tim
Roles are almost always a better option than IAM credentials. I don't know anything about github actions, but if it can assume a role then take that option. Just make sure the role is properly secured so only github actions can assume the role.
viniciuskneves avatar
de flag
Thanks! I will add it as an answer here as soon as I can properly try it with Roles.
cn flag
Looks like to get it to assume the role you just specify the role with no credentials, and it uses OIDC, so should be a lot more secure. https://github.com/aws-actions/configure-aws-credentials
viniciuskneves avatar
de flag
Yeah @shearn89, I tried it yesterday and it works like a charm. I wasn't full aware of it or how it works... I will summarize my findings in the answer. Thanks for pointing it out ‍♂️
Score:0
de flag

It took me a while but I finally wrapped my thoughts around it here: https://dev.to/viniciuskneves/use-aws-through-github-actions-without-secret-keys-32eo

The ideia is to use OpenID Connect to connect with GitHub and then have a Role that can be assumed by GitHub Actions and perform the deployment (same policies I would give to GitHub Actions user).

Using AWS CDK the solution looks like the following:

const provider = new OpenIdConnectProvider(this, "GitHubProvider", {
  url: "https://token.actions.githubusercontent.com",
  clientIds: ["sts.amazonaws.com"], // Referred as "Audience" in the documentation
});
const role = new Role(this, "Role", {
  assumedBy: new OpenIdConnectPrincipal(provider, {
    StringEquals: {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com", // "Audience" from above
    },
    StringLike: {
      "token.actions.githubusercontent.com:sub":
      "repo:<ORGANIZATION>/<REPOSITORY>:*", // Organization and repository that are allowed to assume this role
    },
  }),
});

bucket.grantPut(role); // Creates policy

After that we need to update GitHub Actions to use this instead of our user keys:

...
permissions:
  id-token: write
  contents: read # This is required for actions/checkout (GitHub documentation mentions that)
...
- name: Configure AWS Credentials 
  uses: aws-actions/[email protected] # Version at the time of this writing
  with: # We are loading keys stored in secrets
    role-to-assume: arn:aws:iam::<ACCOUNT>:role/<ROLE-NAME> # You can specify the role name when creating it or AWS will automatically generate one for you
    aws-region: eu-central-1
- run: npm run deploy # Just an example
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.