Wonk is a tool for combining a set of AWS policy files into smaller compiled policy sets.

Overview

The Policy Wonk

Wonk is a tool for combining a set of AWS policy files into smaller compiled policy sets.

Table of Contents

Rationale

Wonk can help you in several situations.

Policies are limited resources

You want to give the people in your organization the AWS permissions needed to do their job. Amazon has helpfully created several hundred policies like AmazonRDSReadOnlyAccess and AmazonS3FullAccess that you can assign to users, groups, or roles. This is super convenient... up until it's not.

AWS has IAM quotas like:

  • Policies may not be more than 6,144 characters long.
  • You can't attach more than 10 groups or roles to a user.
  • You can't attach more than 10 policies to a single group, role, or user.
  • If you're logging into AWS with SSO, each user like gets exactly 1 role assigned to them.

What if your backend engineers log in with Okta and they need 11 policies to do their job?

Wonk to the rescue! You can combine them into one big policy with a command line like:

$ wonk combine -p MyPolicy AWSPolicy1.json AWSPolicy2.json

which reads the contents of AWSPolicy1.json and AWSPolicy2.json and merges them into a MyPolicy.json file.

Your roles share a lot of common permissions

Perhaps you have one role for backend engineers, and another role for backend engineers who are on call this week and need some additional permissions. You really don't want to maintain two policies that are nearly identical, though.

In this case, you could put all of the standard permissions in one policy, all of the additional on-call permissions in another, then combine them:

$ wonk combine -p BackendOnCall Backend.json OnCall.json

Things got really complicated when you weren't looking

Beyond just combining a file or 2 as needed, you want some help managing multiple roles with lots of policies. Say you're setting up policies for both frontend and backend engineers and each of them have special on-call roles that share some extra debugging permissions. Each role uses a combination of some AWS-managed policies with some that you've written yourself.

Wonk loves you and wants you to be happy.

First, it assumes a directory layout like this:

├── wonk.yaml
├── managed
│   ├── AWSPolicy1.json
│   └── AWSPolicy2.json
├── local
│   ├── BackendECSReadOnly.json
│   ├── FrontendCloudWatchReadOnly.json
│   └── OnCall.json
└── combined
    ├── Backend_1.json
    ├── Frontend_1.json
    ├── BackendOnCall_1.json
    └── FrontendOnCall_1.json

where the managed directory is full of policy files that you've downloaded from AWS (maybe using the wonk fetch command), the local directory has policies you've written yourself, and combined has the output files that Wonk creates for you.

You could write a bunch of wonk combine command lines, maybe in a shell script or a Makefile. Alternatively, you could write a wonk.yaml file like this:

policy_sets:
  Backend:
    managed:
      - AWSPolicy1
      - arn:path:to:your:policy/AWSPolicy2
    local:
      - BackendECSReadOnly

  BackendOnCall:
    inherits:
      - Backend
    local:
      - OnCall

  Frontend:
    managed:
      - AWSPolicy3
    local:
      - FrontendCloudWatchReadOnly

  FrontendOnCall:
    inherits:
      - Frontend
    local:
      - OnCall

and then tell Wonk to build them all for you:

$ wonk build --all

which fetches any missing managed policies, then creates a set of combined policies named after their YAML configurations.

A managed policy Foo is fetched by the ARN arn:aws:iam::aws:policy/Foo. However, some Amazon policies don't follow that convention. In that case, you can give an ARN instead of a policy name and that ARN will be fetched instead (and the policy's name will be derived from the ARN). You could also do that if you want to fetch your own policy from Amazon instead of maintaining it locally.

Installation

pip install wonk

Alternatively: clone this repo and run poetry install.

Usage

Fetching policies

Use wonk fetch to retrieve a policy from AWS by name or by ARN and write it to stdout. Each of these commands emit the same output:

$ wonk fetch --arn "arn:aws:iam::aws:policy/AWSLambdaFullAccess"
$ wonk fetch --name AWSLambdaFullAccess
$ wonk fetch --profile my_aws_profile_name --name AWSLambdaFullAccess

Combining policies

Use wonk combine to combine multiple policies into a policy set:

$ wonk combine -p Foo policy1.json policy2.json

Building configured policy statements

The wonk build command interprets a wonk.yaml file as described in the example above and builds the requested policy set(s).

To build one named policy set:

$ wonk build --policy-set BackendOnCall

To build all defined policy sets:

$ wonk build --all

The details

Sounds simple, right? Well, not quite. Remember, IAM quotas limit managed policies to 6,144 characters. You can put a few more characters on an inline policy directly on a role, but that's not best practice and you don't really want to go down that path. Instead, Wonk uses a few tricks to try to make policies fit inside their size limit:

  • It strips all Sid keys from statements, per Amazon's recommendations.
  • It discard duplicate actions.
  • It removes all "shadowed" actions. For instance, if a statement has actions Foo:SomeAction and Foo:*, it discards Foo:SomeAction because Foo:* already has it covered. Similarly, Foo:Get* will shadow Foo:GetSomething, so Foo:GetSomething will be removed.
  • Wonk tries to make the generated policies as human-readable as possible, but will format them very tersely if necessary. You can always use jq to reformat its outputs for viewing.

Note: actions are always grouped by similar principals, resources, conditions, etc. If two statements have different conditions, say, they are processed separately.

Breaking up is hard to do

Wonk does whatever it can to make a policy fit within that magic 6,144 character limit, but somethings that just can't be done. If you try to combine 30 different non-overlapping policies, there's a decent chance that the end result simply can't be shrunk enough. A careful reader might have noticed that all of the command examples specify an output "base" instead of a specific filename, and an output Foo ends up creating a file named Foo.json. This is because in the case that Wonk can't pack everything into a separate file, it creates a set of as few output policies as possible to include all of the actions. The general process is this:

  • Try to make everything fit.
  • If there are any statements with so many actions that they can't be shrunk into the size limit, split them up into equal-size chunks that do fit.
  • Now we have the case of fitting M statements into N policies, of which there can't be more than 10 because of the AWS limits. That looks a lot like the knapsack problem, and indeed it is. Wonk uses Google's SCIP constraint solver to pack all of the statements into as few policies as possible.
  • If none of this is sufficient, Wonk raises an exception and quits.

Policy sets

The end result of many Wonk operations is a collection of files, a policy set, named _1.json through _N.json where N <= 10. This is different from most utilities which operate on individual files, but Wonk can't know how many files it will be creating in advance.

Why 10? Because AWS usually won't allow you to attach more than 10 policies to a user, group, or role. Since policy sets work together like one giant policy and can't be split up, Wonk won't create a policy set that can't actually be attached to anything. If you're bumping up against this limit, consider creating 2 policy sets and applying them to 2 distinct but groups (like Backend_1 and Backend_2), then putting each relevant user into both groups. Alternatively, if your policies cover 99 actions like Service:OnePermission and Service:Another on a service that only has 100 possible actions, and you've done your due diligence and don't mind giving your users access to that 100th action, consider adding a Service:* action to a local policy. That will replace all those individual actions with the single wildcard. Likewise, if you mean to give your users access to all of the various Service:GetThis and Service:GetThat actions, you can cover them all at once with Service:Get*. This also has the nice side effect of documenting that you actually intend to allow access to all of the Get* actions.

Terraforming combined policies

Reasonably recent modern versions of Terraform support fileset and for_each syntax. You can define a single policy resource that exactly expands out to a whole set of policies, then attach them all at once to a group or role:

resource "aws_iam_policy" "Frontend" {
  for_each    = fileset(path.module, "combined/Frontend_*.json")
  name        = split(".", basename(each.value))[0]
  description = "Frontend users need to do stuff"
  policy      = file(each.value)
}

resource "aws_iam_group_policy_attachment" "Frontenders__Frontend" {
  for_each   = aws_iam_policy.Frontend
  group      = data.aws_iam_group.frontenders.group_name
  policy_arn = each.value.arn
}

Limitations

As of this writing, Wonk is usable but not finished. It's missing a few nice features:

  • Wonk doesn't consider action shadowing when one statement has restrictions but another doesn't. For example, given two statement blocks:
{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "Foo:Something",
            "Resource": "arn:aws:service::my_resource"
        },
        {
            "Effect": "Allow",
            "Action": "Foo:Something"
        }
    ]
}

the second statement is broader than the first, so the first could be safely removed. Right now it isn't.

Copyright

The Policy Wonk is copyright 2021 Amino, Inc. and distributed under the terms of the Apache-2.0 License.

Comments
  • Model policies as a class

    Model policies as a class

    The policy / statement / InternalStatement model was getting a bit too complicated to keep a consistent mental model. This change:

    • Replaces functions operating on dicts represent policies with a new Policy class and appropriate methods.
    • More consistently operates on InternalStatement models internally.
    • Makes Policy and InternalStatement objects immutable, greatly simplifying how to reason about them.
    • Simplifies some code (like write_policy_set) thanks to the new properties.

    I think this approach makes both the code, and life for developers, simpler.

    opened by kstrauser 1
  • Combining resources

    Combining resources

    This implements a better algorithm for combining resources:

    • If a statement has actions Foo* and FooBar*, they'll be combined to just Foo*.
    • Before this, if two statements have the same resources but different actions, they'd be combined into one statement with those resources and all the actions.
    • Now, similarly, if two statements have the same actions but different resources, they'll be combined into one statement with those actions and their merged resources.
    • The two steps above are repeated in a loop until no more statements can be combined.

    Taken together, these can improve compression quite a bit.

    opened by kstrauser 1
  • Collect wildcards in statements before splitting

    Collect wildcards in statements before splitting

    In Statement.split, the list of actions wasn't being deduplicated, going through wildcard matching, etc. before being split. The end result was that some of the intermediate statements were by themselves too big to be packed into a single policy. It went something like this:

    1. Calculate the length of the (properly cleaned up) statements.
    2. Use that to figure out how many split up statements to yield.
    3. Now divide the un-cleaned-up statements into that many statements.

    Oops! Since the un-cleaned-up list is typically a lot bigger than the canonicalized output, some of those child statements may be a lot larger than expected.

    This fixes that and refactors just a little bit. All of the test changes are renames due to adjust for the refactoring, plus running a newer version of Black.

    opened by kstrauser 0
  • Move the

    Move the "dict-to-list" function to from_dict

    This moves the "repair AWS malformed policy" fix into import functionality at from_dict. That fixes a regression when handling a broken policy.

    opened by kstrauser 0
  • Deterministically compute policy IDs from their contents

    Deterministically compute policy IDs from their contents

    This is a pretty tiny code change that makes the output policy ID a hash of its contents. This means that creating a policy is deterministic: the same inputs will always generate the same outputs, which Git and Terraform will love.

    opened by kstrauser 0
  • FOU-332: Upgrade to latest ortools

    FOU-332: Upgrade to latest ortools

    Description

    Wonk is using an out-of-date ortools solver not compatible with Debian bullseye or M1 Macs. This PR upgrades ortools (and some other project dependencies) to their latest constrained versions.

    opened by cjduffett 0
  • Better wildcard shadowing

    Better wildcard shadowing

    @briankzimmer

    Previously, the algorithm for removing shadowed actions kept all wildcard actions, even if they're shadowed by other wildcards. For example, given the actions:

    {
        "s3:Get*",
        "s3:GetFoo*",
    }
    

    s3:GetFoo* would mistakenly be kept, even though s3:Get* shadows it. This updates that algorithm to be less "clever" and also more robust.

    This also bumps the version to 0.5.3 for release.

    opened by kstrauser 0
  • Add new tests and fix questionmark bug

    Add new tests and fix questionmark bug

    Changes

    • skip paths that have questionmarks
    • skip paths that have duplicate wildcards
    • add tests

    Open issues

    what about equivalent paths that remove each other from final set?

    opened by JonnyDaenen 0
  • Wonk fails on '?' syntax in policies

    Wonk fails on '?' syntax in policies

    Problem

    Given the following resource description:

    arn:<hidden>:log-group:???-dummy:*
    

    Wonk fails on this.

    Cause

    Wonk tries to use this string as a regex to find out if multiple, more specific strings, match. This happens here.

    To do so, wonk replaces * with .* in order to match with regex syntax in python. The question mark, which also needs a character in front of it, is not adjusted.

    Potential solution

    I believe it makes sense to also replace the questionmark using the following code:

            pattern_string = item.replace("?", ".?")
    

    If I understand the semantics of wonk correctly in this part, this should solve the error while also looking for more specific strings.

    opened by JonnyDaenen 1
  • Increase limit of managed policies from 10 to 20

    Increase limit of managed policies from 10 to 20

    Problem

    Wonk is hard-coded to allow up to max 10 policies per role. However, the limit in AWS can be increased up to 20 (hard limit). Source

    Potential solutions

    • change limit from 10 to 20
    • make it possible to override limit via a cli flag
    opened by JonnyDaenen 0
  • Wonk should support writing least privileged policies not strip hints about privilege problems silently escalate privs.

    Wonk should support writing least privileged policies not strip hints about privilege problems silently escalate privs.

    Or it should at least support a mode that it can be configured to do so.

    For example from "The Details"

    It removes all "shadowed" actions. For instance, if a statement has actions Foo:SomeAction and Foo:, it discards Foo:SomeAction because Foo: already has it covered. Similarly, Foo:Get* will shadow Foo:GetSomething, so Foo:GetSomething will be removed.

    If you are trying to create least privileged policies Foo: should issue a warning, since it and Foo:Get* would shadow. The "shadow" should be cast on the least specific instructions because this is a signal we are trying to be more specific not less specific.

    Under Limitations

    Wonk doesn't consider action shadowing when one statement has restrictions but another doesn't. For example, given two statement blocks:

    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "Foo:Something",
                "Resource": "arn:aws:service::my_resource"
            },
            {
                "Effect": "Allow",
                "Action": "Foo:Something"
            }
        ]
    }
    

    This is a good thing that that it doesn't do this, and I wouldn't want it to. It's suggesting you add the inherent escalating of the privileges unbeknownst to the policy writer. Anybody serious about writing IAM policy shouldn't want this behavior.

    If you have something restricted by resource and something comes along later with this case or with a "Resource": "*" the proper thing to do is collapse this into a statement WITH those resources. If we don't want the * we can remove it. In the particular case above it should probably issue a warning, but the course it should take is producing:

    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "Foo:Something",
                "Resource": "arn:aws:service::my_resource"
            }
       ]
    }
    

    in the input case of

    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "Foo:Something",
                "Resource": "arn:aws:service::my_resource"
            },
            {
                "Effect": "Allow",
                "Action": "Foo:Something",
                "Resource": "*"
            }
        ]
    }
    

    I would expect it to collapse to:

    {
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "Foo:Something",
                "Resource": [
                     "*",
                     "arn:aws:service::my_resource"
                ]
            }
        ]
    }
    

    and issue a warning; because it is elevating privileges. Either you meant to do that and you should remove "arn:aws:service::my_resource" to fix the warning, or you should remove "*" and add the additional cases you meant to add if there are any.

    opened by donnoman 0
  • It appears that the platform you run wonk may impact how case sensitive resources get deduped.

    It appears that the platform you run wonk may impact how case sensitive resources get deduped.

    I've been scratching my head to find out why my policies on AWS seem to not work but when I build the policies locally they all check out.

    There's been a secondary problem I added some diffs, and I get wierd diffs if I push the policies from my mac workstation, vs let them get built and deployed by CI. (I've built a script to push the combined policies into IAM and I use a call to pull down the version already in IAM and diff them to make a decision if I need to kill a version to make room and replace it.)

    I finally found some fire for my smoke:

    On my local workstation doing a find all case sensitive to the arn I seem to keep missing in my deployed policy.

    When I download of all the assets my CI built and deployed, and searched against it case sensitive.

    None of the lower case versions of "arn:aws:s3:::sagemaker/*" are to be found in the CI built wonk policies.

    Of these resources

            "Resource": [
                "arn:aws:s3:::*SageMaker*/*",
                "arn:aws:s3:::*Sagemaker*/*",
                "arn:aws:s3:::*sagemaker*/*"
            ]
    

    The resulting policies when deployed by my CI only the following version survives,

            "Resource": [
                "arn:aws:s3:::*SageMaker*/*"
            ]
    

    when I build the policies locally I can find all of the entries in my combined policies.

            "Resource": [
                "arn:aws:s3:::*SageMaker*/*",
                "arn:aws:s3:::*Sagemaker*/*",
                "arn:aws:s3:::*sagemaker*/*"
            ]
    

    So I'm assuming there is something environment influencing whether the deduping is happening in a case insensitive fashion. Either linux level, or perhaps python level. I need to find the way to force it to be case sensitive.

    opened by donnoman 0
Owner
Amino, Inc
Amino open-source projects
Amino, Inc
Set the draft security HTTP header Permissions-Policy (previously Feature-Policy) on your Django app.

django-permissions-policy Set the draft security HTTP header Permissions-Policy (previously Feature-Policy) on your Django app. Requirements Python 3.

Adam Johnson 76 Nov 30, 2022
PyExtractor is a decompiler that can fully decompile exe's compiled with pyinstaller or py2exe

PyExtractor is a decompiler that can fully decompile exe's compiled with pyinstaller or py2exe with additional features such as malware checker/detector! Also checks file(s) for suspicious words, discord webhooks, discord invites, pastebins, ips etc..

Rdimo 56 Jul 31, 2022
This tool allows to automatically test for Content Security Policy bypass payloads.

CSPass This tool allows to automatically test for Content Security Policy bypass payloads. Usage [cspass]$ ./cspass.py -h usage: cspass.py [-h] [--no-

Ruulian 30 Nov 22, 2022
A wordlist generator tool, that allows you to supply a set of words, giving you the possibility to craft multiple variations from the given words, creating a unique and ideal wordlist to use regarding a specific target.

A wordlist generator tool, that allows you to supply a set of words, giving you the possibility to craft multiple variations from the given words, creating a unique and ideal wordlist to use regarding a specific target.

Cycurity 39 Dec 10, 2022
Format SSSD Raw Kerberos Payloads into CCACHE files for use on Windows systems

KCMTicketFormatter This tools takes the output from https://github.com/fireeye/SSSDKCMExtractor and turns it into properly formatted CCACHE files for

Black Lantern Security 35 Oct 25, 2022
Time Discretization-Invariant Safe Action Repetition for Policy Gradient Methods

Time Discretization-Invariant Safe Action Repetition for Policy Gradient Methods This repository is the official implementation of Seohong Park, Jaeky

Seohong Park 6 Aug 2, 2022
Safe Policy Optimization with Local Features

Safe Policy Optimization with Local Feature (SPO-LF) This is the source-code for implementing the algorithms in the paper "Safe Policy Optimization wi

Akifumi Wachi 6 Jun 5, 2022
Enhancing Twin Delayed Deep Deterministic Policy Gradient with Cross-Entropy Method

Enhancing Twin Delayed Deep Deterministic Policy Gradient with Cross-Entropy Method Hieu Trung Nguyen, Khang Tran and Ngoc Hoang Luong Setup Clone thi

Evolutionary Learning & Optimization (ELO) Lab 6 Jun 29, 2022
Midas ELF64 Injector is a tool that will help you inject a C program from source code into an ELF64 binary.

Midas ELF64 Injector Description Midas ELF64 Injector is a tool that will help you inject a C program from source code into an ELF64 binary. All you n

midas 20 Dec 24, 2022
This is an injection tool that can inject any xposed modules apk into the debug android app

This is an injection tool that can inject any xposed modules apk into the debug android app, the native code in the xposed module can also be injected.

Windy 32 Nov 5, 2022
A honey token manager and alert system for AWS.

SpaceSiren SpaceSiren is a honey token manager and alert system for AWS. With this fully serverless application, you can create and manage honey token

null 287 Nov 9, 2022
Security offerings for AWS Control Tower

Caylent Security Catalyst Reference Architecture Examples This repository contains solutions for Caylent's Security Catalyst. The Security Catalyst is

Steven Connolly 1 Oct 22, 2021
Chapter 1 of the AWS Cookbook

Chapter 1 - Security Set and export your default region: export AWS_REGION=us-east-1 Set your AWS ACCOUNT ID:: AWS_ACCOUNT_ID=$(aws sts get-caller-ide

AWS Cookbook 30 Nov 27, 2022
Providing DevOps and security teams script to identify cloud workloads that may be vulnerable to the Log4j vulnerability(CVE-2021-44228) in their AWS account.

We are providing DevOps and security teams script to identify cloud workloads that may be vulnerable to the Log4j vulnerability(CVE-2021-44228) in their AWS account. The script enables security teams to identify external-facing AWS assets by running the exploit on them, and thus be able to map them and quickly patch them

Mitiga 13 Jan 4, 2022
DirBruter is a Python based CLI tool. It looks for hidden or existing directories/files using brute force method. It basically works by launching a dictionary based attack against a webserver and analyse its response.

DirBruter DirBruter is a Python based CLI tool. It looks for hidden or existing directories/files using brute force method. It basically works by laun

vijay sahu 12 Dec 17, 2022
Python library to remotely extract credentials on a set of hosts.

Python library to remotely extract credentials on a set of hosts.

Pixis 1.5k Dec 31, 2022
A set of blender assets created for the $yb NFT project.

fyb-blender A set of blender assets created for the $yb NFT project. Install just as you would any other Blender Add-on (via Edit->Preferences->Add-on

Pedro Arroyo 1 May 6, 2022
Vulnerability Scanner & Auto Exploiter You can use this tool to check the security by finding the vulnerability in your website or you can use this tool to Get Shells

About create a target list or select one target, scans then exploits, done! Vulnnr is a Vulnerability Scanner & Auto Exploiter You can use this tool t

Nano 108 Dec 4, 2021
All in One CRACKER911181's Tool. This Tool For Hacking and Pentesting. 🎭

All in One CRACKER911181's Tool. This Tool For Hacking and Pentesting. ??

Cracker 331 Jan 1, 2023