Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: adding psp-users CEL policy #537

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

JaydipGabani
Copy link
Contributor

@JaydipGabani JaydipGabani commented May 30, 2024

What this PR does / why we need it:

Which issue(s) does this PR fix (optional, using fixes #<issue number>(, fixes #<issue_number>, ...) format, will close the issue(s) when the PR gets merged):
Fixes #541

Special notes for your reviewer:

Rego policy nehavior is -

  • filed in below list is reference to securityContext fields - [ runAsUser, runAsGroup, fsGroup, supplementalGroup ]

  • Input:
field:
      rule: MustRunAs
      ranges:
        - min: 100
          max: 200
  • Behavior: if field is missing from object then throw missing violation, else throw violation is field is not in required range

  • Input:
runAsUser:
      rule: MustRunAsNonRoot
  • Behavior: if runAsUser and runAsNonRoot both are missing from object then throw missing violation, else throw violation is runAsUser == 0

  • Input:
field:
      rule: mayRunAs
      ranges:
        - min: 100
          max: 200
  • Behavior: No missing field violation, but is field is present and violating the range then throw the violation

  • Input:
field:
  rule: RunAsAny

  • Behavior: No violations

@JaydipGabani JaydipGabani requested a review from a team as a code owner May 30, 2024 00:28
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
container.image in variables.exemptImageExplicit ||
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption)))
- name: badContainers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are not bad containers yet, just containers subject to validation... candidateContainers?

- name: missingRequiredRunAsNonRootContainers
expression: |
variables.badContainers.filter(container,
has(variables.params.runAsUser) && has(variables.params.runAsUser.rule) && (variables.params.runAsUser.rule == "MustRunAsNonRoot") && ((has(container.securityContext) && !(has(container.securityContext.runAsUser) || has(container.securityContext.runAsNonRoot))) ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check that runAsNonRoot is both defined and equal to true (this is implied in the Rego check, since not will invert both "missing" and "false" to true.

Also, has(container.securityContext.runAsUser) || has(container.securityContext.runAsNonRoot) should be has(container.securityContext.runAsUser) && has(container.securityContext.runAsNonRoot) , since both conditions need to be satisfied.

- name: invalidRunAsUserContainers
expression: |
variables.badContainers.filter(container,
!(container.name in variables.processedRunAsUserContainers) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not assume that container names are globally unique, since tools like gator may not perform that kind of validation.

(
variables.params.runAsUser.rule == "MustRunAs" ?
(
has(container.securityContext) && has(container.securityContext.runAsUser) ? !variables.params.runAsUser.ranges.all(range, container.securityContext.runAsUser >= range.min && container.securityContext.runAsUser <= range.max) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be exists() instead of all(), since the constraint is satisfied as long as the user ID lies within at least one of the specified ranges.

(variables.params.runAsGroup.rule == "MustRunAs" || variables.params.runAsGroup.rule == "MayRunAs") &&
(
has(container.securityContext) && has(container.securityContext.runAsGroup) ?
!variables.params.runAsGroup.ranges.all(range, container.securityContext.runAsGroup >= range.min && container.securityContext.runAsGroup <= range.max) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about all() vs. exists()

- name: invalidRunAsFsGroupContainers
expression: |
variables.badContainers.filter(container,
!(variables.missingRequiredFsGroupContainers.exists(c, c.name == container.name)) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above, let's not rely on names being globally unique

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Processing each container type separately.

(variables.params.fsGroup.rule == "MustRunAs" || variables.params.fsGroup.rule == "MayRunAs") &&
(
has(container.securityContext) && has(container.securityContext.fsGroup) ?
!variables.params.fsGroup.ranges.all(range, container.securityContext.fsGroup >= range.min && container.securityContext.fsGroup <= range.max) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about all() vs. exists()

(
has(container.securityContext) && has(container.securityContext.fsGroup) ?
!variables.params.fsGroup.ranges.all(range, container.securityContext.fsGroup >= range.min && container.securityContext.fsGroup <= range.max) :
variables.podRunAsFsGroup == null || !variables.params.fsGroup.ranges.all(range, variables.podRunAsFsGroup >= range.min && variables.podRunAsFsGroup <= range.max)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about all() vs. exists()

(variables.params.supplementalGroups.rule == "MustRunAs" || variables.params.supplementalGroups.rule == "MayRunAs") &&
(
has(container.securityContext) && has(container.securityContext.supplementalGroups) ?
!variables.params.supplementalGroups.ranges.all(range, container.securityContext.supplementalGroups.all(gp, gp>= range.min && gp <= range.max)) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about all() vs. exists()

(
has(container.securityContext) && has(container.securityContext.supplementalGroups) ?
!variables.params.supplementalGroups.ranges.all(range, container.securityContext.supplementalGroups.all(gp, gp>= range.min && gp <= range.max)) :
variables.podRunAsSupplementalGroups == null || !variables.params.supplementalGroups.ranges.all(range, variables.podRunAsSupplementalGroups.all(gp, gp >= range.min && gp <= range.max))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about all() vs. exists()

Signed-off-by: Jaydip Gabani <[email protected]>
Signed-off-by: Jaydip Gabani <[email protected]>
Signed-off-by: Jaydip Gabani <[email protected]>
Signed-off-by: Jaydip Gabani <[email protected]>
@JaydipGabani
Copy link
Contributor Author

@maxsmythe @ritazh @sozercan PTAL

expression: |
!has(variables.params.exemptImages) ? [] :
variables.params.exemptImages.filter(image, !image.endsWith("*"))
- name: exemptImages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need map(container, container.image) ?

expression: |
variables.containers.filter(container,
!(container.image in variables.exemptImages) &&
has(variables.params.runAsUser) && has(variables.params.runAsUser.rule) && (variables.params.runAsUser.rule == "MustRunAsNonRoot") ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need a trinary operator (?) here

(
(!has(container.securityContext) || (
(!has(container.securityContext.runAsNonRoot) || !container.securityContext.runAsNonRoot) && (!has(container.securityContext.runAsUser) || container.securityContext.runAsUser == 0)
)) || variables.missingRunAsNonRootGlobal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like it will be true if either there is no global definition for running as non root OR if there is no local definition for running as non root.

This is incorrect. If local is defined but global is not, then there is no violation, as local definition supersedes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the code to correct this.

(
variables.params.runAsUser.rule == "RunAsAny" ? false :
(
variables.params.runAsUser.rule == "MustRunAsNonRoot" ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the code for this will be much simpler if we:

  • have a variable for each rule type (MustRunAs, MayRunAs, etc.) that returns the containers violating the rule.
  • if the rule type is not used, this variable will return an empty list.

This will allow you to:

  • remove all code duplication (can go back to concatenating all containers again, since you are no longer required to filter based off container name)
  • Reduce the nesting/branching for each variable.

Because this will significantly change/shorten the code, I'll stop reviewing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. Made an update to CEL code, it should now be much more compact. PTAL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add CEL code for PSP Policies in library
2 participants