Sign inSign up

jameswoolfenden/pike

By jameswoolfenden

•Updated 3 days ago

Pike is a tool for determining the permissions or policy required for IAC code

Image
2

9.2K

jameswoolfenden/pike repository overview

⁠Pike

alt text

Maintenance CI Latest Release GitHub tag (latest SemVer) OpenTofu/Terraform Version pre-commit checkov Github All Releases codecov OpenSSF Best Practices

Pike is a tool to determine the minimum IAM permissions required to run OpenTofu/Terraform infrastructure code.

What's new?

  • JSON modules support.
  • GCP compare, checks IAC permissions required versus a deployed role.
  • Backend detection S3 and GCP.

Pike currently supports OpenTofu/Terraform and supports multiple providers (AWS, GCP and AZURE); Azure is the newest with AWS having the most supported resources https://github.com/JamesWoolfenden/pike/tree/master/src/mapping⁠. Feel free to submit PR or Issue if you find an issue or even better add new resources, and then I'll take a look at merging it ASAP.

Note on Placeholder Resources: Some resources in the lookup maps (particularly GCP organization, folder, and billing-level resources) are marked with placeholder entries. These resources are recognized by Pike but do not have empirically validated IAM permissions because they require organization-level access or specialized account types that are difficult to test. When Pike encounters these resources, it will not error, but the permissions have not been validated through actual resource lifecycle testing.

CAVEAT The outputs of this tool are your first step, if you have AWS, you can now generate resources partially, there are no conditions and even partial resources are wild-carded (for now). (for AWS)minimum best practice would go further (and I am working on it as well), you will need to modify these permissions to the required in your environment by adding these restrictions, you can also deploy using short-lived credentials (using this tool or Vault) (in AWS so far), generating short-lived credentials for your build and then remotely (REMOTE) supply and invoke your builds (INVOKE).

Ideally I would like to do this for you, but these policies are currently determined statically (QUICKER), and unrecorded intentions can be impossible to infer.

⁠Quick Start

Get started with Pike in 3 steps:

  1. Install Pike

    # macOS
    brew tap jameswoolfenden/homebrew-tap
    brew install jameswoolfenden/tap/pike
    
    # Windows (using Scoop)
    scoop bucket add iac https://github.com/JamesWoolfenden/scoop.git
    scoop install pike
    
    # Or install from source
    go install github.com/jameswoolfenden/pike@latest
    
  2. Scan your OpenTofu/Terraform code

    pike scan -d ./path/to/your/terraform
    

    This outputs the minimum IAM permissions required as JSON.

  3. Generate as Terraform/OpenTofu code

    pike scan -o terraform -d ./path/to/your/terraform
    

    This creates an aws_iam_policy resource you can deploy.

Next steps: Use pike make to deploy the policy directly, or pike compare to validate against existing policies. See Usage⁠ for all commands.

⁠Table of Contents

⁠Install

Download the latest binary here:

https://github.com/JamesWoolfenden/pike/releases⁠

Install from code:

  • Clone repo
  • Run go install

Install remotely:

go install  github.com/jameswoolfenden/pike@latest
⁠MacOS
brew tap jameswoolfenden/homebrew-tap
brew install jameswoolfenden/tap/pike
⁠Windows

I'm now using Scoop to distribute releases, it's much quicker to update and easier to manage than previous methods, you can install scoop from https://scoop.sh/⁠.

Add my scoop bucket:

scoop bucket add iac https://github.com/JamesWoolfenden/scoop.git

Then you can install a tool:

scoop install pike
⁠Docker
docker pull jameswoolfenden/pike
docker run --tty --volume /local/path/to/tf:/tf jameswoolfenden/pike scan -d /tf

https://hub.docker.com/repository/docker/jameswoolfenden/pike⁠

⁠Usage

⁠Scan

To scan a directory containing OpenTofu/Terraform files:

./pike scan -d .\terraform\
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "ec2:MonitorInstances",
            "ec2:UnmonitorInstances",
            "ec2:DescribeInstances",
            "ec2:DescribeTags",
            "ec2:DescribeInstanceAttribute",
            "ec2:DescribeVolumes",
            "ec2:DescribeInstanceTypes",
            "ec2:RunInstances",
            "ec2:DescribeInstanceCreditSpecifications",
            "ec2:StopInstances",
            "ec2:StartInstances",
            "ec2:ModifyInstanceAttribute",
            "ec2:TerminateInstances",
            "ec2:AuthorizeSecurityGroupIngress",
            "ec2:AuthorizeSecurityGroupEgress",
            "ec2:CreateSecurityGroup",
            "ec2:DescribeSecurityGroups",
            "ec2:DescribeAccountAttributes",
            "ec2:DescribeNetworkInterfaces",
            "ec2:DeleteSecurityGroup",
            "ec2:RevokeSecurityGroupEgress"
        ],
        "Resource": "*"
    }
}

You can also generate the policy as OpenTofu/Terraform instead:

$pike scan -o terraform -d ../modules/aws/terraform-aws-activemq
resource "aws_iam_policy" "terraformXVlBzgba" {
  name        = "terraformXVlBzgba"
  path        = "/"
  description = "Add Description"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateNetworkInterface",
                "ec2:CreateNetworkInterfacePermission",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:DeleteNetworkInterface",
                "ec2:DeleteNetworkInterfacePermission",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteTags",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "ec2:DetachNetworkInterface",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "kms:CreateKey",
                "kms:DescribeKey",
                "kms:EnableKeyRotation",
                "kms:GetKeyPolicy",
                "kms:GetKeyRotationStatus",
                "kms:ListResourceTags",
                "kms:ScheduleKeyDeletion",
                "kms:TagResource",
                "kms:UntagResource"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "mq:CreateBroker",
                "mq:CreateConfiguration",
                "mq:CreateTags",
                "mq:CreateUser",
                "mq:DeleteBroker",
                "mq:DeleteTags",
                "mq:DeleteUser",
                "mq:DescribeBroker",
                "mq:DescribeConfiguration",
                "mq:DescribeConfigurationRevision",
                "mq:DescribeUser",
                "mq:RebootBroker",
                "mq:UpdateBroker",
                "mq:UpdateConfiguration",
                "mq:UpdateUser"
            ],
            "Resource": "*"
        }
    ]
})
}

And I am working on further enhancements to policy generation, if you have AWS auth installed:

e:\pike scan -d . -i -e
9:13AM DBG terraform init at E:\Code\modules\aws\terraform-aws-activemq
9:13AM DBG downloaded ip
resource "aws_iam_policy" "terraform_pike" {
  name_prefix = "terraform_pike"
  path        = "/"
  description = "Pike Autogenerated policy from IAC"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateNetworkInterface",
                "ec2:CreateNetworkInterfacePermission",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:DeleteNetworkInterface",
                "ec2:DeleteNetworkInterfacePermission",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteTags",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "ec2:DetachNetworkInterface",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": [
                "arn:aws:ec2:eu-west-2:680235478471:*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "kms:CreateGrant"
            ],
            "Resource": [
                "arn:aws:kms:eu-west-2:680235478471:*"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "mq:CreateBroker",
                "mq:CreateConfiguration",
                "mq:CreateTags",
                "mq:CreateUser",
                "mq:DeleteBroker",
                "mq:DeleteTags",
                "mq:DeleteUser",
                "mq:DescribeBroker",
                "mq:DescribeConfiguration",
                "mq:DescribeConfigurationRevision",
                "mq:DescribeUser",
                "mq:RebootBroker",
                "mq:UpdateBroker",
                "mq:UpdateConfiguration",
                "mq:UpdateUser"
            ],
            "Resource": [
                "arn:aws:mq:eu-west-2:680235478471:*"
            ]
        }
    ]
})
}
⁠Escalation-class permissions

When a Terraform config manages its own IAM (google_project_iam_*, aws_iam_role_policy*, azurerm_role_assignment), the minimum-sufficient role pike computes is also owner-equivalent — the holder can grant themselves anything. Pike always warns about these on stderr:

WARNING: escalation-class permissions detected — holder can grant themselves additional access.
         Consider the two-role pattern: planner SA (read-only) on all branches,
         applier SA (full) on protected branches only.
  aws:   iam:PassRole, iam:PutRolePolicy

No flag is needed to enable this; it fires whenever escalation-class permissions are present.

The same warning also fires for dangerous combinations of otherwise-unremarkable permissions — e.g. iam:PassRole paired with lambda:CreateFunction or ec2:RunInstances lets you choose which compute resource receives that role, and iam:CreateAccessKey paired with iam:ListUsers lets you mint credentials for any user:

  Dangerous combinations (no single permission above reveals this; together they do):
    aws:   iam:PassRole + lambda:CreateFunction

Pass --strict to pike scan to exit non-zero when either kind of escalation-class permission is present, even though scan still succeeds in producing output (pike compare --strict does the same for the live-vs-IaC comparison). Pass --no-lint to suppress the warning (and the --strict check) entirely — useful if you're piping scan's policy output somewhere and don't want lint noise on stderr.

pike scan --strict -d ./terraform
pike scan --no-lint -o json -d ./terraform | some-other-tool
⁠Split output

Use --output split (or -o split) to emit two permission sets — base (safe to distribute broadly) and escalation (owner-equivalent, restrict to protected branches) — instead of a single flat policy:

pike scan -o split -d ./terraform
{
    "aws": {
        "base": [
            "firehose:CreateDeliveryStream",
            "firehose:DeleteDeliveryStream",
            "iam:GetRole",
            "iam:ListAttachedRolePolicies"
        ],
        "escalation": [
            "iam:AttachRolePolicy",
            "iam:PassRole",
            "iam:PutRolePolicy"
        ]
    }
}

The base set is the planner SA role (safe for terraform plan on every branch). The escalation set is the additional permissions the applier SA needs — bind this role only on protected branches.

With --write / -w, the split JSON is saved to .pike/pike.generated_policy.split.json.

⁠Deletion-sensitive permissions

Pike also warns when your Terraform manages stateful or expensive resources (GKE/EKS clusters, Cloud SQL/RDS databases, storage buckets, KMS keys) where accidental deletion is costly:

WARNING: deletion-sensitive permissions detected — granting destroy on stateful/expensive resources.

  GCP:
    [HIGH] google_container_cluster
           GKE clusters take 10+ minutes to recreate; workloads experience downtime
           → deletion_protection = true
    [HIGH] google_sql_database_instance
           Cloud SQL instances contain stateful data; point-in-time recovery may be lost
           → deletion_protection_enabled = true

Use --sensitive to generate a three-role split that separates high-risk delete permissions into a break-glass role:

pike scan -d ./terraform --sensitive

This outputs three Terraform roles:

  • terraform_pike_safe — day-to-day operations (create, read, update) without high-risk deletes
  • terraform_pike_delete — high-risk destruction permissions only (break-glass, requires approval)
  • terraform_pike_plan — read-only permissions for terraform plan

Grant the delete role only to:

  • Break-glass principals requiring approval workflows
  • Decommissioning pipelines with manual gates
  • Disaster recovery automation with audit logging
⁠Output

If you select the -w flag, pike will write out the role/policy required to build your project into the .pike folder:

$pike scan -w -i -d .
2022/09/17 13:50:51 terraform init at .
2022/09/17 13:50:51 downloaded ip

The .pike folder will contain:

aws_iam_role.terraform_pike.tf
pike.generated_policy.tf

Which you can deploy using OpenTofu/Terraform to create the role/policy to build your infrastructure project.

⁠Make

You can now deploy the policy you need directly (AWS only so far):

$pike make -d ../modules/aws/terraform-aws-apigateway/

2022/09/18 08:53:41 terraform init at ..\modules\aws\terraform-aws-apigateway\
2022/09/18 08:53:41 modules not found at ..\modules\aws\terraform-aws-apigateway\
2022/09/18 08:53:49 aws role create/updated arn:aws:iam::680235478471:role/terraform_pike_20220918071439382800000002
 arn:aws:iam::680235478471:role/terraform_pike_20220918071439382800000002

This new verb returns the ARN of the role created, and you can find the Terraform used in your .pike folder.

⁠Invoke

Invoke is currently for triggering GitHub actions, if supplied with the workflow (defaults to main.yaml), repository and branch (defaults to main) flags, it will trigger the dispatch event.

You'll need to include the dispatch event in your workflow:

on:
  workflow_dispatch:
  push:
    branches:
      - master

To authenticate with the GitHub API, you will need to set your GitHub Personal Access Token, as the environment variable GITHUB_TOKEN

To Invoke a workflow, it is then:

pike invoke -workflow master.yml -branch master -repository JamesWoolfenden/terraform-aws-s3

I created Invoke to be used in tandem with the new remote command which supplies temporary credentials to a workflow.

**Note The GitHub API is rate-limited, usually 5000 calls per hour.

pike make -d ./module/aws/terraform-aws-s3/example/examplea
⁠Apply

Apply is an extension to make and will apply the policy and role and use that role to create your infrastructure:

pike apply -d ./module/aws/terraform-aws-s3/example/examplea -region eu-west-2

It is intended for testing and developing the permissions for Pike itself

⁠Remote

Remote uses the core code of make and apply, to write temporary AWS credentials(only so far) into your workflow.

pike remote -d ./module/aws/terraform-aws-s3/example/examplea -region eu-west-2 -repository terraform-aws-s3
⁠Readme

Pike can now be used to update a projects README.md file:

./pike readme -o terraform -d ..\modules\aws\terraform-aws-activemq\

This looks in the README for the delimiters:

<!-- BEGINNING OF PRE-COMMIT-PIKE DOCS HOOK -->
<!-- END OF PRE-COMMIT-PIKE DOCS HOOK -->

and replaces is either with JSON or Terraform like so:

This is the policy required to build this project:

The Policy required is

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "mq:CreateTags",
            "mq:DeleteTags",
            "ec2:DescribeInternetGateways",
            "ec2:DescribeAccountAttributes",
            "ec2:DescribeVpcs",
            "ec2:DescribeSubnets",
            "ec2:DescribeSecurityGroups",
            "ec2:CreateNetworkInterface",
            "ec2:CreateNetworkInterfacePermission",
            "ec2:DeleteNetworkInterfacePermission",
            "ec2:DetachNetworkInterface",
            "ec2:DeleteNetworkInterface",
            "mq:CreateBroker",
            "mq:DescribeBroker",
            "mq:DescribeUser",
            "mq:UpdateBroker",
            "mq:DeleteBroker",
            "mq:CreateConfiguration",
            "mq:UpdateConfiguration",
            "mq:DescribeConfiguration",
            "mq:DescribeConfigurationRevision",
            "mq:RebootBroker",
            "ec2:CreateTags",
            "ec2:DeleteTags",
            "ec2:CreateSecurityGroup",
            "ec2:DescribeNetworkInterfaces",
            "ec2:DeleteSecurityGroup",
            "ec2:RevokeSecurityGroupEgress",
            "kms:TagResource",
            "kms:UntagResource",
            "kms:EnableKeyRotation",
            "kms:CreateKey",
            "kms:DescribeKey",
            "kms:GetKeyPolicy",
            "kms:GetKeyRotationStatus",
            "kms:ListResourceTags",
            "kms:ScheduleKeyDeletion"
        ],
        "Resource": "*"
    }
}

You can see an example here https://github.com/jamesWoolfenden/terraform-aws-activemq#policy⁠.

⁠Compare

Want to check your deployed IAM policy against your infrastructure code requirement?

This works for AWS and GCP.

$./pike compare -d ../modules/aws/terraform-aws-appsync -a arn:aws:iam::680235478471:policy/basic

IAM Policy arn:aws:iam::680235478471:policy/basic versus Infrastructure Code ../modules/aws/terraform-aws-appsync
 {
   "Statement": [
     0: {
       "Action": [
-        0: "kinesisvideo:CreateStream"
+        0: "firehose:CreateDeliveryStream"
+        0: "firehose:CreateDeliveryStream"
+        1: "firehose:DeleteDeliveryStream"
+        2: "firehose:DescribeDeliveryStream"
+        3: "firehose:ListTagsForDeliveryStream"
+        4: "iam:AttachRolePolicy"
+        5: "iam:CreateRole"
+        6: "iam:DeleteRole"
+        7: "iam:DetachRolePolicy"
+        8: "iam:GetRole"
+        9: "iam:ListAttachedRolePolicies"
+        10: "iam:ListInstanceProfilesForRole"
+        11: "iam:ListRolePolicies"
+        12: "iam:PassRole"
+        13: "iam:TagRole"
+        14: "kms:CreateKey"
+        15: "kms:DescribeKey"
+        16: "kms:EnableKeyRotation"
+        17: "kms:GetKeyPolicy"
+        18: "kms:GetKeyRotationStatus"
+        19: "kms:ListResourceTags"
+        20: "kms:ScheduleKeyDeletion"
+        21: "logs:AssociateKmsKey"
+        22: "logs:CreateLogGroup"
+        23: "logs:DeleteLogGroup"
+        24: "logs:DeleteRetentionPolicy"
+        25: "logs:DescribeLogGroups"
+        26: "logs:DisassociateKmsKey"
+        27: "logs:ListTagsLogGroup"
+        28: "logs:PutRetentionPolicy"
+        29: "s3:CreateBucket"
+        30: "s3:DeleteBucket"
+        31: "s3:GetAccelerateConfiguration"
+        32: "s3:GetBucketAcl"
+        33: "s3:GetBucketCORS"
+        34: "s3:GetBucketLogging"
+        35: "s3:GetBucketObjectLockConfiguration"
+        36: "s3:GetBucketPolicy"
+        37: "s3:GetBucketPublicAccessBlock"
+        38: "s3:GetBucketRequestPayment"
+        39: "s3:GetBucketTagging"
+        40: "s3:GetBucketVersioning"
+        41: "s3:GetBucketWebsite"
+        42: "s3:GetEncryptionConfiguration"
+        43: "s3:GetLifecycleConfiguration"
+        44: "s3:GetObject"
+        45: "s3:GetObjectAcl"
+        46: "s3:GetReplicationConfiguration"
+        47: "s3:ListAllMyBuckets"
+        48: "s3:ListBucket"
+        49: "s3:PutBucketAcl"
+        50: "s3:PutBucketPublicAccessBlock"
+        51: "s3:PutEncryptionConfiguration"
+        52: "wafv2:CreateWebACL"
+        53: "wafv2:DeleteWebACL"
+        54: "wafv2:GetWebACL"
       ],
       "Effect": "Allow",
       "Resource": "*",
-      "Sid": ""
+      "Sid": "VisualEditor0"
     }
   ],
   "Version": "2012-10-17"
 }

Pike always warns to stderr when the computed policy contains escalation-class permissions (see Escalation-class permissions⁠). Pass --strict to also exit non-zero in that case, even if the policies otherwise match — useful in CI to enforce the two-role pattern:

pike compare --strict -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike
⁠Comparing against a file instead of a live policy

--target-file reads the baseline policy from a local file instead of calling the live AWS/GCP API, so CI can diff against a committed snapshot with no cloud credentials at all. --write-baseline writes the freshly generated IaC policy to a file, to seed or refresh that snapshot. -a/--arn is still required (its format is how pike picks AWS vs GCP), but with --target-file set it's never used to make a live call.

# One-off, with live credentials: seed the baseline and confirm it matches deployed today.
pike compare -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike --write-baseline ./policy-baseline.json

# Every CI run after that: no credentials needed.
pike compare -d ./terraform -a arn:aws:iam::680235478471:policy/terraform_pike --target-file ./policy-baseline.json

⁠Audit

pike audit runs a set of deterministic rules over the IAM-relevant resources in your Terraform — wildcard actions, escalation-class permissions granted without a scoping Condition, public principals, admin-class managed policy attachments, risky trust-policy principals, and more — without needing any cloud credentials at all.

pike audit -d ./terraform

Exits non-zero when

Tag summary

Content type

Image

Digest

sha256:35fdb89dd…

Size

21.4 MB

Last updated

3 days ago

docker pull jameswoolfenden/pike