Compare commits

..
20 Commits
Author SHA1 Message Date
Robert Brennan 1a025da649 bump version 2021-02-26 20:35:36 +00:00
Jordan DoigandRobert Brennan 4c3d0e0603 Set full object ObjectMeta on new workload from Pod (#471)
* Unmarshal OriginalObjectJSON into ObjectMeta

* Unmarshal to unst before converting too v1 Object

* Add passing annotated deployment webhook test case

* fix meta accessor

* fix tests

* remove logs

* fix tests

Co-authored-by: Robert Brennan <contact@rbren.io>
2021-02-26 15:33:40 -05:00
Robert Brennan 714b7bfbba trigger CI 2021-02-26 16:32:19 +00:00
Barnabas Makonda 16ffe1e1c4 Merge pull request #487 from FairwindsOps/only-failed-test-audit
Audit- Only show the failed test if flag is specified.
2021-02-26 17:51:46 +03:00
MAKOSCAFEE 7f1c143d77 fix docs 2021-02-24 21:19:09 +03:00
MAKOSCAFEE f42af35352 fix merge conflicts 2021-02-24 21:17:58 +03:00
MAKOSCAFEE 8bbe13b6fa document show only failed test flag 2021-02-24 20:59:10 +03:00
dependabot-preview[bot]andlnx01 dea7143827 Bump github.com/sirupsen/logrus from 1.7.0 to 1.8.0 (#504)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.7.0...v1.8.0)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-24 08:59:47 -05:00
dependabot-preview[bot]andlnx01 41d5f72822 Bump k8s.io/api from 0.20.2 to 0.20.4 (#501)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.20.2 to 0.20.4.
- [Release notes](https://github.com/kubernetes/api/releases)
- [Commits](https://github.com/kubernetes/api/compare/v0.20.2...v0.20.4)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-24 08:59:29 -05:00
dependabot-preview[bot]andlnx01 3e49a3af98 Bump github.com/spf13/cobra from 1.1.1 to 1.1.3 (#497)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.1 to 1.1.3.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.1.1...v1.1.3)

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

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-02-16 11:49:01 -05:00
Fairmay 89ff4a6d53 Fix a small typo in the README file where a sentence is repeated. (#483) 2021-02-16 11:48:28 -05:00
Robert Brennan a5852f3003 Make it easier to run webhook tests locally (#476)
* make it easy to run webhook tests locally

* modify tests so they run locally

* follow the logs

* add instructions

* make it easy to run webhook tests locally

* modify tests so they run locally

* follow the logs

* add instructions

* use universal date command

* fix sed command for portability

* fix date command

* make entire image configurable

* fix instructions
2021-02-16 11:48:19 -05:00
Robert Brennan 4a04999ce7 bump to 3.1.3 2021-02-11 22:14:47 +00:00
Robert Brennan c16aac808f fix checks for k8s defaults (#496)
* fix insecure caps check

* add more tests

* fix privilege escalation allowed
2021-02-11 17:11:16 -05:00
MAKOSCAFEE 0aa173789a refactor resultSet loop 2021-02-09 19:16:42 +03:00
Robert BrennanandDominik Meyer 38b5cd8193 enable action (#495)
* enable action

* Update .github/workflows/test_setup.yml

Co-authored-by: Dominik Meyer <dmnkmyr@gmail.com>

Co-authored-by: Dominik Meyer <dmnkmyr@gmail.com>
2021-02-09 08:40:35 -05:00
ef59f7c26e 🛠 Add GitHub Action (#493)
* Add Github Action from https://github.com/mambax/setup-polaris

* Add tag information to action

* repository name must be lowercase

* repository name must be lowercase

* repository name must be lowercase

* Own image

* Debug

* Make the file local

* Move into actions folder

* Rename action

* Remove path slash

* Rename Dockerfile

* Change path

* Move script

* Remove dot

* Test

* - Change to gh/download setup script
- Move docs into Infra as Code page

* Add small comment about Dockerfile

* Rm unwanted line break

* Improve action yaml

* Update test_setup.yml

* Update test_setup.yml

* Update test_setup.yml

* Pavel fixed all bugs

Co-authored-by: dom <dominik.meyer01@sap.com>
Co-authored-by: Pavel Zorin <pazonec@yandex.ru>
2021-02-08 08:39:45 -05:00
Robert Brennan 08900887e4 Fix dupe badge (#494) 2021-02-05 10:43:32 -05:00
Robert Brennan 9ddbe5a329 add insights (#492)
* add insights

* fix docker vulns

* bump version

* adjust configs to use commit image
2021-02-04 11:38:07 -05:00
MAKOSCAFEE fe0060af77 added test for score 2021-01-29 22:13:01 +03:00
39 changed files with 577 additions and 96 deletions
+21
View File
@@ -169,6 +169,21 @@ jobs:
- *update_coverage
- *test_binary_dashboard
insights:
docker:
- image: quay.io/reactiveops/ci-images:v11.0-stretch
steps:
- checkout
- setup_remote_docker
- run:
name: Adjust configs for latest image
command: |
sed -r "s|'(quay.io/fairwinds/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/webhook.yaml > ./deploy/dashboard.yaml
sed -r "s|'(quay.io/fairwinds/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/dashboard.yaml > ./deploy/webhook.yaml
- run:
name: Insights CI
command: curl -L https://insights.fairwinds.com/v0/insights-ci.sh | bash
release_binary:
working_directory: /go/src/github.com/fairwindsops/polaris/
docker:
@@ -235,6 +250,12 @@ workflows:
filters:
branches:
ignore: /pull\/[0-9]+/
- insights:
requires:
- push
filters:
branches:
ignore: /pull\/[0-9]+/
- test_k8s:
requires:
- push
+15
View File
@@ -0,0 +1,15 @@
# The action uses an own Dockerfile on purpose because the root Dockerfile takes way too long to build for an action
FROM alpine:3.10
RUN apk add --no-cache \
bash \
ca-certificates \
curl \
wget \
tar \
jq
COPY get_polaris.sh /get_polaris.sh
ENTRYPOINT ["/get_polaris.sh"]
+22
View File
@@ -0,0 +1,22 @@
name: 'Install polaris'
description: 'Download a specific polaris version'
inputs:
version:
description: 'version of polaris'
required: true
default: 'latest'
runs:
using: 'docker'
image: './Dockerfile'
args:
- ${{ inputs.version }}
outputs:
version:
description: 'Version of polaris installed'
branding:
icon: 'download-cloud'
color: 'gray-dark'
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
if [[ -z "$INPUT_VERSION" ]]; then
echo "Missing polaris version information"
exit 1
fi
polaris version | grep "$INPUT_VERSION" &> /dev/null
if [ $? == 0 ]; then
echo "Polaris $INPUT_VERSION is already installed! Exiting gracefully."
exit 0
else
echo "Installing polaris to path."
fi
TARGET_FILE="polaris.tar.gz"
curl -LJ -o $TARGET_FILE 'https://github.com/FairwindsOps/polaris/releases/download/'"$INPUT_VERSION"'/polaris_'"$INPUT_VERSION"'_linux_386.tar.gz'
mkdir polaris
tar -xzf $TARGET_FILE -C polaris
rm $TARGET_FILE
echo "polaris" >> $GITHUB_PATH
echo "::set-output name=version::$INPUT_VERSION"
+27
View File
@@ -0,0 +1,27 @@
name: Test setup-polaris
on:
pull_request:
branches: [ master ]
jobs:
build-int:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup polaris
uses: ./.github/actions/setup-polaris
with:
version: 3.0.3
- name: Use command
run: polaris version
build-ext:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup polaris
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
with:
version: 3.0.3
- name: Use command
run: polaris version
+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.10
FROM alpine:3.13
WORKDIR /usr/local/bin
RUN apk --no-cache add ca-certificates
+5 -2
View File
@@ -3,7 +3,7 @@
<br>
<h3>Best Practices for Kubernetes Workload Configuration</h3>
<a href="https://github.com/FairwindsOps/polaris">
<img src="https://img.shields.io/static/v1.svg?label=Version&message=3.1.0&color=239922">
<img src="https://img.shields.io/static/v1.svg?label=Version&message=3.1.4&color=239922">
</a>
<a href="https://goreportcard.com/report/github.com/FairwindsOps/polaris">
<img src="https://goreportcard.com/badge/github.com/FairwindsOps/polaris">
@@ -11,11 +11,14 @@
<a href="https://circleci.com/gh/FairwindsOps/polaris.svg">
<img src="https://circleci.com/gh/FairwindsOps/polaris.svg?style=svg">
</a>
<a href="https://insights.fairwinds.com/gh/FairwindsOps/polaris">
<img src="https://insights.fairwinds.com/v0/gh/FairwindsOps/polaris/badge.svg">
</a>
</div>
Fairwinds' Polaris keeps your clusters sailing smoothly. It runs a variety of checks to ensure that
Kubernetes pods and controllers are configured using best practices, helping you avoid
problems in the future. Polaris can be run in a few different modes:
problems in the future.
Polaris can be run in three different modes:
* As a [dashboard](https://polaris.docs.fairwinds.com/dashboard), so you can audit what's running inside your cluster.
+42 -16
View File
@@ -5,27 +5,53 @@ target: Container
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
required:
- securityContext
properties:
securityContext:
type: object
required:
- capabilities
properties:
capabilities:
type: object
required:
- drop
properties:
add:
enum:
- CHOWN
- DAC_OVERRIDE
- FSETID
- FOWNER
- MKNOD
- NET_RAW
- SETGID
- SETUID
- SETFCAP
- SETPCAP
- NET_BIND_SERVICE
- SYS_CHROOT
- KILL
- AUDIT_WRITE
drop:
type: array
oneOf:
- contains:
const: ALL
- allOf:
- contains:
const: NET_ADMIN
- contains:
const: CHOWN
- contains:
const: DAC_OVERRIDE
- contains:
const: FSETID
- contains:
const: FOWNER
- contains:
const: MKNOD
- contains:
const: NET_RAW
- contains:
const: SETGID
- contains:
const: SETUID
- contains:
const: SETFCAP
- contains:
const: SETPCAP
- contains:
const: NET_BIND_SERVICE
- contains:
const: SYS_CHROOT
- contains:
const: KILL
- contains:
const: AUDIT_WRITE
+5 -2
View File
@@ -5,9 +5,12 @@ target: Container
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
required:
- securityContext
properties:
securityContext:
required:
- allowPrivilegeEscalation
properties:
allowPrivilegeEscalation:
not:
const: true
const: false
+3 -1
View File
@@ -32,6 +32,7 @@ import (
)
var setExitCode bool
var onlyShowFailedTests bool
var minScore int
var auditOutputURL string
var auditOutputFile string
@@ -42,6 +43,7 @@ func init() {
rootCmd.AddCommand(auditCmd)
auditCmd.PersistentFlags().StringVar(&auditPath, "audit-path", "", "If specified, audits one or more YAML files instead of a cluster.")
auditCmd.PersistentFlags().BoolVar(&setExitCode, "set-exit-code-on-danger", false, "Set an exit code of 3 when the audit contains danger-level issues.")
auditCmd.PersistentFlags().BoolVar(&onlyShowFailedTests, "only-show-failed-tests", false, "If specified, audit output will only show failed tests.")
auditCmd.PersistentFlags().IntVar(&minScore, "set-exit-code-below-score", 0, "Set an exit code of 4 when the score is below this threshold (1-100).")
auditCmd.PersistentFlags().StringVar(&auditOutputURL, "output-url", "", "Destination URL to send audit results.")
auditCmd.PersistentFlags().StringVar(&auditOutputFile, "output-file", "", "Destination file for audit results.")
@@ -81,7 +83,7 @@ func runAndReportAudit(ctx context.Context, c conf.Configuration, auditPath, wor
os.Exit(1)
}
var auditData validator.AuditData
auditData, err = validator.RunAudit(c, k)
auditData, err = validator.RunAudit(c, k, onlyShowFailedTests)
if err != nil {
logrus.Errorf("Error while running audit on resources: %v", err)
+17
View File
@@ -1,6 +1,23 @@
---
sidebarDepth: 0
---
## 3.1.4
* Fixes for exemption annotations for the admission controller
## 3.1.3
* Fixes for `privilegeEscalationAllowed` and `insecureCapabilities` checks to take Kubernetes defaults into account
## 3.1.2
* Start checking deployment configuration using Fairwinds Insights
## 3.1.1
* Updated to alpine:3.13
## 3.1.0
* Added support for Ingress objects
* Fixes for exemptions, including support for exempting entire namespaces
## 3.0.0
* **Breaking** - fixed inconsistency in how controller-level checks are handled
Custom checks with `target: Controller` should remove `Object` from the top-level of the
+15 -1
View File
@@ -32,12 +32,26 @@ We label issues with the ["good first issue" tag](https://github.com/FairwindsOp
The following commands are all required to pass as part of Polaris testing:
```
```bash
go list ./... | grep -v vendor | xargs golint -set_exit_status
go list ./... | grep -v vendor | xargs go vet
go test ./pkg/... -v -coverprofile cover.out
```
### Webhook tests
```bash
kind create cluster --wait=90s --image kindest/node:v1.15.11 --name polaris-test
docker build -t quay.io/fairwinds/polaris:debug . # or use your own registry
docker push quay.io/fairwinds/polaris:debug
helm repo add jetstack https://charts.jetstack.io
kubectl create ns cert-manager
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version 0.16.1 --set "installCRDs=true" --wait
POLARIS_IMAGE=quay.io/fairwinds/polaris:debug ./test/webhook_test.sh
```
to avoid the final cleanup for debugging purposes, you can run
```bash
SKIP_FINAL_CLEANUP=true IMAGE_TAG=debug ./test/webhook_test.sh
```
## Creating a New Issue
If you've encountered an issue that is not already reported, please create a [new issue](https://github.com/FairwindsOps/polaris/issues), choose `Bug Report`, `Feature Request` or `Misc.` and follow the instructions in the template.
+49 -2
View File
@@ -21,8 +21,9 @@ polaris version
```
## Running in a CI pipeline
You can tell the CLI to set an exit code if it detects certain issues with your
YAML files.
### Set minimum score for an exit code
You can tell the CLI to set an exit code if it detects certain issues with your YAML files.
For example, to fail if polaris detects *any* danger-level issues, or if the score drops below 90%:
```bash
polaris audit --audit-path ./deploy/ \
@@ -30,3 +31,49 @@ polaris audit --audit-path ./deploy/ \
--set-exit-code-below-score 90
```
### Output only showing failed tests
The CLI to gives you ability to display results containing only failed tests.
For example:
```bash
polaris audit --audit-path ./deploy/ \
--only-show-failed-tests true
```
### As Github Action
#### Setup polaris action
This action downloads a version of [polaris](https://github.com/FairwindsOps/polaris) and adds it to the path. It makes the [polaris cli](https://polaris.docs.fairwinds.com/infrastructure-as-code) ready to use in following steps of the same job.
##### Inputs
###### `version`
The release version to fetch. This has to be in the form `<tag_name>`.
##### Outputs
###### `version`
The version number of the release tag.
##### Example usage
```yaml
uses: fairwindsops/polaris@master
with:
version: "3.0.3"
```
Example inside a job:
```yaml
steps:
- uses: actions/checkout@v2
- name: Setup polaris
uses: fairwindsops/polaris@master
with:
version: 3.0.3
- name: Use command
run: polaris version
```
+14
View File
@@ -0,0 +1,14 @@
options:
organization: fairwinds-opensource
baseBranch: master
# These images will be scanned for vulnerabilities.
images:
docker:
- quay.io/fairwinds/polaris:$CI_SHA1
# These manifests will be scanned for configuration issues.
manifests:
yaml:
- ./deploy/dashboard.yaml
- ./deploy/webhook.yaml
+4 -5
View File
@@ -17,8 +17,8 @@ require (
github.com/prometheus/client_golang v1.9.0 // indirect
github.com/qri-io/jsonschema v0.1.1
github.com/rogpeppe/go-internal v1.6.2 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1
github.com/sirupsen/logrus v1.8.0
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
go.uber.org/zap v1.16.0 // indirect
@@ -27,10 +27,9 @@ require (
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
k8s.io/api v0.20.2
k8s.io/apimachinery v0.20.2
k8s.io/api v0.20.4
k8s.io/apimachinery v0.20.4
k8s.io/client-go v0.20.2
k8s.io/component-base v0.20.1 // indirect
sigs.k8s.io/controller-runtime v0.7.0
+9
View File
@@ -412,6 +412,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -568,6 +569,8 @@ github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -581,6 +584,8 @@ github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -1058,6 +1063,8 @@ k8s.io/api v0.20.1 h1:ud1c3W3YNzGd6ABJlbFfKXBKXO+1KdGfcgGGNgFR03E=
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
k8s.io/api v0.20.4 h1:xZjKidCirayzX6tHONRQyTNDVIR55TYVqgATqo6ZULY=
k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
@@ -1065,6 +1072,8 @@ k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.4 h1:vhxQ0PPUUU2Ns1b9r4/UFp13UPs8cw2iOoTjnY9faa0=
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
k8s.io/client-go v0.20.1 h1:Qquik0xNFbK9aUG92pxHYsyfea5/RPO9o9bSywNor+M=
+1 -1
View File
@@ -20,7 +20,7 @@ import (
const (
// Version represents the current release version of Polaris
Version = "3.1.0"
Version = "3.1.4"
)
func main() {
+2 -2
View File
@@ -181,7 +181,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
}
var auditDataObj validator.AuditData
auditDataObj, err = validator.RunAudit(adjustedConf, k)
auditDataObj, err = validator.RunAudit(adjustedConf, k, false)
if err != nil {
http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError)
return
@@ -214,7 +214,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
}
var auditData validator.AuditData
auditData, err = validator.RunAudit(adjustedConf, k)
auditData, err = validator.RunAudit(adjustedConf, k, false)
if err != nil {
logrus.Errorf("Error getting audit data: %v", err)
http.Error(w, "Error running audit", 500)
+13
View File
@@ -76,6 +76,19 @@ func NewGenericWorkloadFromPod(podResource kubeAPICoreV1.Pod, originalObject int
return workload, err
}
workload.OriginalObjectJSON = bytes
var unst unstructured.Unstructured
err = json.Unmarshal(bytes, &unst.Object)
if err != nil {
logrus.Error("Couldn't marshal JSON for pod ", err)
return workload, err
}
objMeta, err := meta.Accessor(&unst)
if err != nil {
logrus.Error("Couldn't create meta accessor for unstructred ", err)
return workload, err
}
workload.ObjectMeta = objMeta
}
return workload, nil
}
+8 -8
View File
@@ -555,14 +555,14 @@ func TestValidateSecurity(t *testing.T) {
Category: "Security",
}, {
ID: "privilegeEscalationAllowed",
Message: "Privilege escalation not allowed",
Success: true,
Message: "Privilege escalation should not be allowed",
Success: false,
Severity: "danger",
Category: "Security",
}, {
ID: "insecureCapabilities",
Message: "Container does not have any insecure capabilities",
Success: true,
Message: "Container should not have insecure capabilities",
Success: false,
Severity: "warning",
Category: "Security",
}, {
@@ -739,8 +739,8 @@ func TestValidateSecurity(t *testing.T) {
Category: "Security",
}, {
ID: "insecureCapabilities",
Message: "Container does not have any insecure capabilities",
Success: true,
Message: "Container should not have insecure capabilities",
Success: false,
Severity: "warning",
Category: "Security",
}},
@@ -758,8 +758,8 @@ func TestValidateSecurity(t *testing.T) {
Category: "Security",
}, {
ID: "insecureCapabilities",
Message: "Container does not have any insecure capabilities",
Success: true,
Message: "Container should not have insecure capabilities",
Success: false,
Severity: "danger",
Category: "Security",
}, {
-13
View File
@@ -15,16 +15,12 @@
package validator
import (
"strings"
"github.com/sirupsen/logrus"
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
)
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
// ValidateController validates a single controller, returns a Result.
func ValidateController(conf *conf.Configuration, controller kube.GenericWorkload) (Result, error) {
podResult, err := ValidatePod(conf, controller)
@@ -56,9 +52,6 @@ func ValidateControllers(config *conf.Configuration, kubeResources *kube.Resourc
results := []Result{}
for _, controller := range controllersToAudit {
if !config.DisallowExemptions && hasExemptionAnnotation(controller) {
continue
}
result, err := ValidateController(config, controller)
if err != nil {
logrus.Warn("An error occurred validating controller:", err)
@@ -69,9 +62,3 @@ func ValidateControllers(config *conf.Configuration, kubeResources *kube.Resourc
return results, nil
}
func hasExemptionAnnotation(ctrl kube.GenericWorkload) bool {
annot := ctrl.ObjectMeta.GetAnnotations()
val := annot[exemptionAnnotationKey]
return strings.ToLower(val) == "true"
}
+17 -1
View File
@@ -222,5 +222,21 @@ func TestControllerExemptions(t *testing.T) {
if err != nil {
panic(err)
}
assert.Equal(t, 0, len(actualResults))
expectedExemptSum := CountSummary{
Successes: uint(0),
Warnings: uint(0),
Dangers: uint(0),
}
assert.Equal(t, 1, len(actualResults))
assert.Equal(t, "Deployment", actualResults[0].Kind)
assert.EqualValues(t, expectedExemptSum, actualResults[0].GetSummary())
c.DisallowExemptions = true
actualResults, err = ValidateControllers(&c, resources)
if err != nil {
panic(err)
}
assert.Equal(t, 1, len(actualResults))
assert.Equal(t, "Deployment", actualResults[0].Kind)
assert.EqualValues(t, expectedSum, actualResults[0].GetSummary())
}
+5 -1
View File
@@ -16,7 +16,7 @@ import (
)
// RunAudit runs a full Polaris audit and returns an AuditData object
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) {
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider, onlyShowFailedTests bool) (AuditData, error) {
displayName := config.DisplayName
if displayName == "" {
displayName = kubeResources.SourceName
@@ -49,6 +49,10 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (
},
Results: results,
}
auditData.Score = auditData.GetSummary().GetScore()
if onlyShowFailedTests {
auditData.RemoveSuccessfulResults()
}
return auditData, nil
}
+3 -1
View File
@@ -28,10 +28,12 @@ func TestGetTemplateData(t *testing.T) {
Warnings: uint(3),
Dangers: uint(3),
}
score := uint(0)
var actualAudit AuditData
actualAudit, err = RunAudit(c, resources)
actualAudit, err = RunAudit(c, resources, false)
assert.Equal(t, err, nil, "error should be nil")
assert.Equal(t, score, actualAudit.Score, "")
assert.EqualValues(t, sum, actualAudit.GetSummary())
assert.Equal(t, actualAudit.SourceType, "Cluster", "should be from a cluster")
assert.Equal(t, actualAudit.SourceName, "test", "should be from a cluster")
+32
View File
@@ -34,6 +34,14 @@ type AuditData struct {
DisplayName string
ClusterInfo ClusterInfo
Results []Result
Score uint
}
// RemoveSuccessfulResults remove all test that have passed.
func (res *AuditData) RemoveSuccessfulResults() {
for _, auditDataResult := range res.Results {
auditDataResult.removeSuccessfulResults()
}
}
// ClusterInfo contains Polaris results as well as some high-level stats
@@ -57,6 +65,14 @@ type ResultMessage struct {
// ResultSet contiains the results for a set of checks
type ResultSet map[string]ResultMessage
func (res ResultSet) removeSuccessfulResults() {
for k, resultMessage := range res {
if resultMessage.Success {
delete(res, k)
}
}
}
// Result provides results for a Kubernetes object
type Result struct {
Name string
@@ -67,6 +83,11 @@ type Result struct {
CreatedTime time.Time
}
func (res *Result) removeSuccessfulResults() {
res.Results.removeSuccessfulResults()
res.PodResult.removeSuccessfulResults()
}
// PodResult provides a list of validation messages for each pod.
type PodResult struct {
Name string
@@ -74,8 +95,19 @@ type PodResult struct {
ContainerResults []ContainerResult
}
func (res *PodResult) removeSuccessfulResults() {
res.Results.removeSuccessfulResults()
for _, containerResult := range res.ContainerResults {
containerResult.removeSuccessfulResults()
}
}
// ContainerResult provides a list of validation messages for each container.
type ContainerResult struct {
Name string
Results ResultSet
}
func (res *ContainerResult) removeSuccessfulResults() {
res.Results.removeSuccessfulResults()
}
+19 -11
View File
@@ -116,17 +116,28 @@ func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bo
return result
}
func getExemptKey(checkID string) string {
return fmt.Sprintf("polaris.fairwinds.com/%s-exempt", checkID)
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
const exemptionAnnotationPattern = "polaris.fairwinds.com/%s-exempt"
func hasExemptionAnnotation(ctrl kube.GenericWorkload, checkID string) bool {
annot := ctrl.ObjectMeta.GetAnnotations()
val := annot[exemptionAnnotationKey]
if strings.ToLower(val) == "true" {
return true
}
checkKey := fmt.Sprintf(exemptionAnnotationPattern, checkID)
val = annot[checkKey]
if strings.ToLower(val) == "true" {
return true
}
return false
}
func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objectAnnotations := controller.ObjectMeta.GetAnnotations()
for _, checkID := range checkIDs {
exemptValue := objectAnnotations[getExemptKey(checkID)]
if strings.ToLower(exemptValue) == "true" {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetPod, controller.ObjectMeta, "", false)
@@ -148,10 +159,8 @@ func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWor
func applyControllerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objectAnnotations := controller.ObjectMeta.GetAnnotations()
for _, checkID := range checkIDs {
exemptValue := objectAnnotations[getExemptKey(checkID)]
if strings.ToLower(exemptValue) == "true" {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetController, controller.ObjectMeta, "", false)
@@ -173,13 +182,12 @@ func applyControllerSchemaChecks(conf *config.Configuration, controller kube.Gen
func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
objectAnnotations := controller.ObjectMeta.GetAnnotations()
for _, checkID := range checkIDs {
exemptValue := objectAnnotations[getExemptKey(checkID)]
if strings.ToLower(exemptValue) == "true" {
if !conf.DisallowExemptions && hasExemptionAnnotation(controller, checkID) {
continue
}
check, err := resolveCheck(conf, checkID, controller.Kind, config.TargetContainer, controller.ObjectMeta, container.Name, isInit)
if err != nil {
return nil, err
} else if check == nil {
@@ -0,0 +1,29 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
drop:
- NET_ADMIN
- CHOWN
- DAC_OVERRIDE
- FSETID
- FOWNER
- MKNOD
- NET_RAW
- SETGID
- SETUID
- SETFCAP
- SETPCAP
- NET_BIND_SERVICE
- SYS_CHROOT
- KILL
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
drop:
- ALL
@@ -0,0 +1,29 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
drop:
- NET_ADMIN
- CHOWN
- DAC_OVERRIDE
- FSETID
- FOWNER
- MKNOD
- NET_RAW
- SETGID
- SETUID
- SETFCAP
- SETPCAP
- NET_BIND_SERVICE
- SYS_CHROOT
- KILL
- AUDIT_WRITE
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: true
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: false
@@ -2,7 +2,6 @@ apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
@@ -2,7 +2,6 @@ apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
@@ -45,4 +44,4 @@ spec:
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
@@ -2,7 +2,6 @@ apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
@@ -2,7 +2,6 @@ apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
@@ -45,4 +44,4 @@ spec:
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
@@ -0,0 +1,31 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
annotations:
polaris.fairwinds.com/exempt: "true"
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
securityContext:
allowPrivilegeEscalation: true
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
+59 -22
View File
@@ -1,17 +1,27 @@
#!/bin/bash
set -e
#sed is replacing the polaris version with this commit sha so we are testing exactly this verison.
sed -r "s|'(quay.io/fairwinds/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/webhook.yaml > ./deploy/webhook-test.yaml
# Testing to ensure that the webhook starts up, allows a correct deployment to pass,
# and prevents a incorrectly formatted deployment.
# and prevents a incorrectly formatted deployment.
BLUE='\033[0;34m'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
function get_timeout() {
if [[ "$OSTYPE" == "darwin"* ]]; then
date -v+4M +%s
else
date -d "+4 minutes" +%s
fi
}
function check_webhook_is_ready() {
# Get the epoch time in one minute from now
local timeout_epoch
# Reset another 4 minutes to wait for webhook
timeout_epoch=$(date -d "+4 minutes" +%s)
timeout_epoch=$(get_timeout)
# loop until this fails (desired condition is we cannot apply this yaml doc, which means the webhook is working
echo "Waiting for webhook to be ready"
@@ -34,19 +44,23 @@ function check_timeout() {
clean_up
exit 1
fi
}
# Clean up all your stuff
function clean_up() {
echo -e "\n\nCleaning up (you may see some errors)...\n\n"
kubectl delete ns scale-test || true
kubectl delete ns polaris || true
kubectl delete ns tests || true
# Clean up files you've installed (helps with local testing)
for filename in test/webhook_cases/*.yaml; do
# || true to avoid issues when we cannot delete
kubectl delete -f $filename &>/dev/null ||true
kubectl delete -f $filename ||true
done
# Uninstall webhook and webhook config
kubectl delete validatingwebhookconfigurations polaris-webhook --wait=false &>/dev/null
kubectl -n polaris delete deploy -l app=polaris --wait=false &>/dev/null
kubectl delete validatingwebhookconfigurations polaris-webhook --wait=false
kubectl -n polaris delete deploy -l app=polaris --wait=false
echo -e "\n\nDone cleaning up\n\n"
}
function grab_logs() {
@@ -56,39 +70,60 @@ function grab_logs() {
kubectl -n polaris logs -l app=polaris
}
# Install a bad deployment
#sed is replacing the polaris version with this commit sha so we are testing exactly this verison.
if [ -z "${POLARIS_IMAGE}" ]; then
POLARIS_IMAGE="quay.io/fairwinds/polaris:$CIRCLE_SHA1"
fi
echo "using image $POLARIS_IMAGE"
sed -E "s|'(quay.io/fairwinds/polaris:).+'|'${POLARIS_IMAGE}'|" ./deploy/webhook.yaml > ./deploy/webhook-test.yaml
clean_up || true
echo -e "Setting up..."
kubectl create ns scale-test
kubectl create ns polaris
kubectl create ns tests
# Install a bad deployment
kubectl apply -n scale-test -f ./test/webhook_cases/failing_test.deployment.yaml
# Install the webhook
kubectl apply -f ./deploy/webhook-test.yaml &> /dev/null
# Install the webhook
kubectl apply -n polaris -f ./deploy/webhook-test.yaml
# wait for the webhook to come online
check_webhook_is_ready
sleep 30
sleep 5
kubectl logs -n polaris $(kubectl get po -oname -n polaris | grep webhook) --follow &
# Webhook started, setting all tests as passed initially.
ALL_TESTS_PASSED=1
# Run tests against correctly configured objects
for filename in test/webhook_cases/passing_test.*.yaml; do
echo $filename
if ! kubectl apply -f $filename &> /dev/null; then
echo -e "\n\n"
echo -e "${BLUE}TEST CASE: $filename${NC}"
if ! kubectl apply -n tests -f $filename; then
ALL_TESTS_PASSED=0
echo "Test Failed: Polaris prevented a deployment with no configuration issues."
kubectl logs -n polaris $(kubectl get po -oname -n polaris | grep webhook)
echo -e "${RED}****Test Failed: Polaris prevented a resource with no configuration issues****${NC}"
else
echo -e "${GREEN}****Test Passed: Polaris correctly allowed this resource****${NC}"
fi
kubectl delete -n tests -f $filename || true
done
# Run tests against incorrectly configured objects
for filename in test/webhook_cases/failing_test.*.yaml; do
echo $filename
if kubectl apply -f $filename &> /dev/null; then
echo -e "\n\n"
echo -e "${BLUE}TEST CASE: $filename${NC}"
if kubectl apply -n tests -f $filename; then
ALL_TESTS_PASSED=0
echo "Test Failed: Polaris should have prevented this deployment due to configuration issues."
echo -e "${RED}****Test Failed: Polaris should have prevented this resource due to configuration issues.****${NC}"
kubectl logs -n polaris $(kubectl get po -oname -n polaris | grep webhook)
else
echo -e "${GREEN}****Test Passed: Polaris correctly prevented this resource****${NC}"
fi
kubectl delete -n tests -f $filename || true
done
kubectl -n scale-test scale deployment nginx-deployment --replicas=2
@@ -100,7 +135,9 @@ if [ $pod_count != 2 ]; then
echo "Existing deployment was unable to scale after webhook installed: found $pod_count pods"
fi
clean_up
if [ -z $SKIP_FINAL_CLEANUP ]; then
clean_up
fi
#Verify that all the tests passed.
if [ $ALL_TESTS_PASSED -eq 1 ]; then