###################### # Access keys # # For rotation purposes, there are two of these. Delete the oldest one, # add a new one (with a higher version number), and then update the output # # Possible futue improvement: # We could specify a pgp_key attribute, and then the secret will be encrypted # in both the state file and in the output. If we used the salt PGP key, # no user would ever have to see the secret key. locals { # The user in this file will only be created for Commercial C2 is_commercial = var.aws_partition == "aws-us-gov" ? false : true is_c2 = contains(["045312110490", "816914342178"], var.aws_account_id) user_count = local.is_commercial && local.is_c2 ? 1 : 0 } resource "aws_iam_access_key" "salt-master-v2" { count = local.user_count user = aws_iam_user.salt-master[count.index].name } resource "aws_iam_access_key" "salt-master-v3" { count = local.user_count user = aws_iam_user.salt-master[count.index].name } output "access_keys" { # Only output the keys if there _are_ keys value = local.user_count == 0 ? null : { "current" = { "aws_access_key_id" : aws_iam_access_key.salt-master-v3[0].id "aws_secret_access_key" : aws_iam_access_key.salt-master-v3[0].secret }, "previous" = { "aws_access_key_id" : aws_iam_access_key.salt-master-v2[0].id "aws_secret_access_key" : aws_iam_access_key.salt-master-v2[0].secret } } sensitive = true } ###################### # The policy is attached to both the user and the instance profile data "aws_iam_policy_document" "salt_master_policy_doc" { statement { sid = "AllowSaltSecretsCommunication" effect = "Allow" actions = [ "secretsmanager:GetResourcePolicy", "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret", "secretsmanager:ListSecretVersionIds" ] # tfsec:ignore:aws-iam-no-policy-wildcards This is read-only access resources = [ "arn:${var.aws_partition}:secretsmanager:*:*:secret:saltmaster/*" ] } statement { sid = "AllowAssumeRole" effect = "Allow" actions = [ "sts:AssumeRole" ] # tfsec:ignore:aws-iam-no-policy-wildcards This is read-only access resources = [ "arn:${var.aws_partition}:iam::*:role/service/salt-master-inventory-role", # This may not belong here. This is related to the Neustar GeoIP support. # The cron job that runs on the salt master to download Neustar files from # their sftp server and put them on S3 needs to be able to assume this role # both in commercial and govcloud, as we have systems in both partitions that # need the S3 bucket updated with latest geoip files. This covers the case # of a govcloud salt-master pushing files into commercial S3. "arn:${var.aws_partition}:iam::*:role/service/afsxdr-binaries_writers", ] } } resource "aws_iam_policy" "salt-master" { count = local.user_count name = "salt_master_sm" path = "/" policy = data.aws_iam_policy_document.salt_master_policy_doc.json } ###################### # the user # # Note: CIS requires that policies _NOT_ be directly attached to a user. Users must # be members of groups, and those groups can have policies. resource "aws_iam_user" "salt-master" { count = local.user_count name = "salt-master" path = "/instance/" tags = merge(local.standard_tags, var.tags) } # tfsec:ignore:aws-iam-enforce-mfa No MFA for service account resource "aws_iam_group" "salt-master" { count = local.user_count name = "salt-master" path = "/instance/" } resource "aws_iam_user_group_membership" "salt-master" { count = local.user_count user = aws_iam_user.salt-master[count.index].name groups = [aws_iam_group.salt-master[count.index].name] } resource "aws_iam_group_policy_attachment" "salt-master-group" { count = local.user_count group = aws_iam_group.salt-master[count.index].name policy_arn = aws_iam_policy.salt-master[count.index].arn }