206 lines
6.0 KiB
Terraform
206 lines
6.0 KiB
Terraform
# Copyright IBM Corp. 2021, 2026
|
|||
|
|
# SPDX-License-Identifier: MPL-2.0
|
||
|
|
|
||
|
|
resource "random_id" "assume_role_name" {
|
||
|
|
prefix = "BoundaryPluginAssumeRole"
|
||
|
|
byte_length = 4
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role" "assume_role" {
|
||
|
|
name = random_id.assume_role_name.dec
|
||
|
|
|
||
|
|
assume_role_policy = jsonencode({
|
||
|
|
Version = "2012-10-17"
|
||
|
|
Statement = [
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Principal = {
|
||
|
|
AWS = data.aws_caller_identity.current.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
# Also trusted by the cross-role principal role so it can be used
|
||
|
|
# as the target in two-hop AssumeRole tests.
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Principal = {
|
||
|
|
AWS = aws_iam_role.cross_role_principal.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
]
|
||
|
|
})
|
||
|
|
|
||
|
|
tags = {
|
||
|
|
"boundary-demo" = local.hashicorp_email
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role_policy_attachment" "assume_role_ec2_describeinstances" {
|
||
|
|
role = aws_iam_role.assume_role.name
|
||
|
|
policy_arn = aws_iam_policy.ec2_describeinstances.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
output "assume_role_arn" {
|
||
|
|
value = aws_iam_role.assume_role.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Error case: role exists with ec2:DescribeInstances but trust policy
|
||
|
|
# does not allow the caller to assume it.
|
||
|
|
resource "random_id" "assume_role_no_trust_name" {
|
||
|
|
prefix = "BoundaryPluginNoTrust"
|
||
|
|
byte_length = 4
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role" "assume_role_no_trust" {
|
||
|
|
name = random_id.assume_role_no_trust_name.dec
|
||
|
|
|
||
|
|
assume_role_policy = jsonencode({
|
||
|
|
Version = "2012-10-17"
|
||
|
|
Statement = [
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Deny"
|
||
|
|
Principal = {
|
||
|
|
AWS = data.aws_caller_identity.current.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
]
|
||
|
|
})
|
||
|
|
|
||
|
|
tags = {
|
||
|
|
"boundary-demo" = local.hashicorp_email
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
output "assume_role_no_trust_arn" {
|
||
|
|
value = aws_iam_role.assume_role_no_trust.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Error case: role exists and the caller can assume it, but has no
|
||
|
|
# ec2:DescribeInstances permission.
|
||
|
|
# Also trusted by cross_role_principal so this same role can be reused as the
|
||
|
|
# no-EC2-permission target in two-hop (cross-role) error case tests.
|
||
|
|
resource "random_id" "assume_role_no_ec2_permission_name" {
|
||
|
|
prefix = "BoundaryPluginNoEC2Perm"
|
||
|
|
byte_length = 4
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role" "assume_role_no_ec2_permission" {
|
||
|
|
name = random_id.assume_role_no_ec2_permission_name.dec
|
||
|
|
|
||
|
|
assume_role_policy = jsonencode({
|
||
|
|
Version = "2012-10-17"
|
||
|
|
Statement = [
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Principal = {
|
||
|
|
AWS = data.aws_caller_identity.current.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
# Also trusted by the cross-role principal so it can serve as the
|
||
|
|
# no-EC2-permission target in two-hop error case tests.
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Principal = {
|
||
|
|
AWS = aws_iam_role.cross_role_principal.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
]
|
||
|
|
})
|
||
|
|
|
||
|
|
tags = {
|
||
|
|
"boundary-demo" = local.hashicorp_email
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
output "assume_role_no_ec2_permission_arn" {
|
||
|
|
value = aws_iam_role.assume_role_no_ec2_permission.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Cross-role (two-hop) principal role.
|
||
|
|
# The test runner can assume this role. It has permission to AssumeRole into
|
||
|
|
# aws_iam_role.assume_role (the target role) but has no ec2:DescribeInstances
|
||
|
|
# of its own. This simulates the principal side of a two-hop role chain: the
|
||
|
|
# plugin must assume the principal first, then use those credentials to assume
|
||
|
|
# the target role before calling DescribeInstances.
|
||
|
|
# Note: both roles are in the same AWS account. This tests the plugin's
|
||
|
|
# two-hop credential chain logic without requiring a second account.
|
||
|
|
resource "random_id" "cross_role_principal_name" {
|
||
|
|
prefix = "BoundaryPluginCRPrincipal"
|
||
|
|
byte_length = 4
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role" "cross_role_principal" {
|
||
|
|
name = random_id.cross_role_principal_name.dec
|
||
|
|
|
||
|
|
assume_role_policy = jsonencode({
|
||
|
|
Version = "2012-10-17"
|
||
|
|
Statement = [
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Principal = {
|
||
|
|
AWS = data.aws_caller_identity.current.arn
|
||
|
|
}
|
||
|
|
},
|
||
|
|
]
|
||
|
|
})
|
||
|
|
|
||
|
|
tags = {
|
||
|
|
"boundary-demo" = local.hashicorp_email
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Single policy granting cross_role_principal permission to assume all target
|
||
|
|
# roles it needs for both happy path and error case tests.
|
||
|
|
resource "aws_iam_policy" "cross_role_principal_assume_targets" {
|
||
|
|
name = "${random_id.cross_role_principal_name.dec}-assume-targets"
|
||
|
|
|
||
|
|
policy = jsonencode({
|
||
|
|
Version = "2012-10-17"
|
||
|
|
Statement = [
|
||
|
|
{
|
||
|
|
Action = ["sts:AssumeRole", "sts:SetSourceIdentity"]
|
||
|
|
Effect = "Allow"
|
||
|
|
Resource = [
|
||
|
|
aws_iam_role.assume_role.arn,
|
||
|
|
aws_iam_role.assume_role_no_ec2_permission.arn,
|
||
|
|
]
|
||
|
|
},
|
||
|
|
]
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
resource "aws_iam_role_policy_attachment" "cross_role_principal_assume_targets" {
|
||
|
|
role = aws_iam_role.cross_role_principal.name
|
||
|
|
policy_arn = aws_iam_policy.cross_role_principal_assume_targets.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
output "cross_role_principal_arn" {
|
||
|
|
value = aws_iam_role.cross_role_principal.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Error case: caller cannot assume the principal role (first hop denied).
|
||
|
|
# Reuses assume_role_no_trust — it already has a Deny trust policy for the
|
||
|
|
# caller, so it also serves as an unassumable principal in two-hop tests.
|
||
|
|
output "cross_role_principal_no_trust_arn" {
|
||
|
|
value = aws_iam_role.assume_role_no_trust.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Error case: principal can be assumed, but the target role's trust policy
|
||
|
|
# does not allow the principal to assume it (second hop denied).
|
||
|
|
# Reuses assume_role_no_trust as the target — its Deny trust policy blocks
|
||
|
|
# all callers including cross_role_principal.
|
||
|
|
output "cross_role_target_no_trust_arn" {
|
||
|
|
value = aws_iam_role.assume_role_no_trust.arn
|
||
|
|
}
|
||
|
|
|
||
|
|
# Error case: both hops succeed but the target has no ec2:DescribeInstances.
|
||
|
|
# Reuses assume_role_no_ec2_permission — its trust policy allows
|
||
|
|
# cross_role_principal (see above), and it has no EC2 permission.
|
||
|
|
output "cross_role_target_no_ec2_permission_arn" {
|
||
|
|
value = aws_iam_role.assume_role_no_ec2_permission.arn
|
||
|
|
}
|