TFE Version 0.14.11
I am trying to enumerate aws_iam_policy resources that have admin permissions (action:* / resource:*) in a statement. Surprised I couldnt find examples online. This turned out to be quite a bit of unexpected work.
The policy by and large works but has inconsistent behaviour, run the specualtive plan I get an exception [1] when policy set is executed (it obviously has a null reference/undefined issue trying to enumerate a non-enumerable (for resource as res, r line 82). re-run and it picks up the offending policy without issue, debug output [2]
# This policy checks for the occurrence of wildcard (*) when present in both actions and conditions keys of a policy statement or statements
import "tfplan-functions" as plan
import "json"
import "strings"
import "types"
# Forbidden IAM actions #
param forbidden_action default ["*"]
debug = 1
pattern = "^\\*$"
# Forbidden IAM actions
#param forbidden_resource default ["*"]
# Get all IAM policy document data sources
allIAMPolicyDocs = plan.find_resources("aws_iam_policy")
# Filter to IAM policy documents with violations
validated = false
policy_failures = func(x) {
#actionbad = 0
#resourcebad = 0
for allIAMPolicyDocs as address, d {
actionbad = 0
resourcebad = 0
#plan.find_all_datasources
#print("casted value: ",plan.to_string(d))
# Find the statements of the current policy doc
statements = plan.find_blocks(d, "address")
###{"Statement":[{"Action":["*"],"Effect":"Allow","Resource":"*"}],"Version":"2012-10-17"}###
if debug is 1 {
print("-- debug -- about to try unmarshall from json: ",d.change.after.policy)
}
object = json.unmarshal(d.change.after.policy)
if debug is 1 {
print("-- debug -- unmarshalled policy value: ",object)
print("object length: ", length(object))
print("object statement: ", length(object.Statement))
print("object statement type: ", types.type_of(object.Statement))
}
if types.type_of(object.Statement) in ["list","map"] {
action = "null"
resource = "null"
if types.type_of(object.Statement) is "list" {
if debug is 1 {print("type:", types.type_of(object.Statement), "found for: ", object.Statement)}
action = object.Statement[0].Action
resource = object.Statement[0].Resource
/* */
if types.type_of(action) is "string" {
if debug is 1 { print("action string: ", action) }
if action matches pattern {
actionbad = 1
if debug is 1 { print("-- list debug -- bad action found as string") }
}
} else {
for action as act, a {
if debug is 1 { print("action in loop: ", a) }
if a matches pattern {
actionbad = 1
if debug is 1 { print("-- list debug -- bad action found in loop") }
test = true
}
}
}
if types.type_of(resource) is "string" {
if debug is 1 { print("resource string: ", resource) }
if resource matches pattern {
resourcebad = 1
if debug is 1 { print("-- list debug -- bad resource found as string") }
}
} else {
if debug is 1 { print("resourcetype : ", types_type_of(resource)) }
for resource as res, r {
if debug is 1 { print("resource: ", r) }
if r matches pattern {
resourcebad = 1
if debug is 1 { print("-- list debug -- bad resource found in loop") }
}
}
}
} else if types.type_of(object.Statement) is "map" {
if debug is 1 {print("type:", types.type_of(object.Statement), "found for: ", object.Statement)}
action = object.Statement.Action
resource = object.Statement.Resource
if debug is 1 {print("action:",action,"|","resource:",resource)}
/* */
if types.type_of(action) is "string" {
if debug is 1 { print("action string: ", action) }
if action matches pattern {
actionbad = 1
if debug is 1 { print("-- map debug -- bad action found as string", actionbad) }
}
} else {
for action as act, a {
if debug is 1 { print("action in loop: ", a) }
if a matches pattern {
actionbad = 1
if debug is 1 { print("-- map debug -- bad action found in loop") }
}
}
}
if types.type_of(resource) is "string" {
if debug is 1 { print("resource string: ", resource) }
if resource matches pattern {
resourcebad = 1
if debug is 1 { print("-- map debug -- bad resource found as string", resourcebad) }
}
} else {
for resource as res, r {
if debug is 1 { print("resource: ", r) }
if r matches pattern {
resourcebad = 1
if debug is 1 { print("-- map debug -- bad resource found in loop") }
}
}
}
/* */
}
if debug is 1 { print("actionbad",actionbad,"|","resourcebad",resourcebad) }
action = object.Statement[0].Action
resource = object.Statement[0].Resource
}
if debug is 1 {
print("-- debug -- ACTION:",action)
print("-- debug -- RESOURCE:",resource)
}
##################################################################
if actionbad is 1 and resourcebad is 1 {
validated = false
print(address ,"has administrative permissions defined. Please see following link - https://docs.aws.amazon.com/config/latest/developerguide/iam-policy-no-statements-with-admin-access.html")
return true
}
}
return false
}
###
test = func(type) {
return false
}
# Main rule
main = rule {
#actionbad == 1 and resourcebad == 1
policy_failures("ro") is true
}
[1] An error occurred: 1 error occurred:
* ./restrict-admin-access.sentinel:83:11: unsupported type for looping: undefined
[2] – debug – unmarshalled policy value: {“Statement”: [{“Action”: “", “Effect”: “Allow”, “Resource”: "”, “Sid”: “AllowAdminAccess”} {“Action”: [“account:" "aws-portal:” “savingsplans:" "cur:” “ce:"], “Effect”: “Deny”, “Resource”: "”, “Sid”: “DenyAccessToCostAndBilling”} {“Action”: [“iam:DeletePolicy” “iam:DeletePolicyVersion” “iam:CreatePolicyVersion” “iam:SetDefaultPolicyVersion”], “Effect”: “Deny”, “Resource”: [“arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”], “Sid”: “DenyPermBoundaryIAMPolicyAlteration”} {“Action”: [“iam:DeleteUserPermissionsBoundary” “iam:DeleteRolePermissionsBoundary”], “Condition”: {“StringEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: [“arn:aws:iam::123456789012:user/" "arn:aws:iam::123456789012:role/”], “Sid”: “DenyRemovalOfPermBoundaryFromAnyUserOrRole”} {“Action”: [“iam:PutUserPermissionsBoundary” “iam:PutRolePermissionsBoundary”], “Condition”: {“StringNotEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: [“arn:aws:iam::123456789012:user/" "arn:aws:iam::123456789012:role/”], “Sid”: “DenyAccessIfRequiredPermBoundaryIsNotBeingApplied”} {“Action”: [“iam:CreateUser” “iam:CreateRole”], “Condition”: {“StringNotEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: [“arn:aws:iam::123456789012:user/" "arn:aws:iam::123456789012:role/”], “Sid”: “DenyUserAndRoleCreationWithOutPermBoundary”} {“Action”: “sts:AssumeRole”, “Effect”: “Deny”, “NotResource”: “arn:aws:iam::123456789012:role/", “Sid”: “DenyAssumeRoleOutsideOfAccount”}], “Version”: “2012-10-17”}
object length: 2
object statement: 7
object statement type: list
type: list found for: [{“Action”: "”, “Effect”: “Allow”, “Resource”: “", “Sid”: “AllowAdminAccess”} {“Action”: ["account:” “aws-portal:" "savingsplans:” “cur:" "ce:”], “Effect”: “Deny”, “Resource”: “", “Sid”: “DenyAccessToCostAndBilling”} {“Action”: [“iam:DeletePolicy” “iam:DeletePolicyVersion” “iam:CreatePolicyVersion” “iam:SetDefaultPolicyVersion”], “Effect”: “Deny”, “Resource”: [“arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”], “Sid”: “DenyPermBoundaryIAMPolicyAlteration”} {“Action”: [“iam:DeleteUserPermissionsBoundary” “iam:DeleteRolePermissionsBoundary”], “Condition”: {“StringEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: ["arn:aws:iam::123456789012:user/” “arn:aws:iam::123456789012:role/"], “Sid”: “DenyRemovalOfPermBoundaryFromAnyUserOrRole”} {“Action”: [“iam:PutUserPermissionsBoundary” “iam:PutRolePermissionsBoundary”], “Condition”: {“StringNotEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: ["arn:aws:iam::123456789012:user/” “arn:aws:iam::123456789012:role/"], “Sid”: “DenyAccessIfRequiredPermBoundaryIsNotBeingApplied”} {“Action”: [“iam:CreateUser” “iam:CreateRole”], “Condition”: {“StringNotEquals”: {“iam:PermissionsBoundary”: “arn:aws:iam::123456789012:policy/sdlf_adm_permission_boundary_services_policy”}}, “Effect”: “Deny”, “Resource”: ["arn:aws:iam::123456789012:user/” “arn:aws:iam::123456789012:role/"], “Sid”: “DenyUserAndRoleCreationWithOutPermBoundary”} {“Action”: “sts:AssumeRole”, “Effect”: “Deny”, “NotResource”: "arn:aws:iam::123456789012:role/”, “Sid”: “DenyAssumeRoleOutsideOfAccount”}]
action string: *
– list debug – bad action found as string
resource string: *
– list debug – bad resource found as string
actionbad 1 | resourcebad 1
– debug – ACTION: *
– debug – RESOURCE: *
aws_iam_policy.adm_permission_boundary_services_policy has administrative permissions defined. Please see following link - iam-policy-no-statements-with-admin-access - AWS Config
./restrict-admin-access.sentinel:171:1 - Rule “main”
Description:
Main rule
Value:
false