US flag signifying that this is a United States Federal Government website An official website of the United States government

Rotating Secrets II - IAM users via Terraform

Rotating Secrets II - IAM users via Terraform

This page is primarily for the cloud.gov team. It's public so that you can learn from it. For help using cloud.gov, see the user docs.

IAM Users infrastructure as a code with Terraform

In the 18F/cg-provision contains the Terraform files used to maintain our AWS infrastructure as code. User modules are in the terraform/modules/iam_user/* path. Each module is utilized in stack.tf and prints the output.tf contents after a successful Terrafrom apply.

Each of the user modules in iam_user individually leverage the aws_iam_access_key and support having up to two of these resources. We name the resources with a _vX version suffix. These resources are then referenced in our output with a suffix for previous and current keys e.g. _prev and _curr.

This is used to avoid invalidating all the previous credentials during a secrets rotation. This gives the operator enough of a window to update credentials across our various deployments.

Rotating IAM user access key ids and secret access keys

When rotating IAM user credentials, it’s important to generate new ones while making sure the old ones stay around until Concourse pipelines and Bosh manifests are updated with the latest version output. The following examples show file-diffs for a single user. You will repeat this process across all user modules and submit a single PR to begin the secret rotation window. You can also find examples of closed and merged pull requests in the cg-provision repository around rotating IAM roles

Update the rotation iam_user module to have two access key resources

Create a new sequential version of the credentials in the modules found in module/iam_user/*/output.tf and module/iam_user/*/user.tf for each iam_user.

Note: These file-diffs are simply examples. They will not apply properly, so please don’t apply them. These changes will need to be made manually.

diff --git a/terraform/modules/iam_user/limit_check_user/user.tf b/terraform/modules/iam_user/limit_check_user/user.tf
--- a/terraform/modules/iam_user/limit_check_user/user.tf
+++ b/terraform/modules/iam_user/limit_check_user/user.tf
@@ -10,6 +10,10 @@

 resource "aws_iam_access_key" "iam_access_key_v2" {
   user = "${aws_iam_user.iam_user.name}"
 }

+resource "aws_iam_access_key" "iam_access_key_v3" {
+  user = "${aws_iam_user.iam_user.name}"
+}
+
 resource "aws_iam_user_policy" "iam_policy" {
   name = "${aws_iam_user.iam_user.name}-policy"
   user = "${aws_iam_user.iam_user.name}"

The example above shows keys being rotated from _v2 to _v3. The example below shows that the named resources from above iam_access_key_vX are set as values for the _prev and _curr outputs.


diff --git a/terraform/modules/iam_user/limit_check_user/outputs.tf b/terraform/modules/iam_user/limit_check_user/outputs.tf
--- a/terraform/modules/iam_user/limit_check_user/outputs.tf
+++ b/terraform/modules/iam_user/limit_check_user/outputs.tf
@@ -2,14 +2,14 @@ output "username" {
   value = "${aws_iam_user.iam_user.username}"
 }
 output "access_key_id_prev" {
-  value = ""
+  value = "${aws_iam_access_key.iam_access_key_v2.access_key_id}"
 }
 output "secret_access_key_prev" {
-  value = ""
+  value = "${aws_iam_access_key.iam_access_key_v2.secret_access_key}"
 }
 output "access_key_id_curr" {
-  value = "${aws_iam_access_key.iam_access_key_v2.access_key_id}"
+  value = "${aws_iam_access_key.iam_access_key_v3.access_key_id}"
 }
 output "secret_access_key_curr" {
-  value = "${aws_iam_access_key.iam_access_key_v2.secret_access_key}"
+  value = "${aws_iam_access_key.iam_access_key_v3.secret_access_key}"
 }

Repeat this process across all the modules/iam_user/*/user.tf and modules/iam_user/*/outputs.tf files. Once that’s done, submit a pull request against master for cg-provision. Once the pull request is merged, run the provisioning step in Concourse.

Capture the outputs for _curr and replace all the values of _prev in either Bosh manifests secrets stubs or Concourse credentials.

Revert the source for all modules

Once all the iam_user modules have been updated, revert the source back to the iam_user/base with a single updated version. This deletes the previous version credentials from the IAM users.

diff --git a/terraform/modules/iam_user/limit_check_user/user.tf b/terraform/modules/iam_user/limit_check_user/user.tf
--- a/terraform/modules/iam_user/limit_check_user/user.tf
+++ b/terraform/modules/iam_user/limit_check_user/user.tf
@@ -10,6 +10,10 @@

-resource "aws_iam_access_key" "iam_access_key_v2" {
-  user = "${aws_iam_user.iam_user.name}"
-}
-

 resource "aws_iam_access_key" "iam_access_key_v3" {
   user = "${aws_iam_user.iam_user.name}"
 }

 resource "aws_iam_user_policy" "iam_policy" {

The example above references _v3 as the now current version by removing the _v2 version from the user.tf file. The example below removes the output value from the _prev output since the previous version was removed in the user.tf file.


diff --git a/terraform/modules/iam_user/limit_check_user/outputs.tf b/terraform/modules/iam_user/limit_check_user/outputs.tf
--- a/terraform/modules/iam_user/limit_check_user/outputs.tf
+++ b/terraform/modules/iam_user/limit_check_user/outputs.tf
@@ -2,14 +2,14 @@ output "username" {
   value = "${aws_iam_user.iam_user.username}"
 }
 output "access_key_id_prev" {
-  value = "${aws_iam_access_key.iam_access_key_v2.access_key_id}"
+  value = ""
 }
 output "secret_access_key_prev" {
-  value = "${aws_iam_access_key.iam_access_key_v2.secret_access_key}"
+  value = ""
 }
 output "access_key_id_curr" {
   value = "${aws_iam_access_key.iam_access_key_v3.access_key_id}"
 }
 output "secret_access_key_curr" {
   value = "${aws_iam_access_key.iam_access_key_v3.secret_access_key}"
 }

Once that’s done, submit a pull request against master for cg-provision again. Once the pull request is merged, run the provisioning step in Concourse. The Terraform apply will delete all the _prev credentials from AWS.