Skip to content

Commit

Permalink
feat: workload identity federation
Browse files Browse the repository at this point in the history
  • Loading branch information
henryde committed Mar 22, 2024
1 parent a60d551 commit 46f95cb
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added ARNs of managed accounts roles to output
- Added meshStack access role to output
- Renamed metering related parts from kraken to metering
- Added workload identity federation
- Added option to disable access keys

## [v0.1.0]

Expand Down
9 changes: 9 additions & 0 deletions identity_provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# in case of workload identity federation we must add the appropriate identity provider
resource "aws_iam_openid_connect_provider" "meshstack" {
provider = aws.meshcloud
count = var.workload_identity_federation != null ? 1 : 0

url = var.workload_identity_federation.issuer
client_id_list = [var.workload_identity_federation.audience]
thumbprint_list = [var.workload_identity_federation.thumbprint]
}
20 changes: 20 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ module "meshcloud_account_metering_access" {
privileged_external_id = var.cost_explorer_privileged_external_id
management_account_service_role_name = var.cost_explorer_management_account_service_role_name
meshcloud_account_service_user_name = var.cost_explorer_meshcloud_account_service_user_name

workload_identity_federation = var.workload_identity_federation == null ? null : {
issuer = var.workload_identity_federation.issuer,
audience = var.workload_identity_federation.audience,
subject = var.workload_identity_federation.kraken_subject,
identity_provider_arn = aws_iam_openid_connect_provider.meshstack[0].arn
}
}

module "meshcloud_account_replicator_access" {
Expand All @@ -30,6 +37,13 @@ module "meshcloud_account_replicator_access" {
meshcloud_account_service_user_name = var.meshcloud_account_service_user_name
management_account_service_role_name = var.management_account_service_role_name
automation_account_service_role_name = var.automation_account_service_role_name

workload_identity_federation = var.workload_identity_federation == null ? null : {
issuer = var.workload_identity_federation.issuer,
audience = var.workload_identity_federation.audience,
subject = var.workload_identity_federation.replicator_subject,
identity_provider_arn = aws_iam_openid_connect_provider.meshstack[0].arn
}
}

module "management_account_metering_access" {
Expand All @@ -42,6 +56,8 @@ module "management_account_metering_access" {
management_account_service_role_name = var.cost_explorer_management_account_service_role_name
meshcloud_account_service_user_name = var.cost_explorer_meshcloud_account_service_user_name

allow_federated_role = var.workload_identity_federation != null

depends_on = [
module.meshcloud_account_metering_access
]
Expand All @@ -62,6 +78,8 @@ module "management_account_replicator_access" {
management_account_service_role_name = var.management_account_service_role_name
landing_zone_ou_arns = var.landing_zone_ou_arns

allow_federated_role = var.workload_identity_federation != null

depends_on = [
module.meshcloud_account_replicator_access
]
Expand All @@ -77,6 +95,8 @@ module "automation_account_replicator_access" {
meshcloud_account_service_user_name = var.meshcloud_account_service_user_name
automation_account_service_role_name = var.automation_account_service_role_name

allow_federated_role = var.workload_identity_federation != null

depends_on = [
module.meshcloud_account_replicator_access
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,21 @@ data "aws_iam_policy_document" "cost_explorer_service_assume_role" {
version = "2012-10-17"
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:user/${var.meshcloud_account_service_user_name}"]
}

dynamic "principals" {
for_each = var.allow_federated_role ? [true] : []

content {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:role/${var.meshcloud_account_service_user_name}IdentityFederation"]
}
}

actions = ["sts:AssumeRole"]
condition {
test = "StringEquals"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ variable "privileged_external_id" {
type = string
description = "Privileged external ID for the meshfed-service to use"
}

variable "allow_federated_role" {
type = bool
default = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,31 @@ data "aws_iam_policy_document" "meshcloud_cost_explorer_user_assume_role" {
}
}
}

data "aws_iam_policy_document" "workload_identity_federation" {
count = var.workload_identity_federation == null ? 0 : 1
version = "2012-10-17"

statement {
effect = "Allow"
principals {
type = "Federated"
identifiers = [var.workload_identity_federation.identity_provider_arn]
}
actions = ["sts:AssumeRoleWithWebIdentity"]

condition {
test = "StringEquals"
variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:aud"

values = [var.workload_identity_federation.audience]
}

condition {
test = "StringEquals"
variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:sub"

values = [var.workload_identity_federation.subject]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ resource "aws_iam_user" "meshcloud_cost_explorer" {
}

resource "aws_iam_access_key" "meshcloud_cost_explorer" {
user = aws_iam_user.meshcloud_cost_explorer.name
count = var.create_access_key ? 1 : 0
user = aws_iam_user.meshcloud_cost_explorer.name
}

resource "aws_iam_policy" "meshcloud_cost_explorer_user" {
Expand All @@ -16,3 +17,19 @@ resource "aws_iam_user_policy_attachment" "meshcloud_cost_explorer" {
user = aws_iam_user.meshcloud_cost_explorer.name
policy_arn = aws_iam_policy.meshcloud_cost_explorer_user.arn
}
#
# role which can be assumed by federated workload
resource "aws_iam_role" "assume_cost_explorer_role" {
count = var.workload_identity_federation == null ? 0 : 1

name = "${aws_iam_user.meshcloud_cost_explorer.name}IdentityFederation"
assume_role_policy = data.aws_iam_policy_document.workload_identity_federation[0].json
}

# attach permissions to assumed role
resource "aws_iam_role_policy_attachment" "meshfed_service" {
count = var.workload_identity_federation == null ? 0 : 1

role = aws_iam_role.assume_cost_explorer_role[0].name
policy_arn = aws_iam_policy.meshcloud_cost_explorer_user.arn
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
output "aws_iam_keys" {
description = "AWS access and secret keys for cost explorer user."
value = {
value = var.create_access_key ? {
aws_access_key = aws_iam_access_key.meshcloud_cost_explorer.id
aws_secret_key = aws_iam_access_key.meshcloud_cost_explorer.secret
}
} : null
sensitive = true
}

output "workload_identity_federation_role" {
value = var.workload_identity_federation == null ? null : aws_iam_role.assume_cost_explorer_role[0].arn
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,19 @@ variable "privileged_external_id" {
type = string
description = "Privileged external ID for the cost-explorer-service to use"
}

variable "create_access_key" {
type = bool
description = "Create access key for service account"
default = true
}

variable "workload_identity_federation" {
type = object({
issuer = string,
audience = string,
subject = string,
identity_provider_arn = string
})
default = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ data "aws_iam_policy_document" "meshfed_automation_assume_role" {
version = "2012-10-17"
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:user/${var.meshcloud_account_service_user_name}"]
}

dynamic "principals" {
for_each = var.allow_federated_role ? [true] : []

content {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:role/${var.meshcloud_account_service_user_name}IdentityFederation"]
}
}

actions = ["sts:AssumeRole"]
condition {
test = "StringEquals"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ variable "automation_account_service_role_name" {
default = "MeshfedAutomationRole"
description = "Name of the custom role in the automation account. See https://docs.meshcloud.io/docs/meshstack.how-to.integrate-meshplatform-aws-manually.html#set-up-aws-account-3-automation"
}

variable "allow_federated_role" {
type = bool
default = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,23 @@ data "aws_iam_policy_document" "meshfed_service_assume_role" {
version = "2012-10-17"
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:user/${var.meshcloud_account_service_user_name}"]
}

dynamic "principals" {
for_each = var.allow_federated_role ? [true] : []

content {
type = "AWS"
identifiers = ["arn:${data.aws_partition.current.partition}:iam::${var.meshcloud_account_id}:role/${var.meshcloud_account_service_user_name}IdentityFederation"]
}
}

actions = ["sts:AssumeRole"]

condition {
test = "StringEquals"
variable = "sts:ExternalId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ variable "landing_zone_ou_arns" {
"arn:aws:organizations::*:ou/o-*/ou-*"
]
}

variable "allow_federated_role" {
type = bool
default = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data "aws_partition" "current" {}

data "aws_iam_policy_document" "meshfed_service_user_assume_role" {
version = "2012-10-17"

statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
Expand All @@ -30,3 +31,31 @@ data "aws_iam_policy_document" "meshfed_service_user_assume_role" {
}
}
}

data "aws_iam_policy_document" "workload_identity_federation" {
count = var.workload_identity_federation == null ? 0 : 1
version = "2012-10-17"

statement {
effect = "Allow"
principals {
type = "Federated"
identifiers = [var.workload_identity_federation.identity_provider_arn]
}
actions = ["sts:AssumeRoleWithWebIdentity"]

condition {
test = "StringEquals"
variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:aud"

values = [var.workload_identity_federation.audience]
}

condition {
test = "StringEquals"
variable = "${trimprefix(var.workload_identity_federation.issuer, "https://")}:sub"

values = [var.workload_identity_federation.subject]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ resource "aws_iam_user" "meshfed_service" {
}

resource "aws_iam_access_key" "meshfed_service" {
user = aws_iam_user.meshfed_service.name
count = var.create_access_key ? 1 : 0
user = aws_iam_user.meshfed_service.name
}

resource "aws_iam_policy" "meshfed_service_user" {
Expand All @@ -16,3 +17,19 @@ resource "aws_iam_user_policy_attachment" "meshfed_service" {
user = aws_iam_user.meshfed_service.name
policy_arn = aws_iam_policy.meshfed_service_user.arn
}

# role which can be assumed by federated workload
resource "aws_iam_role" "assume_meshfed_service_role" {
count = var.workload_identity_federation == null ? 0 : 1

name = "${aws_iam_user.meshfed_service.name}IdentityFederation"
assume_role_policy = data.aws_iam_policy_document.workload_identity_federation[0].json
}

# attach permissions to assumed role
resource "aws_iam_role_policy_attachment" "meshfed_service" {
count = var.workload_identity_federation == null ? 0 : 1

role = aws_iam_role.assume_meshfed_service_role[0].name
policy_arn = aws_iam_policy.meshfed_service_user.arn
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
output "aws_iam_keys" {
description = "AWS access and secret keys for meshfed-service user."
value = {
aws_access_key = aws_iam_access_key.meshfed_service.id
aws_secret_key = aws_iam_access_key.meshfed_service.secret
}
value = var.create_access_key ? {
aws_access_key = aws_iam_access_key.meshfed_service[0].id
aws_secret_key = aws_iam_access_key.meshfed_service[0].secret
} : null
sensitive = true
}

output "workload_identity_federation_role" {
value = var.workload_identity_federation == null ? null : aws_iam_role.assume_meshfed_service_role[0].arn
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,19 @@ variable "privileged_external_id" {
type = string
description = "Privileged external ID for the meshfed-service to use"
}

variable "create_access_key" {
type = bool
description = "Create access key for service account"
default = true
}

variable "workload_identity_federation" {
type = object({
issuer = string,
audience = string,
subject = string,
identity_provider_arn = string
})
default = null
}
4 changes: 4 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ output "replicator_privileged_external_id" {
sensitive = true
}

output "replicator_workload_identity_federation_role" {
value = var.workload_identity_federation == null ? null : module.meshcloud_account_replicator_access.workload_identity_federation_role
}

output "meshstack_access_role_name" {
description = "The name for the Account Access Role that will be rolled out to all managed accounts."
value = module.management_account_replicator_access.meshstack_access_role_name
Expand Down
Loading

0 comments on commit 46f95cb

Please sign in to comment.