Compare commits

..
65 Commits
Author SHA1 Message Date
Robert Brennan e9bf38fe28 Update triage.yml 2022-08-09 13:00:27 -04:00
Robert Brennan 4601c1fff4 Update triage.yml 2022-08-09 12:46:03 -04:00
Robert Brennan e81c2d3930 Update triage.yml 2022-08-09 12:42:52 -04:00
Robert Brennan 9f33a603e4 Update triage.yml 2022-08-09 12:42:07 -04:00
Robert Brennan 7bae85c493 Update triage.yml 2022-08-09 12:37:55 -04:00
Robert Brennan 468bc2ef64 Update triage.yml 2022-08-09 12:30:48 -04:00
Robert Brennan ab2d52b8d8 Update triage.yml 2022-08-09 12:21:36 -04:00
Robert Brennan b079665364 Update triage.yml 2022-08-09 12:20:31 -04:00
Robert Brennan baad393ea5 Update triage.yml 2022-08-09 12:17:28 -04:00
Robert Brennan 025cac8981 Update triage.yml 2022-08-09 12:15:09 -04:00
Robert Brennan f229f4d663 Update triage.yml 2022-08-09 12:09:33 -04:00
Robert Brennan e1cbe4ce6a Update triage.yml 2022-08-09 12:00:22 -04:00
Robert Brennan 5d91a5f7e0 Update triage.yml 2022-08-09 11:54:00 -04:00
Robert Brennan c5ac49583e Update triage.yml 2022-08-09 11:52:13 -04:00
Robert Brennan 8ff5ad679a Update triage.yml 2022-08-09 11:51:25 -04:00
Robert Brennan c09dac5262 Update triage.yml 2022-08-09 11:49:52 -04:00
Robert Brennan 7c675c6d43 Update triage.yml 2022-08-09 11:40:50 -04:00
Robert Brennan 6ef6193c3d Update triage.yml 2022-08-09 10:35:25 -04:00
Robert Brennan bd9dd660ff Update triage.yml 2022-08-09 10:35:14 -04:00
Robert Brennan 559736b1b2 Update triage.yml 2022-08-09 10:33:51 -04:00
Robert Brennan ef4b6ece30 Update triage.yml 2022-08-09 10:32:34 -04:00
Robert Brennan 34152609a8 Update triage.yml 2022-08-09 10:27:00 -04:00
Robert Brennan 0879f2a040 Update triage.yml 2022-08-09 10:22:17 -04:00
Robert Brennan a2123293f7 Update triage.yml 2022-08-09 10:19:42 -04:00
Robert Brennan a5acc96001 Update triage.yml 2022-08-09 10:17:22 -04:00
Robert Brennan e2e7918d36 Update triage.yml 2022-08-09 10:10:22 -04:00
Robert Brennan ad7b4f6aee Update triage.yml 2022-08-09 10:03:27 -04:00
Robert Brennan 581d06c194 Update triage.yml 2022-08-09 10:02:25 -04:00
Robert Brennan 729e7a9482 Update triage.yml 2022-08-09 10:01:01 -04:00
Robert Brennan a63756a158 Update triage.yml 2022-08-09 09:58:15 -04:00
Robert Brennan affab91d92 Update triage.yml 2022-08-09 09:55:28 -04:00
Robert Brennan ccd707f230 Update triage.yml 2022-08-09 09:53:17 -04:00
Robert Brennan 68064da185 Update triage.yml 2022-08-09 09:52:46 -04:00
Robert Brennan e4758dbc44 Update triage.yml 2022-08-09 09:50:20 -04:00
Robert Brennan 09209c9a7b Update triage.yml 2022-08-09 09:48:45 -04:00
Robert Brennan 8a7e691a8e Update triage.yml 2022-08-09 09:39:34 -04:00
Robert Brennan 3440296557 Update triage.yml 2022-08-09 09:37:04 -04:00
Robert Brennan 511157e010 Update triage.yml 2022-08-09 09:36:15 -04:00
Robert Brennan 195b9a8860 Update triage.yml 2022-08-09 09:35:41 -04:00
Robert Brennan 93b251d5c0 Update triage.yml 2022-08-09 09:33:40 -04:00
Robert Brennan af840db564 Update triage.yml 2022-08-09 09:32:30 -04:00
Robert Brennan 909bc5e86b Update triage.yml 2022-08-09 09:29:33 -04:00
Robert Brennan 3cdaf143a4 Update triage.yml 2022-08-09 09:27:50 -04:00
Robert Brennan 693dbc5b25 Update triage.yml 2022-08-09 09:03:02 -04:00
Robert Brennan b77672bde8 Update triage.yml 2022-08-08 18:14:48 -04:00
Robert Brennan bf96f1dc89 Update triage.yml 2022-08-08 18:07:50 -04:00
Robert Brennan 40c6c569e0 Update triage.yml 2022-08-08 16:05:11 -04:00
Robert Brennan fe014ac2bf Update triage.yml 2022-08-08 16:04:18 -04:00
Robert Brennan 5f87baaa56 Update triage.yml 2022-08-08 16:01:53 -04:00
Robert Brennan 35e7896553 Update triage.yml 2022-08-08 15:55:39 -04:00
Robert Brennan 1d7d3433c1 Update triage.yml 2022-08-08 15:51:21 -04:00
Robert Brennan 3a421412bb Create triage.yml 2022-08-08 15:43:45 -04:00
ivanfetch-fw 206322271c FWI-2509: Add sensitiveContainerEnvVar and sensitiveConfigMapContent checks (#817)
* Add sensitiveContainerEnvVar and sensitiveConfigMapContent checks

* Update full example configfile
2022-08-05 11:58:57 -04:00
ivanfetch-fw e5b9236268 FWI-2476: Add missingNetworkPolicy, automountServiceAccountToken, and linuxHardening checks (#816)
* Add missingNetworkPolicy, automountServiceAccountToken, and linuxHardening checks
2022-08-05 09:44:18 -06:00
ivanfetch-fw c3b57bf6c7 target: container also populates .Polaris.PodSpec|PodTemplate + a new .Polaris.Container representing the currently checked container, GetPodTemplate serializes data to work around a DeepCopy bug with type int (#812) 2022-07-29 07:45:56 -06:00
Igor BeliakovandRobert Brennan 652b65b3c2 fix: properly remove emojis in pretty format with no color (#765)
Signed-off-by: Igor Beliakov <demtis.register@gmail.com>

Co-authored-by: Robert Brennan <accounts@rbren.io>
2022-07-28 15:39:17 -04:00
dependabot[bot]andlnx01 41030320bb Bump github.com/stretchr/testify from 1.7.1 to 1.8.0 (#786)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.8.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-28 15:30:09 -04:00
dependabot[bot]andlnx01 76c42c4799 Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 (#813)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-28 15:29:58 -04:00
65add73e70 Bump k8s.io/api from 0.24.1 to 0.24.3 (#808)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.24.1 to 0.24.3.
- [Release notes](https://github.com/kubernetes/api/releases)
- [Commits](https://github.com/kubernetes/api/compare/v0.24.1...v0.24.3)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Barnabas Makonda <6409210+makoscafee@users.noreply.github.com>
2022-07-28 15:21:50 -04:00
Igor BeliakovandRobert Brennan a0000e1919 Suppress empty results when --only-show-failed-tests is passed (#811)
* Suppress empty results when --only-show-failed-tests is passed

Signed-off-by: Igor Beliakov <demtis.register@gmail.com>

* Fix remaining typo

Signed-off-by: Igor Beliakov <demtis.register@gmail.com>

Co-authored-by: Robert Brennan <accounts@rbren.io>
2022-07-26 09:31:08 -04:00
dependabot[bot]andlnx01 f9e2603b16 Bump alpine from 3.16.0 to 3.16.1 (#810)
Bumps alpine from 3.16.0 to 3.16.1.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-25 17:21:08 +03:00
ivanfetch-fw 50d789fd42 Fix resourceKindMap.addResource() to not assume every Kind has an APIGroup (#805)
This was causing the `ResourceProvider.Resources` map to essentially
loose resources with no APIGroup, such as ServiceAccounts.
2022-07-15 13:53:41 -06:00
ivanfetch-fwandRobert Brennan 25ab600eef Update docs to reflect target: PodTemplate RE: PR #801 (#804)
* Update docs to reflect `target: PodTemplate` and the template being available via the `Polaris.PodTemplate` variable RE: PR #801

* Fix typo

Co-authored-by: Robert Brennan <accounts@rbren.io>

Co-authored-by: Robert Brennan <accounts@rbren.io>
2022-07-14 13:50:41 -06:00
ivanfetch-fw be45519a22 Add target PodTemplate which exposes the full Pod (not only the spec) (#801)
* Add `target PodTemplate` which exposes the full Pod (not only the spec)

* Fix PotTemplate in conjunction with how pod-schema-checks are handled

* Add test for GO template `Polaris` sub-keys, help `NewGenericResourceFromPod` to set `PodTemplate` in more cases

* Clarify PldTemplate logic for `IsActionable()`
2022-07-14 12:51:24 -06:00
ivanfetch-fw ccaa384cd0 expose Polaris.PodSpec for PodSpec targeted checks (#793)
* Add a template `Polaris` variable, expose `Polaris.PodSpec` for checks of `target: PodSpec`.

Polaris checks that are `target: PodSpec` have reflected the original
resource (such as a pod-controller) in the Go template, instead of
reflecting the pod `spec` field. This update makes the PodSpec available
in a new template variable `Polaris.PodSpec`.
2022-07-12 08:04:17 -06:00
61 changed files with 1378 additions and 17 deletions
+98
View File
@@ -0,0 +1,98 @@
name: Triage issues
on:
schedule:
- cron: '0 16 * * Mon' # noon ET on Mondays
issues:
types:
- reopened
- opened
pull_request:
types:
- reopened
- opened
jobs:
notify:
if: github.actor!= 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: octokit/request-action@v2.x
id: need_triage
with:
route: GET /repos/FairwindsOps/${{ github.event.repository.name }}/issues?labels=triage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Scheduled Reminders
env:
TITLES: ${{ join(fromJSON(steps.need_triage.outputs.data).*.title, ';') }}
LINKS: ${{ join(fromJSON(steps.need_triage.outputs.data).*.html_url, ';') }}
run: |
echo "Scheduled reminder! ${TITLES}"
IFS=';' read -r -a titles <<< "$TITLES"
IFS=';' read -r -a links <<< "$LINKS"
message=""
for index in "${!links[@]}"; do
title=${titles[$index]}
link=${links[$index]}
echo "$index $title $link"
message="$message- <$link|$title>\\n"
done
echo "message: $message"
echo '{
"text": "Needs Triage",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "$message"
}
}
]
}' > body-template.json
export message=$(echo "${message}" | sed 's/"//g')
envsubst < body-template.json > body.json
cat body.json
curl -X POST "${{ secrets.SLACK_INCOMING_WEBHOOK }}" -H "Content-type: application/json" -d @./body.json
- name: Issue Notification
if: github.event.issue.title != ''
env:
TITLE: "${{ github.event.issue.title }}"
EVENT: github.event.pull_request.merged == true
LINK: "https://github.com/FairwindsOps/${{ github.event.repository.name }}/pulls/${{ github.event.issue.number }}"
run: |
echo '{
"text": "New Pull Request",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":issue: New Issue: <${LINK}|${TITLE}>"
}
}
]
}' > body-template.json
envsubst < body-template.json > body.json
curl -X POST "${{ secrets.SLACK_INCOMING_WEBHOOK }}" -H "Content-type: application/json" -d @./body.json
- name: PR Notification
if: github.event.pull_request.title != ''
env:
TITLE: "${{ github.event.pull_request.title }}"
LINK: "https://github.com/FairwindsOps/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}"
run: |
echo '{
"text": "New Pull Request",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":pr: New Pull Request: <${LINK}|${TITLE}>"
}
}
]
}' > body-template.json
envsubst < body-template.json > body.json
curl -X POST "${{ secrets.SLACK_INCOMING_WEBHOOK }}" -H "Content-type: application/json" -d @./body.json
+1 -1
View File
@@ -15,7 +15,7 @@ RUN go get -u github.com/gobuffalo/packr/v2/packr2
COPY . .
RUN packr2 build -a -o polaris *.go
FROM alpine:3.16.0
FROM alpine:3.16.1
WORKDIR /usr/local/bin
RUN apk --no-cache add ca-certificates
+36
View File
@@ -0,0 +1,36 @@
successMessage: The ServiceAccount will not be automounted
failureMessage: The ServiceAccount will be automounted
category: Security
target: PodSpec
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
required: ["serviceAccountName"]
properties:
serviceAccountName:
type: string
automountServiceAccountToken:
type: boolean
not:
const: true
additionalSchemaStrings:
ServiceAccount: |
type: object
required:
- metadata
{{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
- automountServiceAccountToken
{{ end }}
properties:
metadata:
type: object
required: ["name"]
properties:
name:
type: string
const: "{{ .Polaris.PodSpec.serviceAccountName }}"
{{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
automountServiceAccountToken:
type: boolean
const: false
{{ end }}
+89
View File
@@ -0,0 +1,89 @@
successMessage: One of AppArmor, Seccomp, SELinux, or dropping Linux Capabilities are used to restrict containers using unwanted privileges
FailureMessage: Use one of AppArmor, Seccomp, SELinux, or dropping Linux Capabilities to restrict containers using unwanted privileges
category: Security
target: Container
schemaString: |
'$schema': http://json-schema.org/draft-07/schema
definitions:
podOrContainerSeccompProfile:
type: object
{{ $podSeccompProfileType := .Polaris.PodSpec.securityContext.seccompProfile.type }}
required:
{{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
- securityContext
{{ end }}
properties:
securityContext:
type: object
required:
{{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
- seccompProfile
{{ end }}
properties:
seccompProfile:
type: object
required:
{{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
- type
{{ end }}
properties:
type:
type: string
allOf:
- not:
const: "Unconfined"
{{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
- minLength: 1
{{ end }}
podOrContainerSELinuxOptions:
type: object
{{ $podSELinuxOptions := .Polaris.PodSpec.securityContext.seLinuxOptions }}
{{ if not $podSELinuxOptions }}
required: ["securityContext"]
properties:
securityContext:
type: object
required: ["seLinuxOptions"]
properties:
seLinuxOptions:
type: object
minProperties: 1
{{ end }}
containerDropCapabilities:
type: object
required: ["securityContext"]
properties:
securityContext:
type: object
required: ["capabilities"]
properties:
capabilities:
type: object
required: ["drop"]
properties:
drop:
type: array
minItems: 1
add:
type: array
items:
type: string
not:
pattern: '^(?i)ALL$'
# End of definitions
{{/* Check for AppArmor which uses pod annotations. IF pod fields are missing,
require one of the other hardening measures. */}}
{{ $annotationName := (print "container.apparmor.security.beta.kubernetes.io/" .Polaris.Container.name) }}
{{/* Checking annotations before using index() avoids a nil panic when there are no annotations */}}
{{ $annotationExists := false }}
{{ if .Polaris.PodTemplate.metadata.annotations }}
{{ $annotationExists = index .Polaris "PodTemplate" "metadata" "annotations" $annotationName }}
{{ end }}
{{ if $annotationExists }}
type: object
{{ else }}
anyOf:
- $ref: "#/definitions/podOrContainerSeccompProfile"
- $ref: "#/definitions/podOrContainerSELinuxOptions"
- $ref: "#/definitions/containerDropCapabilities"
{{ end}}
+49
View File
@@ -0,0 +1,49 @@
successMessage: A NetworkPolicy matches pod labels and contains egress and ingress rules
failureMessage: A NetworkPolicy should match pod labels and contain applied egress and ingress rules
category: Security
target: PodTemplate
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
properties:
metadata:
type: object
properties:
labels:
type: object
minProperties: 1
additionalSchemaStrings:
networking.k8s.io/NetworkPolicy: |
type: object
properties:
spec:
type: object
required: ["podSelector", "egress", "ingress"]
properties:
podSelector:
type: object
required: ["matchLabels"]
properties:
matchLabels:
type: object
oneOf:
{{ range $key, $value := .Polaris.PodTemplate.metadata.labels }}
- properties:
"{{ $key }}":
type: string
const: {{ $value }}
required: ["{{ $key }}"]
{{ end }}
egress:
type: array
minItems: 1
ingress:
minItems: 1
type: array
policyTypes:
type: array
allOf:
- contains:
pattern: '^(?i)Egress$'
- contains:
pattern: '^(?i)Ingress$'
+43
View File
@@ -0,0 +1,43 @@
successMessage: The ConfigMap does not contain potentially sensitive content in its keys and values
failureMessage: Potentially sensitive content is detected in the ConfigMap keys or values
category: Security
target: /ConfigMap
schemaString: |
'$schema': http://json-schema.org/draft-07/schema
type: object
required: ["metadata"]
properties:
metadata:
required: ["name"]
properties:
name:
type: string
data:
type: object
propertyNames:
'$comment': These ConfigMap keys 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 ConfigMap keys not excluded above.
- pattern: '(?i).*'
additionalProperties:
'$comment': These ConfigMap 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*'
+43
View File
@@ -0,0 +1,43 @@
successMessage: The container does not set potentially sensitive environment variables
failureMessage: The container sets potentially sensitive environment variables
category: Security
target: Container
schemaString: |
'$schema': http://json-schema.org/draft-07/schema
type: object
properties:
env:
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*'
+6
View File
@@ -44,6 +44,7 @@ check ID. Note that you'll also have to set its severity in the `checks` section
* `target` - specifies the type of resource to check. This can be:
* a group and kind, e.g. `apps/Deployment` or `networking.k8s.io/Ingress`
* `Controller`, to check _any_ resource that creates Pods (e.g. Deployments, CronJobs, StatefulSets), as well as naked Pods
* `PodTemplate`, same as `Controller`, but the schema applies to the Pod template rather than the top-level controller
* `PodSpec`, same as `Controller`, but the schema applies to the Pod spec rather than the top-level controller
* `Container` same as `Controller`, but the schema applies to all Container specs rather than the top-level controller
* `controllers` - if `target` is `Controller`, `PodSpec` or `Container`, you can use this to change which types of controllers are checked
@@ -134,6 +135,11 @@ schema:
const: "{{ .metadata.name }}"
```
* The object available via the go template is the full object, and not limited by `target`.
* A check of `target: PodSpec` can directly access the pod specification via the go template variable `.Polaris.PodSpec`.
* A check of `target: PodTemplate` can directly access the pod template via the go template variable `.Polaris.PodTemplate`.
* A check of `target: Container` can directly access the container being checked via the go template variable `.Polaris.container`. The pod template and pod specification can also be accessed via the respective variables `.Polaris.PodTemplate` and `.Polaris.PodSpec`. Access to pod-level fields allows a container check to consult related fields from the pod, such as `securityContext`.
You can also use the full [Go template syntax](https://golang.org/pkg/text/template/), though
you may need to specify your schema as a string in order to use concepts like `range`. E.g.
this check ensures that at least one of the object's labels is present in `matchLabels`:
+5
View File
@@ -12,8 +12,11 @@ checks:
memoryRequestsMissing: warning
memoryLimitsMissing: warning
# security
automountServiceAccountToken: warning
hostIPCSet: danger
hostPIDSet: danger
linuxHardening: danger
missingNetworkPolicy: warning
notReadOnlyRootFilesystem: warning
privilegeEscalationAllowed: danger
runAsRootAllowed: danger
@@ -22,6 +25,8 @@ checks:
insecureCapabilities: warning
hostNetworkSet: danger
hostPortSet: warning
sensitiveContainerEnvVar: danger
sensitiveConfigmapContent: danger
# custom
resourceLimits: warning
imageRegistry: danger
+4 -4
View File
@@ -9,15 +9,15 @@ require (
github.com/gorilla/mux v1.8.0
github.com/qri-io/jsonschema v0.1.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.1
github.com/stretchr/testify v1.8.0
github.com/thoas/go-funk v0.9.2
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.24.1
k8s.io/apimachinery v0.24.1
k8s.io/api v0.24.3
k8s.io/apimachinery v0.24.3
k8s.io/client-go v0.24.1
sigs.k8s.io/controller-runtime v0.12.1
sigs.k8s.io/yaml v1.3.0
+10 -4
View File
@@ -134,6 +134,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -539,8 +540,9 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -551,14 +553,16 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/thoas/go-funk v0.9.2 h1:oKlNYv0AY5nyf9g+/GhMgS/UO2ces0QRdPKwkhY3VCk=
github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
@@ -1141,13 +1145,15 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I=
k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY=
k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ=
k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY=
k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI=
k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY=
k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM=
k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I=
k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=
k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA=
k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw=
k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E=
+5
View File
@@ -32,6 +32,7 @@ var (
"hostIPCSet",
"hostPIDSet",
"hostNetworkSet",
"automountServiceAccountToken",
// Container checks
"memoryLimitsMissing",
"memoryRequestsMissing",
@@ -49,11 +50,15 @@ var (
"dangerousCapabilities",
"insecureCapabilities",
"priorityClassNotSet",
"linuxHardening",
"sensitiveContainerEnvVar",
// Other checks
"tlsSettingsMissing",
"pdbDisruptionsIsZero",
"metadataAndNameMismatched",
"missingPodDisruptionBudget",
"missingNetworkPolicy",
"sensitiveConfigmapContent",
}
)
+13
View File
@@ -41,6 +41,8 @@ const (
TargetContainer TargetKind = "Container"
// TargetPodSpec points to the pod spec
TargetPodSpec TargetKind = "PodSpec"
// TargetPodTemplate points to the pod template
TargetPodTemplate TargetKind = "PodTemplate"
)
// HandledTargets is a list of target names that are explicitly handled
@@ -48,6 +50,7 @@ var HandledTargets = []TargetKind{
TargetController,
TargetContainer,
TargetPodSpec,
TargetPodTemplate,
}
// MutationComment is the comments added to a mutated file
@@ -258,6 +261,11 @@ func (check SchemaCheck) CheckPodSpec(pod *corev1.PodSpec) (bool, []jsonschema.V
return check.CheckObject(pod)
}
// CheckPodTemplate checks a pod template against the schema
func (check SchemaCheck) CheckPodTemplate(podTemplate interface{}) (bool, []jsonschema.ValError, error) {
return check.CheckObject(podTemplate)
}
// CheckController checks a controler's spec against the schema
func (check SchemaCheck) CheckController(bytes []byte) (bool, []jsonschema.ValError, error) {
errs, err := check.Validator.ValidateBytes(bytes)
@@ -304,6 +312,11 @@ func (check SchemaCheck) CheckAdditionalObjects(groupkind string, objects []inte
// IsActionable decides if this check applies to a particular target
func (check SchemaCheck) IsActionable(target TargetKind, kind string, isInit bool) bool {
if funk.Contains(HandledTargets, target) {
if check.Target == TargetPodTemplate && target == TargetPodSpec {
// A target=PodSpec and check.Target=PodTemplate is expected
// because applyPodSchemaChecks() explicitly sets check.Target
return true
}
if check.Target != target {
return false
}
+44 -3
View File
@@ -35,6 +35,7 @@ type GenericResource struct {
ObjectMeta kubeAPIMetaV1.Object
Resource unstructured.Unstructured
PodSpec *kubeAPICoreV1.PodSpec
PodTemplate interface{}
OriginalObjectJSON []byte
}
@@ -53,6 +54,10 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecM
return workload, err
}
workload.ObjectMeta = objMeta
workload.PodTemplate, err = GetPodTemplate(unst.UnstructuredContent())
if err != nil {
return workload, err
}
b, err := json.Marshal(&unst)
if err != nil {
@@ -84,10 +89,15 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecM
// NewGenericResourceFromPod builds a new workload for a given Pod without looking at parents
func NewGenericResourceFromPod(podResource kubeAPICoreV1.Pod, originalObject interface{}) (GenericResource, error) {
podMap, err := SerializePod(&podResource)
if err != nil {
return GenericResource{}, err
}
workload := GenericResource{
Kind: "Pod",
PodSpec: &podResource.Spec,
ObjectMeta: podResource.ObjectMeta.GetObjectMeta(),
Kind: "Pod",
PodSpec: &podResource.Spec,
PodTemplate: podMap,
ObjectMeta: podResource.ObjectMeta.GetObjectMeta(),
}
if originalObject != nil {
bytes, err := json.Marshal(originalObject)
@@ -237,3 +247,34 @@ func GetPodSpec(yaml map[string]interface{}) interface{} {
}
return nil
}
// GetPodTemplate looks inside arbitrary YAML for a Pod template, containing
// fields `spec.containers`.
// For example, it returns the `spec.template` level of a Kubernetes Deployment yaml.
func GetPodTemplate(yaml map[string]interface{}) (podTemplate interface{}, err error) {
if yamlSpec, ok := yaml["spec"]; ok {
if yamlSpecMap, ok := yamlSpec.(map[string]interface{}); ok {
if _, ok := yamlSpecMap["containers"]; ok {
// This is a hack around unstructured.SetNestedField using DeepCopy which does
// not support the type int, and panics.
// Related: https://github.com/kubernetes/kubernetes/issues/62769
podTemplateJSON, err := json.Marshal(yaml)
if err != nil {
return nil, err
}
podTemplateMap := make(map[string]interface{})
err = json.Unmarshal(podTemplateJSON, &podTemplateMap)
if err != nil {
return nil, err
}
return podTemplateMap, nil
}
}
}
for _, podSpecField := range podSpecFields {
if childYaml, ok := yaml[podSpecField]; ok {
return GetPodTemplate(childYaml.(map[string]interface{}))
}
}
return nil, nil
}
+49 -1
View File
@@ -17,6 +17,7 @@ package kube
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -58,7 +59,12 @@ type resourceKindMap map[string][]GenericResource
func (rkm resourceKindMap) addResource(r GenericResource) {
gvk := r.Resource.GroupVersionKind()
key := gvk.Group + "/" + gvk.Kind
var key string
if gvk.Group != "" {
key = gvk.Group + "/" + gvk.Kind
} else {
key = gvk.Kind
}
rkm[key] = append(rkm[key], r)
}
@@ -475,3 +481,45 @@ func (resources *ResourceProvider) addResourceFromString(contents string) error
}
return err
}
// SerializePodSpec converts a typed PodSpec into a map[string]interface{}
func SerializePodSpec(pod *corev1.PodSpec) (map[string]interface{}, error) {
podJSON, err := json.Marshal(pod)
if err != nil {
return nil, err
}
podMap := make(map[string]interface{})
err = json.Unmarshal(podJSON, &podMap)
if err != nil {
return nil, err
}
return podMap, nil
}
// SerializePod converts a typed Pod into a map[string]interface{}
func SerializePod(pod *corev1.Pod) (map[string]interface{}, error) {
podJSON, err := json.Marshal(pod)
if err != nil {
return nil, err
}
podMap := make(map[string]interface{})
err = json.Unmarshal(podJSON, &podMap)
if err != nil {
return nil, err
}
return podMap, nil
}
// SerializeContainer converts a typed Container into a map[string]interface{}
func SerializeContainer(container *corev1.Container) (map[string]interface{}, error) {
containerJSON, err := json.Marshal(container)
if err != nil {
return nil, err
}
containerMap := make(map[string]interface{})
err = json.Unmarshal(containerJSON, &containerMap)
if err != nil {
return nil, err
}
return containerMap, nil
}
+37 -3
View File
@@ -16,6 +16,7 @@ package validator
import (
"fmt"
"strings"
"time"
"github.com/fatih/color"
@@ -53,12 +54,21 @@ type AuditData struct {
Score uint
}
// RemoveSuccessfulResults remove all test that have passed.
// RemoveSuccessfulResults removes all tests that have passed
func (res AuditData) RemoveSuccessfulResults() AuditData {
resCopy := res
resCopy.Results = funk.Map(res.Results, func(auditDataResult Result) Result {
resCopy.Results = []Result{}
filteredResults := funk.Map(res.Results, func(auditDataResult Result) Result {
return auditDataResult.removeSuccessfulResults()
}).([]Result)
for _, result := range filteredResults {
if result.isNotEmpty() {
resCopy.Results = append(resCopy.Results, result)
}
}
return resCopy
}
@@ -86,6 +96,10 @@ type ResultMessage struct {
// ResultSet contiains the results for a set of checks
type ResultSet map[string]ResultMessage
func (res ResultSet) isNotEmpty() bool {
return len(res) > 0
}
func (res ResultSet) removeSuccessfulResults() ResultSet {
newResults := ResultSet{}
for k, resultMessage := range res {
@@ -116,6 +130,13 @@ func (res Result) removeSuccessfulResults() Result {
return resCopy
}
func (res Result) isNotEmpty() bool {
if res.PodResult != nil {
return res.PodResult.isNotEmpty()
}
return res.Results.isNotEmpty()
}
// PodResult provides a list of validation messages for each pod.
type PodResult struct {
Name string
@@ -132,6 +153,15 @@ func (res PodResult) removeSuccessfulResults() PodResult {
return resCopy
}
func (res PodResult) isNotEmpty() bool {
for _, cr := range res.ContainerResults {
if cr.isNotEmpty() {
return true
}
}
return res.Results.isNotEmpty()
}
// ContainerResult provides a list of validation messages for each container.
type ContainerResult struct {
Name string
@@ -144,6 +174,10 @@ func (res ContainerResult) removeSuccessfulResults() ContainerResult {
return resCopy
}
func (res ContainerResult) isNotEmpty() bool {
return res.Results.isNotEmpty()
}
func fillString(id string, l int) string {
for len(id) < l {
id += " "
@@ -211,7 +245,7 @@ func (res ResultSet) GetPrettyOutput() string {
}
}
if color.NoColor {
status = status[2:] // remove emoji
status = strings.Fields(status)[1] // remove emoji
}
str += fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status)
str += fmt.Sprintf("%s %s - %s\n", indent, msg.Category, msg.Message)
@@ -0,0 +1,50 @@
// Copyright 2019 FairwindsOps Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validator
import (
"testing"
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
"github.com/fairwindsops/polaris/test"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func TestGetTemplateInputReturnsPolarisSubKeys(t *testing.T) {
pod := test.MockPod() // Includes a container, required by GetPodSpec
pod.Spec.NodeName = "testNodeName"
pod.ObjectMeta.Name = "testpod"
genRes, err := kube.NewGenericResourceFromPod(pod, pod)
require.NoError(t, err, "creating new generic resource from a pod")
schemaTest := schemaTestCase{
Target: conf.TargetPodSpec, // ends up being set in the case of target: PodTemplate
Resource: genRes,
}
templateInput, err := getTemplateInput(schemaTest)
require.NoError(t, err, "getting template input from a generic resource")
require.NotNil(t, templateInput)
nodeName, ok, err := unstructured.NestedString(templateInput, "Polaris", "PodSpec", "nodeName")
require.NoError(t, err, "getting Polaris.PodSpec.nodeName from template input")
require.True(t, ok, "getting Polaris.PodSpec.nodeName from template input")
require.Equal(t, "testNodeName", nodeName, "the nodeName from template output")
podName, ok, err := unstructured.NestedString(templateInput, "Polaris", "PodTemplate", "metadata", "name")
require.NoError(t, err, "getting Polaris.PodTemplate.metadata.name from template input")
require.True(t, ok, "getting Polaris.PodTemplate.metadata.name from template input")
require.Equal(t, "testpod", podName, "the pod from template input")
}
+47 -1
View File
@@ -27,6 +27,7 @@ import (
"gomodules.xyz/jsonpatch/v2"
corev1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
@@ -64,13 +65,55 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
if !check.IsActionable(test.Target, test.Resource.Kind, test.IsInitContianer) {
return nil, nil
}
checkPtr, err := check.TemplateForResource(test.Resource.Resource.Object)
templateInput, err := getTemplateInput(test)
if err != nil {
return nil, err
}
checkPtr, err := check.TemplateForResource(templateInput)
if err != nil {
return nil, err
}
return checkPtr, nil
}
// getTemplateInput augments a schemaTestCase.Resource.Resource.Object with
// Polaris built-in variables. The result can be used as input for
// CheckSchema.TemplateForResource().
func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
templateInput := test.Resource.Resource.Object
if templateInput == nil {
return nil, nil
}
if test.Target == config.TargetPodSpec || test.Target == config.TargetContainer {
podSpecMap, err := kube.SerializePodSpec(test.Resource.PodSpec)
if err != nil {
return nil, err
}
err = unstructured.SetNestedMap(templateInput, podSpecMap, "Polaris", "PodSpec")
if err != nil {
return nil, err
}
podTemplateMap, ok := test.Resource.PodTemplate.(map[string]interface{})
if ok {
err := unstructured.SetNestedMap(templateInput, podTemplateMap, "Polaris", "PodTemplate")
if err != nil {
return nil, err
}
}
if test.Target == config.TargetContainer {
containerMap, err := kube.SerializeContainer(test.Container)
if err != nil {
return nil, err
}
err = unstructured.SetNestedMap(templateInput, containerMap, "Polaris", "Container")
if err != nil {
return nil, err
}
}
}
return templateInput, nil
}
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.ValError) ResultMessage {
details := []string{}
for _, issue := range issues {
@@ -288,6 +331,9 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
} else if check.Target == config.TargetPodSpec {
passes, issues, err = check.CheckPodSpec(test.Resource.PodSpec)
prefix = getJSONSchemaPrefix(test.Resource.Kind)
} else if check.Target == config.TargetPodTemplate {
passes, issues, err = check.CheckPodTemplate(test.Resource.PodTemplate)
prefix = getJSONSchemaPrefix(test.Resource.Kind)
} else if check.Target == config.TargetContainer {
containerIndex := funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
@@ -0,0 +1,19 @@
# This fails because automounting is true for both the pod and ServiceAccount.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
automountServiceAccountToken: true
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
automountServiceAccountToken: true
@@ -0,0 +1,19 @@
# This fails because automounting is true for the pod, overriding the ServiceAccount.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
automountServiceAccountToken: true
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
automountServiceAccountToken: false
@@ -0,0 +1,17 @@
# This fails because automounting is not disabled anywhere.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
@@ -0,0 +1,19 @@
# This succeeds because automounting is disabled at the pod.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
automountServiceAccountToken: false
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
@@ -0,0 +1,19 @@
# This succeeds because automounting is disabled at the pod and ServiceAccount.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
automountServiceAccountToken: false
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
automountServiceAccountToken: false
@@ -0,0 +1,19 @@
# This succeeds because automounting is disabled at the pod, overriding the ServiceAccount.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
automountServiceAccountToken: false
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
automountServiceAccountToken: true
@@ -0,0 +1,18 @@
# This succeeds because automounting is disabled at the ServiceAccount.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
serviceAccountName: test
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
automountServiceAccountToken: false
@@ -0,0 +1,18 @@
# This fails because the annotation names do not match the containers.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
annotations:
container.apparmor.security.beta.kubernetes.io/container1: runtime/default
container.apparmor.security.beta.kubernetes.io/container2: runtime/default
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- name: nginx2
image: nginx
ports:
- containerPort: 81
@@ -0,0 +1,17 @@
# This fails because ALL capabilities are also added.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
drop:
- ALL
add:
- all
ports:
- containerPort: 80
@@ -0,0 +1,11 @@
# This fails because none of the seccompProfile, seLinuxOptions, AppArmor, or dropping capabilities are present.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,17 @@
# This fails because the container overrides the pod profile type.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
securityContext:
seccompProfile:
type: Unconfined
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,13 @@
# This fails because the pod seccompPRofile is missing the type field.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seccompProfile:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This fails because the container overrides the pod profile type.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seccompProfile:
type: Unconfined
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,13 @@
# This fails because seLinuxOptions is defined but without any sub-field.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
securityContext:
seLinuxOptions:
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,13 @@
# This fails because seLinuxOptions is defined but without any sub-field.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seLinuxOptions:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,18 @@
# This succeeds because AppArmor annotations exist matching both container names.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
annotations:
container.apparmor.security.beta.kubernetes.io/nginx: runtime/default
container.apparmor.security.beta.kubernetes.io/nginx2: runtime/default
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- name: nginx2
image: nginx
ports:
- containerPort: 81
@@ -0,0 +1,17 @@
# This succeeds because SOME capability is dropped, and ALL capabilities are not added.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
drop:
- ALL
add:
- someCapabilities
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This succeeds because a seccomp profile is defined for the container.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
securityContext:
seccompProfile:
type: RuntimeDefault
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This succeeds because a seccomp profile is defined for the pod and not undefined for the container.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,17 @@
# This succeeds because the container seccomp profile overrides the pod Unconfined setting.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seccompProfile:
type: Unconfined
containers:
- name: nginx
securityContext:
seccompProfile:
type: RuntimeDefault
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This succeeds because seLinuxOptions are defined for the container.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This succeeds because seLinuxOptions is defined for the pod.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,41 @@
# This failes because the NetworkPolicy `policyTypes` lacks `Egress`,
# without which the egress rules will not be applied.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: medium
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
# Allow outbound with other medium-security pods.
security: medium
ports:
- protocol: TCP
port: 80
@@ -0,0 +1,32 @@
# This fails because the NetworkPolicy lacks egress rules.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: medium
policyTypes:
- Egress
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 8080
@@ -0,0 +1,41 @@
# This failes because the NetworkPolicy `policyTypes` lacks `Ingress`,
# without which the egress rules will not be applied.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: medium
policyTypes:
- Egress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
# Allow outbound with other medium-security pods.
security: medium
ports:
- protocol: TCP
port: 80
@@ -0,0 +1,34 @@
# This fails because the NetworkPolicy lacks ingress rules.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: medium
policyTypes:
- Egress
- Ingress
egress:
- to:
- podSelector:
matchLabels:
# Allow outbound with other medium-security pods.
security: medium
ports:
- protocol: TCP
port: 80
@@ -0,0 +1,41 @@
# This fails because the NetworkPolicy matches a label the pod does not have
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: high
policyTypes:
- Egress
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
# Allow outbound with other medium-security pods.
security: medium
ports:
- protocol: TCP
port: 80
@@ -0,0 +1,41 @@
# This succeeds because the NetworkPolicy contains and enables egress and ingress rules, and targets the pod's label.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
security: medium
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test
spec:
podSelector:
matchLabels:
security: medium
policyTypes:
- Egress
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
# Allow outbound with other medium-security pods.
security: medium
ports:
- protocol: TCP
port: 80
@@ -0,0 +1,7 @@
# This fails because the key AWS_SECRET_ACCESS_KEY is set.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
AWS_SECRET_ACCESS_KEY: xxxxx
@@ -0,0 +1,7 @@
# This fails because the key AZURE_BATCH_KEY is set.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
AZURE_BATCH_KEY: xxxxx
@@ -0,0 +1,7 @@
# This fails because the key OCI_CLI_KEY_CONTENT is set.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
OCI_CLI_KEY_CONTENT: xxxxx
@@ -0,0 +1,8 @@
# This fails because a key contains "password".
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
db_username: postgres
db_password: abc123
@@ -0,0 +1,11 @@
# This fails because a value contains a private key.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
a_key: and value that are ok
ssh_access: |
-----BEGIN PRIVATE KEY-----
xxxxxxxx
-----END OPENSSH PRIVATE KEY-----
@@ -0,0 +1,6 @@
# This succeeds because no data nor keys nor values are defined.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
#data:
@@ -0,0 +1,8 @@
# This succeeds because no keys or values are invalid.
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config
data:
message: This is a test
db_user: postgres
@@ -0,0 +1,16 @@
# This fails because an environment variable name AWS_SECRET_ACCESS_KEY is set.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: password
value: abc123
- name: AWS_SECRET_ACCESS_KEY
value: xxx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,16 @@
# This fails because an environment variable named AZURE_BATCH_KEY is set.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: password
value: abc123
- name: AZURE_BATCH_KEY
value: xxx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,16 @@
# This fails because an environment variable named OCI_CLI_KEY_CONTENT is set.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: password
value: abc123
- name: OCI_CLI_KEY_CONTENT
value: xxx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,16 @@
# This fails because an environment variable name contains "password".
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: user_name
value: postgres
- name: my_password
value: abc123
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,19 @@
# This fails because an environment variable value contains a private key.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: user_name
value: postgres
- name: the_good_stuff
value: |
---BEGIN OPENSSH PRIVATE KEY---
xxxxx
---END OPENSSH PRIVATE KEY---
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,11 @@
# This succeeds because there are no environment variables.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
@@ -0,0 +1,14 @@
# This succeeds because there are no sensitive environment variable names or values.
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
env:
- name: greeting
value: hello
image: nginx
ports:
- containerPort: 80
+3
View File
@@ -199,6 +199,8 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
APIResources: []metav1.APIResource{
{Name: "pods", Namespaced: true, Kind: "Pod"},
{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
{Name: "serviceaccounts", Namespaced: true, Kind: "ServiceAccount"},
{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
},
},
{
@@ -239,6 +241,7 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
GroupVersion: "networking.k8s.io/v1",
APIResources: []metav1.APIResource{
{Name: "ingresses", Namespaced: true, Kind: "Ingress", Version: "v1"},
{Name: "networkpolicies", Namespaced: true, Kind: "NetworkPolicy", Version: "v1"},
},
},
{