mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
228
Commits
tg/improved-ui
...
10.1.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47c7deddfd | ||
|
|
2007a2c317 | ||
|
|
a5af7bb34e | ||
|
|
893e5de589 | ||
|
|
f6eed6589d | ||
|
|
517c8899d7 | ||
|
|
e2df222ded | ||
|
|
d45d67cdd2 | ||
|
|
e8a113388e | ||
|
|
21ef1f495c | ||
|
|
ec1ba2f2db | ||
|
|
39fb27c6dd | ||
|
|
e51b90ef79 | ||
|
|
4de082b443 | ||
|
|
264dc51f4c | ||
|
|
45b1f6a1a5 | ||
|
|
38e540e0cf | ||
|
|
2b17c31957 | ||
|
|
813d9c0a2a | ||
|
|
d7d30ac611 | ||
|
|
8fe7a94260 | ||
|
|
f29571e8bb | ||
|
|
a74714b5e5 | ||
|
|
ba04b7e330 | ||
|
|
d70591b1b8 | ||
|
|
80e6f7214e | ||
|
|
1d20a16cb3 | ||
|
|
37edbc7159 | ||
|
|
d451480acf | ||
|
|
705e6e4016 | ||
|
|
2d9aa54e68 | ||
|
|
4dd3a81bbd | ||
|
|
4b87bafbb9 | ||
|
|
62effa36b6 | ||
|
|
913988e44c | ||
|
|
f281cd1e33 | ||
|
|
073847559a | ||
|
|
9b5438d864 | ||
|
|
be349a885d | ||
|
|
08f2afe6c9 | ||
|
|
9f681b3ed4 | ||
|
|
74cbaefc58 | ||
|
|
bd8d4cc08b | ||
|
|
e66ef8b948 | ||
|
|
7930ede331 | ||
|
|
1625b755ac | ||
|
|
08a7863167 | ||
|
|
8d5247e501 | ||
|
|
d30865cefb | ||
|
|
cb7c10d288 | ||
|
|
bdfcb8fbb8 | ||
|
|
1c1e990454 | ||
|
|
952b6aed65 | ||
|
|
875a8ffd40 | ||
|
|
61e0d34e8b | ||
|
|
8b236c2fa2 | ||
|
|
f504de33cf | ||
|
|
2d33bf2565 | ||
|
|
9824be2c26 | ||
|
|
a81bd29674 | ||
|
|
ac638e01ba | ||
|
|
4a0713c754 | ||
|
|
48fc87a663 | ||
|
|
1ee4df0c81 | ||
|
|
b73aabc42e | ||
|
|
e60793a022 | ||
|
|
c8394bf9d7 | ||
|
|
0b765df04d | ||
|
|
3e96c582f0 | ||
|
|
391b802d4d | ||
|
|
f295dd0fb5 | ||
|
|
97687761a7 | ||
|
|
c856874564 | ||
|
|
09525d01a1 | ||
|
|
10e82cf0ae | ||
|
|
a5144c45b2 | ||
|
|
9be34236cb | ||
|
|
e7eb079921 | ||
|
|
d55cd4bdf8 | ||
|
|
75997f0e93 | ||
|
|
b0d86cdcd2 | ||
|
|
5595de4ef9 | ||
|
|
4ca4c8f0f5 | ||
|
|
4b1d6635e0 | ||
|
|
0a26f3f578 | ||
|
|
85a439653d | ||
|
|
ba63e6691a | ||
|
|
07747d76f2 | ||
|
|
b876d301c8 | ||
|
|
8bfed75498 | ||
|
|
52a5aa8f74 | ||
|
|
8e3cf1ddd9 | ||
|
|
f1bbe9236b | ||
|
|
65c5ff59ca | ||
|
|
166b39b695 | ||
|
|
ef4ea66d49 | ||
|
|
b472f34266 | ||
|
|
1ddd2d985a | ||
|
|
727c6fa2c3 | ||
|
|
f531103fa0 | ||
|
|
d909dfd00b | ||
|
|
6abfbb3570 | ||
|
|
b75db7fc5b | ||
|
|
501744a65f | ||
|
|
9a678b46e2 | ||
|
|
80c433478b | ||
|
|
498ccf2215 | ||
|
|
bf065f9b54 | ||
|
|
2d28ea551a | ||
|
|
71b0e00288 | ||
|
|
a1b63ac417 | ||
|
|
8af4363672 | ||
|
|
c2d1b1cc4a | ||
|
|
81755e32ec | ||
|
|
531322c6b3 | ||
|
|
e3f3254fe6 | ||
|
|
8bc1a4bcde | ||
|
|
467d06f4db | ||
|
|
4091355e18 | ||
|
|
6ef87be59b | ||
|
|
587396a5c9 | ||
|
|
45be5cbbef | ||
|
|
b3d842a1ba | ||
|
|
29102deadf | ||
|
|
68e27571fd | ||
|
|
681ed3e272 | ||
|
|
f6f85c765b | ||
|
|
b6c6bbe893 | ||
|
|
8e7100acaf | ||
|
|
42d2b3368b | ||
|
|
4d96993a18 | ||
|
|
1486e3090f | ||
|
|
a2eaa210f6 | ||
|
|
e8e642b010 | ||
|
|
1b50b85157 | ||
|
|
5f7bbd981b | ||
|
|
3cf4a88b93 | ||
|
|
5a2a72b582 | ||
|
|
01dd7b7b68 | ||
|
|
6b64369698 | ||
|
|
9448686168 | ||
|
|
01d7a8ac00 | ||
|
|
742b21c6a2 | ||
|
|
b90f091bb6 | ||
|
|
e3a6cb3774 | ||
|
|
7addced32c | ||
|
|
7e77350428 | ||
|
|
af0d548a07 | ||
|
|
3efa3b40c9 | ||
|
|
206322271c | ||
|
|
e5b9236268 | ||
|
|
c3b57bf6c7 | ||
|
|
652b65b3c2 | ||
|
|
41030320bb | ||
|
|
76c42c4799 | ||
|
|
65add73e70 | ||
|
|
a0000e1919 | ||
|
|
f9e2603b16 | ||
|
|
50d789fd42 | ||
|
|
25ab600eef | ||
|
|
be45519a22 | ||
|
|
ccaa384cd0 | ||
|
|
1c09ce9e09 | ||
|
|
fec00893b1 | ||
|
|
acadebe9fd | ||
|
|
a2ec025230 | ||
|
|
08682075c6 | ||
|
|
e3e790046f | ||
|
|
50319fb1b8 | ||
|
|
c3eb0811e0 | ||
|
|
5423449177 | ||
|
|
f713d43697 | ||
|
|
e896eec89f | ||
|
|
25a120ba65 | ||
|
|
3b865fcea8 | ||
|
|
f71ca999c9 | ||
|
|
276c168839 | ||
|
|
92f0b6e551 | ||
|
|
6b7d6ab301 | ||
|
|
6c33168378 | ||
|
|
8a8ac2d9b9 | ||
|
|
a59063bdb2 | ||
|
|
c597b162d9 | ||
|
|
322e6f7dcd | ||
|
|
c92819ca9d | ||
|
|
57d0ae3932 | ||
|
|
321bfa8f1f | ||
|
|
78838a606d | ||
|
|
fd16fb993d | ||
|
|
1841b7441d | ||
|
|
ce8786b9d2 | ||
|
|
82d366364f | ||
|
|
bd8b2962dc | ||
|
|
f2833f2c4e | ||
|
|
a4c0b0f555 | ||
|
|
a7e30075a9 | ||
|
|
861fd42d29 | ||
|
|
ef50fbbff6 | ||
|
|
f429f1922a | ||
|
|
06322dc8a4 | ||
|
|
afe718b621 | ||
|
|
5e4d6bd857 | ||
|
|
e61c50a6a1 | ||
|
|
b1e22ab47b | ||
|
|
986d47b5dd | ||
|
|
8c18e40024 | ||
|
|
f79d0ee3ee | ||
|
|
539fa088d1 | ||
|
|
ab7bb94b39 | ||
|
|
2aedaa9ba4 | ||
|
|
e91b9b8824 | ||
|
|
d695a2041e | ||
|
|
19d3af9056 | ||
|
|
31e9f22072 | ||
|
|
675eb15603 | ||
|
|
5e1f758943 | ||
|
|
a7aef2a595 | ||
|
|
fa2438248e | ||
|
|
6bebdb8e17 | ||
|
|
40fbe9cab0 | ||
|
|
be80d91a5c | ||
|
|
975b829f0a | ||
|
|
0e2212dcaa | ||
|
|
94e74e3d80 | ||
|
|
008a7cbe9f | ||
|
|
8584110e73 | ||
|
|
6d89e6bab6 | ||
|
|
1731febeb1 |
+58
-154
@@ -1,10 +1,25 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
rok8s: fairwinds/rok8s-scripts@14
|
||||
oss-docs: fairwinds/oss-docs@0
|
||||
|
||||
executors:
|
||||
vm:
|
||||
machine:
|
||||
enabled: true
|
||||
|
||||
commands:
|
||||
install_goreleaser_dependencies:
|
||||
description: Installs dependencies for CI scripts
|
||||
steps:
|
||||
- run: apk update
|
||||
# gettext provides envsubst
|
||||
- run: apk add gettext
|
||||
# Register other docker platforms, to build arm64.
|
||||
# This shouldn't be needed, why TBD.
|
||||
- run: docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
|
||||
references:
|
||||
set_environment_variables: &set_environment_variables
|
||||
run:
|
||||
@@ -18,6 +33,7 @@ references:
|
||||
echo 'export GOPROXY=https://proxy.golang.org' >> ${BASH_ENV}
|
||||
echo 'export GO111MODULE=on' >> ${BASH_ENV}
|
||||
echo 'export GOFLAGS=-mod=mod' >> ${BASH_ENV}
|
||||
echo 'export GORELEASER_CURRENT_TAG="${CIRCLE_TAG}"' >> $BASH_ENV
|
||||
|
||||
install_k8s: &install_k8s
|
||||
run:
|
||||
@@ -28,20 +44,20 @@ references:
|
||||
sudo apt-get install -yqq jq git
|
||||
|
||||
echo "Installing KIND"
|
||||
curl -sLO https://github.com/kubernetes-sigs/kind/releases/download/v0.8.1/kind-linux-amd64
|
||||
curl -sLO https://github.com/kubernetes-sigs/kind/releases/download/v0.19.0/kind-linux-amd64
|
||||
chmod 0755 kind-linux-amd64
|
||||
sudo mv kind-linux-amd64 /usr/local/bin/kind
|
||||
kind version
|
||||
|
||||
echo "Installing Kubectl"
|
||||
curl -sLO https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl
|
||||
curl -sLO https://storage.googleapis.com/kubernetes-release/release/v1.25.10/bin/linux/amd64/kubectl
|
||||
chmod 0755 kubectl
|
||||
sudo mv kubectl /usr/local/bin/
|
||||
kubectl version --client
|
||||
|
||||
|
||||
echo "Creating Kubernetes Cluster with Kind"
|
||||
kind create cluster --wait=90s --image kindest/node:v1.15.11
|
||||
kind create cluster --wait=90s --image kindest/node:v1.25.9@sha256:c08d6c52820aa42e533b70bce0c2901183326d86dcdcbedecc9343681db45161
|
||||
docker ps -a
|
||||
|
||||
kubectl version
|
||||
@@ -52,18 +68,17 @@ references:
|
||||
./get_helm.sh
|
||||
|
||||
echo "Installing cert-manager"
|
||||
kubectl create namespace cert-manager
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo update
|
||||
echo "Helm install"
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version 0.16.1 --set "installCRDs=true" --wait
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.12.1 --set "installCRDs=true" --wait --create-namespace
|
||||
echo "Install cert-manager successful"
|
||||
|
||||
test_binary_dashboard: &test_binary_dashboard
|
||||
run:
|
||||
name: Test Dashboard
|
||||
command: |
|
||||
go run main.go dashboard --port 3000 --audit-path ./examples &
|
||||
go run main.go dashboard --port 3000 --audit-path ./pkg/config/examples &
|
||||
sleep 30
|
||||
curl -f http://localhost:3000 > /dev/null
|
||||
curl -f http://localhost:3000/health > /dev/null
|
||||
@@ -82,196 +97,85 @@ references:
|
||||
echo "Skipping Kubernetes tests for forked PR"
|
||||
fi
|
||||
|
||||
# Release scripts
|
||||
install_goreleaser: &install_goreleaser
|
||||
install_vault_alpine: &install_vault_alpine
|
||||
run:
|
||||
name: Install GoReleaser
|
||||
name: install hashicorp vault
|
||||
command: |
|
||||
curl -fsSLo goreleaser.deb https://github.com/goreleaser/goreleaser/releases/download/v0.174.2/goreleaser_amd64.deb
|
||||
echo "bad33997ea9977a84196bdca1d5993fada909cd81c3e88d52bd297666bea61a4 goreleaser.deb" | sha256sum -c -
|
||||
sudo dpkg -i goreleaser.deb
|
||||
rm goreleaser.deb
|
||||
|
||||
docker_build: &docker_build
|
||||
run:
|
||||
name: Docker login and build
|
||||
command: |
|
||||
docker-pull -f .circleci/build.config
|
||||
docker-build -f .circleci/build.config
|
||||
|
||||
docker_build_and_push: &docker_build_and_push
|
||||
run:
|
||||
name: Docker login, build, and push
|
||||
command: |
|
||||
docker-pull -f .circleci/build.config
|
||||
docker-build -f .circleci/build.config
|
||||
docker login quay.io -u="${fairwinds_quay_user}" -p="${fairwinds_quay_token}"
|
||||
docker-push -f .circleci/build.config
|
||||
|
||||
release_deploy_configs: &release_deploy_configs
|
||||
run:
|
||||
name: Release deploy configs
|
||||
command: |
|
||||
upload_url=$(curl --silent https://api.github.com/repos/FairwindsOps/polaris/releases/latest | grep upload_url)
|
||||
upload_url=$(echo $upload_url | sed -e 's/.*\(https.*\){.*$/\1/')
|
||||
curl -X POST "$upload_url?name=dashboard.yaml" --data-binary "@./deploy/dashboard.yaml" -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/x-yaml"
|
||||
curl -X POST "$upload_url?name=webhook.yaml" --data-binary "@./deploy/webhook.yaml" -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/x-yaml"
|
||||
|
||||
apk --update add curl yq
|
||||
cd /tmp
|
||||
curl -LO https://releases.hashicorp.com/vault/1.13.2/vault_1.13.2_linux_amd64.zip
|
||||
sha256sum vault_1.13.2_linux_amd64.zip | grep f7930279de8381de7c532164b4a4408895d9606c0d24e2e9d2f9acb5dfe99b3c
|
||||
unzip vault_1.13.2_linux_amd64.zip
|
||||
mv vault /usr/bin/vault
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: quay.io/reactiveops/ci-images:v11.0-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- *set_environment_variables
|
||||
- *docker_build
|
||||
|
||||
push:
|
||||
docker:
|
||||
- image: quay.io/reactiveops/ci-images:v11.0-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- *set_environment_variables
|
||||
- *docker_build_and_push
|
||||
|
||||
test_k8s:
|
||||
working_directory: ~/polaris
|
||||
resource_class: medium
|
||||
executor: vm
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- *install_k8s
|
||||
- *test_k8s
|
||||
|
||||
test:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
docker:
|
||||
- image: circleci/golang:1.16
|
||||
- image: cimg/go:1.26
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- run: go get -u golang.org/x/lint/golint
|
||||
- run: go list ./... | grep -v vendor | xargs golint -set_exit_status
|
||||
- run: go list ./... | grep -v vendor | xargs go vet
|
||||
- run: go vet ./...
|
||||
- run: go test ./... -coverprofile=coverage.txt -covermode=count
|
||||
- run: go run main.go audit --audit-path ./deploy --set-exit-code-below-score 100 --set-exit-code-on-danger
|
||||
- *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:
|
||||
build_and_push:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
resource_class: large
|
||||
shell: /bin/bash
|
||||
docker:
|
||||
- image: circleci/golang:1.16
|
||||
# The goreleaser image tag determins the version of Go.
|
||||
# Manually check goreleaser images for their version of Go.
|
||||
# Ref: https://hub.docker.com/r/goreleaser/goreleaser/tags
|
||||
- image: goreleaser/goreleaser:v2.14.2
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- *install_vault_alpine
|
||||
- rok8s/get_vault_env:
|
||||
vault_path: repo/global/env
|
||||
- rok8s/get_vault_env:
|
||||
vault_path: repo/polaris/env
|
||||
- *set_environment_variables
|
||||
- *install_goreleaser
|
||||
- run: go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
- run: git checkout -- . # FIXME: the go get makes the directory dirty
|
||||
- run: packr2
|
||||
- run: goreleaser
|
||||
- *release_deploy_configs
|
||||
|
||||
release_images:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
docker:
|
||||
- image: quay.io/reactiveops/ci-images:v11.0-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- *set_environment_variables
|
||||
- *docker_build_and_push
|
||||
|
||||
publish_docs:
|
||||
docker:
|
||||
- image: cimg/node:15.5.1
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build Docs Site
|
||||
command: |
|
||||
set -e
|
||||
cd ./docs
|
||||
npm install
|
||||
npm run check-links
|
||||
npm run build
|
||||
- run:
|
||||
name: Install AWS CLI
|
||||
command: |
|
||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||
unzip awscliv2.zip
|
||||
sudo ./aws/install
|
||||
- run:
|
||||
name: Publish Docs Site to S3
|
||||
command: |
|
||||
cd ./dist
|
||||
aws s3 sync ./ s3://polaris.docs.fairwinds.com --delete
|
||||
- run: docker login quay.io -u="${FAIRWINDS_QUAY_USER}" -p="${FAIRWINDS_QUAY_TOKEN}"
|
||||
- install_goreleaser_dependencies
|
||||
- run: scripts/goreleaser.sh
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
build:
|
||||
jobs:
|
||||
- test
|
||||
- build:
|
||||
requires:
|
||||
- test
|
||||
- push:
|
||||
context: org-global
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
- insights:
|
||||
requires:
|
||||
- push
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
- test_k8s:
|
||||
requires:
|
||||
- push
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
|
||||
release:
|
||||
jobs:
|
||||
- release_binary:
|
||||
- test:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build_and_push:
|
||||
context: org-global
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
- release_images:
|
||||
- test_k8s:
|
||||
requires:
|
||||
- release_binary
|
||||
context: org-global
|
||||
- build_and_push
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
- publish_docs:
|
||||
- oss-docs/publish-docs:
|
||||
repository: polaris
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: [bug, triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please fill the form below.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: What happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What did you expect to happen?
|
||||
description: What is the expected or desired behavior?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproducible
|
||||
attributes:
|
||||
label: How can we reproduce this?
|
||||
description: Please share the steps that we can take to reproduce this. Also include any relevant configuration.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: The version of the tool that you are using. If a helm chart, please share the name of the chart.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: search
|
||||
attributes:
|
||||
label: Search
|
||||
options:
|
||||
- label: I did search for other open and closed issues before opening this.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow the CODE_OF_CONDUCT in this repository.
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- type: textarea
|
||||
id: ctx
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Anything else you would like to add
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
name: Bug report
|
||||
about: Let us know about an unexpected error, a crash, or an incorrect behavior.
|
||||
|
||||
---
|
||||
### Installation Process
|
||||
<!---
|
||||
Did you install polaris on your local machine, or in a kubernetes cluster? Did you use kubectl, helm, brew, a GitHub release, or the source code? Let us know below.
|
||||
-->
|
||||
|
||||
### Polaris Version
|
||||
<!---
|
||||
If you are using polaris locally, run `polaris --version` to show the version, and paste the result between the ``` marks below.
|
||||
|
||||
If you are not running the latest version of Polaris, please try upgrading because your issue may have already been fixed.
|
||||
-->
|
||||
|
||||
```
|
||||
...
|
||||
```
|
||||
|
||||
### Expected Behavior
|
||||
<!--
|
||||
What should have happened?
|
||||
-->
|
||||
|
||||
### Actual Behavior
|
||||
<!--
|
||||
What actually happened?
|
||||
-->
|
||||
|
||||
### Steps to Reproduce
|
||||
<!--
|
||||
Please list the full steps required to reproduce the issue, for example:
|
||||
1. `polaris --audit --output-format score`
|
||||
-->
|
||||
|
||||
### Additional Context
|
||||
<!--
|
||||
Are there anything atypical about your situation that we should know?
|
||||
-->
|
||||
|
||||
### References
|
||||
<!--
|
||||
Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example:
|
||||
|
||||
- #6017
|
||||
|
||||
-->
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: [triage, enhancement]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
name: Other
|
||||
about: For misc. tasks like research or continued conversation
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: [triage]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
|
||||
@@ -17,4 +17,4 @@ mkdir polaris
|
||||
tar -xzf $TARGET_FILE -C polaris
|
||||
rm $TARGET_FILE
|
||||
echo "polaris" >> $GITHUB_PATH
|
||||
echo "::set-output name=version::$INPUT_VERSION"
|
||||
echo "version=$INPUT_VERSION" >> $GITHUB_OUTPUT
|
||||
+18
-42
@@ -1,44 +1,20 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
time: "11:00"
|
||||
ignore:
|
||||
- dependency-name: cloud.google.com/go
|
||||
versions:
|
||||
- ">= 0.57.a, < 0.58"
|
||||
- dependency-name: github.com/go-logr/logr
|
||||
versions:
|
||||
- ">= 0.2.a, < 0.3"
|
||||
- dependency-name: github.com/go-logr/zapr
|
||||
versions:
|
||||
- ">= 0.2.a, < 0.3"
|
||||
- dependency-name: github.com/googleapis/gnostic
|
||||
versions:
|
||||
- ">= 0.4.a, < 0.5"
|
||||
- dependency-name: github.com/googleapis/gnostic
|
||||
versions:
|
||||
- ">= 0.5.a, < 0.6"
|
||||
- dependency-name: github.com/qri-io/jsonschema
|
||||
versions:
|
||||
- ">= 0.2.a, < 0.3"
|
||||
- dependency-name: k8s.io/api
|
||||
versions:
|
||||
- ">= 0.19.a, < 0.20"
|
||||
- dependency-name: k8s.io/apimachinery
|
||||
versions:
|
||||
- ">= 0.19.a, < 0.20"
|
||||
- dependency-name: k8s.io/client-go
|
||||
versions:
|
||||
- ">= 0.19.a, < 0.20"
|
||||
- dependency-name: sigs.k8s.io/controller-runtime
|
||||
versions:
|
||||
- ">= 0.5.a, < 0.6"
|
||||
- dependency-name: sigs.k8s.io/controller-runtime
|
||||
versions:
|
||||
- ">= 0.8.a, < 0.9"
|
||||
- dependency-name: k8s.io/apimachinery
|
||||
versions:
|
||||
- 0.20.4
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/docs"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 0
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
This PR fixes #
|
||||
|
||||
## Checklist
|
||||
* [ ] I have signed the CLA
|
||||
* [ ] I have updated/added any relevant documentation
|
||||
|
||||
## Description
|
||||
### What's the goal of this PR?
|
||||
|
||||
### What changes did you make?
|
||||
|
||||
### What alternative solution should we consider, if any?
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
daysUntilStale: 30
|
||||
daysUntilClose: 7
|
||||
onlyLabels: []
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
|
||||
exemptProjects: false
|
||||
exemptMilestones: true
|
||||
exemptAssignees: false
|
||||
staleLabel: stale
|
||||
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
limitPerRun: 30
|
||||
@@ -0,0 +1,18 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '32 1 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10.2.0
|
||||
with:
|
||||
exempt-issue-labels: pinned
|
||||
stale-pr-label: stale
|
||||
stale-issue-label: stale
|
||||
@@ -7,7 +7,7 @@ jobs:
|
||||
build-int:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- name: Setup polaris
|
||||
uses: ./.github/actions/setup-polaris
|
||||
with:
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
build-ext:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- name: Setup polaris
|
||||
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
|
||||
with:
|
||||
|
||||
+6
-2
@@ -1,4 +1,8 @@
|
||||
# Binaries for programs and plugins
|
||||
# goreleaser is run via a wrapper that creates .goreleaser.yml from .goreleaser.yml.envsubst
|
||||
.goreleaser.yml
|
||||
# dist
|
||||
# # Binaries for programs and plugins
|
||||
.go-version
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
@@ -19,7 +23,6 @@ Tiltfile
|
||||
main
|
||||
.DS_Store
|
||||
|
||||
*-packr.go
|
||||
dist
|
||||
.vscode
|
||||
|
||||
@@ -27,3 +30,4 @@ dist
|
||||
|
||||
node_modules
|
||||
/dist
|
||||
docs/README.md
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
builds:
|
||||
- id: polaris
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 6
|
||||
- 7
|
||||
archives:
|
||||
- id: polaris
|
||||
builds: ["polaris"]
|
||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
|
||||
brews:
|
||||
- name: polaris
|
||||
tap:
|
||||
owner: FairwindsOps
|
||||
name: homebrew-tap
|
||||
folder: Formula
|
||||
description: Open Source Best Practices for Kubernetes
|
||||
test: |
|
||||
system "#{bin}/polaris version"
|
||||
@@ -0,0 +1,118 @@
|
||||
version: 2
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
env:
|
||||
- GOBIN={{ .Env.TMPDIR }}/go-bin
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- id: polaris
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}} -X main.Commit={{.Commit}} -s -w
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 6
|
||||
- 7
|
||||
# Go does not support windows/arm (32-bit); only windows/arm64 is supported.
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
archives:
|
||||
- id: polaris
|
||||
builds: ["polaris"]
|
||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
|
||||
signs:
|
||||
- cmd: cosign
|
||||
args: ["sign-blob", "--key=hashivault://cosign", "-output-signature=${signature}", "${artifact}"]
|
||||
artifacts: checksum
|
||||
release:
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
disable: ${skip_release}
|
||||
prerelease: auto
|
||||
footer: |
|
||||
You can verify the signature of the checksums.txt file using [cosign](https://github.com/sigstore/cosign).
|
||||
|
||||
```
|
||||
cosign verify-blob checksums.txt --signature=checksums.txt.sig --key https://artifacts.fairwinds.com/cosign.pub
|
||||
```
|
||||
brews:
|
||||
- name: polaris
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_upload: ${skip_release}
|
||||
repository:
|
||||
owner: FairwindsOps
|
||||
name: homebrew-tap
|
||||
directory: Formula
|
||||
description: Open Source Best Practices for Kubernetes
|
||||
url_template: "https://github.com/FairwindsOps/polaris/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
test: |
|
||||
system "#{bin}/polaris version"
|
||||
dockers:
|
||||
# There are multiple images to match the `--platform` docker build flag with
|
||||
# combinations of `GOOS`, `GOARCH`, and `GOARM`
|
||||
- image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
use: buildx
|
||||
goarch: arm64
|
||||
goos: linux
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
docker_manifests:
|
||||
# Create DOcker manifests that make multiple architectures available within a tag,
|
||||
# and provide partial-version tags like 2, and 2.2.
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .FullCommit }}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Env.feature_docker_tag }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_feature_docker_tags}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:latest
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Tag }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Major }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Major }}.{{ .Minor }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
@@ -0,0 +1,24 @@
|
||||
header:
|
||||
license:
|
||||
spdx-id: Apache-2.0
|
||||
copyright-owner: 'FairwindsOps, Inc.'
|
||||
content: |
|
||||
// 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.
|
||||
paths:
|
||||
- '**/*.go'
|
||||
comment: on-failure
|
||||
dependency:
|
||||
files:
|
||||
- go.mod
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
* @rbren @makoscafee
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
* @sudermanjr @jdesouza @vitorvezani
|
||||
|
||||
+13
-17
@@ -1,27 +1,23 @@
|
||||
FROM golang:1.16 AS build-env
|
||||
WORKDIR /go/src/github.com/fairwindsops/polaris/
|
||||
FROM alpine:3.23
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://proxy.golang.org
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.vendor="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.title="polaris" \
|
||||
org.opencontainers.image.description="Polaris is a cli tool to help discover deprecated apiVersions in Kubernetes" \
|
||||
org.opencontainers.image.documentation="https://polaris.docs.fairwinds.com/" \
|
||||
org.opencontainers.image.source="https://github.com/FairwindsOps/polaris" \
|
||||
org.opencontainers.image.url="https://github.com/FairwindsOps/polaris" \
|
||||
org.opencontainers.image.licenses="Apache License 2.0"
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN go mod download
|
||||
RUN go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
|
||||
COPY . .
|
||||
RUN packr2 build -a -o polaris *.go
|
||||
|
||||
FROM alpine:3.14
|
||||
WORKDIR /usr/local/bin
|
||||
# Install ca-certs
|
||||
RUN apk --no-cache add ca-certificates
|
||||
# Upgrade only packages with known HIGH/CRITICAL issues (not a full apk upgrade).
|
||||
RUN apk --no-cache add --upgrade libcrypto3 libssl3 zlib
|
||||
|
||||
RUN addgroup -S polaris && adduser -u 1200 -S polaris -G polaris
|
||||
USER 1200
|
||||
COPY --from=build-env /go/src/github.com/fairwindsops/polaris/polaris .
|
||||
COPY polaris .
|
||||
|
||||
WORKDIR /opt/app
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div align="center" class="no-border">
|
||||
<img src="https://polaris.docs.fairwinds.com/img/polaris-logo.png" alt="Polaris Logo">
|
||||
<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=4.2.0&color=239922">
|
||||
<h3>Polaris is an open source policy engine for Kubernetes</h3>
|
||||
<a href="https://github.com/FairwindsOps/polaris/releases">
|
||||
<img src="https://img.shields.io/github/v/release/FairwindsOps/polaris">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/FairwindsOps/polaris">
|
||||
<img src="https://goreportcard.com/badge/github.com/FairwindsOps/polaris">
|
||||
@@ -16,15 +16,12 @@
|
||||
</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 is an open source policy engine for Kubernetes that validates and remediates resource configuration. It includes 30+ built in configuration policies, as well as the ability to build custom policies with JSON Schema. When run on the command line or as a mutating webhook, Polaris can automatically remediate issues based on policy criteria.
|
||||
|
||||
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.
|
||||
* As an [admission controller](https://polaris.docs.fairwinds.com/admission-controller), so you can automatically reject workloads that don't adhere to your organization's policies.
|
||||
* As a [command-line tool](https://polaris.docs.fairwinds.com/infrastructure-as-code), so you can test local YAML files, e.g. as part of a CI/CD process.
|
||||
|
||||
* As a [dashboard](https://polaris.docs.fairwinds.com/dashboard) - Validate Kubernetes resources against policy-as-code.
|
||||
* As an [admission controller](https://polaris.docs.fairwinds.com/admission-controller) - Automatically reject or modify workloads that don't adhere to your organization's policies.
|
||||
* As a [command-line tool](https://polaris.docs.fairwinds.com/infrastructure-as-code) - Incorporate policy-as-code into the CI/CD process to test local YAML files.
|
||||
<p align="center">
|
||||
<img src="https://polaris.docs.fairwinds.com/img/architecture.svg" alt="Polaris Architecture" width="550"/>
|
||||
</p>
|
||||
@@ -37,13 +34,7 @@ Check out the [documentation at docs.fairwinds.com](https://polaris.docs.fairwin
|
||||
|
||||
The goal of the Fairwinds Community is to exchange ideas, influence the open source roadmap,
|
||||
and network with fellow Kubernetes users.
|
||||
[Chat with us on Slack](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-e3c6vj4l-3lIH6dvKqzWII5fSSFDi1g)
|
||||
or
|
||||
[join the user group](https://www.fairwinds.com/open-source-software-user-group) to get involved!
|
||||
|
||||
<a href="https://www.fairwinds.com/t-shirt-offer?utm_source=polaris&utm_medium=polaris&utm_campaign=polaris-tshirt">
|
||||
<img src="https://www.fairwinds.com/hubfs/Doc_Banners/Fairwinds_OSS_User_Group_740x125_v6.png" alt="Love Fairwinds Open Source? Share your business email and job title and we'll send you a free Fairwinds t-shirt!" />
|
||||
</a>
|
||||
[Chat with us on Slack](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-2na8gtwb4-DGQ4qgmQbczQyB2NlFlYQQ)
|
||||
|
||||
## Other Projects from Fairwinds
|
||||
|
||||
@@ -58,9 +49,5 @@ Or [check out the full list](https://www.fairwinds.com/open-source-software?utm_
|
||||
If you're interested in running Polaris in multiple clusters,
|
||||
tracking the results over time, integrating with Slack, Datadog, and Jira,
|
||||
or unlocking other functionality, check out
|
||||
[Fairwinds Insights](https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=polaris&utm_campaign=polaris),
|
||||
[Fairwinds Insights](https://fairwinds.com/insights),
|
||||
a platform for auditing and enforcing policy in Kubernetes clusters.
|
||||
|
||||
<a href="https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=ad&utm_campaign=polarisad">
|
||||
<img src="https://www.fairwinds.com/hubfs/Doc_Banners/Fairwinds_Polaris_Ad.png" alt="Fairwinds Insights" />
|
||||
</a>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
successMessage: Priority class has been set
|
||||
failureMessage: Priority class should be set
|
||||
category: Security
|
||||
target: Pod
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required:
|
||||
- priorityClassName
|
||||
+81
-28
@@ -17,13 +17,15 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
cfg "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -31,16 +33,23 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var setExitCode bool
|
||||
var onlyShowFailedTests bool
|
||||
var minScore int
|
||||
var auditOutputURL string
|
||||
var auditOutputFile string
|
||||
var auditOutputFormat string
|
||||
var resourceToAudit string
|
||||
var useColor bool
|
||||
var helmChart string
|
||||
var helmValues string
|
||||
var (
|
||||
setExitCode bool
|
||||
onlyShowFailedTests bool
|
||||
minScore int
|
||||
auditOutputURL string
|
||||
auditOutputFile string
|
||||
auditOutputFormat string
|
||||
resourceToAudit string
|
||||
useColor bool
|
||||
helmChart string
|
||||
helmValues []string
|
||||
helmSkipTests bool
|
||||
checks []string
|
||||
auditNamespace string
|
||||
severityLevel string
|
||||
skipSslValidation bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(auditCmd)
|
||||
@@ -55,7 +64,12 @@ func init() {
|
||||
auditCmd.PersistentFlags().StringVar(&displayName, "display-name", "", "An optional identifier for the audit.")
|
||||
auditCmd.PersistentFlags().StringVar(&resourceToAudit, "resource", "", "Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.")
|
||||
auditCmd.PersistentFlags().StringVar(&helmChart, "helm-chart", "", "Will fill out Helm template")
|
||||
auditCmd.PersistentFlags().StringVar(&helmValues, "helm-values", "", "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&helmValues, "helm-values", []string{}, "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().BoolVar(&helmSkipTests, "helm-skip-tests", false, "Corresponds to --skip-tests of helm template")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&checks, "checks", []string{}, "Optional flag to specify specific checks to check")
|
||||
auditCmd.PersistentFlags().StringVar(&auditNamespace, "namespace", "", "Namespace to audit. Only applies to in-cluster audits")
|
||||
auditCmd.PersistentFlags().StringVar(&severityLevel, "severity", "", "Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)")
|
||||
auditCmd.PersistentFlags().BoolVar(&skipSslValidation, "skip-ssl-validation", false, "Skip https certificate verification")
|
||||
}
|
||||
|
||||
var auditCmd = &cobra.Command{
|
||||
@@ -66,28 +80,49 @@ var auditCmd = &cobra.Command{
|
||||
if displayName != "" {
|
||||
config.DisplayName = displayName
|
||||
}
|
||||
if len(checks) > 0 {
|
||||
targetChecks := make(map[string]bool)
|
||||
for _, check := range checks {
|
||||
targetChecks[check] = true
|
||||
}
|
||||
for key := range config.Checks {
|
||||
if isTarget := targetChecks[key]; !isTarget {
|
||||
config.Checks[key] = cfg.SeverityIgnore
|
||||
}
|
||||
}
|
||||
}
|
||||
if auditNamespace != "" {
|
||||
if helmChart != "" {
|
||||
logrus.Warn("--namespace and --helm-chart are mutually exclusive. --namespace will be ignored.")
|
||||
}
|
||||
if auditPath != "" {
|
||||
logrus.Warn("--namespace and --audit-path are mutually exclusive. --namespace will be ignored.")
|
||||
}
|
||||
config.Namespace = auditNamespace
|
||||
}
|
||||
if helmChart != "" {
|
||||
var err error
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues)
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues, helmSkipTests)
|
||||
if err != nil {
|
||||
logrus.Infof("Couldn't process helm chart: %v", err)
|
||||
logrus.Errorf("Couldn't process helm chart: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
k, err := kube.CreateResourceProvider(context.TODO(), auditPath, resourceToAudit, config)
|
||||
ctx := context.TODO()
|
||||
k, err := kube.CreateResourceProvider(ctx, auditPath, resourceToAudit, config)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes resources %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
auditData, err := validator.RunAudit(config, k)
|
||||
auditData, err := validator.RunAudit(context.Background(), config, k)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while running audit on resources: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests)
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests, severityLevel)
|
||||
|
||||
summary := auditData.GetSummary()
|
||||
score := summary.GetScore()
|
||||
@@ -102,7 +137,7 @@ var auditCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// ProcessHelmTemplates turns helm into yaml to be processed by Polaris or the other tools.
|
||||
func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
func ProcessHelmTemplates(helmChart string, helmValues []string, helmSkipTests bool) (string, error) {
|
||||
cmd := exec.Command("helm", "dependency", "update", helmChart)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -110,18 +145,22 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "*")
|
||||
dir, err := os.MkdirTemp("", "*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := []string{
|
||||
"template", helmChart,
|
||||
helmChart,
|
||||
"--generate-name",
|
||||
"--output-dir",
|
||||
dir,
|
||||
}
|
||||
if helmValues != "" {
|
||||
params = append(params, "--values", helmValues)
|
||||
for _, v := range helmValues {
|
||||
params = append(params, "--values", v)
|
||||
}
|
||||
|
||||
if helmSkipTests {
|
||||
params = append(params, "--skip-tests")
|
||||
}
|
||||
|
||||
cmd = exec.Command("helm", params...)
|
||||
@@ -134,14 +173,24 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool) {
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool, severityLevel string) {
|
||||
if onlyShowFailedTests {
|
||||
auditData = auditData.RemoveSuccessfulResults()
|
||||
}
|
||||
|
||||
if severityLevel != "" {
|
||||
switch severityLevel {
|
||||
case "danger":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityDanger)
|
||||
case "warning":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityWarning)
|
||||
}
|
||||
}
|
||||
|
||||
var outputBytes []byte
|
||||
var err error
|
||||
if outputFormat == "score" {
|
||||
outputBytes = []byte(fmt.Sprintf("%d\n", auditData.GetSummary().GetScore()))
|
||||
outputBytes = fmt.Appendf(nil, "%d\n", auditData.GetSummary().GetScore())
|
||||
} else if outputFormat == "yaml" {
|
||||
var jsonBytes []byte
|
||||
jsonBytes, err = json.Marshal(auditData)
|
||||
@@ -175,9 +224,13 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
} else {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
client := &http.Client{}
|
||||
if skipSslValidation {
|
||||
transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||
client = &http.Client{Transport: transport}
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error making request for output: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -185,7 +238,7 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("Error reading response: %v", err)
|
||||
@@ -196,7 +249,7 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
}
|
||||
|
||||
if outputFile != "" {
|
||||
err := ioutil.WriteFile(outputFile, []byte(outputBytes), 0644)
|
||||
err := os.WriteFile(outputFile, outputBytes, 0644)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error writing output to file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -54,7 +55,11 @@ var dashboardCmd = &cobra.Command{
|
||||
auditData := validator.ReadAuditFromFile(loadAuditFile)
|
||||
auditDataPtr = &auditData
|
||||
}
|
||||
router := dashboard.GetRouter(config, auditPath, serverPort, basePath, auditDataPtr)
|
||||
router, err := dashboard.GetRouter(context.Background(), config, auditPath, serverPort, basePath, auditDataPtr)
|
||||
if err != nil {
|
||||
logrus.Fatalf("error creating router: %v", err)
|
||||
}
|
||||
|
||||
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2020 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 cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/fix"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
filesPath string
|
||||
checksToFix []string
|
||||
isTemplate bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(fixCommand)
|
||||
fixCommand.PersistentFlags().StringVar(&filesPath, "files-path", "", "mutate and fix one or more YAML files in a specified folder")
|
||||
fixCommand.PersistentFlags().BoolVar(&isTemplate, "template", false, "set to true when modifyng a YAML template, like a Helm chart (experimental)")
|
||||
fixCommand.PersistentFlags().StringSliceVar(&checksToFix, "checks", []string{}, "Optional flag to specify specific checks to fix eg. checks=hostIPCSet,hostPIDSet and checks=all applies fix to all defined checks mutations")
|
||||
}
|
||||
|
||||
var fixCommand = &cobra.Command{
|
||||
Use: "fix",
|
||||
Short: "Fix Infrastructure as code files.",
|
||||
Long: `Fix Infrastructure as code files.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logrus.Debug("Setting up controller manager")
|
||||
|
||||
err := fix.Execute(context.Background(), config, filesPath, isTemplate, checksToFix...)
|
||||
if err != nil {
|
||||
if errors.Is(err, fix.ErrFilesPathRequired) {
|
||||
logrus.Error("Please specify a files-path flag")
|
||||
cmd.Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
+25
-11
@@ -15,20 +15,27 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var configPath string
|
||||
var disallowExemptions, disallowConfigExemptions, disallowAnnotationExemptions bool
|
||||
var logLevel string
|
||||
var auditPath string
|
||||
var displayName string
|
||||
var (
|
||||
mergeConfig bool
|
||||
configPath string
|
||||
disallowExemptions bool
|
||||
disallowConfigExemptions bool
|
||||
disallowAnnotationExemptions bool
|
||||
fixChecks bool
|
||||
logLevel string
|
||||
auditPath string
|
||||
displayName string
|
||||
kubeContext string
|
||||
insightsHost string
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
@@ -36,13 +43,14 @@ var (
|
||||
|
||||
func init() {
|
||||
// Flags
|
||||
rootCmd.PersistentFlags().BoolVarP(&mergeConfig, "merge-config", "m", false, "If true, custom configuration will be merged with default configuration instead of replacing it.")
|
||||
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Location of Polaris configuration file.")
|
||||
rootCmd.PersistentFlags().StringVarP(&kubeContext, "context", "x", "", "Set the kube context.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowExemptions, "disallow-exemptions", "", false, "Disallow any configured exemption.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowConfigExemptions, "disallow-config-exemptions", "", false, "Disallow exemptions set within the configuration file.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowAnnotationExemptions, "disallow-annotation-exemptions", "", false, "Disallow any exemption defined as a controller annotation.")
|
||||
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "", logrus.InfoLevel.String(), "Logrus log level.")
|
||||
flag.Parse()
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "", logrus.InfoLevel.String(), "Logrus log level to be output (trace, debug, info, warning, error, fatal, panic).")
|
||||
rootCmd.PersistentFlags().StringVar(&insightsHost, "insights-host", "https://insights.fairwinds.com", "Fairwinds Insights host URL")
|
||||
}
|
||||
|
||||
var config conf.Configuration
|
||||
@@ -59,7 +67,7 @@ var rootCmd = &cobra.Command{
|
||||
logrus.SetLevel(parsedLevel)
|
||||
}
|
||||
|
||||
config, err = conf.ParseFile(configPath)
|
||||
config, err = conf.MergeConfigAndParseFile(configPath, mergeConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing config at %s: %v", configPath, err)
|
||||
os.Exit(1)
|
||||
@@ -68,6 +76,7 @@ var rootCmd = &cobra.Command{
|
||||
config.DisallowExemptions = disallowExemptions
|
||||
config.DisallowConfigExemptions = disallowConfigExemptions
|
||||
config.DisallowAnnotationExemptions = disallowAnnotationExemptions
|
||||
config.KubeContext = kubeContext
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logrus.Error("You must specify a sub-command.")
|
||||
@@ -77,6 +86,11 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
os.Exit(1)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if !strings.HasPrefix(cmd.Use, "audit") {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Execute the stuff
|
||||
|
||||
@@ -31,4 +31,7 @@ var versionCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Polaris version:" + version)
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
+26
-12
@@ -15,8 +15,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -25,15 +25,22 @@ import (
|
||||
k8sConfig "sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
)
|
||||
|
||||
var webhookPort int
|
||||
var disableWebhookConfigInstaller bool
|
||||
var enableMutations bool
|
||||
var enableValidations bool
|
||||
var certDir string
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(webhookCmd)
|
||||
webhookCmd.PersistentFlags().IntVarP(&webhookPort, "port", "p", 9876, "Port for the dashboard webserver.")
|
||||
webhookCmd.PersistentFlags().BoolVar(&disableWebhookConfigInstaller, "disable-webhook-config-installer", false, "disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.")
|
||||
webhookCmd.PersistentFlags().BoolVar(&disableWebhookConfigInstaller, "disable-webhook-config-installer", false, "Disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.")
|
||||
webhookCmd.PersistentFlags().BoolVar(&enableValidations, "validate", true, "Enable the validating webhook to reject workloads with issues")
|
||||
webhookCmd.PersistentFlags().BoolVar(&enableMutations, "mutate", false, "Enable the mutating webhook to modify workloads with issues")
|
||||
webhookCmd.PersistentFlags().StringVar(&certDir, "cert-dir", "/opt/cert", "Directory in which tls certificate is located")
|
||||
}
|
||||
|
||||
var webhookCmd = &cobra.Command{
|
||||
@@ -44,27 +51,34 @@ var webhookCmd = &cobra.Command{
|
||||
logrus.Debug("Setting up controller manager")
|
||||
|
||||
mgr, err := manager.New(k8sConfig.GetConfigOrDie(), manager.Options{
|
||||
CertDir: "/opt/cert",
|
||||
Port: webhookPort,
|
||||
WebhookServer: webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
CertName: "tls.crt",
|
||||
KeyName: "tls.key",
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to set up overall controller manager: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = os.Stat("/opt/cert/tls.crt")
|
||||
_, err = os.Stat(certDir + "/tls.crt")
|
||||
if os.IsNotExist(err) {
|
||||
time.Sleep(time.Second * 10)
|
||||
panic("Cert does not exist")
|
||||
}
|
||||
server := mgr.GetWebhookServer()
|
||||
server.CertName = "tls.crt"
|
||||
server.KeyName = "tls.key"
|
||||
|
||||
// Iterate all the configurations supported controllers to scan and register them for webhooks
|
||||
// Should only register controllers that are configured to be scanned
|
||||
fwebhook.NewWebhook(mgr, fwebhook.Validator{Config: config, Client: mgr.GetClient()})
|
||||
if !enableMutations && !enableValidations {
|
||||
logrus.Errorf("One of --mutate or --validate must be set to true")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if enableValidations {
|
||||
fwebhook.NewValidateWebhook(mgr, config)
|
||||
}
|
||||
if enableMutations {
|
||||
fwebhook.NewMutateWebhook(context.Background(), mgr, config)
|
||||
}
|
||||
logrus.Infof("Polaris webhook server listening on port %d", webhookPort)
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
logrus.Errorf("Error starting manager: %v", err)
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
# Source: polaris/templates/0-namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: polaris
|
||||
---
|
||||
# Source: polaris/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
rules:
|
||||
# required by controller-runtime code doing a cluster wide lookup
|
||||
# when it seems namespace would suffice
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- 'nodes'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- 'monitoring.coreos.com'
|
||||
resources:
|
||||
- 'prometheuses'
|
||||
- 'alertmanagers'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: polaris-view
|
||||
labels:
|
||||
app: polaris
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: view
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: polaris
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
---
|
||||
# Source: polaris/templates/dashboard.service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: polaris-dashboard
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
annotations:
|
||||
spec:
|
||||
ports:
|
||||
- name: http-dashboard
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: polaris
|
||||
component: dashboard
|
||||
type: ClusterIP
|
||||
---
|
||||
# Source: polaris/templates/dashboard.deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: polaris-dashboard
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
component: dashboard
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: polaris
|
||||
component: dashboard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: polaris
|
||||
component: dashboard
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- polaris
|
||||
- dashboard
|
||||
- --port
|
||||
- "8080"
|
||||
image: 'quay.io/fairwinds/polaris:4.2'
|
||||
imagePullPolicy: 'Always'
|
||||
name: dashboard
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 20
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 20
|
||||
resources:
|
||||
limits:
|
||||
cpu: 150m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
serviceAccountName: polaris
|
||||
nodeSelector:
|
||||
tolerations:
|
||||
@@ -1,270 +0,0 @@
|
||||
---
|
||||
# Source: polaris/templates/0-namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: polaris
|
||||
---
|
||||
# Source: polaris/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
rules:
|
||||
# required by controller-runtime code doing a cluster wide lookup
|
||||
# when it seems namespace would suffice
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- 'nodes'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- 'monitoring.coreos.com'
|
||||
resources:
|
||||
- 'prometheuses'
|
||||
- 'alertmanagers'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: polaris-view
|
||||
labels:
|
||||
app: polaris
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: view
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: polaris
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: polaris
|
||||
namespace: polaris
|
||||
---
|
||||
# Source: polaris/templates/webhook.service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: polaris-webhook
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
spec:
|
||||
ports:
|
||||
- name: webhook
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: 9876
|
||||
selector:
|
||||
app: polaris
|
||||
component: webhook
|
||||
type: ClusterIP
|
||||
---
|
||||
# Source: polaris/templates/webhook.deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: polaris-webhook
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
component: webhook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: polaris
|
||||
component: webhook
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: polaris
|
||||
component: webhook
|
||||
spec:
|
||||
containers:
|
||||
- name: webhook
|
||||
command:
|
||||
- polaris
|
||||
- webhook
|
||||
image: 'quay.io/fairwinds/polaris:4.2'
|
||||
imagePullPolicy: 'Always'
|
||||
ports:
|
||||
- containerPort: 9876
|
||||
# These are fairly useless readiness/liveness probes for now
|
||||
# Follow this issue for potential improvements:
|
||||
# https://github.com/kubernetes-sigs/controller-runtime/issues/356
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- ps -ef | grep polaris
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- ps -ef | grep polaris
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
volumeMounts:
|
||||
- name: secret
|
||||
mountPath: /opt/cert/
|
||||
readOnly: true
|
||||
- name: cr-logs
|
||||
mountPath: /tmp/
|
||||
readOnly: false
|
||||
serviceAccountName: polaris
|
||||
nodeSelector:
|
||||
tolerations:
|
||||
volumes:
|
||||
- name: secret
|
||||
secret:
|
||||
secretName: polaris
|
||||
- name: cr-logs
|
||||
emptyDir: {}
|
||||
---
|
||||
# Source: polaris/templates/webhook.cert.yaml
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: polaris-cert
|
||||
namespace: polaris
|
||||
labels:
|
||||
app: polaris
|
||||
spec:
|
||||
commonName: polaris-webhook.polaris.svc
|
||||
dnsNames:
|
||||
- polaris-webhook.polaris.svc
|
||||
- polaris-webhook.polaris
|
||||
- polaris-webhook
|
||||
- polaris-webhook.polaris.svc.
|
||||
issuerRef:
|
||||
kind: Issuer
|
||||
name: polaris-selfsigned
|
||||
secretName: polaris
|
||||
---
|
||||
# Source: polaris/templates/webhook.cert.yaml
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: polaris-selfsigned
|
||||
namespace: polaris
|
||||
spec:
|
||||
selfSigned: {}
|
||||
---
|
||||
# Source: polaris/templates/webhook.configuration.yaml
|
||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: polaris-webhook
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: polaris/polaris-cert
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
service:
|
||||
name: polaris-webhook
|
||||
namespace: polaris
|
||||
path: /validate
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: polaris.fairwinds.com
|
||||
namespaceSelector:
|
||||
|
||||
matchExpressions:
|
||||
- key: control-plane
|
||||
operator: DoesNotExist
|
||||
objectSelector:
|
||||
|
||||
{}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
- v1beta2
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
- statefulsets
|
||||
scope: Namespaced
|
||||
- apiGroups:
|
||||
- batch
|
||||
apiVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- jobs
|
||||
- cronjobs
|
||||
scope: Namespaced
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- pods
|
||||
- replicationcontrollers
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: 10
|
||||
@@ -11,6 +11,16 @@ var sf14gv = 32793;
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(sf14g, s);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var gtag = document.createElement('script');
|
||||
gtag.src = "https://www.googletagmanager.com/gtag/js?id=G-ZR5M5SRYKY";
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gtag, s);
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-ZR5M5SRYKY');
|
||||
})();
|
||||
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
@@ -21,9 +31,3 @@ s.parentNode.insertBefore(t,s)}(window,document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '521127644762074');
|
||||
fbq('track', 'PageView');
|
||||
|
||||
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TM95WXQ');
|
||||
|
||||
@@ -25,11 +25,6 @@ If you don't use cert-manager, you'll need to:
|
||||
* Create a TLS secret in your cluster with a valid certificate that uses that CA
|
||||
* Pass the name of that secret with the webhook.secretName parameter.
|
||||
|
||||
### kubectl
|
||||
```bash
|
||||
kubectl apply -f https://github.com/fairwindsops/polaris/releases/latest/download/webhook.yaml
|
||||
```
|
||||
|
||||
### Helm
|
||||
```bash
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
@@ -50,3 +45,38 @@ output unless we are rejecting a workload altogether.
|
||||
This means that any checks with a severity of `warning` will still pass webhook validation,
|
||||
and the only evidence of that warning will either be in the Polaris dashboard or the
|
||||
Polaris webhook logs. This will change in a future version of Kubernetes.
|
||||
|
||||
## Mutating Webhook
|
||||
By default, the Admission Controller is just pass/fail, but
|
||||
Polaris can also operate as a mutating webhook for many of the issues it checks for.
|
||||
This means Polaris will remediate the issue it finds, rather than rejecting
|
||||
the deployment.
|
||||
|
||||
To enable the mutating webhook, add `--set webhook.mutate=true` to your
|
||||
Helm installation command.
|
||||
|
||||
The following default checks currently have mutation support enabled:
|
||||
* `hostPIDSet`
|
||||
* `hostNetworkSet`
|
||||
* `hostIPCSet`
|
||||
* `priorityClassNotSet`
|
||||
* `hostPortSet`
|
||||
* `pullPolicyNotAlways`
|
||||
* `deploymentMissingReplicas`
|
||||
* `dangerousCapabilities`
|
||||
* `cpuLimitsMissing`
|
||||
* `memoryLimitsMissing`
|
||||
* `livenessProbeMissing`
|
||||
* `memoryRequestsMissing`
|
||||
* `cpuRequestsMissing`
|
||||
* `runAsPrivileged`
|
||||
* `readinessProbeMissing`
|
||||
* `privilegeEscalationAllowed`
|
||||
* `notReadOnlyRootFilesystem`
|
||||
* `insecureCapabilities`
|
||||
* `runAsRootAllowed`
|
||||
|
||||
If you'd like to
|
||||
enable other mutations, you can set the `webhook.mutations` flag.
|
||||
|
||||
|
||||
|
||||
+120
-1
@@ -6,6 +6,125 @@ meta:
|
||||
|
||||
---
|
||||
|
||||
## 9.1.1
|
||||
* Fix `hpaMinAvailability` failure message
|
||||
* Fix `missingPodDisruptionBudget` typo
|
||||
* Rewrite `hpaMaxAvailability` check to use go-template
|
||||
|
||||
## 9.1.0
|
||||
* Add HPA `minAvailable` and HPA `maxAvailable` checks
|
||||
* Fix typo for PDB `minAvailable`
|
||||
|
||||
## 9.0.1
|
||||
* Fix comments handling in `addOrReplaceValue` function
|
||||
|
||||
## 9.0.0
|
||||
* Expose issue fixer and mutations in the library
|
||||
* Remove `packr` in favor of `go:embed`
|
||||
|
||||
## 8.5.6
|
||||
* Fix trying to list cluster-level resources
|
||||
|
||||
## 8.5.5
|
||||
* Fix missing PDB check
|
||||
|
||||
## 8.5.4
|
||||
* Fix conditional expressions should be at very top of `additionalSchemaStrings`
|
||||
* Update alpine to 3.19
|
||||
|
||||
## 8.5.3
|
||||
* Add quiet flag to polaris audit CLI command to suppress 'upload to Insights' prompt
|
||||
|
||||
## 8.5.2
|
||||
* Switch to `controller-utils` package to get workloads
|
||||
|
||||
## 8.5.1
|
||||
* Update `topologySpreadConstraint` check
|
||||
|
||||
## 8.5.0
|
||||
* Add helm-skip-tests flag
|
||||
* Update CLI docs
|
||||
* Handle multiple helm-values files
|
||||
|
||||
## 8.4.0
|
||||
* Change kubernetes.io/ label from name to instance
|
||||
|
||||
## 8.3.0
|
||||
* Add option to filter audit results by severity level
|
||||
* Add insights prompt
|
||||
|
||||
## 8.2.4
|
||||
* Fix nil pointer issue with webhook
|
||||
|
||||
## 8.2.3
|
||||
* Add category for `metadataAndNameMismatched`.
|
||||
* Fix category for `priorityClassNotSet`.
|
||||
|
||||
## 8.2.2
|
||||
* Fix webhook server cert dir argument
|
||||
|
||||
## 8.2.1
|
||||
* Fix on Insights integration
|
||||
|
||||
## 8.2.0
|
||||
* Minor fixes for NSA checks
|
||||
|
||||
## 8.1.0
|
||||
* Add `insights-host` global flag to configure Fairwinds Insights host (defaults to `https://insights.fairwinds.com`).
|
||||
* Add new `auth` sub-commands be able to authenticate on Polaris using Fairwinds Insights credentials
|
||||
- `login` - login using Fairwinds Insights credentials via the web interface or provide a token
|
||||
- `logout` - logout from Fairwinds Insights
|
||||
- `status` - show relevant information regarding login state
|
||||
- `token` - prints the token from local storage
|
||||
* Add new `audit` flags to be able to upload Workloads and Polaris results to Fairwinds Insights
|
||||
- `upload-insights` - indicates that the results should be uploaded to Fairwinds Insights. (defaults to `false`)
|
||||
- `cluster-name` - cluster name that the results belongs to. Creates the cluster if it does not exist. (required if `upload-insights` is used)
|
||||
|
||||
|
||||
## 8.0.0
|
||||
* Change default severity from `ignore` to `warning` for `priorityClassNotSet`, `metadataAndNameMismatched`, `missingPodDisruptionBudget`, `automountServiceAccountToken`, `missingNetworkPolicy` checks.
|
||||
* Change default severity from `warning` to `danger` for `sensitiveContainerEnvVar`, `sensitiveConfigmapContent`, `clusterrolePodExecAttach`, `rolePodExecAttach`, `clusterrolebindingPodExecAttach`, `rolebindingClusterRolePodExecAttach`, `rolebindingRolePodExecAttach`,`clusterrolebindingClusterAdmin`,`rolebindingClusterAdminClusterRole`,`rolebindingClusterAdminRole` checks.
|
||||
|
||||
## 7.4.0
|
||||
* Skip https certificate verification (#920)
|
||||
|
||||
## 7.3.0
|
||||
* Add a check for `topologySpreadConstraint` (#879)
|
||||
|
||||
## 7.2.0
|
||||
* Enable new RBAC / sensitive content / Pod exec checks, add `hasPrefix` and `hasSuffix` functions to the GO template, exempt `system:` name prefixes for RBAC checks, sensitive content checks ignore `valueFrom`, (#832)
|
||||
|
||||
## 7.1.0
|
||||
* Let Polaris modify YAML without losing comments/formatting (#821)
|
||||
* Add checks for RBAC allowing exec or attaching to a Pod (#820)
|
||||
* Add `clusterrolebindingClusterAdmin`, `rolebindingClusterAdminRole`, and `rolebindingClusterAdminClusterRole` checks + schema tests (#823)
|
||||
|
||||
## 7.0.2
|
||||
* Fixes for pretty CLI output
|
||||
* Some new checks (disabled by default)
|
||||
* Some additional features in templating engine
|
||||
|
||||
## 7.0.1
|
||||
* Documentation updates
|
||||
|
||||
## 7.0.0
|
||||
* Better support for `polaris fix`
|
||||
* `target: Pod` is now `target: PodSpec` (to differentiate naked Pods from Controllers)
|
||||
|
||||
## 6.0.0
|
||||
* Preliminary support for `polaris fix` command
|
||||
* Changes to how Pod owners are determined
|
||||
* Removed YAML manifests from the `deploy/` directory - Helm is now the default install mechanism
|
||||
|
||||
## 5.1.0
|
||||
* Support `--context` flag for kubecontext
|
||||
* Treat core Kubernetes controllers (`Deployments`, `StatefulSets`, `DaemonSets`, `CronJobs`) as top-level objects, instead of following owner refs
|
||||
|
||||
## 5.0.0
|
||||
* Renamed `multipleReplicasForDeployment` to `deploymentMissingReplicas`
|
||||
* Changed `RunAsRootAllowed` and `hostNetworkSet` default severity to `danger`
|
||||
* Changed `deploymentMissingReplicas` default severity to `warning`
|
||||
|
||||
## 4.2.0
|
||||
* New flags `--disallow-(config|annotation)-exemptions`
|
||||
* Kubernetes dependency updates
|
||||
@@ -132,7 +251,7 @@ JSON schema (see changes to `./checks/multipleReplicasForDeployment.yaml`)
|
||||
* Docker image now includes the default config
|
||||
|
||||
### Breaking Changes
|
||||
* Breaking changes in both input and output formats. See [Examples](https://github.com/FairwindsOps/polaris/tree/master/examples) for examples of the new formats.
|
||||
* Breaking changes in both input and output formats. See [Examples](https://github.com/FairwindsOps/polaris/tree/master/pkg/config/examples) for examples of the new formats.
|
||||
* removed config-level configuration for checks like max/min memory settings
|
||||
* changed severity `error` to `danger`
|
||||
* Breaking changes to the CLI
|
||||
|
||||
@@ -14,22 +14,58 @@ key | default | description
|
||||
`livenessProbeMissing` | `warning` | Fails when a liveness probe is not configured for a pod.
|
||||
`tagNotSpecified` | `danger` | Fails when an image tag is either not specified or `latest`.
|
||||
`pullPolicyNotAlways` | `warning` | Fails when an image pull policy is not `always`.
|
||||
`priorityClassNotSet` | `ignore` | Fails when a priorityClassName is not set for a pod.
|
||||
`multipleReplicasForDeployment` | `ignore` | Fails when there is only one replica for a deployment.
|
||||
`missingPodDisruptionBudget` | `ignore`
|
||||
`priorityClassNotSet` | `warning` | Fails when a priorityClassName is not set for a pod.
|
||||
`deploymentMissingReplicas` | `warning` | Fails when there is only one replica for a deployment.
|
||||
`missingPodDisruptionBudget` | `warning` | Fails when PDB is missing.
|
||||
`metadataAndInstanceMismatched` | `warning` | Fails when label `app.kubernetes.io/instance` and `metadata.name` mismatch
|
||||
`topologySpreadConstraint` | `warning` | Fails when there is no topology spread constraint on the pod
|
||||
`hpaMaxAvailability` | `warning` | Fails when `maxAvailable` lesser or equal than `minAvailable` (if defined) for a HorizontalPodAutoscaler
|
||||
`hpaMinAvailability` | `warning` | Fails when `minAvailable` (if defined) lesser or equal to one for a HorizontalPodAutoscaler
|
||||
`pdbMinAvailableGreaterThanHPAMinReplicas` | `warning` | Fails when PDB `minAvailable` is greater than HPA `minReplicas`
|
||||
|
||||
## Background
|
||||
|
||||
### Liveness and Readiness Probes
|
||||
Readiness and liveness probes can help maintain the health of applications running inside Kubernetes. By default, Kubernetes only knows whether or not a process is running, not if it's healthy. Properly configured readiness and liveness probes will also be able to ensure the health of an application.
|
||||
|
||||
Readiness probes are designed to ensure that an application has reached a "ready" state. In many cases there is a period of time between when a webserver process starts and when it is ready to receive traffic. A readiness probe can ensure the traffic is not sent to a pod until it is actually ready to receive traffic.
|
||||
|
||||
Liveness probes are designed to ensure that an application stays in a healthy state. When a liveness probe fails, the pod will be restarted.
|
||||
|
||||
### Image Pull Policy
|
||||
Docker's `latest` tag is applied by default to images where a tag hasn't been specified. Not specifying a specific version of an image can lead to a wide variety of problems. The underlying image could include unexpected breaking changes that break your application whenever the latest image is pulled. Reusing the same tag for multiple versions of an image can lead to different nodes in the same cluster having different versions of an image, even if the tag is identical.
|
||||
|
||||
Related to that, relying on cached versions of a Docker image can become a security vulnerability. By default, an image will be pulled if it isn't already cached on the node attempting to run it. This can result in variations in images that are running per node, or potentially provide a way to gain access to an image without having direct access to the ImagePullSecret. With that in mind, it's often better to ensure the a pod has `pullPolicy: Always` specified, so images are always pulled directly from their source.
|
||||
|
||||
### Topology Spread Constraints
|
||||
|
||||
By default, the Kubernetes scheduler uses a bin-packing algorithm to fit as many pods as possible into a cluster. The scheduler prefers a more evenly distributed general node load to app replicas precisely spread across nodes. Therefore, by default, multi-replica is not guaranteed to be spread across multiple availability zones. Kubernetes provides topologySpreadConstraint configuration in order to better ensure pod spread across multiple AZs and/or Hosts.
|
||||
|
||||
Example of a topologySpreadConstraint spreading across zones:
|
||||
|
||||
```
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: demo-basic-demo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: basic-demo
|
||||
app.kubernetes.io/instance: demo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: basic-demo
|
||||
app.kubernetes.io/instance: demo
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: "topology.kubernetes.io/zone"
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
```
|
||||
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [What's Wrong With The Docker :latest Tag?](https://vsupalov.com/docker-latest-tag/)
|
||||
@@ -37,3 +73,4 @@ Related to that, relying on cached versions of a Docker image can become a secur
|
||||
- [Kubernetes Docs: Configure Liveness and Readiness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/)
|
||||
- [Utilizing Kubernetes Liveness and Readiness Probes to Automatically Recover From Failure](https://medium.com/spire-labs/utilizing-kubernetes-liveness-and-readiness-probes-to-automatically-recover-from-failure-2fe0314f2b2e)
|
||||
- [Kubernetes Liveness and Readiness Probes: How to Avoid Shooting Yourself in the Foot](https://blog.colinbreck.com/kubernetes-liveness-and-readiness-probes-how-to-avoid-shooting-yourself-in-the-foot/)
|
||||
- [Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
|
||||
+15
-2
@@ -11,17 +11,30 @@ for privilege escalation.
|
||||
|
||||
key | default | description
|
||||
----|---------|------------
|
||||
`automountServiceAccountToken` | `warning` | Fails when `automountServiceAccountToken` is automounted.
|
||||
`hostIPCSet` | `danger` | Fails when `hostIPC` attribute is configured.
|
||||
`hostPIDSet` | `danger` | Fails when `hostPID` attribute is configured.
|
||||
`linuxHardening` | `danger` | Fails when neither `AppArmor`, `Seccomp`, `SELinux`, or dropping Linux Capabilities is in use.
|
||||
`notReadOnlyRootFilesystem` | `warning` | Fails when `securityContext.readOnlyRootFilesystem` is not true.
|
||||
`privilegeEscalationAllowed` | `danger` | Fails when `securityContext.allowPrivilegeEscalation` is true.
|
||||
`runAsRootAllowed` | `warning` | Fails when `securityContext.runAsNonRoot` is not true.
|
||||
`runAsPrivileged` | `danger` | Fails when `securityContext.privileged` is true.
|
||||
`insecureCapabilities` | `warning` | Fails when `securityContext.capabilities` includes one of the capabilities [listed here](https://github.com/FairwindsOps/polaris/tree/master/checks/insecureCapabilities.yaml)
|
||||
`dangerousCapabilities` | `danger` | Fails when `securityContext.capabilities` includes one of the capabilities [listed here](https://github.com/FairwindsOps/polaris/tree/master/checks/dangerousCapabilities.yaml)
|
||||
`insecureCapabilities` | `warning` | Fails when `securityContext.capabilities` includes one of the capabilities [listed here](https://github.com/FairwindsOps/polaris/tree/master/pkg/config/checks/insecureCapabilities.yaml)
|
||||
`dangerousCapabilities` | `danger` | Fails when `securityContext.capabilities` includes one of the capabilities [listed here](https://github.com/FairwindsOps/polaris/tree/master/pkg/config/checks/dangerousCapabilities.yaml)
|
||||
`hostNetworkSet` | `warning` | Fails when `hostNetwork` attribute is configured.
|
||||
`hostPortSet` | `warning` | Fails when `hostPort` attribute is configured.
|
||||
`tlsSettingsMissing` | `warning` | Fails when an Ingress lacks TLS settings.
|
||||
`sensitiveContainerEnvVar` | `danger` | Fails when the container sets potentially sensitive environment variables.
|
||||
`sensitiveConfigmapContent` | `danger` | Fails when potentially sensitive content is detected in the ConfigMap keys or values.
|
||||
`missingNetworkPolicy` | `warning`
|
||||
`clusterrolePodExecAttach` | `danger` | Fails when the ClusterRole allows Pods/exec or pods/attach.
|
||||
`rolePodExecAttach` | `danger` | Fails when the Role allows Pods/exec or pods/attach.
|
||||
`clusterrolebindingPodExecAttach` | `danger` | Fails when the ClusterRoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`rolebindingRolePodExecAttach` | `danger` | Fails when the RoleBinding references a Role that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`rolebindingClusterRolePodExecAttach` | `danger` | Fails when the RoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`clusterrolebindingClusterAdmin` | `danger` | Fails when the ClusterRoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions.
|
||||
`rolebindingClusterAdminClusterRole` | `danger` | Fails when the RoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions.
|
||||
`rolebindingClusterAdminRole` | `danger` | Fails when the RoleBinding references a Role with wildcard permissions.
|
||||
|
||||
## Background
|
||||
|
||||
|
||||
+18
-2
@@ -11,19 +11,23 @@ audit
|
||||
Runs a one-time audit.
|
||||
dashboard
|
||||
Runs the webserver for Polaris dashboard.
|
||||
fix
|
||||
Fix Infrastructure as code files.
|
||||
help
|
||||
Prints help, if you give it a command then it will print help for that command. Same as -h
|
||||
version
|
||||
Prints the version of Polaris
|
||||
webhook
|
||||
Runs the webhook webserver
|
||||
Runs the webhook webserver.
|
||||
|
||||
# global flags
|
||||
-c, --config string Location of Polaris configuration file.
|
||||
-x, --context string Set the kube context.
|
||||
--disallow-exemptions Disallow any exemptions from configuration file.
|
||||
--disallow-config-exemptions Disallow exemptions set within the configuration file.
|
||||
--disallow-annotation-exemptions Disallow any exemption defined as a controller annotation.
|
||||
--kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster.
|
||||
--insights-host string Fairwinds Insights host URL. (default "https://insights.fairwinds.com")
|
||||
--log-level string Logrus log level. (default "info")
|
||||
|
||||
# dashboard flags
|
||||
@@ -37,22 +41,34 @@ webhook
|
||||
|
||||
# audit flags
|
||||
--audit-path string If specified, audits one or more YAML files instead of a cluster.
|
||||
--checks strings Optional flag to specify specific checks to check
|
||||
--color Whether to use color in pretty format. (default true)
|
||||
--display-name string An optional identifier for the audit.
|
||||
-f, --format string Output format for results - json, yaml, pretty, or score. (default "json")
|
||||
--helm-chart string Will fill out Helm template
|
||||
--helm-values string Optional flag to add helm values
|
||||
--helm-skip-tests bool Corresponds to --skip-tests of helm template
|
||||
-h, --help help for audit
|
||||
--namespace string Namespace to audit. Only applies to in-cluster audits
|
||||
--only-show-failed-tests If specified, audit output will only show failed tests.
|
||||
--output-file string Destination file for audit results.
|
||||
--output-url string Destination URL to send audit results.
|
||||
--resource string Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.
|
||||
--set-exit-code-below-score int Set an exit code of 4 when the score is below this threshold (1-100).
|
||||
--set-exit-code-on-danger Set an exit code of 3 when the audit contains danger-level issues.
|
||||
--severity string Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)
|
||||
--skip-ssl-validation Skip https certificate verification
|
||||
|
||||
# fix flags
|
||||
--checks strings Optional flag to specify specific checks to fix eg. checks=hostIPCSet,hostPIDSet and checks=all applies fix to all defined checks mutations
|
||||
--files-path string mutate and fix one or more YAML files in a specified folder
|
||||
-h, --help help for fix
|
||||
--template set to true when modifyng a YAML template, like a Helm chart (experimental)
|
||||
|
||||
|
||||
# webhook flags
|
||||
--disable-webhook-config-installer disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.
|
||||
-h, --help help for webhook
|
||||
-p, --port int Port for the dashboard webserver. (default 9876)
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
+4
-48
@@ -49,8 +49,7 @@ 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
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.12.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
|
||||
@@ -73,50 +72,7 @@ Each new pull request should:
|
||||
- Be up to date and/or rebased on the master branch
|
||||
|
||||
## Creating a new release
|
||||
To create a new release, simply tag this repo with the new version.
|
||||
|
||||
### Patch releases
|
||||
Patch releases only need to change this repo. The Helm chart and deploy scripts
|
||||
will automatically pull in the latest changes.
|
||||
|
||||
If the release involves changes to anything in the `deploy/` folder (e.g. new RBAC permissions),
|
||||
it needs to be a minor or major release in order to prevent breaking the Helm chart.
|
||||
|
||||
1. Create a PR for this repo
|
||||
1. Bump the version number in:
|
||||
1. main.go
|
||||
2. README.md
|
||||
2. Update CHANGELOG.md
|
||||
3. Merge your PR
|
||||
2. Tag the latest branch for this repo
|
||||
1. Pull the latest commit for the `master` branch (which you just merged in your PR)
|
||||
2. Run `git tag $VERSION && git push --tags`
|
||||
3. Make sure CircleCI runs successfully for the new tag - this will push images to quay.io and create a release in GitHub
|
||||
1. If CircleCI fails, check with Codeowners ASAP
|
||||
|
||||
### Minor/Major releases
|
||||
Minor and major releases need to change both this repository and the
|
||||
[Helm chart repo](https://github.com/FairwindsOps/charts/).
|
||||
|
||||
The steps are:
|
||||
1. Modify the [Helm chart](https://github.com/FairwindsOps/charts/stable/polaris)
|
||||
1. Clone the helm charts repo
|
||||
1. `git clone https://github.com/FairwindsOps/charts`
|
||||
2. `git checkout -b yourname/update-polaris`
|
||||
1. Bump the version number in `stable/polaris/Chart.yaml`
|
||||
2. Make any necessary changes to the chart to support the new version of Polaris (e.g. new RBAC permissions)
|
||||
3. **Don't merge yet!**
|
||||
2. Create a PR for this repo
|
||||
1. Create a new branch named `yourname/update-version`
|
||||
2. Bump the version number in:
|
||||
1. main.go
|
||||
2. README.md
|
||||
3. Regenerate the deployment files. Assuming you've cloned the charts repo to `~/git/charts`:
|
||||
1. `CHARTS_DIR=~/git/charts ./scripts/generate-deployment-files.sh`
|
||||
4. Update CHANGELOG.md
|
||||
5. Merge your PR
|
||||
3. Tag the latest branch for this repo
|
||||
1. Pull the latest for the `master` branch
|
||||
2. Run `git tag $VERSION && git push --tags`
|
||||
3. Make sure CircleCI runs successfully for the new tag - this will push images to quay.io and create a release in GitHub
|
||||
1. If CircleCI fails, check with Codeowners ASAP
|
||||
4. Create and merge a PR for your changes to the Helm chart
|
||||
For major and minor releases, don't forget to update the Helm chart at
|
||||
https://github.com/FairwindsOps/charts
|
||||
|
||||
@@ -5,7 +5,7 @@ meta:
|
||||
---
|
||||
# Configuration
|
||||
|
||||
The default Polaris configuration can be [seen here](https://github.com/FairwindsOps/polaris/blob/master/examples/config.yaml).
|
||||
The default Polaris configuration can be [seen here](https://github.com/FairwindsOps/polaris/blob/master/pkg/config/default.yaml).
|
||||
|
||||
You can customize the configuration to do things like:
|
||||
* Turn checks [on and off](checks.md)
|
||||
|
||||
@@ -7,7 +7,7 @@ meta:
|
||||
|
||||
If you'd like to create your own checks, you can use [JSON Schema](https://json-schema.org/).
|
||||
This is how built-in Polaris checks are defined as well - you can see all the built-in checks
|
||||
in the [checks folder](https://github.com/FairwindsOps/polaris/tree/master/checks) for examples.
|
||||
in the [checks folder](https://github.com/FairwindsOps/polaris/tree/master/pkg/config/checks) for examples.
|
||||
|
||||
If you write a check that could be useful for others, feel free to open a PR to add it in!
|
||||
|
||||
@@ -25,7 +25,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
@@ -43,10 +43,11 @@ check ID. Note that you'll also have to set its severity in the `checks` section
|
||||
* `category` - one of `Security`, `Efficiency`, or `Reliability`
|
||||
* `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 contains a pod spec (e.g. Deployments, CronJobs, StatefulSets), as well as naked Pods
|
||||
* `Pod`, same as `Controller`, but the schema applies to the Pod spec rather than the top-level controller
|
||||
* `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`, `Pod` or `Container`, you can use this to change which types of controllers are checked
|
||||
* `controllers` - if `target` is `Controller`, `PodSpec` or `Container`, you can use this to change which types of controllers are checked
|
||||
* `controllers.include` - _only_ check these controllers
|
||||
* `controllers.exclude` - check all controllers except these
|
||||
* `containers` - if `target` is `Container`, you can use this to decide if `initContainers`, `containers`, or both should be checked
|
||||
@@ -72,7 +73,7 @@ customChecks:
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -119,7 +120,7 @@ successMessage: Label app.kubernetes.io/name matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/name must match metadata.name
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
@@ -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`:
|
||||
@@ -161,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.
|
||||
@@ -175,7 +193,7 @@ controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
@@ -215,7 +233,7 @@ customChecks:
|
||||
foo:
|
||||
jsonSchema: |
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"type": "object"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -16,11 +16,6 @@ don't conform to best practices.
|
||||
|
||||
## Installation
|
||||
|
||||
### kubectl
|
||||
```bash
|
||||
kubectl apply -f https://github.com/fairwindsops/polaris/releases/latest/download/dashboard.yaml
|
||||
kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80
|
||||
```
|
||||
### Helm
|
||||
```bash
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
|
||||
@@ -25,6 +25,32 @@ brew install FairwindsOps/tap/polaris
|
||||
polaris version
|
||||
```
|
||||
|
||||
## Checking Infrastructure as Code files
|
||||
You can audit Kubernetes YAML files by running:
|
||||
```bash
|
||||
polaris audit --audit-path ./deploy/ --format=pretty
|
||||
```
|
||||
This will print out any issues Polaris finds in your manifests.
|
||||
|
||||
Polaris can only check raw YAML manifests. If you'd like to check a Helm template,
|
||||
you can run `helm template` to generate a manifest that Polaris can check.
|
||||
|
||||
## Fixing Issues
|
||||
Polaris can automatically fix many of the issues it finds. For example, you can run
|
||||
```bash
|
||||
polaris fix --files-path ./deploy/ --checks=all
|
||||
```
|
||||
|
||||
to fix any issues inside the `deploy` directory. Polaris may leave
|
||||
comments next to some changes (e.g. liveness and readiness probes) prompting
|
||||
the user to set them to something more appropriate given the context of their
|
||||
application.
|
||||
|
||||
Note that not all issues can be automatically fixed.
|
||||
|
||||
Currently only raw YAML manifests can be mutated. Helm charts etc.
|
||||
still need to be changed manually.
|
||||
|
||||
## Running in a CI pipeline
|
||||
|
||||
### Set minimum score for an exit code
|
||||
@@ -90,7 +116,7 @@ The version number of the release tag.
|
||||
```yaml
|
||||
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
|
||||
with:
|
||||
version: 4.2.0
|
||||
version: 5.0.0
|
||||
```
|
||||
|
||||
Example inside a job:
|
||||
@@ -101,7 +127,7 @@ steps:
|
||||
- name: Setup polaris
|
||||
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
|
||||
with:
|
||||
version: 4.2.0
|
||||
version: 5.0.0
|
||||
|
||||
- name: Use command
|
||||
run: polaris version
|
||||
|
||||
Generated
+6592
-14121
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -8,9 +8,9 @@
|
||||
},
|
||||
"description": "A repository with a Vuepress template for Fairwinds projects",
|
||||
"devDependencies": {
|
||||
"vuepress": "^1.4.0",
|
||||
"vuepress": "^1.9.7",
|
||||
"vuepress-plugin-clean-urls": "^1.1.1",
|
||||
"vuepress-plugin-redirect": "^1.2.3"
|
||||
"vuepress-plugin-redirect": "^1.2.5"
|
||||
},
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
@@ -26,7 +26,8 @@
|
||||
"scripts": {
|
||||
"build": "npm run build:readme && npm run build:docs",
|
||||
"build:docs": "vuepress build -d ../dist/",
|
||||
"build:readme": "cat ../README.md | grep -v 'ocumentation' | sed \"s/https:\\/\\/\\w\\+.docs.fairwinds.com//g\" > README.md",
|
||||
"build:metadata": "cat main-metadata.md > README.md || true",
|
||||
"build:readme": "npm run build:metadata && cat ../README.md | grep -v 'ocumentation' | sed \"s/https:\\/\\/\\w\\+.docs.fairwinds.com//g\" >> README.md",
|
||||
"check-links": "vuepress check-md",
|
||||
"serve": "npm run build:readme && vuepress dev --port 3003",
|
||||
"vuepress": "vuepress"
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
options:
|
||||
organization: fairwinds-opensource
|
||||
organization: fairwinds-production
|
||||
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
|
||||
|
||||
@@ -1,25 +1,87 @@
|
||||
module github.com/fairwindsops/polaris
|
||||
|
||||
go 1.15
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gobuffalo/packr/v2 v2.8.1
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.1.3 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/karrick/godirwalk v1.16.1 // indirect
|
||||
github.com/qri-io/jsonschema v0.1.1
|
||||
github.com/rogpeppe/go-internal v1.6.2 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/thoas/go-funk v0.9.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/client-go v0.22.2
|
||||
sigs.k8s.io/controller-runtime v0.10.1
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
github.com/fairwindsops/controller-utils v0.3.4
|
||||
github.com/fatih/color v1.19.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qri-io/jsonpointer v0.1.1
|
||||
github.com/qri-io/jsonschema v0.2.1
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.35.3
|
||||
k8s.io/apimachinery v0.35.3
|
||||
k8s.io/client-go v0.35.3
|
||||
sigs.k8s.io/controller-runtime v0.23.3
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/swag v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/google/gnostic-models v0.7.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/samber/lo v1.53.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.35.3 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf // indirect
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
|
||||
)
|
||||
|
||||
@@ -18,9 +18,11 @@ import (
|
||||
cmd "github.com/fairwindsops/polaris/cmd/polaris"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version represents the current release version of Polaris
|
||||
Version = "4.2.0"
|
||||
var (
|
||||
// Version is set during build
|
||||
Version = "development"
|
||||
// Commit is set during build
|
||||
Commit = ""
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
+46
-8
@@ -1,24 +1,42 @@
|
||||
// Copyright 2022 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 config
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// BuiltInChecks contains the checks that come pre-installed w/ Polaris
|
||||
BuiltInChecks = map[string]SchemaCheck{}
|
||||
schemaBox = (*packr.Box)(nil)
|
||||
// We explicitly set the order to avoid thrash in the
|
||||
// tests as we migrate toward JSON schema
|
||||
checkOrder = []string{
|
||||
// Controller Checks
|
||||
"multipleReplicasForDeployment",
|
||||
"deploymentMissingReplicas",
|
||||
// Pod checks
|
||||
"hostIPCSet",
|
||||
"hostPathSet",
|
||||
"hostProcess",
|
||||
"hostPIDSet",
|
||||
"hostNetworkSet",
|
||||
"automountServiceAccountToken",
|
||||
"topologySpreadConstraint",
|
||||
// Container checks
|
||||
"procMount",
|
||||
"memoryLimitsMissing",
|
||||
"memoryRequestsMissing",
|
||||
"cpuLimitsMissing",
|
||||
@@ -35,18 +53,38 @@ var (
|
||||
"dangerousCapabilities",
|
||||
"insecureCapabilities",
|
||||
"priorityClassNotSet",
|
||||
"linuxHardening",
|
||||
"sensitiveContainerEnvVar",
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndNameMismatched",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
"missingNetworkPolicy",
|
||||
"sensitiveConfigmapContent",
|
||||
"clusterrolePodExecAttach",
|
||||
"rolePodExecAttach",
|
||||
"clusterrolebindingPodExecAttach",
|
||||
"rolebindingClusterRolePodExecAttach",
|
||||
"rolebindingRolePodExecAttach",
|
||||
"clusterrolebindingClusterAdmin",
|
||||
"rolebindingClusterAdminClusterRole",
|
||||
"rolebindingClusterAdminRole",
|
||||
"hpaMaxAvailability",
|
||||
"hpaMinAvailability",
|
||||
"pdbMinAvailableGreaterThanHPAMinReplicas",
|
||||
}
|
||||
|
||||
// BuiltInChecks contains the checks that come pre-installed w/ Polaris
|
||||
BuiltInChecks = map[string]SchemaCheck{}
|
||||
|
||||
//go:embed all:checks
|
||||
checksFS embed.FS
|
||||
)
|
||||
|
||||
func init() {
|
||||
schemaBox = packr.New("Schemas", "../../checks")
|
||||
for _, checkID := range checkOrder {
|
||||
contents, err := schemaBox.Find(checkID + ".yaml")
|
||||
contents, err := checksFS.ReadFile(fmt.Sprintf("checks/%s.yaml", checkID))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
successMessage: The ServiceAccount will not be automounted
|
||||
failureMessage: The ServiceAccount will be automounted
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
serviceAccountName:
|
||||
type: string
|
||||
automountServiceAccountToken:
|
||||
type: boolean
|
||||
not:
|
||||
const: true
|
||||
additionalSchemaStrings:
|
||||
ServiceAccount: |
|
||||
{{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
|
||||
type: object
|
||||
required:
|
||||
- metadata
|
||||
- automountServiceAccountToken
|
||||
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 }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,54 @@
|
||||
successMessage: The ClusterRole does not allow pods/exec or pods/attach
|
||||
failureMessage: The ClusterRole allows Pods/exec or pods/attach
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRole
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
anyOf:
|
||||
# Do not alert on default ClusterRoles.
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
anyOf:
|
||||
- const: 'admin'
|
||||
- const: "cluster-admin"
|
||||
- const: "edit"
|
||||
- pattern: '^system:'
|
||||
- const: "gce:podsecuritypolicy:calico-sa"
|
||||
- properties:
|
||||
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'
|
||||
@@ -0,0 +1,90 @@
|
||||
successMessage: The ClusterRoleBinding does not reference the default cluster-admin ClusterRole or one with wildcard permissions
|
||||
failureMessage: The ClusterRoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
- required: ["metadata"]
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
anyOf:
|
||||
- const: "cluster-admin"
|
||||
- pattern: '^system:'
|
||||
- const: "gce:podsecuritypolicy:calico-sa"
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "ClusterRole"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
not:
|
||||
const: "cluster-admin"
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
{{ if (ne .roleRef.name "view") }}
|
||||
{{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
not:
|
||||
required: ["apiGroups", "resources", "verbs"]
|
||||
properties:
|
||||
apiGroups:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: "*"
|
||||
resources:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: '*'
|
||||
verbs:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
oneOf:
|
||||
- contains:
|
||||
type: string
|
||||
const: '*'
|
||||
- minItems: 7
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- "create"
|
||||
- "update"
|
||||
- "patch"
|
||||
- "delete"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,84 @@
|
||||
successMessage: The ClusterRoleBinding does not reference a ClusterRole allowing pods/exec or pods/attach
|
||||
failureMessage: The ClusterRoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
- required: ["metadata"]
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
anyOf:
|
||||
- const: "cluster-admin"
|
||||
- pattern: '^system:'
|
||||
- const: "gce:podsecuritypolicy:calico-sa"
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "ClusterRole"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
type: object
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
{{ if (ne .roleRef.name "view") }}
|
||||
{{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
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'
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -25,3 +25,8 @@ schema:
|
||||
type: string
|
||||
not:
|
||||
const: ''
|
||||
mutations:
|
||||
- op: add
|
||||
path: /resources/limits/cpu
|
||||
value: 100m
|
||||
comment: "TODO: Set this to the maximum amount of CPU you want your workload to use"
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -25,3 +25,8 @@ schema:
|
||||
type: string
|
||||
not:
|
||||
const: ''
|
||||
mutations:
|
||||
- op: add
|
||||
path: /resources/requests/cpu
|
||||
value: 100m
|
||||
comment: "TODO: Set this to the amount of CPU you want to reserve for your workload"
|
||||
@@ -3,7 +3,7 @@ failureMessage: Container should not have dangerous capabilities
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
@@ -24,3 +24,8 @@ schema:
|
||||
- not:
|
||||
contains:
|
||||
pattern: '^(?i)NET_ADMIN$'
|
||||
|
||||
|
||||
mutations:
|
||||
- op: remove
|
||||
path: /securityContext/capabilities/add
|
||||
+5
-1
@@ -6,7 +6,7 @@ controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
@@ -19,3 +19,7 @@ schema:
|
||||
replicas:
|
||||
type: integer
|
||||
minimum: 2
|
||||
mutations:
|
||||
- op: add
|
||||
path: /spec/replicas
|
||||
value: 3
|
||||
@@ -1,11 +1,14 @@
|
||||
successMessage: Host IPC is not configured
|
||||
failureMessage: Host IPC should not be configured
|
||||
category: Security
|
||||
target: Pod
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostIPC:
|
||||
not:
|
||||
const: true
|
||||
mutations:
|
||||
- op: remove
|
||||
path: /hostIPC
|
||||
@@ -1,11 +1,14 @@
|
||||
successMessage: Host network is not configured
|
||||
failureMessage: Host network should not be configured
|
||||
category: Security
|
||||
target: Pod
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostNetwork:
|
||||
not:
|
||||
const: true
|
||||
mutations:
|
||||
- op: remove
|
||||
path: /hostNetwork
|
||||
@@ -1,11 +1,14 @@
|
||||
successMessage: Host PID is not configured
|
||||
failureMessage: Host PID should not be configured
|
||||
category: Security
|
||||
target: Pod
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostPID:
|
||||
not:
|
||||
const: true
|
||||
mutations:
|
||||
- op: remove
|
||||
path: /hostPID
|
||||
@@ -0,0 +1,16 @@
|
||||
successMessage: HostPath volumes are not configured
|
||||
failureMessage: HostPath volumes must be forbidden
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
volumes:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
hostPath:
|
||||
type: string
|
||||
const: ''
|
||||
@@ -3,7 +3,7 @@ failureMessage: Host port should not be configured
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
properties:
|
||||
@@ -13,3 +13,6 @@ schema:
|
||||
properties:
|
||||
hostPort:
|
||||
const: 0
|
||||
mutations:
|
||||
- op: remove
|
||||
path: /ports/*/hostPort
|
||||
@@ -0,0 +1,31 @@
|
||||
successMessage: Privileged access to the host check is valid
|
||||
failureMessage: Privileged access to the host is disallowed
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
windowsOptions:
|
||||
type: object
|
||||
properties:
|
||||
hostProcess:
|
||||
type: boolean
|
||||
const: false
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
windowsOptions:
|
||||
type: object
|
||||
properties:
|
||||
hostProcess:
|
||||
type: boolean
|
||||
const: false
|
||||
@@ -0,0 +1,35 @@
|
||||
successMessage: HPA has a valid max and min replica configuration
|
||||
failureMessage: HPA maxReplicas and minReplicas should be different
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
schemaString: |
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maxReplicas:
|
||||
type: integer
|
||||
minimum: 1
|
||||
required:
|
||||
- maxReplicas
|
||||
{{- if .spec.minReplicas }}
|
||||
if:
|
||||
properties:
|
||||
minReplicas:
|
||||
type: integer
|
||||
maxReplicas:
|
||||
type: integer
|
||||
then:
|
||||
properties:
|
||||
maxReplicas:
|
||||
exclusiveMinimum: {{ .spec.minReplicas }}
|
||||
else:
|
||||
properties:
|
||||
maxReplicas:
|
||||
minimum: 1
|
||||
{{- end }}
|
||||
@@ -0,0 +1,14 @@
|
||||
successMessage: HPA has a valid min replica configuration
|
||||
failureMessage: HPA minReplicas should be 2 or more
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
schema:
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: integer
|
||||
minimum: 2
|
||||
@@ -3,7 +3,7 @@ failureMessage: Container should not have insecure capabilities
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
@@ -54,3 +54,7 @@ schema:
|
||||
pattern: '^(?i)KILL$'
|
||||
- contains:
|
||||
pattern: '^(?i)AUDIT_WRITE$'
|
||||
mutations:
|
||||
- op: replace
|
||||
path: /securityContext/capabilities
|
||||
value: {"drop": ["ALL"]}
|
||||
@@ -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': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
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: "#/$defs/podOrContainerSeccompProfile"
|
||||
- $ref: "#/$defs/podOrContainerSELinuxOptions"
|
||||
- $ref: "#/$defs/containerDropCapabilities"
|
||||
{{ end}}
|
||||
@@ -10,7 +10,7 @@ containers:
|
||||
- initContainer
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- livenessProbe
|
||||
@@ -19,3 +19,8 @@ schema:
|
||||
type: object
|
||||
not:
|
||||
const: null
|
||||
mutations:
|
||||
- op: add
|
||||
path: /livenessProbe
|
||||
value: {"exec": { "command": [ "cat", "/tmp/healthy" ] }, "initialDelaySeconds": 5, "periodSeconds": 5 }
|
||||
comment: "TODO: Change the livenessProbe setting to reflect your application's health"
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -25,3 +25,8 @@ schema:
|
||||
type: string
|
||||
not:
|
||||
const: ''
|
||||
mutations:
|
||||
- op: add
|
||||
path: /resources/limits/memory
|
||||
value: "512Mi"
|
||||
comment: "TODO: Set this to the maximum amount of memory you want your workload to use"
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -25,3 +25,8 @@ schema:
|
||||
type: string
|
||||
not:
|
||||
const: ''
|
||||
mutations:
|
||||
- op: add
|
||||
path: /resources/requests/memory
|
||||
value: "512Mi"
|
||||
comment: "TODO: Set this to the amount of Memory you want to reserve for your workload"
|
||||
@@ -0,0 +1,18 @@
|
||||
successMessage: Label app.kubernetes.io/instance matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/instance must match metadata.name
|
||||
category: Reliability
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
required: ["labels"]
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
required: ["app.kubernetes.io/instance"]
|
||||
properties:
|
||||
app.kubernetes.io/instance:
|
||||
const: "{{ .metadata.name }}"
|
||||
@@ -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': https://json-schema.org/draft/2019-09/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$'
|
||||
+16
-6
@@ -4,17 +4,27 @@ category: Reliability
|
||||
target: Controller
|
||||
controllers:
|
||||
include:
|
||||
- Deployment
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
metadata:
|
||||
spec:
|
||||
type: object
|
||||
required: [template]
|
||||
properties:
|
||||
labels:
|
||||
template:
|
||||
type: object
|
||||
minProperties: 1
|
||||
required: [metadata]
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
required: [labels]
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
minProperties: 1
|
||||
additionalSchemaStrings:
|
||||
policy/PodDisruptionBudget: |
|
||||
type: object
|
||||
@@ -30,7 +40,7 @@ additionalSchemaStrings:
|
||||
matchLabels:
|
||||
type: object
|
||||
anyOf:
|
||||
{{ range $key, $value := .metadata.labels }}
|
||||
{{ range $key, $value := .spec.template.metadata.labels }}
|
||||
- properties:
|
||||
"{{ $key }}":
|
||||
type: string
|
||||
+11
-6
@@ -2,10 +2,10 @@ successMessage: Filesystem is read only
|
||||
failureMessage: Filesystem should be read only
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -25,13 +25,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
@@ -40,4 +40,9 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/readOnlyRootFilesystem
|
||||
value: true
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Voluntary evictions are not possible
|
||||
category: Reliability
|
||||
target: policy/PodDisruptionBudget
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
@@ -0,0 +1,7 @@
|
||||
successMessage: PDB and HPA are correctly configured
|
||||
failureMessage: PDB minAvailable is greater than HPA minReplicas
|
||||
category: Reliability
|
||||
target: Controller
|
||||
controllers:
|
||||
include:
|
||||
- Deployment
|
||||
@@ -0,0 +1,13 @@
|
||||
successMessage: Priority class has been set
|
||||
failureMessage: Priority class should be set
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- priorityClassName
|
||||
mutations:
|
||||
- op: add
|
||||
path: /priorityClassName
|
||||
value: high-priority
|
||||
+11
-6
@@ -2,10 +2,10 @@ successMessage: Privilege escalation not allowed
|
||||
failureMessage: Privilege escalation should not be allowed
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -25,13 +25,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
@@ -40,4 +40,9 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/allowPrivilegeEscalation
|
||||
value: false
|
||||
@@ -0,0 +1,19 @@
|
||||
successMessage: The default /proc masks are set up to reduce attack surface, and should be required
|
||||
failureMessage: Proc mount must not be changed from the default
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
procMount:
|
||||
type: string
|
||||
const: Default
|
||||
@@ -3,9 +3,13 @@ failureMessage: Image pull policy should be "Always"
|
||||
category: Reliability
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
required:
|
||||
- imagePullPolicy
|
||||
properties:
|
||||
imagePullPolicy:
|
||||
const: Always
|
||||
mutations:
|
||||
- op: add
|
||||
path: /imagePullPolicy
|
||||
value: Always
|
||||
@@ -10,7 +10,7 @@ containers:
|
||||
- initContainer
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- readinessProbe
|
||||
@@ -19,3 +19,8 @@ schema:
|
||||
type: object
|
||||
not:
|
||||
const: null
|
||||
mutations:
|
||||
- op: add
|
||||
path: /readinessProbe
|
||||
value: {"exec": { "command": [ "cat", "/tmp/healthy" ] }, "initialDelaySeconds": 5, "periodSeconds": 5 }
|
||||
comment: "TODO: Change the readinessProbe setting to reflect your application's readiness to serve traffic"
|
||||
@@ -0,0 +1,56 @@
|
||||
successMessage: The Role does not allow pods/exec or pods/attach
|
||||
failureMessage: The Role allows Pods/exec or pods/attach
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/Role
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
anyOf:
|
||||
# Do not alert on default Roles.
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
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'
|
||||
@@ -0,0 +1,98 @@
|
||||
successMessage: The RoleBinding does not reference the default cluster-admin ClusterRole or one with wildcard permissions
|
||||
failureMessage: The RoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a Role.
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["kind"]
|
||||
properties:
|
||||
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:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "ClusterRole"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
not:
|
||||
const: "cluster-admin"
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
{{ if eq .roleRef.kind "ClusterRole" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
not:
|
||||
required: ["apiGroups", "resources", "verbs"]
|
||||
properties:
|
||||
apiGroups:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: "*"
|
||||
resources:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: '*'
|
||||
verbs:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
oneOf:
|
||||
- contains:
|
||||
type: string
|
||||
const: '*'
|
||||
- minItems: 7
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- "create"
|
||||
- "update"
|
||||
- "patch"
|
||||
- "delete"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,96 @@
|
||||
successMessage: The RoleBinding does not reference a Role with wildcard permissions
|
||||
failureMessage: The RoleBinding references a Role with wildcard permissions
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a ClusterRole.
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["kind"]
|
||||
properties:
|
||||
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:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "Role"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/Role: |
|
||||
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:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
not:
|
||||
required: ["apiGroups", "resources", "verbs"]
|
||||
properties:
|
||||
apiGroups:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: "*"
|
||||
resources:
|
||||
type: array
|
||||
contains:
|
||||
type: string
|
||||
const: '*'
|
||||
verbs:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
oneOf:
|
||||
- contains:
|
||||
type: string
|
||||
const: '*'
|
||||
- minItems: 7
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- "create"
|
||||
- "update"
|
||||
- "patch"
|
||||
- "delete"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,92 @@
|
||||
successMessage: The RoleBinding does not reference a ClusterRole allowing pods/exec or pods/attach
|
||||
failureMessage: The RoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a Role.
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["kind"]
|
||||
properties:
|
||||
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:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "ClusterRole"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
{{ if eq .roleRef.kind "ClusterRole" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
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'
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,95 @@
|
||||
successMessage: The RoleBinding does not reference a Role allowing Pod exec or attach
|
||||
failureMessage: The RoleBinding references a Role that allows Pods/exec, allows pods/attach, or that does not exist
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a ClusterRole.
|
||||
- required: ["roleRef"]
|
||||
properties:
|
||||
roleRef:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
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:
|
||||
required: ["apiGroup", "kind", "name"]
|
||||
properties:
|
||||
apiGroup:
|
||||
type: string
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "Role"
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/Role: |
|
||||
{{ if eq .roleRef.kind "Role" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
metadata:
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
const: "{{ .roleRef.name }}"
|
||||
- properties:
|
||||
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'
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -2,10 +2,10 @@ successMessage: Not running as privileged
|
||||
failureMessage: Should not be running as privileged
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
notBadSecurityContext:
|
||||
type: object
|
||||
properties:
|
||||
@@ -15,10 +15,14 @@ schema:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/privileged
|
||||
value: false
|
||||
@@ -2,10 +2,10 @@ successMessage: Is not allowed to run as root
|
||||
failureMessage: Should not be allowed to run as root
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -33,13 +33,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
# non-root specified at container level
|
||||
- properties:
|
||||
containers:
|
||||
@@ -49,4 +49,8 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/runAsNonRoot
|
||||
value: true
|
||||
@@ -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': https://json-schema.org/draft/2019-09/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*'
|
||||
@@ -0,0 +1,52 @@
|
||||
successMessage: The container does not set potentially sensitive environment variables
|
||||
failureMessage: The container sets potentially sensitive environment variables
|
||||
category: Security
|
||||
target: Container
|
||||
schemaString: |
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
env:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
anyOf:
|
||||
- not:
|
||||
required: ["value"]
|
||||
- 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
|
||||
@@ -3,7 +3,7 @@ failureMessage: Image tag should be specified
|
||||
category: Reliability
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
required:
|
||||
- image
|
||||
allOf:
|
||||
@@ -3,7 +3,7 @@ failureMessage: Ingress does not have TLS configured
|
||||
category: Security
|
||||
target: networking.k8s.io/Ingress
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
@@ -0,0 +1,17 @@
|
||||
successMessage: Pod has a valid topology spread constraint
|
||||
failureMessage: Pod should be configured with a valid topology spread constraint
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
controllers:
|
||||
exclude:
|
||||
- Job
|
||||
- CronJob
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- topologySpreadConstraints
|
||||
properties:
|
||||
topologySpreadConstraints:
|
||||
type: array
|
||||
minItems: 1
|
||||
@@ -0,0 +1,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRequiredFieldsOnBuiltInChecks(t *testing.T) {
|
||||
for _, v := range BuiltInChecks {
|
||||
assert.NotEmpty(t, v.SuccessMessage)
|
||||
assert.NotEmpty(t, v.FailureMessage)
|
||||
assert.NotEmpty(t, v.Category)
|
||||
assert.NotEmpty(t, v.Target)
|
||||
}
|
||||
}
|
||||
+47
-27
@@ -16,14 +16,14 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
@@ -36,6 +36,9 @@ type Configuration struct {
|
||||
DisallowExemptions bool `json:"disallowExemptions"`
|
||||
DisallowConfigExemptions bool `json:"disallowConfigExemptions"`
|
||||
DisallowAnnotationExemptions bool `json:"disallowAnnotationExemptions"`
|
||||
Mutations []string `json:"mutations"`
|
||||
KubeContext string `json:"kubeContext"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
// Exemption represents an exemption to normal rules
|
||||
@@ -46,38 +49,55 @@ type Exemption struct {
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
var configBox = (*packr.Box)(nil)
|
||||
//go:embed default.yaml
|
||||
var defaultConfig []byte
|
||||
|
||||
func getConfigBox() *packr.Box {
|
||||
if configBox == (*packr.Box)(nil) {
|
||||
configBox = packr.New("Config", "../../examples")
|
||||
}
|
||||
return configBox
|
||||
}
|
||||
|
||||
// ParseFile parses config from a file.
|
||||
func ParseFile(path string) (Configuration, error) {
|
||||
var rawBytes []byte
|
||||
var err error
|
||||
if path == "" {
|
||||
rawBytes, err = getConfigBox().Find("config.yaml")
|
||||
} else if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") {
|
||||
// path is a url
|
||||
response, err2 := http.Get(path)
|
||||
if err2 != nil {
|
||||
return Configuration{}, err2
|
||||
}
|
||||
rawBytes, err = ioutil.ReadAll(response.Body)
|
||||
} else {
|
||||
// path is local
|
||||
rawBytes, err = ioutil.ReadFile(path)
|
||||
}
|
||||
// MergeConfigAndParseFile parses config from a file.
|
||||
func MergeConfigAndParseFile(customConfigPath string, mergeConfig bool) (Configuration, error) {
|
||||
rawBytes, err := mergeConfigFile(customConfigPath, mergeConfig)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
return Parse(rawBytes)
|
||||
}
|
||||
|
||||
func mergeConfigFile(customConfigPath string, mergeConfig bool) ([]byte, error) {
|
||||
if customConfigPath == "" {
|
||||
return defaultConfig, nil
|
||||
}
|
||||
|
||||
var customConfigContent []byte
|
||||
var err error
|
||||
if strings.HasPrefix(customConfigPath, "https://") || strings.HasPrefix(customConfigPath, "http://") {
|
||||
// path is a url
|
||||
response, err := http.Get(customConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customConfigContent, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// path is local
|
||||
customConfigContent, err = os.ReadFile(customConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if mergeConfig {
|
||||
mergedConfig, err := mergeYaml(defaultConfig, customConfigContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
return customConfigContent, nil
|
||||
}
|
||||
|
||||
// Parse parses config from a byte array.
|
||||
func Parse(rawBytes []byte) (Configuration, error) {
|
||||
reader := bytes.NewReader(rawBytes)
|
||||
|
||||
+14
-14
@@ -52,7 +52,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
@@ -69,7 +69,7 @@ customChecks:
|
||||
target: Container
|
||||
jsonSchema: >
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"type": "object",
|
||||
"required": ["securityContext"]
|
||||
}
|
||||
@@ -83,7 +83,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
@@ -125,7 +125,7 @@ func TestConfigFromURL(t *testing.T) {
|
||||
}()
|
||||
time.Sleep(time.Second)
|
||||
|
||||
parsedConf, err = ParseFile("http://localhost:8081/exampleURL")
|
||||
parsedConf, err = MergeConfigAndParseFile("http://localhost:8081/exampleURL", false)
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML from URL")
|
||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||
panic(err)
|
||||
@@ -136,41 +136,41 @@ func TestConfigFromURL(t *testing.T) {
|
||||
|
||||
func TestConfigNoServerError(t *testing.T) {
|
||||
var err error
|
||||
_, err = ParseFile("http://localhost:8081/exampleURL")
|
||||
_, err = MergeConfigAndParseFile("http://localhost:8081/exampleURL", false)
|
||||
assert.Error(t, err)
|
||||
assert.Regexp(t, regexp.MustCompile("connection refused"), err.Error())
|
||||
}
|
||||
|
||||
func TestConfigWithCustomChecks(t *testing.T) {
|
||||
valid := map[string]interface{}{
|
||||
"securityContext": map[string]interface{}{
|
||||
valid := map[string]any{
|
||||
"securityContext": map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
invalid := map[string]interface{}{
|
||||
"notSecurityContext": map[string]interface{}{},
|
||||
invalid := map[string]any{
|
||||
"notSecurityContext": map[string]any{},
|
||||
}
|
||||
|
||||
parsedConf, err := Parse([]byte(confCustomChecks))
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML config")
|
||||
assert.Equal(t, 1, len(parsedConf.CustomChecks))
|
||||
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]interface{}{})
|
||||
isValid, _, err := check.CheckObject(valid)
|
||||
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]any{})
|
||||
isValid, _, err := check.CheckObject(context.TODO(), valid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, isValid)
|
||||
isValid, _, err = check.CheckObject(invalid)
|
||||
isValid, _, err = check.CheckObject(context.TODO(), invalid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, isValid)
|
||||
|
||||
parsedConf, err = Parse([]byte(confCustomChecksWithJSONSchema))
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML config")
|
||||
assert.Equal(t, 1, len(parsedConf.CustomChecks))
|
||||
isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(valid)
|
||||
isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(context.TODO(), valid)
|
||||
assert.NoError(t, err)
|
||||
if !assert.Equal(t, true, isValid) {
|
||||
fmt.Println(problems[0].PropertyPath, problems[0].InvalidValue, problems[0].Message)
|
||||
}
|
||||
isValid, _, err = check.CheckObject(invalid)
|
||||
isValid, _, err = check.CheckObject(context.TODO(), invalid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, isValid)
|
||||
}
|
||||
|
||||
@@ -1,34 +1,98 @@
|
||||
checks:
|
||||
# reliability
|
||||
multipleReplicasForDeployment: ignore
|
||||
priorityClassNotSet: ignore
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
tagNotSpecified: danger
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
metadataAndNameMismatched: ignore
|
||||
metadataAndInstanceMismatched: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: ignore
|
||||
missingPodDisruptionBudget: warning
|
||||
topologySpreadConstraint: warning
|
||||
hpaMaxAvailability: warning
|
||||
hpaMinAvailability: warning
|
||||
pdbMinAvailableGreaterThanHPAMinReplicas: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
memoryRequestsMissing: warning
|
||||
memoryLimitsMissing: warning
|
||||
|
||||
# security
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
hostPathSet: warning
|
||||
hostProcess: warning
|
||||
hostPIDSet: danger
|
||||
linuxHardening: warning
|
||||
missingNetworkPolicy: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: danger
|
||||
runAsRootAllowed: warning
|
||||
procMount: warning
|
||||
runAsRootAllowed: danger
|
||||
runAsPrivileged: danger
|
||||
dangerousCapabilities: danger
|
||||
insecureCapabilities: warning
|
||||
hostNetworkSet: warning
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
rolePodExecAttach: danger
|
||||
clusterrolebindingPodExecAttach: danger
|
||||
rolebindingClusterRolePodExecAttach: danger
|
||||
rolebindingRolePodExecAttach: danger
|
||||
clusterrolebindingClusterAdmin: danger
|
||||
rolebindingClusterAdminClusterRole: danger
|
||||
rolebindingClusterAdminRole: danger
|
||||
|
||||
|
||||
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
|
||||
@@ -51,7 +115,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:
|
||||
@@ -69,6 +174,9 @@ exemptions:
|
||||
- runAsRootAllowed
|
||||
- readinessProbeMissing
|
||||
- livenessProbeMissing
|
||||
- automountServiceAccountToken
|
||||
- linuxHardening
|
||||
- missingNetworkPolicy
|
||||
|
||||
- controllerNames:
|
||||
- cluster-autoscaler
|
||||
@@ -76,6 +184,9 @@ exemptions:
|
||||
- notReadOnlyRootFilesystem
|
||||
- runAsRootAllowed
|
||||
- readinessProbeMissing
|
||||
- automountServiceAccountToken
|
||||
- linuxHardening
|
||||
- missingNetworkPolicy
|
||||
|
||||
- controllerNames:
|
||||
- vpa
|
||||
@@ -92,6 +203,10 @@ exemptions:
|
||||
- readinessProbeMissing
|
||||
- livenessProbeMissing
|
||||
- notReadOnlyRootFilesystem
|
||||
- automountServiceAccountToken
|
||||
- linuxHardening
|
||||
- missingNetworkPolicy
|
||||
- sensitiveContainerEnvVar
|
||||
|
||||
- controllerNames:
|
||||
- nginx-ingress-controller
|
||||
@@ -1,27 +1,53 @@
|
||||
checks:
|
||||
# reliability
|
||||
multipleReplicasForDeployment: warning
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
tagNotSpecified: danger
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
topologySpreadConstraint: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
hpaMaxAvailability: warning
|
||||
hpaMinAvailability: warning
|
||||
pdbMinAvailableGreaterThanHPAMinReplicas: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
memoryRequestsMissing: warning
|
||||
memoryLimitsMissing: warning
|
||||
|
||||
# security
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
hostPathSet: warning
|
||||
hostProcess: warning
|
||||
hostPIDSet: danger
|
||||
linuxHardening: danger
|
||||
missingNetworkPolicy: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: danger
|
||||
runAsRootAllowed: warning
|
||||
procMount: warning
|
||||
runAsRootAllowed: danger
|
||||
runAsPrivileged: danger
|
||||
dangerousCapabilities: danger
|
||||
insecureCapabilities: warning
|
||||
hostNetworkSet: warning
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
rolePodExecAttach: danger
|
||||
clusterrolebindingPodExecAttach: danger
|
||||
rolebindingClusterRolePodExecAttach: danger
|
||||
rolebindingRolePodExecAttach: danger
|
||||
clusterrolebindingClusterAdmin: danger
|
||||
rolebindingClusterAdminClusterRole: danger
|
||||
rolebindingClusterAdminRole: danger
|
||||
# custom
|
||||
resourceLimits: warning
|
||||
imageRegistry: danger
|
||||
@@ -49,7 +75,7 @@ customChecks:
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -79,11 +105,10 @@ customChecks:
|
||||
category: Images
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
type: string
|
||||
not:
|
||||
pattern: ^quay.io
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright 2022 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 config
|
||||
|
||||
import (
|
||||
|
||||
@@ -25,40 +25,40 @@ import (
|
||||
|
||||
var confContainerTest = `
|
||||
checks:
|
||||
multipleReplicasForDeployment: warning
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
pullPolicyNotAlways: warning
|
||||
exemptions:
|
||||
- namespace: prometheus
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- controllerNames:
|
||||
- controller2
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- namespace: kube-system
|
||||
controllerNames:
|
||||
- controller3
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- containerNames:
|
||||
- container41
|
||||
- container42
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- namespace: kube-system
|
||||
containerNames:
|
||||
- container51
|
||||
- container52
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- controllerNames:
|
||||
- controller6
|
||||
containerNames:
|
||||
- container61
|
||||
- container62
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- namespace: kube-system
|
||||
controllerNames:
|
||||
- controller7
|
||||
@@ -66,7 +66,7 @@ exemptions:
|
||||
- container71
|
||||
- container72
|
||||
rules:
|
||||
- multipleReplicasForDeployment
|
||||
- deploymentMissingReplicas
|
||||
- priorityClassNotSet
|
||||
- namespace: polaris
|
||||
`
|
||||
@@ -86,22 +86,22 @@ func TestNamespaceExemptionForSpecifiedRules(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", ""), "")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", ""), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller1"), "container11")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", "controller1"), "container11")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", ""), "container11")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", ""), "container11")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller1"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", "controller1"), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("pullPolicyNotAlways", createMeta("prometheus", "controller1"), "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", ""), "")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
@@ -109,16 +109,16 @@ func TestNamespaceExemptionForAllRules(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", ""), "")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("polaris", ""), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", "controller1"), "container11")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("polaris", "controller1"), "container11")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", ""), "container11")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("polaris", ""), "container11")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("polaris", "controller1"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("polaris", "controller1"), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("pullPolicyNotAlways", createMeta("polaris", "controller1"), "")
|
||||
@@ -129,28 +129,28 @@ func TestControllerExemption(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller2"), "")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller2"), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller2"), "container21")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller2"), "container21")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller2"), "container21")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", "controller2"), "container21")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("prometheus", "controller2"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("prometheus", "controller2"), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller3"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller3"), "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller3"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller3"), "")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller3"), "container31")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller3"), "container31")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller4"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller4"), "")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
@@ -158,22 +158,22 @@ func TestOnlyContainerExemption(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container41")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container41")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container42")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container42")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller4"), "container41")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller4"), "container41")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container41")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", ""), "container41")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller4"), "container41")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller4"), "container41")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container51")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container51")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
@@ -181,25 +181,25 @@ func TestNamespaceAndContainerExemption(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container51")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", ""), "container51")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("kube-system", ""), "container51")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller5"), "container51")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller5"), "container51")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller5"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller5"), "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("insights-agent", ""), "container51")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("insights-agent", ""), "container51")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container51")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container51")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller5"), "container51")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller5"), "container51")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
@@ -207,25 +207,25 @@ func TestControllerAndContainerExemption(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller6"), "container61")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller6"), "container61")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("", "controller6"), "container61")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "container61")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller6"), "container61")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller6"), "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller7"), "container61")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller7"), "container61")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container61")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container61")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container61")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", ""), "container61")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
@@ -233,28 +233,28 @@ func TestContainerExemption(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
actionable := parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", ""), "container71")
|
||||
actionable := parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", ""), "container71")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", ""), "container71")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", ""), "container71")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("", "controller7"), "container71")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("", "controller7"), "container71")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller7"), "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "container71")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller7"), "container71")
|
||||
assert.False(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("insights-agent", "controller7"), "container71")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("insights-agent", "controller7"), "container71")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller6"), "container71")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller6"), "container71")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", createMeta("kube-system", "controller7"), "container61")
|
||||
actionable = parsedConf.IsActionable("deploymentMissingReplicas", createMeta("kube-system", "controller7"), "container61")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("priorityClassNotSet", createMeta("kube-system", "controller7"), "container71")
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3" // do not change the yaml import
|
||||
)
|
||||
|
||||
func mergeYaml(defaultConfig, overridesConfig []byte) ([]byte, error) {
|
||||
var defaultData, overrideConfig map[string]any
|
||||
|
||||
err := yaml.Unmarshal([]byte(defaultConfig), &defaultData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal([]byte(overridesConfig), &overrideConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergedData := mergeYAMLMaps(defaultData, overrideConfig)
|
||||
|
||||
mergedConfig, err := yaml.Marshal(mergedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
func mergeYAMLMaps(defaults, overrides map[string]any) map[string]any {
|
||||
for k, v := range overrides {
|
||||
if vMap, ok := v.(map[string]any); ok {
|
||||
// if the key exists in defaults and is a map, recursively merge
|
||||
if mv1, ok := defaults[k].(map[string]any); ok {
|
||||
defaults[k] = mergeYAMLMaps(mv1, vMap)
|
||||
} else {
|
||||
defaults[k] = vMap
|
||||
}
|
||||
} else {
|
||||
// add or overwrite the value in defaults
|
||||
defaults[k] = v
|
||||
}
|
||||
}
|
||||
return defaults
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var defaults = `
|
||||
checks:
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
tagNotSpecified: danger
|
||||
existing:
|
||||
sub:
|
||||
key: value
|
||||
`
|
||||
|
||||
var overrides = `
|
||||
checks:
|
||||
pullPolicyNotAlways: ignore
|
||||
tagNotSpecified: overrides
|
||||
existing:
|
||||
sub:
|
||||
key1: value1
|
||||
new: value
|
||||
new:
|
||||
key: value
|
||||
`
|
||||
|
||||
func TestMergeYaml(t *testing.T) {
|
||||
mergedContent, err := mergeYaml([]byte(defaults), []byte(overrides))
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedYAML := `checks:
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
pullPolicyNotAlways: ignore
|
||||
tagNotSpecified: overrides
|
||||
existing:
|
||||
new: value
|
||||
sub:
|
||||
key: value
|
||||
key1: value1
|
||||
new:
|
||||
key: value
|
||||
`
|
||||
|
||||
assert.Equal(t, expectedYAML, string(mergedContent))
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user