GoogleCloudPlatform / policy-library

A library of constraint templates and sample constraints for Constraint Framework tools
Apache License 2.0
223 stars 129 forks source link

How to allow only a small number of projects to grant an IAM role? #354

Closed lvaylet closed 4 years ago

lvaylet commented 4 years ago

Hello,

I want to prevent all the projects in my GCP organization from granting the roles/resourcemanager.projectIamAdmin role to any member (user, group or service), except for a small number of projects.

I tried a combination of blacklist and exclude to allow a single project (irn-70740-alpha-31) to grant the otherwise forbidden role, but that does not work:

apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPIAMAllowedBindingsConstraintV1
metadata:
  name: blacklist_project_iam_admin_role
  annotations:
    description: Ban any users from being granted Project IAM Admin access (except for IRN-70740)
spec:
  severity: high
  match:
    target: ["organization/*"]
    exclude: ["//cloudresourcemanager.googleapis.com/projects/irn-70740-alpha-31"] # optional, default is no exclusions
  parameters:
    mode: blacklist
    assetType: cloudresourcemanager.googleapis.com/Project
    role: roles/resourcemanager.projectIamAdmin
    members:
    - "*"

Are you able to spot anything wrong with my syntax or my logic? Note that I also tried to exclude organization/<my-org-id>/projects/irn-70740-alpha-31. Same result.

Do you recommend I whitelist the authorized projects instead? I tried that too, but then every project ends up being able to grant the forbidden role:

apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPIAMAllowedBindingsConstraintV1
metadata:
  name: whitelist_project_iam_admin_role
  annotations:
    description: Allow certain projects only to grant Project IAM Admin access
spec:
  severity: high
  match:
    target: ["organization/*/projects/irn-70740-alpha-31"]
    exclude: [] # optional, default is no exclusions
  parameters:
    mode: whitelist
    assetType: cloudresourcemanager.googleapis.com/Project
    role: roles/resourcemanager.projectIamAdmin
    members:
    - "*"

Could a combination of blacklist and whitelist work better?

Thanks in advance!

morgante commented 4 years ago

The exclusion list uses project numbers not project IDs. Could you try something like this?


apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPIAMAllowedBindingsConstraintV1
metadata:
  name: blacklist_project_iam_admin_role
  annotations:
    description: Ban any users from being granted Project IAM Admin access (except for IRN-70740)
spec:
  severity: high
  match:
    target: ["organization/*"]
    exclude: ["organization/**/project/323233] # optional, default is no exclusions
  parameters:
    mode: blacklist
    assetType: cloudresourcemanager.googleapis.com/Project
    role: roles/resourcemanager.projectIamAdmin
    members:
    - "*"
``
lvaylet commented 4 years ago

Thanks @morgante for your quick reply. I ajusted the constraints with the project number:

apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPIAMAllowedBindingsConstraintV1
metadata:
  name: blacklist_project_iam_admin_role
  annotations:
    description: Ban any users from being granted Project IAM Admin access (except for IRN-70740)
    # This constraint is not certified by CIS.
    bundles.validator.forsetisecurity.org/cis-v1.1: 1.05
spec:
  severity: high
  match:
    target: ["organization/*"]
    exclude: ["organization/*/project/827673899623"] # optional, default is no exclusions
  parameters:
    mode: blacklist
    assetType: cloudresourcemanager.googleapis.com/Project
    role: roles/resourcemanager.projectIamAdmin
    members:
    - "*"

However, Terraform Validator still detects a violation when applied to irn-70740-alpha-31 (project number 827673899623, in the exclusion list):

Found Violations:
 Constraint blacklist_project_iam_admin_role on resource //cloudresourcemanager.googleapis.com/projects/irn-70740-alpha-31: IAM policy for //cloudresourcemanager.googleapis.com/projects/irn-70740-alpha-31 grants roles/resourcemanager.projectIamAdmin to serviceAccount:sa-irn-70740-alpha-infra@irn-70740-alpha-31.iam.gserviceaccount.com

Running a similar pipeline on a project that is not excluded also raises a violation, but this one is expected:

Found Violations:
 Constraint blacklist_project_iam_admin_role on resource //cloudresourcemanager.googleapis.com/projects/irn-00000-dev-7c: IAM policy for //cloudresourcemanager.googleapis.com/projects/irn-00000-dev-7c grants roles/resourcemanager.projectIamAdmin to group:gcp_renault_irn70788_xch_adm@renault.com

I use the latest terraform-validator Docker image in a GitLab CI pipeline.

Is there anything I can share that would help reproduce the issue?

Finally, please note that I had to replace organization/**/ with organization/* in order to fix this error message:

 +++ terraform-validator validate --policy-path=./policy-library --project=irn-00000-dev-7c ./terraform.tfplan.json
 ERROR: logging before flag.Parse: I0529 19:10:46.022068      69 validator.go:177] starting 1 workers
 Error: validating: FCV: auditing: core.dependencies.audit:28: eval_builtin_error: re_match: error parsing regexp: invalid nested repetition operator: `**`
morgante commented 4 years ago

Odd, that does sound like a bug. I'll do a deeper dive.

lvaylet commented 4 years ago

Thanks @morgante. Let me know if I can help reproduce the issue.

Could the 'bug" be related to how I run terraform-validator and how I use --project option?

$ terraform-validator validate --policy-path=./policy-library --project=irn-70740-alpha-31 ./terraform.tfplan.json
 Found Violations:
 Constraint blacklist_project_iam_admin_role on resource //cloudresourcemanager.googleapis.com/projects/irn-70740-alpha-31: IAM policy for //cloudresourcemanager.googleapis.com/projects/irn-70740-alpha-31 grants roles/resourcemanager.projectIamAdmin to serviceAccount:sa-irn-70740-alpha-infra@irn-70740-alpha-31.iam.gserviceaccount.com

Should I use the project number instead, same as in the exclusion list? I found that terraform-validator is unable to guess the project ID from the terraform plan most of the time, so I find it safer to pass it directly.

Finally, would it help if you got your hands on the terraform.tfplan.json files of both the pipeline that is supposed to fail (on a generic project) and the pipeline that is supposed to succeed (the exception to the rule)? I am pretty sure there is nothing confidential there, apart from our organization ID and billing account ID.

lvaylet commented 4 years ago

Hi @morgante. Any update to share? Can I help with anything?

morgante commented 4 years ago

Sorry for the delay but I've been able to successfully reproduce this. It looks like there are actually two different issues at play:

As a temporary workaround, could you try updating your exclusion rule to look like this. It should work:

apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPIAMAllowedBindingsConstraintV1
metadata:
  name: blacklist_project_iam_admin_role
  annotations:
    description: Ban any users from being granted Project IAM Admin access (except for IRN-70740)
    # This constraint is not certified by CIS.
    bundles.validator.forsetisecurity.org/cis-v1.1: 1.05
spec:
  severity: high
  match:
    target: ["organization/*"]
    exclude: ["organization/<your_org_number>/project/<your_project_id>"] # optional, default is no exclusions
  parameters:
    mode: blacklist
    assetType: cloudresourcemanager.googleapis.com/Project
    role: roles/resourcemanager.projectIamAdmin
    members:
    - "*"
lvaylet commented 4 years ago

No apologies needed and thanks a lot for looking into the issue. Let me update the exclusion rule first thing tomorrow morning and keep you posted. Is it the only change you recommend I make? Shall I keep calling terraform-validator with --project=<project-id>? Shall I try specifying the ancestry manually too?

morgante commented 4 years ago

In theory, that should work. Note you'll need to include the full ancestry for the project (including projects) in the constraint.

lvaylet commented 4 years ago

Sounds good to me. I will make sure to include the full ancestry and use the project IDs instead of the project numbers.

lvaylet commented 4 years ago

Hi @morgante. I was able to tweak the exclusion rule so the constraint works as expected. Just note that I had to include the full ancestry path (as provided by gcloud projects get-ancestors <project-id>) including all the folders in between the organization and the project itself:

organization/<org-number>/folder/<folder-number>/folder/<folder-number>/project/<project-id>

I was pretty sure your suggestion was my first failed attempt but I was willing to retry, just in case. It failed again but I was not surprised. Then I tried to put myself in terraform-validator's shoes and realized the ** wildcard could mean much more than just the organization number. That is when I decided to include the full ancestry path.

Apologies for not mentioning the folders in the first place. We might have been able to jump to the right conclusion a bit earlier.

I think we can safely close this issue now.

morgante commented 4 years ago

Glad it worked for you! I'm going to go ahead and close this issue, but you can follow the linked issues to watch for a more permanent fix.