ForAnyValue:StringEqualsIfExists behaving unexpectedly

0

I'm digging into some corners of IAM conditions that I don't use a lot, and I've been testing some sample policies to make sure real-world behavior matches my assumptions.

I've created a role with only the following policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sqs:*"
            ],
            "Resource": [
                "*"
            ],
            "Condition": {
                "ForAnyValue:StringEqualsIfExists": {
                    "aws:TagKeys": [
                        "team"
                    ]
                }
            }
        }
    ]
}

When I try to ListQueues with this role, I get a no identity-based policy allows the sqs:listqueues action error. If I remove the condition, ListQueues works as expected.

My understanding is that ListQueues would not have aws:TagKeys in the request context, so for that action, this policy would behave the same as if the condition were not there. The docs do not list aws:TagKeys as an available condition key for ListQueues.

Am I misunderstanding how …IfExists works? Or does that request include aws:TagKeys for some reason?

2 Answers
1
Accepted Answer

Hello.

"ForAnyValue" is considered an error because if there is no matching context key or the dataset is null, the condition returns false.
In other words, I think that the request context key does not include "aws:TagKeys", so it becomes False and causes an error in "StringEqualsIfExists".
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html

ForAnyValue – This qualifier tests whether at least one member of the set of request context key values matches at least one member of the set of context key values in your policy condition. The context key returns true if any one of the context key values in the request matches any one of the context key values in the policy. For no matching context key or a null dataset, the condition returns false.

profile picture
EXPERT
answered 7 months ago
profile picture
EXPERT
reviewed 7 months ago
profile picture
EXPERT
reviewed 7 months ago
  • AWS has examples published which utilized ForAnyValue:StringEqualsIfExists. See: https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/deploy-preventative-attribute-based-access-controls-for-public-subnets.html

    I do understand that ForAnyValue would return false for no matching context key, but the purpose of the …IfExists variant of operators is to always evaluate the condition to true if the key is not present.

    It seems like IfExists needs to have some affect when used with ForAnyValue, for AWS to include it in sample code.

  • I thought that the error might be occurring not because the value is Null, but because the key doesn't exist in the first place, since 'aws:TagKeys' is not included in the request context keys of the 'ListQueues' API. Therefore, I thought that it might not work for APIs where the request context key is not included in the first place.

  • Yeah I think that's the crux of the question. ForAnyValue is meant to behave one way when the key is missing, and …IfExists is meant to behave the opposite way, so there's potentially a conflict. It seems like it would make sense for …IfExists to have priority, though, because otherwise ForAnyValue behaves the same with and without …IfExists. Meaning, if …IfExists has priority, that's a choice of how the policy should behave, and if ForAnyValue has priory, there is no choice.

  • I've received confirmation from AWS Support that when both ForAnyValue and …IfExists are present, ForAnyValue is resolved first, so a missing condition key will always return false.

0

I think this is correct, if not intuitive behaviour. It's caused by ...IfExists being part of the StringEquals comparison operator and not of the ForAnyValue set operator.

Your statement tells IAM to loop through all the values in aws:TagKeys and to return true if any of the values encountered either matches the string literal on the right side (=StringEquals) or is missing (...IfExists). Of course, it's logically impossible to encounter a non-existent value while iterating through a collection/array, which is why having an ...IfExists variant available for use in a loop is non-intuitive and can never make a difference to the outcome, as your example demonstrated.

The example of ForAnyValue:StringEqualsIfExists in the link is simply an error in AWS's documentation. The example is evaluating aws:ResourceTag/SubnetType, which is a single-valued condition key. ForAnyValue is a condition set operator, none of which are designed to work with single-valued condition keys. It's stated explicitly in the first bullet point on this page: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html

Since a resource tag key can have only a single tag value, aws:ResourceTag/tag-key is a single-valued context key. Do not use a condition set operator with a single-valued context key.

I understand your original scenario was just experimentation and isn't intended to make sense as such (allowing the operation if the "team" tag key is specified in the request -- but also if it isn't specified), but it would be expressed like this:

"Condition": {
    "StringLikeIfExists": {
        "aws:RequestTag/team": "*"
    }
}

It would make practical sense if a list of allowed values were supplied in place of the single full wildcard "*". This would allow specifying any of the listed values for the "team" tag, or not specifying it at all, but wouldn't allow specifying any other value for it:

"Condition": {
    "StringEqualsIfExists": {
        "aws:RequestTag/team": ["teamA","teamB","teamC"]
    }
}
EXPERT
answered 7 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions