An AWS IAM Policy Linter: Parliament
The interactions between Amazon Web Services (AWS) users, services and resources are governed by policies implemented in AWS Identity and Access Management (IAM). These policies are free-form segments of text that provide enormous flexibility for administrators. Accordingly, they must take care to ensure that this text does not include errors that could cause their policies not to function as intended. This has traditionally been a manual process, as there haven’t been any tools available to provide automatic error.
Duo Labs is releasing a first-of-its-kind open-source tool which automates the evaluation of IAM policies: Parliament.
Parliament is designed to work with other tools and can be used to validate policies as small as a single S3 bucket policy or as complex as the 500+ AWS-managed policies that are available to all users. We are happy to share that if you use AWS managed policies, then you are already benefiting from the insights that Parliament provides! During development, we discovered a number of errors in those published policies, and we communicated with the AWS security team to share our findings.
What Makes Policy-Writing Challenging?
To start with an example, one class of simple mistake is conflating the name of an IAM action with the name of the API operation that is controlled via that IAM action. While the two components do often share a name, they also frequently do not.
AWS goes into further detail in the IAM policy reference documentation. The mapping of S3 actions to API operations is one such “mismatch.” The name of the IAM action to list S3 buckets in an account is s3:ListAllMyBuckets,
but the API operation to use this privilege is called s3:ListBuckets.
This can result in authors writing IAM policies referencing the operation name s3:ListBuckets,
but policies can only refer to IAM actions, so this is simply not a valid permission to assign. If an IAM principal (a person or application that can make an AWS request) is granted permission via such a policy, it would be unable to list the S3 buckets as intended. Indeed, it would have no effect at all.
IAM Policy Language Features
“Globbing” is the term used to describe the ability to use a wildcard character (often *, as is the case with IAM policies) to act as a substitute for one or more characters. IAM policies support globbing, for example, Describe*
lets an author write a policy where they want to allow describing all the things without having to reference each and every action that starts with Describe.
So, if the service they are referencing is Cloudfront, cloudfront:Describe*
would be the action string.
There’s one problem with that, though: there aren’t any CloudFront actions that begin with Describe.
There are actions that begin with List
and Get,
but not Describe.
As such, this attempt to allow “describing” would actually have no effect, and would provide no feedback to the author as to why their policy isn’t doing what they intended.
Some privileges allow the author to provide additional information, such as the name of a resource to apply it to or a conditional statement to be used by the action. Not all actions are as flexible and either require the presence of additional components or disallow them entirely. For example, some EC2 actions work with tags in their conditions—but not all.
A policy that contained the action ec2:ModifyInstanceAttribute
and used the condition ec2:ResourceTag
would be invalid, since that action’s definition doesn’t allow for the use of tags to select EC2 instances.
Policies have to be expressed in JSON format. Within that format, there is a high degree of flexibility, which has the benefit of allowing for arbitrary names and logical expressions, as well as being easily able to support the addition of new AWS services, resources and actions. The freedom provided by the IAM policy language also introduces the opportunity for errors to occur.
Writing complex IAM policies today is similar to composing an essay with only Notepad. Errors that occur such as simple typos or mixing up the privilege name with the API action, as seen above, are analogous to the errors a word processor’s spell-check would catch. Other errors might violate more complex rules, similar to what grammar-check can understand.
Can This Help With Security?
Parliament doesn’t stop at helping authors ensure they’re writing functional policies. We have identified certain types of security issues that can be detected through this same approach of static analysis of the text of the policy, in conjunction with knowledge about the IAM policy rules and the available IAM actions.
One such class of security problem is resource policy privilege escalation. Imagine an IAM user is granted privileges to do anything with a specific S3 bucket except delete objects from it. A security risk is introduced if one of the actions the user is granted on that bucket is S3:PutBucketPolicy.
Using that action, they can put a resource policy on the S3 bucket that grants everyone, including anonymous users and themselves, the ability to delete objects from the S3 bucket. Therefore, this allows them to perform the action (deleting objects) that their policy is not intended to grant, and should be considered a form of privilege escalation.
How Does Parliament Work?
To create Parliament, we first needed a source for definitions of which IAM actions actually exist and all of their constraints. Prior tools have gathered this information either by extracting out the data from the JavaScript files in the AWS Policy Generator or the AWS console itself.
Unfortunately, neither of those sources gets as detailed as we need to implement the linter capabilities we want. For example, those resources might specify conditions that a service can use, but do not describe what specific actions within that service are allowed with a condition.
The only complete source for these definitions are the online docs. Consequently, we built a web scraper to extract the knowledge of the policies from AWS’s HTML and to export it into JSON data that Parliament can use.
Upon loading a new policy to process, the linter iterates through all of the Statements, unglobbing the Action and NotAction elements. For example, it will convert s3:Get*
into every possible action that matches that globbing string. It ensures that the Resources and Conditions are valid for each of the actions under consideration and that the Condition values are the correct types. Another example is that a Bool should only match against the values "true" or "false". Many other similar checks are performed along the way.
One challenge introduced by globbing is matching the required resource type from the documentation against the value given in a policy. For example, s3:GetObject must have a resource that matches arn:*:s3:::*/*,
and an IAM policy might use the string arn:aws:s3:::mybucket*.
We thus have to ask the question, “can both of these match on one or more S3 ARNs?”
Humans can readily look at the two expressions, hypothesize an answer of “yes”, and intuitively construct a proof by example such as arn:aws:s3:::mybucket/foo.
Of course, computers need a little more explicit instructions than “just look at them and find an example.” This problem is more generally described as “finding the intersection of regular expressions,” and it is computationally expensive in many scenarios. Fortunately, since IAM only offers string globbing and not the full set of regular expression features, the problem becomes dramatically simpler.
Is It Worth Using?
We used the entirety of AWS’s managed IAM policies during development of Parliament as a corpus of real-world test cases. As we implemented increasingly advanced capabilities, Parliament actually identified discrepancies such as those described above with about 100 of those managed policies, with an average of three errors per affected policy. The typical impact of any one of these items would have been that the policy didn’t quite do what the user expected in some circumstances. We’re glad to report that the AWS Security, IAM and engineering teams engaged with these reports to triage and organize resolutions to each.
These results during our testing show that Parliament fills a real need in our community. This library could (and should!) be used by any project that ships IAM policies, either as a manual check or via integration into a CI/CD pipeline.
The first such integration has already been deployed, in the CI/CD pipeline of CloudMapper, one of our other projects. Going forward, we plan to expand both the detections Parliament has and its ability to interact with IAM policies programmatically. This library helps policy authors work with stronger confidence that their IAM policies will work as intended and are free from those suspicious constructs that we can identify currently.
Try out Parliament for yourself. Parliament is available on Github.