Repository: FairwindsOps/polaris
Pull request #832 (2022-08-31 15:26:02)

FWI-2719: Enable and add exemptions for recently-added checks
See also [ticket 3025](https://fairwinds.myjetbrains.com/youtrack/issue/FWI-3025/Validate-new-Polaris-check-results-using-Insights-telemetry) which describes results testing these checks in some live environments.

## Checklist
* [x] I have signed the CLA
* [x] I have updated/added any relevant documentation

## Description
### What's the goal of this PR?
These checks are now enabled in the default Polaris configuration:
* linuxHardening
* sensitiveConfigmapContent and sensitiveContainerEnvVar
* clusterrolebindingClusterAdmin, rolebindingClusterAdminClusterRole, and rolebindingClusterAdminRole
* clusterrolePodExecAttach, rolePodExecAttach, clusterrolebindingPodExecAttach, rolebindingClusterRolePodExecAttach, and  rolebindingRolePodExecAttach

These checks are listed in the default configuration but remain set to `ignore` to avoid a storm of results:
* automountServiceAccountToken
* missingNetworkPolicy

Fixed the `sensitiveContainerEnvVar` check to ignore sensitive environment
variable names when those variables use `valueFrom` to reference an
external resource.

Added exemptions to RBAC related checks for names prefixed with `system:`, and also `gce:podsecuritypolicy:calico-sa`. This avoids chasing a growing list of exemptions as new versions of Kube adjust built-in resources.

Added the `*ClusterAdmin` checks to `examples/config-full.yaml`.


ivanfetch-fw: I'm waiting to merge this (making these checks live for the Polaris community) until a deeper round of initial testing has completed.
rbren: Thanks for adding all the new test cases!

Patch:
diff --git a/checks/clusterrolePodExecAttach.yaml b/checks/clusterrolePodExecAttach.yaml
index efc7bdc2..44a419f9 100644
--- a/checks/clusterrolePodExecAttach.yaml
+++ b/checks/clusterrolePodExecAttach.yaml
@@ -18,9 +18,8 @@ schemaString: |
                 - const: 'admin'
                 - const: "cluster-admin"
                 - const: "edit"
-                - const: "system:aggregate-to-edit"
-                - const: "system:controller:generic-garbage-collector"
-                - const: "system:controller:namespace-controller"
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - properties:
         rules:
           type: array
diff --git a/checks/clusterrolebindingClusterAdmin.yaml b/checks/clusterrolebindingClusterAdmin.yaml
index 113b6c30..c4464ec7 100644
--- a/checks/clusterrolebindingClusterAdmin.yaml
+++ b/checks/clusterrolebindingClusterAdmin.yaml
@@ -17,8 +17,8 @@ schemaString: |
               type: string
               anyOf:
                 - const: "cluster-admin"
-                - const: "system:controller:generic-garbage-collector"
-                - const: "system:controller:namespace-controller"
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -39,7 +39,7 @@ additionalSchemaStrings:
   rbac.authorization.k8s.io/ClusterRole: |
     type: object
     # Do not alert on default ClusterRoleBindings.
-    {{ if and (ne .metadata.name "cluster-admin") (ne .metadata.name "system:controller:generic-garbage-collector") (ne .metadata.name "system:controller:namespace-controller") }}
+    {{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
diff --git a/checks/clusterrolebindingPodExecAttach.yaml b/checks/clusterrolebindingPodExecAttach.yaml
index f1a4a6d4..cf5d510c 100644
--- a/checks/clusterrolebindingPodExecAttach.yaml
+++ b/checks/clusterrolebindingPodExecAttach.yaml
@@ -17,8 +17,8 @@ schemaString: |
               type: string
               anyOf:
                 - const: "cluster-admin"
-                - const: "system:controller:generic-garbage-collector"
-                - const: "system:controller:namespace-controller"
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -37,7 +37,7 @@ additionalSchemaStrings:
   rbac.authorization.k8s.io/ClusterRole: |
     type: object
     # Do not alert on default ClusterRoleBindings.
-    {{ if and (ne .metadata.name "cluster-admin") (ne .metadata.name "system:controller:generic-garbage-collector") (ne .metadata.name "system:controller:namespace-controller") }}
+    {{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
diff --git a/checks/rolePodExecAttach.yaml b/checks/rolePodExecAttach.yaml
index 61c27021..39c780d0 100644
--- a/checks/rolePodExecAttach.yaml
+++ b/checks/rolePodExecAttach.yaml
@@ -6,40 +6,51 @@ schemaString: |
   '$schema': http://json-schema.org/draft-07/schema
   type: object
   required: ["metadata", "rules"]
-  properties:
-    metadata:
-      required: ["name"]
-      properties:
-        name:
-          type: string
-    rules:
-      type: array
-      items:
-        type: object
-        not:
-          required: ["apiGroups", "resources", "verbs"]
+  anyOf:
+    # Do not alert on default Roles.
+    - properties:
+        metadata:
+          required: ["name"]
           properties:
-            apiGroups:
-              type: array
-              contains:
-                type: string
-                anyOf:
-                  - const: ""
-                  - const: '*'
-            resources:
-              type: array
-              contains:
-                type: string
-                anyOf:
-                  - const: '*'
-                  - const: "pods/exec"
-                  - const: "pods/attach"
-            verbs:
-              type: array
-              contains:
-                type: string
-                anyOf:
-                  - const: '*'
-                  # An exec is also possible by `get`ing a web socket.
-                  - const: 'get'
-                  - const: 'create'
+            name:
+              type: string
+              anyOf:
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
+    - properties:
+        metadata:
+          required: ["name"]
+          properties:
+            name:
+              type: string
+        rules:
+          type: array
+          items:
+            type: object
+            not:
+              required: ["apiGroups", "resources", "verbs"]
+              properties:
+                apiGroups:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: ""
+                      - const: '*'
+                resources:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: '*'
+                      - const: "pods/exec"
+                      - const: "pods/attach"
+                verbs:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: '*'
+                      # An exec is also possible by `get`ing a web socket.
+                      - const: 'get'
+                      - const: 'create'
diff --git a/checks/rolebindingClusterAdminClusterRole.yaml b/checks/rolebindingClusterAdminClusterRole.yaml
index 89d845b8..c515bbb4 100644
--- a/checks/rolebindingClusterAdminClusterRole.yaml
+++ b/checks/rolebindingClusterAdminClusterRole.yaml
@@ -15,6 +15,18 @@ schemaString: |
             kind:
               type: string
               const: "Role"
+    # Do not alert on default ClusterRoleBindings.
+    - required: ["metadata"]
+      properties:
+        metadata:
+          type: object
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -36,6 +48,7 @@ additionalSchemaStrings:
     type: object
     # This schema is validated for all roleBindings, regardless of their roleRef.
     {{ if eq .roleRef.kind "ClusterRole" }}
+    {{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
@@ -82,3 +95,4 @@ additionalSchemaStrings:
                             - "patch"
                             - "delete"
     {{ end }}
+    {{ end }}
diff --git a/checks/rolebindingClusterAdminRole.yaml b/checks/rolebindingClusterAdminRole.yaml
index 914497b4..2e5af37e 100644
--- a/checks/rolebindingClusterAdminRole.yaml
+++ b/checks/rolebindingClusterAdminRole.yaml
@@ -15,6 +15,18 @@ schemaString: |
             kind:
               type: string
               const: "ClusterRole"
+    # Do not alert on default RoleBindings.
+    - required: ["metadata"]
+      properties:
+        metadata:
+          type: object
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -34,6 +46,7 @@ additionalSchemaStrings:
     type: object
     # This schema is validated for all roleBindings, regardless of their roleRef.
     {{ if eq .roleRef.kind "Role" }}
+    {{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
@@ -80,3 +93,4 @@ additionalSchemaStrings:
                             - "patch"
                             - "delete"
     {{ end }}
+    {{ end }}
diff --git a/checks/rolebindingClusterRolePodExecAttach.yaml b/checks/rolebindingClusterRolePodExecAttach.yaml
index 87e1c02e..1842ea6d 100644
--- a/checks/rolebindingClusterRolePodExecAttach.yaml
+++ b/checks/rolebindingClusterRolePodExecAttach.yaml
@@ -15,6 +15,18 @@ schemaString: |
             kind:
               type: string
               const: "Role"
+    # Do not alert on default RoleBindings.
+    - required: ["metadata"]
+      properties:
+        metadata:
+          type: object
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -34,6 +46,7 @@ additionalSchemaStrings:
     type: object
     # This schema is validated for all roleBindings, regardless of their roleRef.
     {{ if eq .roleRef.kind "ClusterRole" }}
+    {{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
@@ -76,3 +89,4 @@ additionalSchemaStrings:
                         - const: 'get'
                         - const: 'create'
     {{ end }}
+    {{ end }}
diff --git a/checks/rolebindingRolePodExecAttach.yaml b/checks/rolebindingRolePodExecAttach.yaml
index 06fb8287..95e20fa5 100644
--- a/checks/rolebindingRolePodExecAttach.yaml
+++ b/checks/rolebindingRolePodExecAttach.yaml
@@ -18,6 +18,18 @@ schemaString: |
             kind:
               type: string
               const: "Role"
+    # Do not alert on default RoleBindings.
+    - required: ["metadata"]
+      properties:
+        metadata:
+          type: object
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - pattern: '^system:'
+                - const: "gce:podsecuritypolicy:calico-sa"
     - required: ["roleRef"]
       properties:
         roleRef:
@@ -37,6 +49,7 @@ additionalSchemaStrings:
     type: object
     # This schema is validated for all roleBindings, regardless of their roleRef.
     {{ if eq .roleRef.kind "Role" }}
+    {{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
     required: ["metadata", "rules"]
     allOf:
       - properties:
@@ -79,3 +92,4 @@ additionalSchemaStrings:
                         - const: 'get'
                         - const: 'create'
     {{ end }}
+    {{ end }}
diff --git a/checks/sensitiveContainerEnvVar.yaml b/checks/sensitiveContainerEnvVar.yaml
index 2a7ea903..8527a94c 100644
--- a/checks/sensitiveContainerEnvVar.yaml
+++ b/checks/sensitiveContainerEnvVar.yaml
@@ -10,34 +10,41 @@ schemaString: |
       type: array
       items:
         type: object
-        required: ["name"]
-        properties:
-          name:
-            type: string
-            '$comment': These environment variable names will be disallowed.
-            allOf:
-              - not:
-                  pattern: '(?i)^AWS_SECRET_ACCESS_KEY$'
-              - not:
-                  pattern: '(?i)^GOOGLE_APPLICATION_CREDENTIALS$'
-              - not:
-                  pattern: '(?i)^AZURE_.+KEY$'
-              - not:
-                  pattern: '(?i)^OCI_CLI_KEY_CONTENT$'
-              - not:
-                  pattern: '(?i)password'
-              - not:
-                  pattern: '(?i)token'
-              - not:
-                  pattern: '(?i)bearer'
-              - not:
-                  pattern: '(?i)secret'
-                '$comment': This allows variable names not excluded above.
-              - pattern: '(?i).*'
-          value:
-            type: string
-            '$comment': These environment variable values will be disallowed.
-            allOf:
-            - not:
-                '$comment': THis matches variations like begin private key, begin rsa private key ...
-                pattern: '(?i)\s*-BEGIN\s+.*PRIVATE KEY-\s*'
+        oneOf:
+          - required: ["name", "value"]
+            properties:
+              name:
+                type: string
+                '$comment': These environment variable names will be disallowed.
+                allOf:
+                  - not:
+                      pattern: '(?i)^AWS_SECRET_ACCESS_KEY$'
+                  - not:
+                      pattern: '(?i)^GOOGLE_APPLICATION_CREDENTIALS$'
+                  - not:
+                      pattern: '(?i)^AZURE_.+KEY$'
+                  - not:
+                      pattern: '(?i)^OCI_CLI_KEY_CONTENT$'
+                  - not:
+                      pattern: '(?i)password'
+                  - not:
+                      pattern: '(?i)token'
+                  - not:
+                      pattern: '(?i)bearer'
+                  - not:
+                      pattern: '(?i)secret'
+                    '$comment': This allows variable names not excluded above.
+                  - pattern: '(?i).*'
+              value:
+                type: string
+                '$comment': These environment variable values will be disallowed.
+                allOf:
+                - not:
+                    '$comment': THis matches variations like begin private key, begin rsa private key ...
+                    pattern: '(?i)\s*-BEGIN\s+.*PRIVATE KEY-\s*'
+          - required: ["name", "valueFrom"]
+            properties:
+              name:
+                type: string
+              valueFrom:
+                type: object
diff --git a/docs/customization/custom-checks.md b/docs/customization/custom-checks.md
index 9d905d65..ca22683c 100644
--- a/docs/customization/custom-checks.md
+++ b/docs/customization/custom-checks.md
@@ -167,6 +167,18 @@ schemaString: |
               {{ end }}
 ```
 
+### Additional Go Template Functions
+
+These functions are also available in the GO template.
+
+* [hasPrefix](https://pkg.go.dev/strings#HasPrefix) - for example, `hasPrefix "string" "prefix"`
+* [hasSuffix](https://pkg.go.dev/strings#HasSuffix) - for example, `hasSuffix "string" "suffix"`
+
+For example, the `hasPrefix` function can be used in a template to determine whether a resource name starts with `system:`
+```
+{{ if hasPrefix .metadata.name "system:" }}
+```
+
 ## Multi-Resource Checks
 You can write checks that span multiple resources. This is helpful for ensuring e.g.
 that every Deployment has a PDB or an HPA associated with it.
diff --git a/examples/config-full.yaml b/examples/config-full.yaml
index 15804c58..38a24435 100644
--- a/examples/config-full.yaml
+++ b/examples/config-full.yaml
@@ -32,6 +32,9 @@ checks:
   clusterrolebindingPodExecAttach: danger
   rolebindingClusterRolePodExecAttach: danger
   rolebindingRolePodExecAttach: danger
+  clusterrolebindingClusterAdmin: danger
+  rolebindingClusterAdminClusterRole: danger
+  rolebindingClusterAdminRole: danger
   # custom
   resourceLimits: warning
   imageRegistry: danger
diff --git a/examples/config.yaml b/examples/config.yaml
index 5ea55016..ec448383 100644
--- a/examples/config.yaml
+++ b/examples/config.yaml
@@ -16,8 +16,11 @@ checks:
   memoryRequestsMissing: warning
   memoryLimitsMissing: warning
   # security
+  automountServiceAccountToken: ignore
   hostIPCSet: danger
   hostPIDSet: danger
+  linuxHardening: warning
+  missingNetworkPolicy: ignore
   notReadOnlyRootFilesystem: warning
   privilegeEscalationAllowed: danger
   runAsRootAllowed: danger
@@ -27,11 +30,62 @@ checks:
   hostNetworkSet: danger
   hostPortSet: warning
   tlsSettingsMissing: warning
+  # These are initially warning and will later be promoted to danger.
+  sensitiveContainerEnvVar: warning
+  sensitiveConfigmapContent: warning
+  clusterrolePodExecAttach: warning
+  rolePodExecAttach: warning
+  clusterrolebindingPodExecAttach: warning
+  rolebindingClusterRolePodExecAttach: warning
+  rolebindingRolePodExecAttach: warning
+  clusterrolebindingClusterAdmin: warning
+  rolebindingClusterAdminClusterRole: warning
+  rolebindingClusterAdminRole: warning
+
 
 mutations:
   - pullPolicyNotAlways
 
 exemptions:
+  - namespace: kube-system
+    controllerNames:
+      - dns-controller
+      - ebs-csi-controller
+      - ebs-csi-node
+      - kindnet
+      - kops-controller
+      - kube-dns
+      - kube-flannel-ds
+      - kube-proxy
+      - kube-scheduler
+      - vpa-recommender
+    rules:
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
+  - namespace: kube-system
+    controllerNames:
+      - coredns
+    rules:
+      - automountServiceAccountToken
+      - missingNetworkPolicy
+  - namespace: kube-system
+    controllerNames:
+      - ebs-csi-controller
+    rules:
+      - sensitiveContainerEnvVar
+  - namespace: kube-system
+    controllerNames:
+      - coredns-autoscaler
+    rules:
+      - linuxHardening
+  - namespace: local-path-storage
+    controllerNames:
+      - local-path-provisioner
+    rules:
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
   - namespace: kube-system
     controllerNames:
       - kube-apiserver
@@ -54,7 +108,48 @@ exemptions:
       - runAsPrivileged
       - notReadOnlyRootFilesystem
       - hostPIDSet
+  - namespace: datadog
+    controllerNames:
+      - datadogtoken
+    rules:
+      - sensitiveConfigmapContent
+  - namespace: datadog
+    controllerNames:
+      - datadog-cluster-agent-apiserver
+    rules:
+      - rolebindingClusterAdminRole
+      - rolebindingRolePodExecAttach
 
+  - controllerNames:
+      - ingress-nginx-controller
+    rules:
+      - sensitiveConfigmapContent
+  - controllerNames:
+      - ingress-nginx-controller
+      - ingress-nginx-default-backend
+      - polaris
+      - rbac-manager
+    rules:
+      - automountServiceAccountToken
+      - missingNetworkPolicy
+  - controllerNames:
+      - aws-iam-authenticator
+      - aws-load-balancer-controller
+      - docker-registry
+      - external-dns
+      - kube2iam
+      - metrics-server
+    rules:
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
+  - controllerNames:
+      - oauth2-proxy
+    rules:
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
+      - sensitiveContainerEnvVar
   - controllerNames:
       - kube-flannel-ds
     rules:
@@ -72,6 +167,9 @@ exemptions:
       - runAsRootAllowed
       - readinessProbeMissing
       - livenessProbeMissing
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
 
   - controllerNames:
       - cluster-autoscaler
@@ -79,6 +177,9 @@ exemptions:
       - notReadOnlyRootFilesystem
       - runAsRootAllowed
       - readinessProbeMissing
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
 
   - controllerNames:
       - vpa
@@ -95,6 +196,10 @@ exemptions:
       - readinessProbeMissing
       - livenessProbeMissing
       - notReadOnlyRootFilesystem
+      - automountServiceAccountToken
+      - linuxHardening
+      - missingNetworkPolicy
+      - sensitiveContainerEnvVar
 
   - controllerNames:
       - nginx-ingress-controller
diff --git a/pkg/config/schema.go b/pkg/config/schema.go
index 3528acd6..b7270042 100644
--- a/pkg/config/schema.go
+++ b/pkg/config/schema.go
@@ -223,7 +223,10 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err
 	newCheck.AdditionalSchemaStrings = map[string]string{}
 
 	for kind, tmplString := range templateStrings {
-		tmpl := template.New(newCheck.ID)
+		tmpl := template.New(newCheck.ID).Funcs(template.FuncMap{
+			"hasPrefix": strings.HasPrefix,
+			"hasSuffix": strings.HasSuffix,
+		})
 		tmpl, err := tmpl.Parse(tmplString)
 		if err != nil {
 			return nil, err


Test patch:
diff --git a/test/checks/clusterrolePodExecAttach/success.gce_calico_clusterrole.yaml b/test/checks/clusterrolePodExecAttach/success.gce_calico_clusterrole.yaml
new file mode 100644
index 00000000..ad053d6a
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.gce_calico_clusterrole.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the clusterRole is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/clusterrolePodExecAttach/success.system_prefix.yaml b/test/checks/clusterrolePodExecAttach/success.system_prefix.yaml
new file mode 100644
index 00000000..ba2d4e40
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.system_prefix.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the clusterRole has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: system:test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/clusterrolebindingClusterAdmin/failure.binding_to_system_prefix.yaml b/test/checks/clusterrolebindingClusterAdmin/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..444af22f
--- /dev/null
+++ b/test/checks/clusterrolebindingClusterAdmin/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,35 @@
+# This fails because the clusterRoleBinding references a ClusterRole that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test-binding-to-system-prefix-clusterrole
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingClusterAdmin/success.gce_calico_binding.yaml b/test/checks/clusterrolebindingClusterAdmin/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..3f70eb5e
--- /dev/null
+++ b/test/checks/clusterrolebindingClusterAdmin/success.gce_calico_binding.yaml
@@ -0,0 +1,33 @@
+# This succeeds because the clusterRoleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingClusterAdmin/success.system_prefix_binding.yaml b/test/checks/clusterrolebindingClusterAdmin/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..46481287
--- /dev/null
+++ b/test/checks/clusterrolebindingClusterAdmin/success.system_prefix_binding.yaml
@@ -0,0 +1,33 @@
+# This succeeds because the clusterRoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: system:test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.binding_to_system_prefix.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..444af22f
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,35 @@
+# This fails because the clusterRoleBinding references a ClusterRole that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test-binding-to-system-prefix-clusterrole
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.gce_calico_binding.yaml b/test/checks/clusterrolebindingPodExecAttach/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..3f70eb5e
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.gce_calico_binding.yaml
@@ -0,0 +1,33 @@
+# This succeeds because the clusterRoleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.system_prefix_binding.yaml b/test/checks/clusterrolebindingPodExecAttach/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..46481287
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.system_prefix_binding.yaml
@@ -0,0 +1,33 @@
+# This succeeds because the clusterRoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: system:test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/rolePodExecAttach/success.gce_calico_binding.yaml b/test/checks/rolePodExecAttach/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..8a257a83
--- /dev/null
+++ b/test/checks/rolePodExecAttach/success.gce_calico_binding.yaml
@@ -0,0 +1,10 @@
+# This succeeds because the role is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+  namespace: kube-system
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/rolePodExecAttach/success.system_prefix_binding.yaml b/test/checks/rolePodExecAttach/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..4e89775e
--- /dev/null
+++ b/test/checks/rolePodExecAttach/success.system_prefix_binding.yaml
@@ -0,0 +1,10 @@
+# This succeeds because the Role has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: system:controller:glbc
+  namespace: kube-system
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/rolebindingClusterAdminClusterRole/failure.binding_to_system_prefix.yaml b/test/checks/rolebindingClusterAdminClusterRole/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..35ee8544
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminClusterRole/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,26 @@
+# This fails because the roleBinding references a ClusterRole that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test-binding-to-system-prefix-role
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterAdminClusterRole/success.gce_calico_binding.yaml b/test/checks/rolebindingClusterAdminClusterRole/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..8d13a900
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminClusterRole/success.gce_calico_binding.yaml
@@ -0,0 +1,23 @@
+# This succeeds because the roleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterAdminClusterRole/success.system_prefix_binding.yaml b/test/checks/rolebindingClusterAdminClusterRole/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..7ad4ed01
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminClusterRole/success.system_prefix_binding.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the RoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: system:test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+
diff --git a/test/checks/rolebindingClusterAdminRole/failure.binding_to_system_prefix.yaml b/test/checks/rolebindingClusterAdminRole/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..7214a785
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminRole/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,36 @@
+# This fails because the roleBinding references a Role that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test-binding-to-system-prefix-role
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This ClusterRole exists so there is at least one ClusterRole for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: not-used
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/rolebindingClusterAdminRole/success.gce_calico_binding.yaml b/test/checks/rolebindingClusterAdminRole/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..344d6b0d
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminRole/success.gce_calico_binding.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the roleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This ClusterRole exists so there is at least one ClusterRole for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: not-used
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/rolebindingClusterAdminRole/success.system_prefix_binding.yaml b/test/checks/rolebindingClusterAdminRole/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..8ac7dcc8
--- /dev/null
+++ b/test/checks/rolebindingClusterAdminRole/success.system_prefix_binding.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the RoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: system:test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This ClusterRole exists so there is at least one ClusterRole for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: not-used
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_to_system_prefix.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..35ee8544
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,26 @@
+# This fails because the roleBinding references a ClusterRole that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test-binding-to-system-prefix-role
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.gce_calico_binding.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..8d13a900
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.gce_calico_binding.yaml
@@ -0,0 +1,23 @@
+# This succeeds because the roleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.system_prefix_binding.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..7ad4ed01
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.system_prefix_binding.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the RoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: system:test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.binding_to_system_prefix.yaml b/test/checks/rolebindingRolePodExecAttach/failure.binding_to_system_prefix.yaml
new file mode 100644
index 00000000..cb3c3dc1
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.binding_to_system_prefix.yaml
@@ -0,0 +1,26 @@
+# This fails because the roleBinding references a Role that uses all wildcards which happens to have a `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  # The system: prefix does not cause this test to fail, but this test
+  # avoids incorectly ignoring user-created bindings to system ClusterRoles.
+  name: system:test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test-binding-to-system-prefix-role
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: system:test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.gce_calico_binding.yaml b/test/checks/rolebindingRolePodExecAttach/success.gce_calico_binding.yaml
new file mode 100644
index 00000000..3426273a
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.gce_calico_binding.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the roleBinding is an exempt name `gce:podsecuritypolicy:calico-sa`
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: gce:podsecuritypolicy:calico-sa
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.system_prefix_binding.yaml b/test/checks/rolebindingRolePodExecAttach/success.system_prefix_binding.yaml
new file mode 100644
index 00000000..a813d876
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.system_prefix_binding.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the RoleBinding has an exempt `system:` prefix.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: system:test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/sensitiveContainerEnvVar/success.sensitive_key_uses_valueFrom.yaml b/test/checks/sensitiveContainerEnvVar/success.sensitive_key_uses_valueFrom.yaml
new file mode 100644
index 00000000..788bf97f
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/success.sensitive_key_uses_valueFrom.yaml
@@ -0,0 +1,17 @@
+# This succeeds because a sensitive environment variable name references an external value.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: token
+      valueFrom:
+        secretKeyRef:
+          key: token
+          name: a-secret
+    image: nginx
+    ports:
+    - containerPort: 80