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.
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.
Get started with Pike in 3 steps:
-
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
-
Scan your OpenTofu/Terraform code
pike scan -d ./path/to/your/terraform
This outputs the minimum IAM permissions required as JSON.
-
Generate as Terraform/OpenTofu code
pike scan -o terraform -d ./path/to/your/terraform
This creates an
aws_iam_policyresource 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.
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@latestbrew tap jameswoolfenden/homebrew-tap
brew install jameswoolfenden/tap/pikeI'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.gitThen you can install a tool:
scoop install pikedocker pull jameswoolfenden/pike
docker run --tty --volume /local/path/to/tf:/tf jameswoolfenden/pike scan -d /tfhttps://hub.docker.com/repository/docker/jameswoolfenden/pike
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:*"
]
}
]
})
}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 ipThe .pike folder will contain:
aws_iam_role.terraform_pike.tf
pike.generated_policy.tfWhich you can deploy using OpenTofu/Terraform to create the role/policy to build your infrastructure project.
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_20220918071439382800000002This new verb returns the ARN of the role created, and you can find the Terraform used in your .pike folder.
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:
- masterTo 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-s3I 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/exampleaApply 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-2It is intended for testing and developing the permissions for Pike itself
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-s3Pike 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.
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"
}Pull adds the ability to work with Git repositories (thanks to go-git), to output the required permissions in JSON or OpenTofu/Terraform:
./pike pull
NAME:
pike pull - Clones remote repo and scans it using pike
USAGE:
pike pull [command options] [arguments...]
OPTIONS:
--directory value, -d value Directory to scan (defaults to .) (default: ".")
--destination value, --dest value Where to clone repository (default: ".destination")
--output json, -o json Policy Output types e.g. json terraform (default: "terraform") [%OUTPUT%]
--repository value, -r value Repository url
--init, -i Run Terraform init to download modules (default: false)
--write, -w Write the policy output to a file at .pike (default: false)
--help, -h show help
Like so:
$ ./pike.exe pull -r https://github.com/JamesWoolfenden/terraform-aws-codebuild -i -d .
10:31PM INF .destination was not empty, removing
10:31PM INF git clone https://github.com/JamesWoolfenden/terraform-aws-codebuild .destination --recursive
10:31PM DBG terraform init at E:\Code\pike\.destination
10:31PM DBG modules not found at .destination
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": [
"codebuild:BatchGetProjects",
"codebuild:CreateProject",
"codebuild:DeleteProject",
"codebuild:UpdateProject"
],
"Resource": [
"*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"events:DeleteRule",
"events:DescribeRule",
"events:ListTagsForResource",
"events:ListTargetsByRule",
"events:PutRule",
"events:PutTargets",
"events:RemoveTargets"
],
"Resource": [
"*"
]
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:CreatePolicy",
"iam:CreateRole",
"iam:DeletePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:ListPolicyVersions",
"iam:ListRolePolicies",
"iam:PassRole",
"iam:PutRolePolicy",
"iam:TagRole"
],
"Resource": [
"*"
]
},
{
"Sid": "VisualEditor3",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"*"
]
},
{
"Sid": "VisualEditor4",
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:GetAccelerateConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLogging",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketPolicy",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetEncryptionConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetReplicationConfiguration",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:PutBucketAcl",
"s3:PutBucketLogging",
"s3:PutBucketPublicAccessBlock",
"s3:PutBucketVersioning",
"s3:PutEncryptionConfiguration",
"s3:PutLifecycleConfiguration"
],
"Resource": [
"*"
]
},
{
"Sid": "VisualEditor5",
"Effect": "Allow",
"Action": [
"ssm:AddTagsToResource",
"ssm:DeleteParameter",
"ssm:DescribeParameters",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListTagsForResource",
"ssm:PutParameter"
],
"Resource": [
"*"
]
}
]
})
}
./pike -h
NAME:
pike - Generate IAM policy from your IAC code
USAGE:
pike [global options] command [command options]
VERSION:
9.9.9
AUTHOR:
James Woolfenden <james.woolfenden@gmail.com>
COMMANDS:
apply, a Create a policy and use it to instantiate the IAC
compare, c policy comparison of deployed versus IAC
inspect, x policy comparison of environment versus IAC
invoke, i Triggers a gitHub action specified with the workflow flag
make, m make the policy/role required for this IAC to deploy
parse, p Triggers a gitHub action specified with the workflow flag
pull, l Clones remote repo and scans it using pike
readme, r Looks in dir for a README.md and updates it with the Policy required to build the code
remote, o Create/Update the Policy and set credentials/secret for Github Action
scan, s scan a directory for IAM code
version, v Outputs the application version
watch, w Waits for policy update
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
go buildor
Make buildThis new feature is in beta and is not yet fully supported and currently only for AWS. When Pike is run with inspect, it will scan your code and output a policy that is required to deploy the code, as normal, but it will also detect the running IAM credentials. It will then report on the overlap between the running credentials and the minimum policy.
This works with AWS IAM user, group and role/assumed role credentials.
./pike inspect -d terraform/aws
The following are over-permissive:
s3:*
s3-object-lambda:*
*
account:GetAccountInformation
aws-portal:*Billing
aws-portal:*PaymentMethods
aws-portal:*Usage
billing:GetBillingData
billing:GetBillingDetails
billing:GetBillingNotifications
billing:GetBillingPreferences
This currently uses a different AWS profile to run the scan - presently hardcoded to "basic", which only has the following permissions:
statement {
effect = "Allow"
actions = [
"iam:ListUserPolicies",
"iam:ListAttachedUserPolicies",
"iam:ListRolePolicies",
"iam:ListAttachedRolePolicies",
"iam:ListGroupPolicies",
"iam:ListAttachedGroupPolicies",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetUserPolicy",
"iam:GetRolePolicy",
"iam:GetGroupPolicy",
"iam:ListGroupsForUser"
]
resources = ["*"]
}Determine and create IAM mapping files ("./src/mapping") by working out the permissions required for your resource. For example, aws_security_group.json:
[
{
"apply": [
"ec2:CreateSecurityGroup",
"ec2:DescribeSecurityGroups",
"ec2:DescribeAccountAttributes",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteSecurityGroup",
"ec2:RevokeSecurityGroupEgress"
],
"attributes": {
"ingress": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress"
],
"tags": [
"ec2:CreateTags",
"ec2:DeleteTags"
]
},
"destroy": [
"ec2:DeleteSecurityGroup"
],
"modify": [],
"plan": []
}
]
Datasources are the easiest to start with. There's a script (resource.ps1 - requires pwsh: brew install --cask powershell) that creates a blank mapping file and .tf resource file, but you've seen the example JSON file - make one without any entries. You also need to create a minimal resource/datasource that you are trying to figure out the permissions for, and place it in the correct directory (e.g., ../terraform/aws). There's a script for making a profile in the role directory. You can then run OpenTofu/Terraform using the empty role against the resource/datasource with no permissions. The debug output from the run will help you figure out the permissions you need to add to your basic role. You then update your "basic" role.
Issues? The providers, don't always tell you what you need to add, you will need to check the IAM docs and the online IAM policymakers. Not all resources are as easy as others, anything that make/scripts CF internally. Some roles require Passrole and CreateLinkedRole but won't say so. Trail and error
Some cloud providers require extra permissions depending on the attributes you add; this is how this is handled. Build out your .tf resources to cover all reasonable scenarios.
Some cloud providers follow this model which means your test IAM role will take time after you change it to be changed, how long? This seems to vary on time of day and the resource. Whilst other providers like Azure just take a long time for the TF to change.
Update files.go with:
//go:embed aws_security_group.json
var securityGroup []byteOnce you have added the JSON import, as above, you then need to update the lookup table, so we can read it and get the permissions:
func GetAWSResourcePermissions(result template) []interface{} {
TFLookup := map[string]interface{}{
"aws_s3_bucket": awsS3Bucket,
"aws_s3_bucket_acl": awsS3BucketACL,
+ "aws_security_group": awsSecurityGroup,
Also add an example .tf file into the folder terraform//backups. This helps test that all your new code is picked up by pike.