mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-29 22:17:16 +00:00
Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45e6c398ff | ||
|
|
f6d3c14245 | ||
|
|
2929c7c42e | ||
|
|
d858b3f32a | ||
|
|
7bd4d967e4 | ||
|
|
ff4d08dcf5 | ||
|
|
6adc1bdeb7 | ||
|
|
a55315870d | ||
|
|
87e0e539af | ||
|
|
d04696e3ae | ||
|
|
6835925805 | ||
|
|
743d556524 | ||
|
|
2021ecb373 | ||
|
|
18c0f4e84c | ||
|
|
1371b79c03 | ||
|
|
8ad8776e00 | ||
|
|
47c92b5860 | ||
|
|
657d2e0c7f | ||
|
|
a6c196044c | ||
|
|
9120949f62 | ||
|
|
f66f3aa467 | ||
|
|
32e2f71ed8 | ||
|
|
5b8c1b28a9 | ||
|
|
2d8e78cf8f | ||
|
|
56ff966ed1 | ||
|
|
46f9549a7a | ||
|
|
a0d1b2ce04 | ||
|
|
db0fa8f359 | ||
|
|
0ee630a295 | ||
|
|
026a016b64 | ||
|
|
560425cf72 | ||
|
|
eac3d86e37 | ||
|
|
4ec5349ecf | ||
|
|
94190db1b1 |
+14
-4
@@ -17,7 +17,6 @@ references:
|
||||
echo 'export PUSH_ALL_VERSION_TAGS=true' >> ${BASH_ENV}
|
||||
echo 'export GOPROXY=https://proxy.golang.org' >> ${BASH_ENV}
|
||||
echo 'export GO111MODULE=on' >> ${BASH_ENV}
|
||||
echo 'export GOFLAGS=-mod=mod' >> ${BASH_ENV}
|
||||
|
||||
install_k8s: &install_k8s
|
||||
run:
|
||||
@@ -59,6 +58,16 @@ references:
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version 0.16.1 --set "installCRDs=true" --wait
|
||||
echo "Install cert-manager successful"
|
||||
|
||||
# Test scripts
|
||||
update_coverage: &update_coverage
|
||||
run:
|
||||
name: Update Coverage
|
||||
command: |
|
||||
if [[ -z $CIRCLE_PR_NUMBER ]]; then
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
else
|
||||
echo "Skipping coverage for forked PR"
|
||||
fi
|
||||
test_binary_dashboard: &test_binary_dashboard
|
||||
run:
|
||||
name: Test Dashboard
|
||||
@@ -148,15 +157,16 @@ jobs:
|
||||
test:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
docker:
|
||||
- image: circleci/golang:1.16
|
||||
- image: circleci/golang:1.13
|
||||
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 test ./... -coverprofile=coverage.txt -covermode=count
|
||||
- run: go test ./pkg/... -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
|
||||
- *update_coverage
|
||||
- *test_binary_dashboard
|
||||
|
||||
insights:
|
||||
@@ -177,7 +187,7 @@ jobs:
|
||||
release_binary:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
docker:
|
||||
- image: circleci/golang:1.16
|
||||
- image: circleci/golang:1.13
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -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
|
||||
+2
-5
@@ -7,7 +7,7 @@ changelog:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
builds:
|
||||
- id: polaris
|
||||
- id: watcher
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
@@ -15,13 +15,10 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- 386
|
||||
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
|
||||
github:
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
* @rbren @makoscafee
|
||||
* @rbren @makoscafee @jordandoig @baderbuddy @shaswa
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.16 AS build-env
|
||||
FROM golang:1.13 AS build-env
|
||||
WORKDIR /go/src/github.com/fairwindsops/polaris/
|
||||
|
||||
ENV GO111MODULE=on
|
||||
@@ -15,7 +15,7 @@ RUN go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
COPY . .
|
||||
RUN packr2 build -a -o polaris *.go
|
||||
|
||||
FROM alpine:3.14
|
||||
FROM alpine:3.13
|
||||
WORKDIR /usr/local/bin
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<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.0.8&color=239922">
|
||||
<img src="https://img.shields.io/static/v1.svg?label=Version&message=3.2.0&color=239922">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/FairwindsOps/polaris">
|
||||
<img src="https://goreportcard.com/badge/github.com/FairwindsOps/polaris">
|
||||
</a>
|
||||
<a href="https://circleci.com/gh/FairwindsOps/polaris">
|
||||
<a href="https://circleci.com/gh/FairwindsOps/polaris.svg">
|
||||
<img src="https://circleci.com/gh/FairwindsOps/polaris.svg?style=svg">
|
||||
</a>
|
||||
<a href="https://insights.fairwinds.com/gh/FairwindsOps/polaris">
|
||||
@@ -29,30 +29,41 @@ Polaris can be run in three different modes:
|
||||
<img src="https://polaris.docs.fairwinds.com/img/architecture.svg" alt="Polaris Architecture" width="550"/>
|
||||
</p>
|
||||
|
||||
**Want to learn more?** Reach out on [the Slack channel](https://fairwindscommunity.slack.com/messages/polaris) ([request invite](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-e3c6vj4l-3lIH6dvKqzWII5fSSFDi1g)), send an email to `opensource@fairwinds.com`, or join us for [office hours on Zoom](https://fairwindscommunity.slack.com/messages/office-hours)
|
||||
|
||||
|
||||
## Documentation
|
||||
Check out the [documentation at docs.fairwinds.com](https://polaris.docs.fairwinds.com)
|
||||
|
||||
## Join the Fairwinds Open Source Community
|
||||
|
||||
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!
|
||||
|
||||
|
||||
## Other Projects from Fairwinds
|
||||
|
||||
Enjoying Polaris? Check out some of our other projects:
|
||||
* [Goldilocks](https://github.com/FairwindsOps/Goldilocks) - Right-size your Kubernetes Deployments by compare your memory and CPU settings against actual usage
|
||||
* [Pluto](https://github.com/FairwindsOps/Pluto) - Detect Kubernetes resources that have been deprecated or removed in future versions
|
||||
* [Nova](https://github.com/FairwindsOps/Nova) - Check to see if any of your Helm charts have updates available
|
||||
* [rbac-manager](https://github.com/FairwindsOps/rbac-manager) - Simplify the management of RBAC in your Kubernetes clusters
|
||||
|
||||
## Fairwinds Insights
|
||||
## Integration with Fairwinds Insights
|
||||
<p align="center">
|
||||
<a href="https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=ad&utm_campaign=polarisad">
|
||||
<img src="https://polaris.docs.fairwinds.com/img/insights-banner.png" alt="Fairwinds Insights" width="550"/>
|
||||
</a>
|
||||
<img src="https://polaris.docs.fairwinds.com/img/FW_Insights_Polaris.svg" alt="Fairwinds Insights" width="550"/>
|
||||
</p>
|
||||
|
||||
[Fairwinds Insights](https://www.fairwinds.com/fairwinds-polaris-upgrade)
|
||||
is a platform for auditing Kubernetes clusters and enforcing policy. If you'd like to:
|
||||
* manage Polaris across a fleet of clusters
|
||||
* track findings over time
|
||||
* send results to services like Slack and Datadog
|
||||
* add additional checks from tools like
|
||||
[Trivy](https://github.com/aquasecurity/trivy),
|
||||
[Goldilocks](https://github.com/FairwindsOps/goldilocks/), and
|
||||
[OPA](https://www.openpolicyagent.org)
|
||||
|
||||
you can sign up for a [free account here](https://insights.fairwinds.com?source=polaris).
|
||||
|
||||
## Contributing
|
||||
PRs welcome! Check out the [Contributing Guidelines](https://polaris.docs.fairwinds.com/contributing) and [Code of Conduct](https://polaris.docs.fairwinds.com/code-of-conduct) for more information.
|
||||
|
||||
## Further Information
|
||||
A history of changes to this project can be viewed in the [Changelog](https://polaris.docs.fairwinds.com/changelog)
|
||||
|
||||
If you'd like to learn more about Polaris, or if you'd like to speak with
|
||||
a Kubernetes expert, you can contact `info@fairwinds.com` or [visit our website](https://fairwinds.com)
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img src="https://polaris.docs.fairwinds.com/img/dashboard-screenshot.png" alt="Polaris Dashboard" width="550"/>
|
||||
</p>
|
||||
|
||||
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), a platform for auditing and enforcing policy in Kubernetes clusters.
|
||||
|
||||
@@ -14,13 +14,12 @@ schema:
|
||||
properties:
|
||||
add:
|
||||
type: array
|
||||
allOf:
|
||||
- not:
|
||||
contains:
|
||||
const: ALL
|
||||
- not:
|
||||
contains:
|
||||
const: SYS_ADMIN
|
||||
- not:
|
||||
contains:
|
||||
const: NET_ADMIN
|
||||
not:
|
||||
contains:
|
||||
const: ALL
|
||||
not:
|
||||
contains:
|
||||
const: SYS_ADMIN
|
||||
not:
|
||||
contains:
|
||||
const: NET_ADMIN
|
||||
|
||||
@@ -53,4 +53,5 @@ schema:
|
||||
- contains:
|
||||
const: KILL
|
||||
- contains:
|
||||
const: AUDIT_WRITE
|
||||
const: AUDIT_WRITE
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required: ["metadata"]
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
@@ -14,4 +15,4 @@ schema:
|
||||
required: ["app.kubernetes.io/name"]
|
||||
properties:
|
||||
app.kubernetes.io/name:
|
||||
const: "{{ .metadata.name }}"
|
||||
const: "{{ metadata.name }}"
|
||||
@@ -1,39 +0,0 @@
|
||||
successMessage: A PodDisruptionBudget is attached
|
||||
failureMessage: Should have a PodDisruptionBudget
|
||||
category: Reliability
|
||||
target: Controller
|
||||
controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
minProperties: 1
|
||||
additionalSchemaStrings:
|
||||
policy/PodDisruptionBudget: |
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: ["selector"]
|
||||
properties:
|
||||
selector:
|
||||
type: object
|
||||
required: ["matchLabels"]
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
anyOf:
|
||||
{{ range $key, $value := .metadata.labels }}
|
||||
- properties:
|
||||
"{{ $key }}":
|
||||
type: string
|
||||
const: {{ $value }}
|
||||
required: ["{{ $key }}"]
|
||||
{{ end }}
|
||||
@@ -2,42 +2,15 @@ successMessage: Filesystem is read only
|
||||
failureMessage: Filesystem should be read only
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
- required:
|
||||
- readOnlyRootFilesystem
|
||||
properties:
|
||||
readOnlyRootFilesystem:
|
||||
const: true
|
||||
notBadSecurityContext:
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
required:
|
||||
- readOnlyRootFilesystem
|
||||
properties:
|
||||
readOnlyRootFilesystem:
|
||||
const: true
|
||||
type: object
|
||||
anyOf:
|
||||
- required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
@@ -1,7 +1,7 @@
|
||||
successMessage: disruptionsAllowed is greater than zero
|
||||
failureMessage: disruptionsAllowed is not greater than zero
|
||||
category: Reliability
|
||||
target: policy/PodDisruptionBudget
|
||||
target: PodDisruptionBudget
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
@@ -2,42 +2,15 @@ successMessage: Privilege escalation not allowed
|
||||
failureMessage: Privilege escalation should not be allowed
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
- required:
|
||||
- allowPrivilegeEscalation
|
||||
properties:
|
||||
allowPrivilegeEscalation:
|
||||
const: false
|
||||
notBadSecurityContext:
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
required:
|
||||
- allowPrivilegeEscalation
|
||||
properties:
|
||||
allowPrivilegeEscalation:
|
||||
const: false
|
||||
type: object
|
||||
anyOf:
|
||||
- required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
required:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
@@ -2,23 +2,12 @@ successMessage: Not running as privileged
|
||||
failureMessage: Should not be running as privileged
|
||||
category: Security
|
||||
target: Container
|
||||
schemaTarget: Pod
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
notBadSecurityContext:
|
||||
type: object
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
properties:
|
||||
privileged:
|
||||
not:
|
||||
const: true
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
+13
-60
@@ -22,8 +22,8 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -39,8 +39,6 @@ var auditOutputFile string
|
||||
var auditOutputFormat string
|
||||
var resourceToAudit string
|
||||
var useColor bool
|
||||
var helmChart string
|
||||
var helmValues string
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(auditCmd)
|
||||
@@ -54,8 +52,6 @@ func init() {
|
||||
auditCmd.PersistentFlags().BoolVar(&useColor, "color", true, "Whether to use color in pretty format.")
|
||||
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")
|
||||
}
|
||||
|
||||
var auditCmd = &cobra.Command{
|
||||
@@ -66,28 +62,8 @@ var auditCmd = &cobra.Command{
|
||||
if displayName != "" {
|
||||
config.DisplayName = displayName
|
||||
}
|
||||
if helmChart != "" {
|
||||
var err error
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues)
|
||||
if err != nil {
|
||||
logrus.Infof("Couldn't process helm chart: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
k, err := kube.CreateResourceProvider(context.TODO(), auditPath, resourceToAudit, config)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes resources %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
auditData, err := validator.RunAudit(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)
|
||||
auditData := runAndReportAudit(cmd.Context(), config, auditPath, resourceToAudit, auditOutputFile, auditOutputURL, auditOutputFormat, useColor)
|
||||
|
||||
summary := auditData.GetSummary()
|
||||
score := summary.GetScore()
|
||||
@@ -101,50 +77,26 @@ 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) {
|
||||
cmd := exec.Command("helm", "dependency", "update", helmChart)
|
||||
output, err := cmd.CombinedOutput()
|
||||
func runAndReportAudit(ctx context.Context, c conf.Configuration, auditPath, workload, outputFile, outputURL, outputFormat string, useColor bool) validator.AuditData {
|
||||
// Create a kubernetes client resource provider
|
||||
k, err := kube.CreateResourceProvider(ctx, auditPath, workload, c)
|
||||
if err != nil {
|
||||
logrus.Error(string(output))
|
||||
return "", err
|
||||
logrus.Errorf("Error fetching Kubernetes resources %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := []string{
|
||||
"template", helmChart,
|
||||
helmChart,
|
||||
"--output-dir",
|
||||
dir,
|
||||
}
|
||||
if helmValues != "" {
|
||||
params = append(params, "--values", helmValues)
|
||||
}
|
||||
|
||||
cmd = exec.Command("helm", params...)
|
||||
output, err = cmd.CombinedOutput()
|
||||
var auditData validator.AuditData
|
||||
auditData, err = validator.RunAudit(c, k, onlyShowFailedTests)
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(string(output))
|
||||
return "", err
|
||||
logrus.Errorf("Error while running audit on resources: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool) {
|
||||
if onlyShowFailedTests {
|
||||
auditData = auditData.RemoveSuccessfulResults()
|
||||
}
|
||||
var outputBytes []byte
|
||||
var err error
|
||||
if outputFormat == "score" {
|
||||
outputBytes = []byte(fmt.Sprintf("%d\n", auditData.GetSummary().GetScore()))
|
||||
} else if outputFormat == "yaml" {
|
||||
var jsonBytes []byte
|
||||
jsonBytes, err = json.Marshal(auditData)
|
||||
jsonBytes, err := json.Marshal(auditData)
|
||||
if err == nil {
|
||||
outputBytes, err = yaml.JSONToYAML(jsonBytes)
|
||||
}
|
||||
@@ -203,4 +155,5 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
}
|
||||
}
|
||||
}
|
||||
return auditData
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
coverage:
|
||||
range: 50...80
|
||||
+1
-11
@@ -31,14 +31,6 @@ rules:
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- 'monitoring.coreos.com'
|
||||
resources:
|
||||
- 'prometheuses'
|
||||
- 'alertmanagers'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
@@ -117,9 +109,7 @@ spec:
|
||||
- command:
|
||||
- polaris
|
||||
- dashboard
|
||||
- --port
|
||||
- "8080"
|
||||
image: 'quay.io/fairwinds/polaris:4.0'
|
||||
image: 'quay.io/fairwinds/polaris:3.1'
|
||||
imagePullPolicy: 'Always'
|
||||
name: dashboard
|
||||
ports:
|
||||
|
||||
+1
-9
@@ -31,14 +31,6 @@ rules:
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- 'monitoring.coreos.com'
|
||||
resources:
|
||||
- 'prometheuses'
|
||||
- 'alertmanagers'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'list'
|
||||
---
|
||||
# Source: polaris/templates/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
@@ -117,7 +109,7 @@ spec:
|
||||
command:
|
||||
- polaris
|
||||
- webhook
|
||||
image: 'quay.io/fairwinds/polaris:4.0'
|
||||
image: 'quay.io/fairwinds/polaris:3.1'
|
||||
imagePullPolicy: 'Always'
|
||||
ports:
|
||||
- containerPort: 9876
|
||||
|
||||
@@ -42,7 +42,7 @@ const baseConfig = {
|
||||
],
|
||||
themeConfig: {
|
||||
docsRepo: "",
|
||||
docsDir: 'docs',
|
||||
docsDir: 'docs-md',
|
||||
editLinks: true,
|
||||
editLinkText: "Help us improve this page",
|
||||
logo: '/img/fairwinds-logo.svg',
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
@@ -23,13 +23,6 @@
|
||||
color: $successColor;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 0.2rem solid $warningColor;
|
||||
}
|
||||
blockquote p {
|
||||
color: $warningColor;
|
||||
}
|
||||
|
||||
.theme-default-content:not(.custom),
|
||||
.page-nav,
|
||||
.page-edit,
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Documentation: Polaris can be run as an admission controller that acts as a validating webhook."
|
||||
---
|
||||
# Admission Controller
|
||||
> Want to manage the Admission Controller across multiple clusters? Check out
|
||||
> [Fairwinds Insights](https://www.fairwinds.com/fairwinds-polaris-upgrade)
|
||||
@@ -33,7 +28,7 @@ kubectl apply -f https://github.com/fairwindsops/polaris/releases/latest/downloa
|
||||
### Helm
|
||||
```bash
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
helm upgrade --install polaris fairwinds-stable/polaris --namespace polaris --create-namespace \
|
||||
helm upgrade --install polaris fairwinds-stable/polaris --namespace polaris \
|
||||
--set webhook.enable=true --set dashboard.enable=false
|
||||
```
|
||||
|
||||
|
||||
@@ -1,45 +1,6 @@
|
||||
---
|
||||
sidebarDepth: 0
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Changelog"
|
||||
|
||||
---
|
||||
## 4.0.8
|
||||
* Fix support for namespace checks
|
||||
|
||||
## 4.0.7
|
||||
* Fix goreleaser format
|
||||
|
||||
## 4.0.6
|
||||
* Change goreleaser format
|
||||
* Fix `--helm-values` flag
|
||||
|
||||
## 4.0.5
|
||||
* Bugfix for repeated objects on the dashboard
|
||||
|
||||
## 4.0.4
|
||||
* Bugfix for validating webhook and non-pod checks
|
||||
|
||||
## 4.0.3
|
||||
* Fixed bad interaction between `--set-exit-score-below` and `--only-show-failed-tests`
|
||||
* Dependency updates
|
||||
* Support for Helm chart scanning
|
||||
|
||||
## 4.0.2
|
||||
* Goreleaser fix
|
||||
|
||||
## 4.0.1
|
||||
* Goreleaser fix
|
||||
|
||||
## 4.0.0
|
||||
* Add support for arbitrary resources, like Ingress or PodDisruptionBudget
|
||||
* Add support check templating (see docs)
|
||||
* Add support for multi-resource checks (see docs)
|
||||
|
||||
### Breaking Changes
|
||||
* In custom checks, `jsonSchema` is now `schemaString`
|
||||
* Check `pdbDisruptionsAllowedGreaterThanZero` is now called `pdbDisruptionsIsZero`
|
||||
|
||||
## 3.2.0
|
||||
* Add `--format=pretty` option for CLI output
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Ensure that CPU and memory settings are configured, so that Kubernetes can schedule your workload effectively"
|
||||
---
|
||||
# Efficiency
|
||||
|
||||
These checks ensure that CPU and memory settings are configured, so that
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Make sure your Kubernetes workloads are always available, and are running the correct image."
|
||||
---
|
||||
# Reliability
|
||||
|
||||
These checks help to make sure your workloads are always available,
|
||||
@@ -16,7 +11,6 @@ key | default | description
|
||||
`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`
|
||||
|
||||
## Background
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Security Checks Documentation"
|
||||
---
|
||||
# Security
|
||||
|
||||
These checks are related to security concerns. Workloads that fail these
|
||||
|
||||
+1
-7
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Learn your CLI options"
|
||||
---
|
||||
#### CLI Options
|
||||
|
||||
```
|
||||
@@ -38,8 +33,6 @@ webhook
|
||||
--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
|
||||
-h, --help help for audit
|
||||
--only-show-failed-tests If specified, audit output will only show failed tests.
|
||||
--output-file string Destination file for audit results.
|
||||
@@ -48,6 +41,7 @@ webhook
|
||||
--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.
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Contribution Code of Conduct"
|
||||
---
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Contribution Guidelines"
|
||||
---
|
||||
# Contributing
|
||||
|
||||
Issues, whether bugs, tasks, or feature requests are essential for keeping Polaris great. We believe it should be as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can keep on top of things.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Documentation on setting checks by severity "
|
||||
---
|
||||
# Check Settings
|
||||
Each check can be assigned a `severity`. Only checks with a severity of `danger` or `warning` will be validated. The results of these validations are visible on the dashboard. In the case of the validating webhook, only failures with a severity of `danger` will result in a change being rejected.
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Learn about Polaris defaults and how to customize configurations. "
|
||||
---
|
||||
# Configuration
|
||||
|
||||
The default Polaris configuration can be [seen here](https://github.com/FairwindsOps/polaris/blob/master/examples/config.yaml).
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Documentation: Create your own checks, you can use JSON Schema"
|
||||
---
|
||||
# Custom Checks
|
||||
|
||||
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.
|
||||
|
||||
If you write a check that could be useful for others, feel free to open a PR to add it in!
|
||||
|
||||
## Basic Example
|
||||
For example, to disallow images from quay.io:
|
||||
If you'd like to create your own checks, you can use [JSON Schema](https://json-schema.org/). For example,
|
||||
to disallow images from quay.io:
|
||||
|
||||
```yaml
|
||||
checks:
|
||||
imageRegistry: warning
|
||||
|
||||
customChecks:
|
||||
imageRegistry:
|
||||
successMessage: Image comes from allowed registries
|
||||
failureMessage: Image should not be from disallowed registry
|
||||
category: Security
|
||||
target: Container
|
||||
category: Images
|
||||
target: Container # target can be "Container" or "Pod"
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
@@ -34,181 +21,6 @@ customChecks:
|
||||
pattern: ^quay.io
|
||||
```
|
||||
|
||||
## Available Options
|
||||
All custom checks should go under the `customChecks` field in your Polaris config, keyed by the
|
||||
check ID. Note that you'll also have to set its severity in the `checks` section of your Polaris config.
|
||||
|
||||
* `successMessage` - the message to show when the check succeeds
|
||||
* `failureMessage` - the message to show when the check fails
|
||||
* `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
|
||||
* `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.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
|
||||
* `containers.exclude` - can be set to a list including `initContainer` or `container`
|
||||
* `schema` - the JSON Schema to check against, as a YAML object
|
||||
* `schemaString` - this JSON Schema to check against, as a YAML or JSON string. See [Templating](#templating) below
|
||||
* Note: only _one_ of `schema` and `schemaString` can be specified.
|
||||
* `additionalSchemas` - see [Multi-Resource Checks](#multi-resource-checks) below
|
||||
* `additionalSchemaStrings` - see [Multi-Resource Checks](#multi-resource-checks) below
|
||||
* Note: only _one_ of `additionalSchemas` and `additionalSchemaStrings` can be specified.
|
||||
|
||||
## Checking CPU and Memory
|
||||
We extend JSON Schema with `resourceMinimum` and `resourceMaximum` fields to help compare memory and CPU resource
|
||||
strings like `1000m` and `1G`. Here's an example check that memory and CPU falls within a certain range.
|
||||
```yaml
|
||||
customChecks:
|
||||
resourceLimits:
|
||||
containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
successMessage: Resource limits are within the required range
|
||||
failureMessage: Resource limits should be within the required range
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
required:
|
||||
- limits
|
||||
properties:
|
||||
limits:
|
||||
type: object
|
||||
required:
|
||||
- memory
|
||||
- cpu
|
||||
properties:
|
||||
memory:
|
||||
type: string
|
||||
resourceMinimum: 100M
|
||||
resourceMaximum: 6G
|
||||
cpu:
|
||||
type: string
|
||||
resourceMinimum: 100m
|
||||
resourceMaximum: "2"
|
||||
```
|
||||
|
||||
## Resource Presence
|
||||
You can test for the presence of a resource in each Namespace. For example, to
|
||||
ensure an AlertmanagerConfig is in every Namespace:
|
||||
```yaml
|
||||
successMessage: Namespace has monitoring configuration
|
||||
failureMessage: Namespace should have monitoring configuration
|
||||
category: Security
|
||||
target: Namespace
|
||||
schema: {}
|
||||
additionalSchemas:
|
||||
monitoring.coreos.com/AlertmanagerConfig: {}
|
||||
```
|
||||
|
||||
## Templating
|
||||
You can also utilize go templating in your JSON schema in order to match one field against another.
|
||||
E.g. here is the built-in check to ensure that the `name` annotation matches the object's name:
|
||||
```yaml
|
||||
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
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
required: ["labels"]
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
required: ["app.kubernetes.io/name"]
|
||||
properties:
|
||||
app.kubernetes.io/name:
|
||||
const: "{{ .metadata.name }}"
|
||||
```
|
||||
|
||||
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`:
|
||||
```yaml
|
||||
schemaString: |
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: ["selector"]
|
||||
properties:
|
||||
selector:
|
||||
type: object
|
||||
required: ["matchLabels"]
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
anyOf:
|
||||
{{ range $key, $value := .metadata.labels }}
|
||||
- properties:
|
||||
"{{ $key }}":
|
||||
type: string
|
||||
const: {{ $value }}
|
||||
required: ["{{ $key }}"]
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
Here's the check to ensure that every Deployment has a PDB:
|
||||
```yaml
|
||||
successMessage: A PodDisruptionBudget is attached
|
||||
failureMessage: Should have a PodDisruptionBudget
|
||||
category: Reliability
|
||||
target: Controller
|
||||
controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
minProperties: 1
|
||||
additionalSchemaStrings:
|
||||
policy/PodDisruptionBudget: |
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: ["selector"]
|
||||
properties:
|
||||
selector:
|
||||
type: object
|
||||
required: ["matchLabels"]
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
anyOf:
|
||||
{{ range $key, $value := .metadata.labels }}
|
||||
- properties:
|
||||
"{{ $key }}":
|
||||
type: string
|
||||
const: {{ $value }}
|
||||
required: ["{{ $key }}"]
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
## JSON vs YAML
|
||||
Schemas can also be specified as JSON strings instead of YAML, for easier copy/pasting:
|
||||
```yaml
|
||||
customChecks:
|
||||
@@ -220,3 +32,8 @@ customChecks:
|
||||
}
|
||||
```
|
||||
|
||||
We extend JSON Schema with `resourceMinimum` and `resourceMaximum` fields to help compare memory and CPU resource
|
||||
strings like `1000m` and `1G`. You can see an example in [the extended config](https://github.com/FairwindsOps/polaris/tree/master/examples/config-full.yaml)
|
||||
|
||||
There are additional examples in the [checks folder](https://github.com/FairwindsOps/polaris/tree/master/checks).
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Exemptions"
|
||||
---
|
||||
# Exemptions
|
||||
Sometimes a workload really does need to do things that Polaris considers insecure. For instance,
|
||||
many of the `kube-system` workloads need to run as root, or need access to the host network. In these
|
||||
|
||||
+1
-6
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Ways to run Polaris | The Polaris dashboard can be installed on a cluster using kubectl or Helm"
|
||||
---
|
||||
# Dashboard
|
||||
|
||||
> Want to see Polaris results for all your clusters in a single dashboard? Check out
|
||||
@@ -24,7 +19,7 @@ kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80
|
||||
### Helm
|
||||
```bash
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
helm upgrade --install polaris fairwinds-stable/polaris --namespace polaris --create-namespace
|
||||
helm upgrade --install polaris fairwinds-stable/polaris --namespace polaris
|
||||
kubectl port-forward --namespace polaris svc/polaris-dashboard 8080:80
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Documentation | Polaris can be used on the command line to audit local Kubernetes manifests stored in YAML files."
|
||||
---
|
||||
# Infrastructure as Code
|
||||
> Want to see results for all your IaC repos in one place? Check out
|
||||
> [Fairwinds Insights](https://www.fairwinds.com/fairwinds-polaris-upgrade)
|
||||
@@ -60,14 +55,6 @@ polaris audit --audit-path ./deploy/ \
|
||||
--only-show-failed-tests true
|
||||
```
|
||||
|
||||
### Audit Helm Charts
|
||||
You can audit helm charts using the `--helm-chart` and `--helm-values` flags:
|
||||
```
|
||||
polaris audit \
|
||||
--helm-chart ./deploy/chart \
|
||||
--helm-values ./deploy/chart/values.yml
|
||||
```
|
||||
|
||||
### As Github Action
|
||||
#### Setup polaris action
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
meta:
|
||||
- name: description
|
||||
content: "Fairwinds Polaris | Documentation"
|
||||
---
|
||||
Generated
+111
-79
@@ -2516,42 +2516,16 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.16.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
|
||||
"integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
|
||||
"version": "4.14.7",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz",
|
||||
"integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001219",
|
||||
"colorette": "^1.2.2",
|
||||
"electron-to-chromium": "^1.3.723",
|
||||
"caniuse-lite": "^1.0.30001157",
|
||||
"colorette": "^1.2.1",
|
||||
"electron-to-chromium": "^1.3.591",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^1.1.71"
|
||||
},
|
||||
"dependencies": {
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001239",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz",
|
||||
"integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==",
|
||||
"dev": true
|
||||
},
|
||||
"colorette": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
|
||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.752",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz",
|
||||
"integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.73",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
|
||||
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
|
||||
"dev": true
|
||||
}
|
||||
"node-releases": "^1.1.66"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
@@ -2907,6 +2881,18 @@
|
||||
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
|
||||
"dev": true
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
|
||||
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
@@ -2988,9 +2974,9 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
|
||||
"integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
|
||||
"integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
@@ -3806,6 +3792,13 @@
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
},
|
||||
"delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
@@ -3873,9 +3866,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"dns-packet": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
|
||||
"integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
|
||||
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ip": "^1.1.0",
|
||||
@@ -4019,25 +4012,31 @@
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.599",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.599.tgz",
|
||||
"integrity": "sha512-u6VGpFsIzSCNrWJb1I72SUypz3EGoBaiEgygoMkd0IOcGR3WF3je5VTx9OIRI9Qd8UOMHinLImyJFkYHTq6nsg==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.1",
|
||||
"inherits": "^2.0.4",
|
||||
"minimalistic-assert": "^1.0.1",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"version": "4.11.9",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -4839,6 +4838,16 @@
|
||||
"slash": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
|
||||
@@ -5338,9 +5347,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"dev": true
|
||||
},
|
||||
"internal-ip": {
|
||||
@@ -5913,9 +5922,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._reinterpolate": {
|
||||
@@ -6462,6 +6471,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.67",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz",
|
||||
"integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==",
|
||||
"dev": true
|
||||
},
|
||||
"nopt": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||
@@ -6893,9 +6908,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
@@ -7008,9 +7023,9 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.36",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
|
||||
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
|
||||
"version": "7.0.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
|
||||
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
@@ -7644,10 +7659,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.0.tgz",
|
||||
"integrity": "sha512-SqV5GRsNqnzCL8k5dfAjCNhUrF3pR0A9lTDSCUZeh/LIshheXJEaP0hwLz2t4XHivd2J/v2HR+gRnigzeKe3cQ==",
|
||||
"dev": true
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.22.0.tgz",
|
||||
"integrity": "sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clipboard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
@@ -8269,6 +8287,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -8809,9 +8834,9 @@
|
||||
}
|
||||
},
|
||||
"ssri": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
|
||||
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
|
||||
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"figgy-pudding": "^3.5.1"
|
||||
@@ -9272,6 +9297,13 @@
|
||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"to-arraybuffer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
||||
@@ -9723,9 +9755,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
|
||||
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
@@ -10566,9 +10598,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
@@ -10587,9 +10619,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
|
||||
+1
-2
@@ -26,8 +26,7 @@
|
||||
"scripts": {
|
||||
"build": "npm run build:readme && npm run build:docs",
|
||||
"build:docs": "vuepress build -d ../dist/",
|
||||
"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",
|
||||
"build:readme": "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"
|
||||
|
||||
@@ -6,10 +6,6 @@ checks:
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
metadataAndNameMismatched: ignore
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: ignore
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
module github.com/fairwindsops/polaris
|
||||
|
||||
go 1.15
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.12.0
|
||||
cloud.google.com/go v0.74.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.15 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.10 // indirect
|
||||
github.com/fatih/color v1.10.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/googleapis/gnostic v0.5.3 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
github.com/karrick/godirwalk v1.16.1 // indirect
|
||||
github.com/qri-io/jsonschema v0.1.1
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/prometheus/client_golang v1.9.0 // indirect
|
||||
github.com/qri-io/jsonpointer v0.1.1
|
||||
github.com/qri-io/jsonschema v0.2.0
|
||||
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/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/thoas/go-funk v0.9.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/api v0.22.0
|
||||
k8s.io/apimachinery v0.22.0
|
||||
k8s.io/client-go v0.22.0
|
||||
sigs.k8s.io/controller-runtime v0.9.6
|
||||
github.com/thoas/go-funk v0.7.0
|
||||
go.uber.org/zap v1.16.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
k8s.io/api v0.20.4
|
||||
k8s.io/apimachinery v0.20.4
|
||||
k8s.io/client-go v0.20.4
|
||||
k8s.io/component-base v0.20.1 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.7.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
const (
|
||||
// Version represents the current release version of Polaris
|
||||
Version = "4.0.8"
|
||||
Version = "3.2.0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"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",
|
||||
// Pod checks
|
||||
"hostIPCSet",
|
||||
"hostPIDSet",
|
||||
"hostNetworkSet",
|
||||
// Container checks
|
||||
"memoryLimitsMissing",
|
||||
"memoryRequestsMissing",
|
||||
"cpuLimitsMissing",
|
||||
"cpuRequestsMissing",
|
||||
"readinessProbeMissing",
|
||||
"livenessProbeMissing",
|
||||
"pullPolicyNotAlways",
|
||||
"tagNotSpecified",
|
||||
"hostPortSet",
|
||||
"runAsRootAllowed",
|
||||
"runAsPrivileged",
|
||||
"notReadOnlyRootFilesystem",
|
||||
"privilegeEscalationAllowed",
|
||||
"dangerousCapabilities",
|
||||
"insecureCapabilities",
|
||||
"priorityClassNotSet",
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndNameMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
schemaBox = packr.New("Schemas", "../../checks")
|
||||
for _, checkID := range checkOrder {
|
||||
contents, err := schemaBox.Find(checkID + ".yaml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
check, err := ParseCheck(checkID, contents)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while parsing check %s", checkID)
|
||||
panic(err)
|
||||
}
|
||||
BuiltInChecks[checkID] = check
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -143,9 +142,7 @@ func TestConfigNoServerError(t *testing.T) {
|
||||
|
||||
func TestConfigWithCustomChecks(t *testing.T) {
|
||||
valid := map[string]interface{}{
|
||||
"securityContext": map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
"securityContext": map[string]interface{}{},
|
||||
}
|
||||
invalid := map[string]interface{}{
|
||||
"notSecurityContext": map[string]interface{}{},
|
||||
@@ -154,23 +151,20 @@ func TestConfigWithCustomChecks(t *testing.T) {
|
||||
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)
|
||||
isValid, err := parsedConf.CustomChecks["foo"].CheckObject(valid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, isValid)
|
||||
isValid, _, err = check.CheckObject(invalid)
|
||||
isValid, err = parsedConf.CustomChecks["foo"].CheckObject(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, err = parsedConf.CustomChecks["foo"].CheckObject(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)
|
||||
assert.Equal(t, true, isValid)
|
||||
isValid, err = parsedConf.CustomChecks["foo"].CheckObject(invalid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, isValid)
|
||||
}
|
||||
|
||||
+97
-140
@@ -2,15 +2,17 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/qri-io/jsonpointer"
|
||||
"github.com/qri-io/jsonschema"
|
||||
"github.com/thoas/go-funk"
|
||||
"gopkg.in/yaml.v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
k8sYaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
@@ -37,53 +39,40 @@ var HandledTargets = []TargetKind{
|
||||
|
||||
// SchemaCheck is a Polaris check that runs using JSON Schema
|
||||
type SchemaCheck struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Category string `yaml:"category" json:"category"`
|
||||
SuccessMessage string `yaml:"successMessage" json:"successMessage"`
|
||||
FailureMessage string `yaml:"failureMessage" json:"failureMessage"`
|
||||
Controllers includeExcludeList `yaml:"controllers" json:"controllers"`
|
||||
Containers includeExcludeList `yaml:"containers" json:"containers"`
|
||||
Target TargetKind `yaml:"target" json:"target"`
|
||||
SchemaTarget TargetKind `yaml:"schemaTarget" json:"schemaTarget"`
|
||||
Schema map[string]interface{} `yaml:"schema" json:"schema"`
|
||||
SchemaString string `yaml:"schemaString" json:"schemaString"`
|
||||
Validator jsonschema.RootSchema `yaml:"-" json:"-"`
|
||||
AdditionalSchemas map[string]map[string]interface{} `yaml:"additionalSchemas" json:"additionalSchemas"`
|
||||
AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"`
|
||||
AdditionalValidators map[string]jsonschema.RootSchema `yaml:"-" json:"-"`
|
||||
ID string `yaml:"id"`
|
||||
Category string `yaml:"category"`
|
||||
SuccessMessage string `yaml:"successMessage"`
|
||||
FailureMessage string `yaml:"failureMessage"`
|
||||
Controllers includeExcludeList `yaml:"controllers"`
|
||||
Containers includeExcludeList `yaml:"containers"`
|
||||
Target TargetKind `yaml:"target"`
|
||||
SchemaTarget TargetKind `yaml:"schemaTarget"`
|
||||
Schema map[string]interface{} `yaml:"schema"`
|
||||
SchemaFoo jsonschema.Schema `yaml:""`
|
||||
JSONSchema string `yaml:"jsonSchema"`
|
||||
}
|
||||
|
||||
type resourceMinimum string
|
||||
type resourceMaximum string
|
||||
|
||||
func unmarshalYAMLOrJSON(raw []byte, dest interface{}) error {
|
||||
reader := bytes.NewReader(raw)
|
||||
func ParseCheck(rawBytes []byte) (SchemaCheck, error) {
|
||||
reader := bytes.NewReader(rawBytes)
|
||||
check := SchemaCheck{}
|
||||
d := k8sYaml.NewYAMLOrJSONDecoder(reader, 4096)
|
||||
for {
|
||||
if err := d.Decode(dest); err != nil {
|
||||
if err := d.Decode(&check); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
//fmt.Printf("parse check %#v", check.Schema)
|
||||
return check, nil
|
||||
}
|
||||
return fmt.Errorf("Decoding schema check failed: %v", err)
|
||||
return check, fmt.Errorf("Decoding schema check failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseCheck parses a check from a byte array
|
||||
func ParseCheck(id string, rawBytes []byte) (SchemaCheck, error) {
|
||||
check := SchemaCheck{}
|
||||
err := unmarshalYAMLOrJSON(rawBytes, &check)
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
check.Initialize(id)
|
||||
return check, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
jsonschema.RegisterValidator("resourceMinimum", newResourceMinimum)
|
||||
jsonschema.RegisterValidator("resourceMaximum", newResourceMaximum)
|
||||
jsonschema.RegisterKeyword("resourceMinimum", newResourceMinimum)
|
||||
jsonschema.RegisterKeyword("resourceMaximum", newResourceMaximum)
|
||||
}
|
||||
|
||||
type includeExcludeList struct {
|
||||
@@ -91,47 +80,55 @@ type includeExcludeList struct {
|
||||
Exclude []string `yaml:"exclude"`
|
||||
}
|
||||
|
||||
func newResourceMinimum() jsonschema.Validator {
|
||||
func newResourceMinimum() jsonschema.Keyword {
|
||||
return new(resourceMinimum)
|
||||
}
|
||||
|
||||
func newResourceMaximum() jsonschema.Validator {
|
||||
func newResourceMaximum() jsonschema.Keyword {
|
||||
return new(resourceMaximum)
|
||||
}
|
||||
|
||||
func (min *resourceMinimum) Register(uri string, registry *jsonschema.SchemaRegistry) {}
|
||||
func (min *resourceMinimum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema {
|
||||
return nil
|
||||
}
|
||||
func (min *resourceMinimum) Validate(propPath string, data interface{}, errs *[]jsonschema.KeyError) {}
|
||||
|
||||
// Validate checks that a specified quanitity is not less than the minimum
|
||||
func (min resourceMinimum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) {
|
||||
err := validateRange(path, string(min), data, true)
|
||||
func (min *resourceMinimum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) {
|
||||
err := validateRange(string(*min), data, true)
|
||||
if err != nil {
|
||||
*errs = append(*errs, *err...)
|
||||
currentState.AddError(data, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Validate checks that a specified quanitity is not greater than the maximum
|
||||
func (max resourceMaximum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) {
|
||||
err := validateRange(path, string(max), data, false)
|
||||
func (min *resourceMaximum) Register(uri string, registry *jsonschema.SchemaRegistry) {}
|
||||
func (min *resourceMaximum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema {
|
||||
return nil
|
||||
}
|
||||
func (min *resourceMaximum) Validate(propPath string, data interface{}, errs *[]jsonschema.KeyError) {}
|
||||
|
||||
// Validate checks that a specified quanitity is not less than the minimum
|
||||
func (min *resourceMaximum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) {
|
||||
err := validateRange(string(*min), data, false)
|
||||
if err != nil {
|
||||
*errs = append(*errs, *err...)
|
||||
currentState.AddError(data, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.ValError) {
|
||||
func parseQuantity(i interface{}) (resource.Quantity, error) {
|
||||
resStr, ok := i.(string)
|
||||
if !ok {
|
||||
return resource.Quantity{}, &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("Resource quantity %v is not a string", i)},
|
||||
}
|
||||
return resource.Quantity{}, fmt.Errorf("Resource quantity %v is not a string", i)
|
||||
}
|
||||
q, err := resource.ParseQuantity(resStr)
|
||||
if err != nil {
|
||||
return resource.Quantity{}, &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("Could not parse resource quantity: %s", resStr)},
|
||||
}
|
||||
return resource.Quantity{}, fmt.Errorf("Could not parse resource quantity: %s", resStr)
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func validateRange(path string, limit interface{}, data interface{}, isMinimum bool) *[]jsonschema.ValError {
|
||||
func validateRange(limit interface{}, data interface{}, isMinimum bool) error {
|
||||
limitQuantity, err := parseQuantity(limit)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -143,15 +140,11 @@ func validateRange(path string, limit interface{}, data interface{}, isMinimum b
|
||||
cmp := limitQuantity.Cmp(actualQuantity)
|
||||
if isMinimum {
|
||||
if cmp == 1 {
|
||||
return &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("%s quantity %v is > %v", path, actualQuantity, limitQuantity)},
|
||||
}
|
||||
return fmt.Errorf("quantity %v is > %v", actualQuantity, limitQuantity)
|
||||
}
|
||||
} else {
|
||||
if cmp == -1 {
|
||||
return &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("%s quantity %v is < %v", path, actualQuantity, limitQuantity)},
|
||||
}
|
||||
return fmt.Errorf("quantity %v is < %v", actualQuantity, limitQuantity)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -160,122 +153,86 @@ func validateRange(path string, limit interface{}, data interface{}, isMinimum b
|
||||
// Initialize sets up the schema
|
||||
func (check *SchemaCheck) Initialize(id string) error {
|
||||
check.ID = id
|
||||
if check.SchemaString == "" {
|
||||
if check.JSONSchema != "" {
|
||||
if err := json.Unmarshal([]byte(check.JSONSchema), &check.SchemaFoo); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
jsonBytes, err := json.Marshal(check.Schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
check.SchemaString = string(jsonBytes)
|
||||
}
|
||||
if check.AdditionalSchemaStrings == nil {
|
||||
check.AdditionalSchemaStrings = make(map[string]string)
|
||||
}
|
||||
for kind, schema := range check.AdditionalSchemas {
|
||||
jsonBytes, err := json.Marshal(schema)
|
||||
err = json.Unmarshal(jsonBytes, &check.SchemaFoo)
|
||||
fmt.Printf("unmarshed: %s\n%#v\n", string(jsonBytes), &check.SchemaFoo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
check.AdditionalSchemaStrings[kind] = string(jsonBytes)
|
||||
}
|
||||
check.Schema = map[string]interface{}{}
|
||||
check.AdditionalSchemas = map[string]map[string]interface{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TemplateForResource fills out a check's templated fields given a particular resource
|
||||
func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, error) {
|
||||
newCheck := check // Make a copy of the check, since we're going to modify the schema
|
||||
|
||||
templateStrings := map[string]string{
|
||||
"": newCheck.SchemaString,
|
||||
if true == true {
|
||||
return &check, nil
|
||||
}
|
||||
for kind, schema := range newCheck.AdditionalSchemaStrings {
|
||||
templateStrings[kind] = schema
|
||||
}
|
||||
newCheck.SchemaString = ""
|
||||
newCheck.AdditionalSchemaStrings = map[string]string{}
|
||||
|
||||
for kind, tmplString := range templateStrings {
|
||||
tmpl := template.New(newCheck.ID)
|
||||
tmpl, err := tmpl.Parse(tmplString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := bytes.Buffer{}
|
||||
err = tmpl.Execute(&w, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if kind == "" {
|
||||
newCheck.SchemaString = w.String()
|
||||
} else {
|
||||
newCheck.AdditionalSchemaStrings[kind] = w.String()
|
||||
}
|
||||
}
|
||||
|
||||
newCheck.AdditionalValidators = map[string]jsonschema.RootSchema{}
|
||||
for kind, schemaStr := range newCheck.AdditionalSchemaStrings {
|
||||
val := jsonschema.RootSchema{}
|
||||
err := unmarshalYAMLOrJSON([]byte(schemaStr), &val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newCheck.AdditionalValidators[kind] = val
|
||||
}
|
||||
err := unmarshalYAMLOrJSON([]byte(newCheck.SchemaString), &newCheck.Validator)
|
||||
yamlBytes, err := yaml.Marshal(check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &newCheck, err
|
||||
|
||||
tmpl := template.New(check.ID)
|
||||
tmpl, err = tmpl.Parse(string(yamlBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := bytes.Buffer{}
|
||||
err = tmpl.Execute(&w, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newCheck, err := ParseCheck(w.Bytes())
|
||||
if check.ID == "metadataMatchesName" {
|
||||
fmt.Println("got tpl", w.String())
|
||||
fmt.Printf("got check %#v", check.SchemaFoo)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = newCheck.Initialize(check.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &newCheck, nil
|
||||
}
|
||||
|
||||
// CheckPod checks a pod spec against the schema
|
||||
func (check SchemaCheck) CheckPod(pod *corev1.PodSpec) (bool, []jsonschema.ValError, error) {
|
||||
func (check SchemaCheck) CheckPod(pod *corev1.PodSpec) (bool, error) {
|
||||
return check.CheckObject(pod)
|
||||
}
|
||||
|
||||
// CheckController checks a controler's spec against the schema
|
||||
func (check SchemaCheck) CheckController(bytes []byte) (bool, []jsonschema.ValError, error) {
|
||||
errs, err := check.Validator.ValidateBytes(bytes)
|
||||
return len(errs) == 0, errs, err
|
||||
func (check SchemaCheck) CheckController(bytes []byte) (bool, error) {
|
||||
errs, err := check.SchemaFoo.ValidateBytes(context.TODO(), bytes)
|
||||
fmt.Println("vbytes2", errs, err)
|
||||
return len(errs) == 0, err
|
||||
}
|
||||
|
||||
// CheckContainer checks a container spec against the schema
|
||||
func (check SchemaCheck) CheckContainer(container *corev1.Container) (bool, []jsonschema.ValError, error) {
|
||||
func (check SchemaCheck) CheckContainer(container *corev1.Container) (bool, error) {
|
||||
return check.CheckObject(container)
|
||||
}
|
||||
|
||||
// CheckObject checks arbitrary data against the schema
|
||||
func (check SchemaCheck) CheckObject(obj interface{}) (bool, []jsonschema.ValError, error) {
|
||||
func (check SchemaCheck) CheckObject(obj interface{}) (bool, error) {
|
||||
bytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return false, err
|
||||
}
|
||||
errs, err := check.Validator.ValidateBytes(bytes)
|
||||
return len(errs) == 0, errs, err
|
||||
}
|
||||
|
||||
// CheckAdditionalObjects looks for an object that passes the specified additional schema
|
||||
func (check SchemaCheck) CheckAdditionalObjects(groupkind string, objects []interface{}) (bool, error) {
|
||||
val, ok := check.AdditionalValidators[groupkind]
|
||||
if !ok {
|
||||
return false, errors.New("No validator found for " + groupkind)
|
||||
}
|
||||
for _, obj := range objects {
|
||||
bytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
errs, err := val.ValidateBytes(bytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
errs, err := check.SchemaFoo.ValidateBytes(context.TODO(), bytes)
|
||||
fmt.Println("vbytes1", errs, err)
|
||||
return len(errs) == 0, err
|
||||
}
|
||||
|
||||
// IsActionable decides if this check applies to a particular target
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
padding-right: 40px;
|
||||
box-shadow: none;
|
||||
}
|
||||
.card.transparent {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
.card.insights img {
|
||||
max-width: 400px;
|
||||
}
|
||||
.card.insights a {
|
||||
background-color: #20162D;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 34 KiB |
@@ -181,7 +181,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
|
||||
}
|
||||
|
||||
var auditDataObj validator.AuditData
|
||||
auditDataObj, err = validator.RunAudit(adjustedConf, k)
|
||||
auditDataObj, err = validator.RunAudit(adjustedConf, k, false)
|
||||
if err != nil {
|
||||
http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -214,7 +214,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
|
||||
}
|
||||
|
||||
var auditData validator.AuditData
|
||||
auditData, err = validator.RunAudit(adjustedConf, k)
|
||||
auditData, err = validator.RunAudit(adjustedConf, k, false)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error getting audit data: %v", err)
|
||||
http.Error(w, "Error running audit", 500)
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWarningWidth(t *testing.T) {
|
||||
input1 := validator.CountSummary{
|
||||
Successes: 0,
|
||||
Warnings: 0,
|
||||
Dangers: 0,
|
||||
}
|
||||
input2 := 6
|
||||
|
||||
expectedOutput := uint(0x6)
|
||||
actual := getWarningWidth(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
|
||||
input1 = validator.CountSummary{
|
||||
Successes: 10,
|
||||
Warnings: 3,
|
||||
Dangers: 1,
|
||||
}
|
||||
input2 = 3
|
||||
|
||||
expectedOutput = uint(0x2)
|
||||
actual = getWarningWidth(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
}
|
||||
func TestSuccessWidth(t *testing.T) {
|
||||
input1 := validator.CountSummary{
|
||||
Successes: 0,
|
||||
Warnings: 0,
|
||||
Dangers: 0,
|
||||
}
|
||||
input2 := 6
|
||||
|
||||
expectedOutput := uint(0x6)
|
||||
actual := getSuccessWidth(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
|
||||
input1 = validator.CountSummary{
|
||||
Successes: 8,
|
||||
Warnings: 6,
|
||||
Dangers: 4,
|
||||
}
|
||||
input2 = 7
|
||||
|
||||
expectedOutput = uint(0x3)
|
||||
actual = getSuccessWidth(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
}
|
||||
|
||||
func TestGetGrade(t *testing.T) {
|
||||
input := validator.CountSummary{
|
||||
Successes: 10,
|
||||
Warnings: 3,
|
||||
Dangers: 1,
|
||||
}
|
||||
expectedOutput := "B-"
|
||||
actual := getGrade(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "A+", actual)
|
||||
}
|
||||
|
||||
func TestGetWeatherIcon(t *testing.T) {
|
||||
input := validator.CountSummary{
|
||||
Successes: 10,
|
||||
Warnings: 3,
|
||||
Dangers: 1,
|
||||
}
|
||||
|
||||
expectedOutput := "fa-cloud-sun"
|
||||
actual := getWeatherIcon(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fa-cloud-showers-heavy", actual)
|
||||
}
|
||||
|
||||
func TestGetResultClass(t *testing.T) {
|
||||
input := validator.ResultMessage{
|
||||
ID: "",
|
||||
Message: "",
|
||||
Details: []string(nil),
|
||||
Success: false,
|
||||
Severity: "",
|
||||
Category: "",
|
||||
}
|
||||
|
||||
expectedOutput := " failure"
|
||||
actual := getResultClass(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, " success", actual)
|
||||
|
||||
input = validator.ResultMessage{
|
||||
ID: "",
|
||||
Message: "",
|
||||
Details: []string(nil),
|
||||
Success: true,
|
||||
Severity: "",
|
||||
Category: "",
|
||||
}
|
||||
|
||||
expectedOutput = " success"
|
||||
actual = getResultClass(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, " failure", actual)
|
||||
}
|
||||
|
||||
func TestGetWeatherText(t *testing.T) {
|
||||
input := validator.CountSummary{
|
||||
Successes: 10,
|
||||
Warnings: 3,
|
||||
Dangers: 1,
|
||||
}
|
||||
|
||||
expectedOutput := "Mostly smooth sailing"
|
||||
actual := getWeatherText(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "Storms ahead, be careful", actual)
|
||||
}
|
||||
|
||||
func TestGetIcon(t *testing.T) {
|
||||
input := validator.ResultMessage{
|
||||
ID: "",
|
||||
Message: "",
|
||||
Details: []string(nil),
|
||||
Success: false,
|
||||
Severity: "",
|
||||
Category: "",
|
||||
}
|
||||
|
||||
expectedOutput := "fas fa-times"
|
||||
actual := getIcon(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fas fa-check", actual)
|
||||
|
||||
input = validator.ResultMessage{
|
||||
ID: "",
|
||||
Message: "",
|
||||
Details: []string(nil),
|
||||
Success: true,
|
||||
Severity: "",
|
||||
Category: "",
|
||||
}
|
||||
|
||||
expectedOutput = "fas fa-check"
|
||||
actual = getIcon(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fas fa-times", actual)
|
||||
|
||||
input = validator.ResultMessage{
|
||||
ID: "",
|
||||
Message: "",
|
||||
Details: []string(nil),
|
||||
Success: false,
|
||||
Severity: config.SeverityWarning,
|
||||
Category: "",
|
||||
}
|
||||
|
||||
expectedOutput = "fas fa-exclamation"
|
||||
actual = getIcon(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fas fa-times", actual)
|
||||
}
|
||||
|
||||
func TestGetCategoryLink(t *testing.T) {
|
||||
input := "Efficiency"
|
||||
|
||||
expectedOutput := "https://polaris.docs.fairwinds.com/checks/efficiency"
|
||||
actual := getCategoryLink(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "ttps://polaris.docs.fairwinds.com/checks/reliability", actual)
|
||||
}
|
||||
|
||||
func TestGetCategoryInfo(t *testing.T) {
|
||||
input := "Security"
|
||||
|
||||
expectedOutput :=
|
||||
`
|
||||
Kubernetes provides a great deal of configurability when it comes to the
|
||||
security of your workloads. A key principle here involves limiting the level
|
||||
of access any individual workload has. Polaris has validations for a number of
|
||||
best practices, mostly focused on ensuring that unnecessary access has not
|
||||
been granted to an application workload.
|
||||
`
|
||||
actual := getCategoryInfo(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fas fa-times", actual)
|
||||
|
||||
input = "Reliability"
|
||||
|
||||
expectedOutput =
|
||||
`
|
||||
Kubernetes is built to reliabily run highly available applications.
|
||||
Polaris includes a number of checks to ensure that you are maximizing
|
||||
the reliability potential of Kubernetes.
|
||||
`
|
||||
actual = getCategoryInfo(input)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, "fas fa-times", actual)
|
||||
}
|
||||
|
||||
func TestStringInSlice(t *testing.T) {
|
||||
input1 := "a"
|
||||
input2 := []string{"a", "b", "cde"}
|
||||
|
||||
expectedOutput := true
|
||||
actual := stringInSlice(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, false, actual)
|
||||
|
||||
input1 = "f"
|
||||
input2 = []string{"a", "b", "cde"}
|
||||
|
||||
expectedOutput = false
|
||||
actual = stringInSlice(input1, input2)
|
||||
|
||||
assert.Equal(t, expectedOutput, actual)
|
||||
assert.NotEqual(t, true, actual)
|
||||
}
|
||||
|
||||
@@ -83,12 +83,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card transparent">
|
||||
<a class="align-self-center"
|
||||
target="_blank"
|
||||
href="https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=ad&utm_campaign=polarisad">
|
||||
<img src="static/images/FW_Insights_Polaris.svg" />
|
||||
</a>
|
||||
<div id="insights" class="card insights py-2">
|
||||
<img class="align-self-center" src="static/images/FW_Insights_Polaris.svg" />
|
||||
<span>Fairwinds, the company behind Polaris, now offers Fairwinds Insights, a multi-cluster, multi-user Kubernetes configuration validation and policy enforcement platform. Fairwinds Insights can help you avoid errors that lead to wasted time, compute costs, and increased risk.</span>
|
||||
<a class="col-auto py-1 px-3 mt-2 align-self-center" href="https://fairwinds.com/insights?source=polaris" target="_blank">Try Insights</a>
|
||||
</div>
|
||||
|
||||
<div id="categories" class="card category">
|
||||
@@ -136,11 +134,7 @@
|
||||
|
||||
{{ range $namespace, $results := .FilteredAuditData.GetResultsByNamespace }}
|
||||
<div id="{{ $namespace }}" class="card namespace">
|
||||
{{ if eq $namespace "" }}
|
||||
<h3>Cluster Resources</h3>
|
||||
{{ else }}
|
||||
<h3>Namespace: <strong>{{ $namespace }}</strong></h3>
|
||||
{{ end }}
|
||||
<div class="expandable-table">
|
||||
{{ range $index, $result := $results }}
|
||||
<div class="resource-info">
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<hr class="col-11">
|
||||
<div class="align-self-center d-flex flex-column justify-content-center">
|
||||
<img class="row mb-4 fw-logo" src="static/images/purple_logo_fairwinds.svg" alt="Fairwinds" />
|
||||
<a class="row justify-content-center" href="https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=polaris&utm_campaign=polaris" target="_blank">© 2020 Fairwinds Ops Inc.</a>
|
||||
<a class="row justify-content-center" href="https://fairwinds.com?source=polaris" target="_blank">© 2020 Fairwinds Ops Inc.</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="navbar">
|
||||
<div class="fw-nav">
|
||||
<div class="container p-2">
|
||||
<a href="https://www.fairwinds.com/polaris-user-insights-demo?utm_source=polaris&utm_medium=polaris&utm_campaign=polaris" target="_blank">
|
||||
<a href="https://fairwinds.com?source=polaris" target="_blank">
|
||||
<img class="fw-logo" src="static/images/white_logo_fairwinds.svg" alt="Fairwinds" />
|
||||
</a>
|
||||
<div class="right-section p-0 d-flex justify-content-between">
|
||||
|
||||
@@ -25,23 +25,24 @@ type GenericResource struct {
|
||||
}
|
||||
|
||||
// NewGenericResourceFromUnstructured creates a workload from an unstructured.Unstructured
|
||||
func NewGenericResourceFromUnstructured(unst unstructured.Unstructured) (GenericResource, error) {
|
||||
func NewGenericResourceFromUnstructured(unst *unstructured.Unstructured) (GenericResource, error) {
|
||||
workload := GenericResource{
|
||||
Kind: unst.GetKind(),
|
||||
Resource: unst,
|
||||
Resource: *unst,
|
||||
}
|
||||
|
||||
objMeta, err := meta.Accessor(&unst)
|
||||
objMeta, err := meta.Accessor(unst)
|
||||
if err != nil {
|
||||
return workload, err
|
||||
}
|
||||
workload.ObjectMeta = objMeta
|
||||
|
||||
b, err := json.Marshal(&unst)
|
||||
b, err := json.Marshal(unst)
|
||||
if err != nil {
|
||||
return workload, err
|
||||
}
|
||||
workload.OriginalObjectJSON = b
|
||||
|
||||
m := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
@@ -60,6 +61,7 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured) (Generic
|
||||
}
|
||||
workload.PodSpec = &podSpec
|
||||
}
|
||||
|
||||
return workload, nil
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ func NewGenericResourceFromBytes(contentBytes []byte) (GenericResource, error) {
|
||||
if err != nil {
|
||||
return GenericResource{}, err
|
||||
}
|
||||
return NewGenericResourceFromUnstructured(unst)
|
||||
return NewGenericResourceFromUnstructured(&unst)
|
||||
}
|
||||
|
||||
// ResolveControllerFromPod builds a new workload for a given Pod
|
||||
@@ -159,7 +161,7 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
|
||||
|
||||
if lastKey != "" {
|
||||
unst := objectCache[lastKey]
|
||||
return NewGenericResourceFromUnstructured(unst)
|
||||
return NewGenericResourceFromUnstructured(&unst)
|
||||
}
|
||||
workload, err := NewGenericResourceFromPod(podResource, podResource)
|
||||
if err != nil {
|
||||
|
||||
+12
-40
@@ -43,9 +43,10 @@ type ResourceProvider struct {
|
||||
type resourceKindMap map[string][]GenericResource
|
||||
|
||||
func (rkm resourceKindMap) addResource(r GenericResource) {
|
||||
gvk := r.Resource.GroupVersionKind()
|
||||
key := gvk.Group + "/" + gvk.Kind
|
||||
rkm[key] = append(rkm[key], r)
|
||||
if _, ok := rkm[r.Kind]; !ok {
|
||||
rkm[r.Kind] = make([]GenericResource, 0)
|
||||
}
|
||||
rkm[r.Kind] = append(rkm[r.Kind], r)
|
||||
}
|
||||
|
||||
func (rkm resourceKindMap) addResources(rs []GenericResource) {
|
||||
@@ -75,24 +76,11 @@ func (rkm resourceKindMap) GetNumberOfControllers() int {
|
||||
}
|
||||
|
||||
// This is here for backward compatibility reasons
|
||||
func maybeTransformKindIntoGroupKind(k string) string {
|
||||
func maybeTransformKindIntoGroupKind(k conf.TargetKind) string {
|
||||
if k == "Ingress" {
|
||||
return "networking.k8s.io/Ingress"
|
||||
} else if k == "PodDisruptionBudget" {
|
||||
return "policy/PodDisruptionBudget"
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func parseGroupKind(gk string) schema.GroupKind {
|
||||
i := strings.Index(gk, "/")
|
||||
if i == -1 {
|
||||
return schema.GroupKind{Kind: gk}
|
||||
}
|
||||
|
||||
group := gk[:i]
|
||||
kind := gk[i+1:]
|
||||
return schema.GroupKind{Group: group, Kind: kind}
|
||||
return ""
|
||||
}
|
||||
|
||||
func newResourceProvider(version, sourceType, sourceName string) ResourceProvider {
|
||||
@@ -168,7 +156,7 @@ func CreateResourceProviderFromResource(ctx context.Context, workload string) (*
|
||||
logrus.Errorf("Could not find workload %s: %v", workload, err)
|
||||
return nil, err
|
||||
}
|
||||
workloadObj, err := NewGenericResourceFromUnstructured(*obj)
|
||||
workloadObj, err := NewGenericResourceFromUnstructured(obj)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not parse workload %s: %v", workload, err)
|
||||
return nil, err
|
||||
@@ -263,32 +251,16 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
return nil, err
|
||||
}
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(resources)
|
||||
allChecks := []conf.SchemaCheck{}
|
||||
for _, check := range c.CustomChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
for _, check := range conf.BuiltInChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
|
||||
var additionalKinds []conf.TargetKind
|
||||
for _, check := range allChecks {
|
||||
neededKinds := []conf.TargetKind{check.Target}
|
||||
for key := range check.AdditionalSchemas {
|
||||
neededKinds = append(neededKinds, conf.TargetKind(key))
|
||||
}
|
||||
for key := range check.AdditionalSchemaStrings {
|
||||
neededKinds = append(neededKinds, conf.TargetKind(key))
|
||||
}
|
||||
for _, kind := range neededKinds {
|
||||
if !funk.Contains(conf.HandledTargets, kind) && !funk.Contains(additionalKinds, kind) {
|
||||
additionalKinds = append(additionalKinds, kind)
|
||||
}
|
||||
for _, check := range c.CustomChecks {
|
||||
if !funk.Contains(conf.HandledTargets, check.Target) {
|
||||
additionalKinds = append(additionalKinds, check.Target)
|
||||
}
|
||||
}
|
||||
|
||||
for _, kind := range additionalKinds {
|
||||
groupKind := parseGroupKind(maybeTransformKindIntoGroupKind(string(kind)))
|
||||
groupKind := schema.ParseGroupKind(maybeTransformKindIntoGroupKind(kind))
|
||||
mapping, err := (restMapper).RESTMapping(groupKind)
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving mapping of Kind %s because of error: %v", kind, err)
|
||||
@@ -301,7 +273,7 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
return nil, err
|
||||
}
|
||||
for _, obj := range objects.Items {
|
||||
res, err := NewGenericResourceFromUnstructured(obj)
|
||||
res, err := NewGenericResourceFromUnstructured(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package kube
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -28,7 +29,8 @@ func TestGetResourcesFromPath(t *testing.T) {
|
||||
assert.Equal(t, "two", provider.Namespaces[0].ObjectMeta.Name)
|
||||
|
||||
namespaceCount := map[string]int{}
|
||||
for _, resources := range provider.Resources {
|
||||
for kind, resources := range provider.Resources {
|
||||
fmt.Println("found", kind, len(resources))
|
||||
for _, controller := range resources {
|
||||
namespaceCount[controller.ObjectMeta.GetNamespace()]++
|
||||
}
|
||||
@@ -50,8 +52,8 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
|
||||
assert.Equal(t, 1, len(resources.Resources["extensions/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["extensions/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
assert.Equal(t, 1, len(resources.Resources["Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
@@ -73,8 +75,8 @@ func TestAddResourcesFromReader(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
|
||||
assert.Equal(t, 1, len(resources.Resources["extensions/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["extensions/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
assert.Equal(t, 1, len(resources.Resources["Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
|
||||
@@ -29,18 +29,18 @@ import (
|
||||
func TestValidatePDB(t *testing.T) {
|
||||
c := conf.Configuration{
|
||||
Checks: map[string]conf.Severity{
|
||||
"pdbDisruptionsIsZero": conf.SeverityWarning,
|
||||
"pdbDisruptionsAllowedGreaterThanZero": conf.SeverityWarning,
|
||||
},
|
||||
}
|
||||
pdb := unstructured.Unstructured{}
|
||||
res, err := kube.NewGenericResourceFromUnstructured(pdb)
|
||||
res, err := kube.NewGenericResourceFromUnstructured(&pdb)
|
||||
res.Kind = "PodDisruptionBudget"
|
||||
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, nil, res)
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
results := actualResult.Results["pdbDisruptionsIsZero"]
|
||||
results := actualResult.Results["pdbDisruptionsAllowedGreaterThanZero"]
|
||||
|
||||
assert.False(t, results.Success)
|
||||
assert.Equal(t, conf.SeverityWarning, results.Severity)
|
||||
@@ -70,13 +70,13 @@ func TestValidateIngress(t *testing.T) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res, err := kube.NewGenericResourceFromUnstructured(unst)
|
||||
res, err := kube.NewGenericResourceFromUnstructured(&unst)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res.Kind = "Ingress"
|
||||
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, nil, res)
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -69,23 +69,20 @@ func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourc
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
|
||||
var results ResultSet
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, nil, workload, container, false)
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, workload, container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
summary := results.GetSummary()
|
||||
|
||||
if assert.Equal(t, uint(len(expectedWarnings)), summary.Warnings) {
|
||||
assert.ElementsMatch(t, expectedWarnings, results.GetWarnings())
|
||||
}
|
||||
assert.Equal(t, uint(len(expectedWarnings)), summary.Warnings)
|
||||
assert.ElementsMatch(t, expectedWarnings, results.GetWarnings())
|
||||
|
||||
if assert.Equal(t, uint(len(expectedDangers)), summary.Dangers) {
|
||||
assert.ElementsMatch(t, expectedDangers, results.GetDangers())
|
||||
}
|
||||
assert.Equal(t, uint(len(expectedDangers)), summary.Dangers)
|
||||
assert.ElementsMatch(t, expectedDangers, results.GetDangers())
|
||||
|
||||
if assert.Equal(t, uint(len(expectedSuccesses)), summary.Successes) {
|
||||
assert.ElementsMatch(t, expectedSuccesses, results.GetSuccesses())
|
||||
}
|
||||
assert.Equal(t, uint(len(expectedSuccesses)), summary.Successes)
|
||||
assert.ElementsMatch(t, expectedSuccesses, results.GetSuccesses())
|
||||
}
|
||||
|
||||
func TestValidateResourcesEmptyConfig(t *testing.T) {
|
||||
@@ -93,7 +90,7 @@ func TestValidateResourcesEmptyConfig(t *testing.T) {
|
||||
Name: "Empty",
|
||||
}
|
||||
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{}, nil, getEmptyWorkload(t, ""), container, false)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{}, getEmptyWorkload(t, ""), container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -190,7 +187,7 @@ func TestValidateHealthChecks(t *testing.T) {
|
||||
for idx, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, nil, controller, tt.container, tt.isInit)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, controller, tt.container, tt.isInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -304,7 +301,7 @@ func TestValidateImage(t *testing.T) {
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, nil, controller, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -421,7 +418,7 @@ func TestValidateNetworking(t *testing.T) {
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, nil, controller, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -926,7 +923,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
|
||||
assert.NoError(t, err)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, nil, workload, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1071,7 +1068,7 @@ func TestValidateRunAsRoot(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
|
||||
assert.NoError(t, err)
|
||||
results, err := applyContainerSchemaChecks(&config, nil, workload, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(&config, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestValidateController(t *testing.T) {
|
||||
}
|
||||
|
||||
var actualResult Result
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, deployment)
|
||||
actualResult, err = applyControllerSchemaChecks(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func TestControllerLevelChecks(t *testing.T) {
|
||||
Category: "Reliability",
|
||||
}
|
||||
for _, controller := range res.Resources["Deployment"] {
|
||||
actualResult, err := applyControllerSchemaChecks(&c, nil, controller)
|
||||
actualResult, err := applyControllerSchemaChecks(&c, controller)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
"livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Reliability"},
|
||||
}
|
||||
var actualResult Result
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, deployment)
|
||||
actualResult, err = applyControllerSchemaChecks(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
Dangers: uint(0),
|
||||
}
|
||||
expectedResults = ResultSet{}
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, job)
|
||||
actualResult, err = applyControllerSchemaChecks(&c, job)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
Dangers: uint(0),
|
||||
}
|
||||
expectedResults = ResultSet{}
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, cronjob)
|
||||
actualResult, err = applyControllerSchemaChecks(&c, cronjob)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
resources := []kube.GenericResource{workload}
|
||||
|
||||
var actualResults []Result
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
c.Exemptions = []conf.Exemption{{
|
||||
Namespace: "foo",
|
||||
}}
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -233,7 +233,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
resources[0].ObjectMeta.SetAnnotations(map[string]string{
|
||||
exemptionAnnotationKey: "true",
|
||||
})
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
assert.EqualValues(t, expectedExemptSum, actualResults[0].GetSummary())
|
||||
|
||||
c.DisallowExemptions = true
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -16,15 +16,19 @@ import (
|
||||
)
|
||||
|
||||
// RunAudit runs a full Polaris audit and returns an AuditData object
|
||||
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) {
|
||||
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider, onlyShowFailedTests bool) (AuditData, error) {
|
||||
displayName := config.DisplayName
|
||||
if displayName == "" {
|
||||
displayName = kubeResources.SourceName
|
||||
}
|
||||
|
||||
results, err := ApplyAllSchemaChecksToResourceProvider(&config, kubeResources)
|
||||
if err != nil {
|
||||
return AuditData{}, err
|
||||
results := []Result{}
|
||||
for _, resources := range kubeResources.Resources {
|
||||
kindResults, err := ApplyAllSchemaChecksToAllResources(&config, resources)
|
||||
if err != nil {
|
||||
return AuditData{}, err
|
||||
}
|
||||
results = append(results, kindResults...)
|
||||
}
|
||||
|
||||
auditData := AuditData{
|
||||
@@ -42,6 +46,9 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (
|
||||
Results: results,
|
||||
}
|
||||
auditData.Score = auditData.GetSummary().GetScore()
|
||||
if onlyShowFailedTests {
|
||||
auditData.RemoveSuccessfulResults()
|
||||
}
|
||||
return auditData, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestGetTemplateData(t *testing.T) {
|
||||
score := uint(0)
|
||||
|
||||
var actualAudit AuditData
|
||||
actualAudit, err = RunAudit(c, resources)
|
||||
actualAudit, err = RunAudit(c, resources, false)
|
||||
assert.Equal(t, err, nil, "error should be nil")
|
||||
assert.Equal(t, score, actualAudit.Score, "")
|
||||
assert.EqualValues(t, sum, actualAudit.GetSummary())
|
||||
|
||||
+21
-37
@@ -19,7 +19,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/thoas/go-funk"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
)
|
||||
@@ -53,12 +52,10 @@ type AuditData struct {
|
||||
}
|
||||
|
||||
// RemoveSuccessfulResults remove all test that have passed.
|
||||
func (res AuditData) RemoveSuccessfulResults() AuditData {
|
||||
resCopy := res
|
||||
resCopy.Results = funk.Map(res.Results, func(auditDataResult Result) Result {
|
||||
return auditDataResult.removeSuccessfulResults()
|
||||
}).([]Result)
|
||||
return resCopy
|
||||
func (res *AuditData) RemoveSuccessfulResults() {
|
||||
for _, auditDataResult := range res.Results {
|
||||
auditDataResult.removeSuccessfulResults()
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterInfo contains Polaris results as well as some high-level stats
|
||||
@@ -74,7 +71,6 @@ type ClusterInfo struct {
|
||||
type ResultMessage struct {
|
||||
ID string
|
||||
Message string
|
||||
Details []string
|
||||
Success bool
|
||||
Severity config.Severity
|
||||
Category string
|
||||
@@ -83,14 +79,12 @@ type ResultMessage struct {
|
||||
// ResultSet contiains the results for a set of checks
|
||||
type ResultSet map[string]ResultMessage
|
||||
|
||||
func (res ResultSet) removeSuccessfulResults() ResultSet {
|
||||
newResults := ResultSet{}
|
||||
func (res ResultSet) removeSuccessfulResults() {
|
||||
for k, resultMessage := range res {
|
||||
if !resultMessage.Success {
|
||||
newResults[k] = resultMessage
|
||||
if resultMessage.Success {
|
||||
delete(res, k)
|
||||
}
|
||||
}
|
||||
return newResults
|
||||
}
|
||||
|
||||
// Result provides results for a Kubernetes object
|
||||
@@ -103,14 +97,9 @@ type Result struct {
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
func (res Result) removeSuccessfulResults() Result {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.removeSuccessfulResults()
|
||||
if res.PodResult != nil {
|
||||
podCopy := res.PodResult.removeSuccessfulResults()
|
||||
resCopy.PodResult = &podCopy
|
||||
}
|
||||
return resCopy
|
||||
func (res *Result) removeSuccessfulResults() {
|
||||
res.Results.removeSuccessfulResults()
|
||||
res.PodResult.removeSuccessfulResults()
|
||||
}
|
||||
|
||||
// PodResult provides a list of validation messages for each pod.
|
||||
@@ -120,13 +109,14 @@ type PodResult struct {
|
||||
ContainerResults []ContainerResult
|
||||
}
|
||||
|
||||
func (res PodResult) removeSuccessfulResults() PodResult {
|
||||
resCopy := PodResult{}
|
||||
resCopy.Results = res.Results.removeSuccessfulResults()
|
||||
resCopy.ContainerResults = funk.Map(res.ContainerResults, func(containerResult ContainerResult) ContainerResult {
|
||||
return containerResult.removeSuccessfulResults()
|
||||
}).([]ContainerResult)
|
||||
return resCopy
|
||||
func (res *PodResult) removeSuccessfulResults() {
|
||||
if res == nil {
|
||||
return
|
||||
}
|
||||
res.Results.removeSuccessfulResults()
|
||||
for _, containerResult := range res.ContainerResults {
|
||||
containerResult.removeSuccessfulResults()
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerResult provides a list of validation messages for each container.
|
||||
@@ -135,10 +125,8 @@ type ContainerResult struct {
|
||||
Results ResultSet
|
||||
}
|
||||
|
||||
func (res ContainerResult) removeSuccessfulResults() ContainerResult {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.removeSuccessfulResults()
|
||||
return resCopy
|
||||
func (res *ContainerResult) removeSuccessfulResults() {
|
||||
res.Results.removeSuccessfulResults()
|
||||
}
|
||||
|
||||
func fillString(id string, l int) string {
|
||||
@@ -164,11 +152,7 @@ func (res AuditData) GetPrettyOutput(useColor bool) string {
|
||||
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
func (res Result) GetPrettyOutput() string {
|
||||
str := titleColor.Sprint(fmt.Sprintf("%s %s", res.Kind, res.Name))
|
||||
if res.Namespace != "" {
|
||||
str += titleColor.Sprint(fmt.Sprintf("in namespace %s", res.Namespace))
|
||||
}
|
||||
str += "\n"
|
||||
str := titleColor.Sprint(fmt.Sprintf("%s %s in namespace %s\n", res.Kind, res.Name, res.Namespace))
|
||||
str += res.Results.GetPrettyOutput()
|
||||
if res.PodResult != nil {
|
||||
str += res.PodResult.GetPrettyOutput()
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestValidatePod(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, nil, deployment)
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func TestInvalidIPCPod(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ func TestInvalidNetworkPod(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func TestInvalidPIDPod(t *testing.T) {
|
||||
"hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -200,7 +200,7 @@ func TestExemption(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
|
||||
actualPodResult, err := applyControllerSchemaChecks(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+86
-79
@@ -5,8 +5,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/qri-io/jsonschema"
|
||||
"github.com/thoas/go-funk"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -14,12 +13,63 @@ import (
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
)
|
||||
|
||||
var (
|
||||
schemaBox = (*packr.Box)(nil)
|
||||
builtInChecks = map[string]config.SchemaCheck{}
|
||||
// We explicitly set the order to avoid thrash in the
|
||||
// tests as we migrate toward JSON schema
|
||||
checkOrder = []string{
|
||||
// Controller Checks
|
||||
"multipleReplicasForDeployment",
|
||||
// Pod checks
|
||||
"hostIPCSet",
|
||||
"hostPIDSet",
|
||||
"hostNetworkSet",
|
||||
// Container checks
|
||||
"memoryLimitsMissing",
|
||||
"memoryRequestsMissing",
|
||||
"cpuLimitsMissing",
|
||||
"cpuRequestsMissing",
|
||||
"readinessProbeMissing",
|
||||
"livenessProbeMissing",
|
||||
"pullPolicyNotAlways",
|
||||
"tagNotSpecified",
|
||||
"hostPortSet",
|
||||
"runAsRootAllowed",
|
||||
"runAsPrivileged",
|
||||
"notReadOnlyRootFilesystem",
|
||||
"privilegeEscalationAllowed",
|
||||
"dangerousCapabilities",
|
||||
"insecureCapabilities",
|
||||
"priorityClassNotSet",
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsAllowedGreaterThanZero",
|
||||
"metadataMatchesName",
|
||||
}
|
||||
)
|
||||
|
||||
type schemaTestCase struct {
|
||||
Target config.TargetKind
|
||||
Resource kube.GenericResource
|
||||
IsInitContianer bool
|
||||
Container *corev1.Container
|
||||
ResourceProvider *kube.ResourceProvider
|
||||
Target config.TargetKind
|
||||
Resource kube.GenericResource
|
||||
IsInitContianer bool
|
||||
Container *corev1.Container
|
||||
}
|
||||
|
||||
func init() {
|
||||
schemaBox = packr.New("Schemas", "../../checks")
|
||||
for _, checkID := range checkOrder {
|
||||
contents, err := schemaBox.Find(checkID + ".yaml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
check, err := config.ParseCheck(contents)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
check.Initialize(checkID)
|
||||
builtInChecks[checkID] = check
|
||||
}
|
||||
}
|
||||
|
||||
func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCase) (*config.SchemaCheck, error) {
|
||||
@@ -28,7 +78,7 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
|
||||
}
|
||||
check, ok := conf.CustomChecks[checkID]
|
||||
if !ok {
|
||||
check, ok = config.BuiltInChecks[checkID]
|
||||
check, ok = builtInChecks[checkID]
|
||||
}
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Check %s not found", checkID)
|
||||
@@ -51,18 +101,12 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
|
||||
return checkPtr, nil
|
||||
}
|
||||
|
||||
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.ValError) ResultMessage {
|
||||
details := []string{}
|
||||
for _, issue := range issues {
|
||||
details = append(details, issue.Message)
|
||||
}
|
||||
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool) ResultMessage {
|
||||
result := ResultMessage{
|
||||
ID: check.ID,
|
||||
Severity: conf.Checks[check.ID],
|
||||
Category: check.Category,
|
||||
Success: passes,
|
||||
// FIXME: need to fix the tests before adding this back
|
||||
//Details: details,
|
||||
}
|
||||
if passes {
|
||||
result.Message = check.SuccessMessage
|
||||
@@ -89,24 +133,11 @@ func hasExemptionAnnotation(objMeta metaV1.Object, checkID string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ApplyAllSchemaChecksToResourceProvider applies all available checks to a ResourceProvider
|
||||
func ApplyAllSchemaChecksToResourceProvider(conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) {
|
||||
results := []Result{}
|
||||
for _, resources := range resourceProvider.Resources {
|
||||
kindResults, err := ApplyAllSchemaChecksToAllResources(conf, resourceProvider, resources)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
results = append(results, kindResults...)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// ApplyAllSchemaChecksToAllResources applies available checks to a list of resources
|
||||
func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resources []kube.GenericResource) ([]Result, error) {
|
||||
func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resources []kube.GenericResource) ([]Result, error) {
|
||||
results := []Result{}
|
||||
for _, resource := range resources {
|
||||
result, err := ApplyAllSchemaChecks(conf, resourceProvider, resource)
|
||||
result, err := ApplyAllSchemaChecks(conf, resource)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
@@ -116,37 +147,37 @@ func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProv
|
||||
}
|
||||
|
||||
// ApplyAllSchemaChecks applies available checks to a single resource
|
||||
func ApplyAllSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
|
||||
func ApplyAllSchemaChecks(conf *config.Configuration, resource kube.GenericResource) (Result, error) {
|
||||
if resource.PodSpec == nil {
|
||||
return applyNonControllerSchemaChecks(conf, resourceProvider, resource)
|
||||
return applyNonControllerSchemaChecks(conf, resource)
|
||||
}
|
||||
return applyControllerSchemaChecks(conf, resourceProvider, resource)
|
||||
return applyControllerSchemaChecks(conf, resource)
|
||||
}
|
||||
|
||||
func applyNonControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
|
||||
func applyNonControllerSchemaChecks(conf *config.Configuration, resource kube.GenericResource) (Result, error) {
|
||||
finalResult := Result{
|
||||
Kind: resource.Kind,
|
||||
Name: resource.ObjectMeta.GetName(),
|
||||
Namespace: resource.ObjectMeta.GetNamespace(),
|
||||
}
|
||||
resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, false)
|
||||
resultSet, err := applyTopLevelSchemaChecks(conf, resource, false)
|
||||
finalResult.Results = resultSet
|
||||
return finalResult, err
|
||||
}
|
||||
|
||||
func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
|
||||
func applyControllerSchemaChecks(conf *config.Configuration, resource kube.GenericResource) (Result, error) {
|
||||
finalResult := Result{
|
||||
Kind: resource.Kind,
|
||||
Name: resource.ObjectMeta.GetName(),
|
||||
Namespace: resource.ObjectMeta.GetNamespace(),
|
||||
}
|
||||
resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, true)
|
||||
resultSet, err := applyTopLevelSchemaChecks(conf, resource, true)
|
||||
if err != nil {
|
||||
return finalResult, err
|
||||
}
|
||||
finalResult.Results = resultSet
|
||||
|
||||
podRS, err := applyPodSchemaChecks(conf, resourceProvider, resource)
|
||||
podRS, err := applyPodSchemaChecks(conf, resource)
|
||||
if err != nil {
|
||||
return finalResult, err
|
||||
}
|
||||
@@ -157,7 +188,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
|
||||
finalResult.PodResult = &podRes
|
||||
|
||||
for _, container := range resource.PodSpec.InitContainers {
|
||||
results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, true)
|
||||
results, err := applyContainerSchemaChecks(conf, resource, &container, true)
|
||||
if err != nil {
|
||||
return finalResult, err
|
||||
}
|
||||
@@ -168,7 +199,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
|
||||
podRes.ContainerResults = append(podRes.ContainerResults, cRes)
|
||||
}
|
||||
for _, container := range resource.PodSpec.Containers {
|
||||
results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, false)
|
||||
results, err := applyContainerSchemaChecks(conf, resource, &container, false)
|
||||
if err != nil {
|
||||
return finalResult, err
|
||||
}
|
||||
@@ -182,10 +213,9 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
|
||||
return finalResult, nil
|
||||
}
|
||||
|
||||
func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, res kube.GenericResource, isController bool) (ResultSet, error) {
|
||||
func applyTopLevelSchemaChecks(conf *config.Configuration, res kube.GenericResource, isController bool) (ResultSet, error) {
|
||||
test := schemaTestCase{
|
||||
ResourceProvider: resources,
|
||||
Resource: res,
|
||||
Resource: res,
|
||||
}
|
||||
if isController {
|
||||
test.Target = config.TargetController
|
||||
@@ -193,22 +223,20 @@ func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.Resou
|
||||
return applySchemaChecks(conf, test)
|
||||
}
|
||||
|
||||
func applyPodSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource) (ResultSet, error) {
|
||||
func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericResource) (ResultSet, error) {
|
||||
test := schemaTestCase{
|
||||
Target: config.TargetPod,
|
||||
ResourceProvider: resources,
|
||||
Resource: controller,
|
||||
Target: config.TargetPod,
|
||||
Resource: controller,
|
||||
}
|
||||
return applySchemaChecks(conf, test)
|
||||
}
|
||||
|
||||
func applyContainerSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) {
|
||||
func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) {
|
||||
test := schemaTestCase{
|
||||
Target: config.TargetContainer,
|
||||
ResourceProvider: resources,
|
||||
Resource: controller,
|
||||
Container: container,
|
||||
IsInitContianer: isInit,
|
||||
Target: config.TargetContainer,
|
||||
Resource: controller,
|
||||
Container: container,
|
||||
IsInitContianer: isInit,
|
||||
}
|
||||
return applySchemaChecks(conf, test)
|
||||
}
|
||||
@@ -236,47 +264,26 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
|
||||
return nil, nil
|
||||
}
|
||||
var passes bool
|
||||
var issues []jsonschema.ValError
|
||||
if check.SchemaTarget != "" {
|
||||
if check.SchemaTarget == config.TargetPod && check.Target == config.TargetContainer {
|
||||
podCopy := *test.Resource.PodSpec
|
||||
podCopy.InitContainers = []corev1.Container{}
|
||||
podCopy.Containers = []corev1.Container{*test.Container}
|
||||
passes, issues, err = check.CheckPod(&podCopy)
|
||||
passes, err = check.CheckPod(&podCopy)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget)
|
||||
}
|
||||
} else if check.Target == config.TargetPod {
|
||||
passes, issues, err = check.CheckPod(test.Resource.PodSpec)
|
||||
passes, err = check.CheckPod(test.Resource.PodSpec)
|
||||
} else if check.Target == config.TargetContainer {
|
||||
passes, issues, err = check.CheckContainer(test.Container)
|
||||
passes, err = check.CheckContainer(test.Container)
|
||||
} else {
|
||||
passes, issues, err = check.CheckObject(test.Resource.Resource.Object)
|
||||
passes, err = check.CheckObject(test.Resource.Resource.Object)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for groupkind := range check.AdditionalValidators {
|
||||
if !passes {
|
||||
break
|
||||
}
|
||||
resources := test.ResourceProvider.Resources[groupkind]
|
||||
namespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
if test.Resource.Kind == "Namespace" {
|
||||
namespace = test.Resource.ObjectMeta.GetName()
|
||||
}
|
||||
resources = funk.Filter(resources, func(res kube.GenericResource) bool {
|
||||
return res.ObjectMeta.GetNamespace() == namespace
|
||||
}).([]kube.GenericResource)
|
||||
objects := funk.Map(resources, func(res kube.GenericResource) interface{} {
|
||||
return res.Resource.Object
|
||||
}).([]interface{})
|
||||
passes, err = check.CheckAdditionalObjects(groupkind, objects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
result := makeResult(conf, check, passes, issues)
|
||||
result := makeResult(conf, check, passes)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -144,14 +144,14 @@ func TestValidateResourcesInit(t *testing.T) {
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
|
||||
var results ResultSet
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, false)
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
assert.Equal(t, uint(1), results.GetSummary().Dangers)
|
||||
assert.Equal(t, uint(1), results.GetSummary().Warnings)
|
||||
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, true)
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ func (cs CountSummary) GetScore() uint {
|
||||
if total == 0 {
|
||||
return uint(100)
|
||||
}
|
||||
score := uint((float64(cs.Successes*2) / float64(total)) * 100)
|
||||
return score
|
||||
return uint((float64(cs.Successes*2) / float64(total)) * 100)
|
||||
}
|
||||
|
||||
// AddSummary adds two CountSummaries together
|
||||
@@ -125,8 +124,10 @@ func (c Result) GetSummaryByCategory() CountSummaryByCategory {
|
||||
// GetSummary summarizes AuditData
|
||||
func (a AuditData) GetSummary() CountSummary {
|
||||
summary := CountSummary{}
|
||||
for _, res := range a.Results {
|
||||
summary.AddSummary(res.GetSummary())
|
||||
for _, ctrlResult := range a.Results {
|
||||
if ctrlResult.PodResult != nil {
|
||||
summary.AddSummary(ctrlResult.GetSummary())
|
||||
}
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
+49
-28
@@ -16,6 +16,8 @@ package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -54,11 +56,37 @@ func NewWebhook(mgr manager.Manager, validator Validator) {
|
||||
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &validator})
|
||||
}
|
||||
|
||||
func (v *Validator) handleInternal(req admission.Request) (*validator.Result, error) {
|
||||
var controller kube.GenericResource
|
||||
// GetObjectFromRawRequest returns the pod object and the controller's object from the raw json bytes.
|
||||
func GetObjectFromRawRequest(raw []byte) (corev1.Pod, interface{}, error) {
|
||||
pod := corev1.Pod{}
|
||||
var originalObject interface{}
|
||||
|
||||
decoded := map[string]interface{}{}
|
||||
err := json.Unmarshal(raw, &decoded)
|
||||
if err != nil {
|
||||
return pod, originalObject, err
|
||||
}
|
||||
podMap := kube.GetPodSpec(decoded)
|
||||
if podMap == nil {
|
||||
return pod, originalObject, errors.New("Object does not contain pods")
|
||||
}
|
||||
encoded, err := json.Marshal(podMap)
|
||||
if err != nil {
|
||||
return pod, originalObject, err
|
||||
}
|
||||
err = json.Unmarshal(encoded, &pod.Spec)
|
||||
if err != nil {
|
||||
return pod, originalObject, err
|
||||
}
|
||||
originalObject = decoded
|
||||
return pod, originalObject, err
|
||||
}
|
||||
|
||||
func (v *Validator) handleInternal(req admission.Request) (*validator.PodResult, error) {
|
||||
pod := corev1.Pod{}
|
||||
var originalObject interface{}
|
||||
var err error
|
||||
if req.AdmissionRequest.Kind.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
err := v.decoder.Decode(req, &pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,64 +95,57 @@ func (v *Validator) handleInternal(req admission.Request) (*validator.Result, er
|
||||
logrus.Infof("Allowing owned pod %s/%s to pass through webhook", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
return nil, nil
|
||||
}
|
||||
controller, err = kube.NewGenericResourceFromPod(pod, pod)
|
||||
originalObject = pod
|
||||
} else {
|
||||
controller, err = kube.NewGenericResourceFromBytes(req.Object.Raw)
|
||||
pod, originalObject, err = GetObjectFromRawRequest(req.Object.Raw)
|
||||
}
|
||||
controller, err := kube.NewGenericResourceFromPod(pod, originalObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: consider enabling multi-resource checks
|
||||
controllerResult, err := validator.ApplyAllSchemaChecks(&v.Config, nil, controller)
|
||||
controller.Kind = req.AdmissionRequest.Kind.Kind
|
||||
var controllerResult validator.Result
|
||||
controllerResult, err = validator.ApplyAllSchemaChecks(&v.Config, controller)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerResult, nil
|
||||
return controllerResult.PodResult, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
logrus.Info("Starting request")
|
||||
result, err := v.handleInternal(req)
|
||||
podResult, err := v.handleInternal(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error validating request: %v", err)
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
allowed := true
|
||||
reason := ""
|
||||
if result != nil {
|
||||
numDangers := result.GetSummary().Dangers
|
||||
if podResult != nil {
|
||||
numDangers := podResult.GetSummary().Dangers
|
||||
if numDangers > 0 {
|
||||
allowed = false
|
||||
reason = getFailureReason(*result)
|
||||
reason = getFailureReason(*podResult)
|
||||
}
|
||||
logrus.Infof("%d validation errors found when validating %s", numDangers, result.Name)
|
||||
logrus.Infof("%d validation errors found when validating %s", numDangers, podResult.Name)
|
||||
}
|
||||
return admission.ValidationResponse(allowed, reason)
|
||||
}
|
||||
|
||||
func getFailureReason(result validator.Result) string {
|
||||
func getFailureReason(podResult validator.PodResult) string {
|
||||
reason := "\nPolaris prevented this deployment due to configuration problems:\n"
|
||||
|
||||
for _, message := range result.Results {
|
||||
for _, message := range podResult.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- %s: %s\n", result.Kind, message.Message)
|
||||
reason += fmt.Sprintf("- Pod: %s\n", message.Message)
|
||||
}
|
||||
}
|
||||
|
||||
podResult := result.PodResult
|
||||
if podResult != nil {
|
||||
for _, message := range podResult.Results {
|
||||
for _, containerResult := range podResult.ContainerResults {
|
||||
for _, message := range containerResult.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- Pod: %s\n", message.Message)
|
||||
}
|
||||
}
|
||||
|
||||
for _, containerResult := range podResult.ContainerResults {
|
||||
for _, message := range containerResult.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- Container %s: %s\n", containerResult.Name, message.Message)
|
||||
}
|
||||
reason += fmt.Sprintf("- Container %s: %s\n", containerResult.Name, message.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 128Mi
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 256Mi
|
||||
cpu: 100m
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
requests:
|
||||
memory: 128Mi
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
requests:
|
||||
memory: 256Mi
|
||||
cpu: 100m
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
add: ["ALL"]
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostIPC: true
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostIPC: false
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostNetwork: false
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostPID: true
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
hostPID: false
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
hostPort: 8080
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- NET_ADMIN
|
||||
- CHOWN
|
||||
- DAC_OVERRIDE
|
||||
- FSETID
|
||||
- FOWNER
|
||||
- MKNOD
|
||||
- NET_RAW
|
||||
- SETGID
|
||||
- SETUID
|
||||
- SETFCAP
|
||||
- SETPCAP
|
||||
- NET_BIND_SERVICE
|
||||
- SYS_CHROOT
|
||||
- KILL
|
||||
- AUDIT_WRITE
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
test: liveness
|
||||
name: liveness-exec
|
||||
spec:
|
||||
containers:
|
||||
- name: liveness
|
||||
image: k8s.gcr.io/busybox
|
||||
args:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600.
|
||||
@@ -1,21 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
test: liveness
|
||||
name: liveness-exec
|
||||
spec:
|
||||
containers:
|
||||
- name: liveness
|
||||
image: k8s.gcr.io/busybox
|
||||
args:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- cat
|
||||
- /tmp/healthy
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
requests:
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
@@ -1,24 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
namespace: rightnamespace
|
||||
labels:
|
||||
app: zookeeper
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zk-pdb
|
||||
namespace: wrongnamespace
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: zookeeper
|
||||
@@ -1,22 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app: zookeeper
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zk-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: asdfasf
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app: zookeeper
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
@@ -1,42 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zookeeper-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: abcd-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: abcd
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: fghi-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: fghi
|
||||
@@ -1,22 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zookeeper-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
spec:
|
||||
containers: []
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers: []
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user