mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
677598c19c | ||
|
|
10e82cf0ae | ||
|
|
a5144c45b2 | ||
|
|
9be34236cb | ||
|
|
e7eb079921 | ||
|
|
d55cd4bdf8 | ||
|
|
75997f0e93 | ||
|
|
b0d86cdcd2 | ||
|
|
5595de4ef9 | ||
|
|
4ca4c8f0f5 | ||
|
|
4b1d6635e0 | ||
|
|
0a26f3f578 | ||
|
|
85a439653d | ||
|
|
ba63e6691a | ||
|
|
07747d76f2 | ||
|
|
b876d301c8 |
@@ -114,6 +114,7 @@ jobs:
|
||||
executor: vm
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- *install_k8s
|
||||
- *test_k8s
|
||||
|
||||
@@ -161,8 +162,6 @@ workflows:
|
||||
only: /.*/
|
||||
- build_and_push:
|
||||
context: org-global
|
||||
requires:
|
||||
- test
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
successMessage: CPU limits are within the required range
|
||||
failureMessage: CPU limits should be within the required range
|
||||
category: Efficiency
|
||||
target: Container
|
||||
containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
required:
|
||||
- limits
|
||||
properties:
|
||||
limits:
|
||||
required:
|
||||
- cpu
|
||||
properties:
|
||||
cpu:
|
||||
resourceMinimum: 200m
|
||||
resourceMaximum: "2"
|
||||
@@ -1,5 +1,6 @@
|
||||
successMessage: Label app.kubernetes.io/name matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/name must match metadata.name
|
||||
successMessage: Label app.kubernetes.io/instance matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/instance must match metadata.name
|
||||
category: Reliability
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
@@ -11,7 +12,7 @@ schema:
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
required: ["app.kubernetes.io/name"]
|
||||
required: ["app.kubernetes.io/instance"]
|
||||
properties:
|
||||
app.kubernetes.io/name:
|
||||
app.kubernetes.io/instance:
|
||||
const: "{{ .metadata.name }}"
|
||||
@@ -1,6 +1,6 @@
|
||||
successMessage: Priority class has been set
|
||||
failureMessage: Priority class should be set
|
||||
category: Security
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
|
||||
+24
-9
@@ -24,6 +24,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
workloads "github.com/fairwindsops/insights-plugins/plugins/workloads"
|
||||
workloadsPkg "github.com/fairwindsops/insights-plugins/plugins/workloads/pkg"
|
||||
@@ -48,9 +49,10 @@ var (
|
||||
resourceToAudit string
|
||||
useColor bool
|
||||
helmChart string
|
||||
helmValues string
|
||||
helmValues []string
|
||||
checks []string
|
||||
auditNamespace string
|
||||
severityLevel string
|
||||
skipSslValidation bool
|
||||
uploadInsights bool
|
||||
clusterName string
|
||||
@@ -69,9 +71,10 @@ func init() {
|
||||
auditCmd.PersistentFlags().StringVar(&displayName, "display-name", "", "An optional identifier for the audit.")
|
||||
auditCmd.PersistentFlags().StringVar(&resourceToAudit, "resource", "", "Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.")
|
||||
auditCmd.PersistentFlags().StringVar(&helmChart, "helm-chart", "", "Will fill out Helm template")
|
||||
auditCmd.PersistentFlags().StringVar(&helmValues, "helm-values", "", "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&helmValues, "helm-values", []string{}, "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&checks, "checks", []string{}, "Optional flag to specify specific checks to check")
|
||||
auditCmd.PersistentFlags().StringVar(&auditNamespace, "namespace", "", "Namespace to audit. Only applies to in-cluster audits")
|
||||
auditCmd.PersistentFlags().StringVar(&severityLevel, "severity", "", "Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)")
|
||||
auditCmd.PersistentFlags().BoolVar(&skipSslValidation, "skip-ssl-validation", false, "Skip https certificate verification")
|
||||
auditCmd.PersistentFlags().BoolVar(&uploadInsights, "upload-insights", false, "Upload scan results to Fairwinds Insights")
|
||||
auditCmd.PersistentFlags().StringVar(&clusterName, "cluster-name", "", "Set --cluster-name to a descriptive name for the cluster you're auditing")
|
||||
@@ -172,10 +175,12 @@ var auditCmd = &cobra.Command{
|
||||
logrus.Errorf("reporting audit file to insights: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logrus.Println("Success! You can see your results at:")
|
||||
logrus.Printf("%s/orgs/%s/clusters/%s\n", insightsHost, auth.Organization, clusterName)
|
||||
os.Stderr.WriteString("\n\nSuccess! You can see your results at:")
|
||||
os.Stderr.WriteString(fmt.Sprintf("\n\n%s/orgs/%s/clusters/%s/action-items\n\n", insightsHost, auth.Organization, clusterName))
|
||||
} else {
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests)
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests, severityLevel)
|
||||
os.Stderr.WriteString("\n\n🚀 Upload your Polaris findings to Fairwinds Insights to see remediation advice, add teammates, integrate with Slack or Jira, and more:")
|
||||
os.Stderr.WriteString("\n\n❯ polaris " + strings.Join(os.Args[1:], " ") + " --upload-insights --cluster-name=my-cluster\n\n")
|
||||
}
|
||||
|
||||
summary := auditData.GetSummary()
|
||||
@@ -191,7 +196,7 @@ var auditCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// ProcessHelmTemplates turns helm into yaml to be processed by Polaris or the other tools.
|
||||
func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
func ProcessHelmTemplates(helmChart string, helmValues []string) (string, error) {
|
||||
cmd := exec.Command("helm", "dependency", "update", helmChart)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -209,8 +214,8 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
"--output-dir",
|
||||
dir,
|
||||
}
|
||||
if helmValues != "" {
|
||||
params = append(params, "--values", helmValues)
|
||||
for _, v := range helmValues {
|
||||
params = append(params, "--values", v)
|
||||
}
|
||||
|
||||
cmd = exec.Command("helm", params...)
|
||||
@@ -223,10 +228,20 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool) {
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool, severityLevel string) {
|
||||
if onlyShowFailedTests {
|
||||
auditData = auditData.RemoveSuccessfulResults()
|
||||
}
|
||||
|
||||
if severityLevel != "" {
|
||||
switch severityLevel {
|
||||
case "danger":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityDanger)
|
||||
case "warning":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityWarning)
|
||||
}
|
||||
}
|
||||
|
||||
var outputBytes []byte
|
||||
var err error
|
||||
if outputFormat == "score" {
|
||||
|
||||
+4
-1
@@ -16,6 +16,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -84,7 +85,9 @@ var rootCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
if !strings.HasPrefix(cmd.Use, "audit") {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ var webhookCmd = &cobra.Command{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
WebhookServer: webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
CertName: "tls.crt",
|
||||
KeyName: "tls.key",
|
||||
}),
|
||||
@@ -73,10 +75,10 @@ var webhookCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if enableValidations {
|
||||
fwebhook.NewValidateWebhook(mgr, fwebhook.Validator{Config: config, Client: mgr.GetClient()})
|
||||
fwebhook.NewValidateWebhook(mgr, config)
|
||||
}
|
||||
if enableMutations {
|
||||
fwebhook.NewMutateWebhook(mgr, fwebhook.Mutator{Config: config, Client: mgr.GetClient()})
|
||||
fwebhook.NewMutateWebhook(mgr, config)
|
||||
}
|
||||
logrus.Infof("Polaris webhook server listening on port %d", webhookPort)
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
|
||||
@@ -53,7 +53,7 @@ This means Polaris will remediate the issue it finds, rather than rejecting
|
||||
the deployment.
|
||||
|
||||
To enable the mutating webhook, add `--set webhook.mutate=true` to your
|
||||
Helm instlallation command.
|
||||
Helm installation command.
|
||||
|
||||
The following default checks currently have mutation support enabled:
|
||||
* `hostPIDSet`
|
||||
|
||||
@@ -6,6 +6,13 @@ meta:
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
* Change `metadataAndNameMismatched` to `metadataAndInstanceMismatched`
|
||||
|
||||
## 8.1.1
|
||||
* Add category for `metadataAndNameMismatched`.
|
||||
* Fix category for `priorityClassNotSet`.
|
||||
|
||||
## 8.1.0
|
||||
* Add `insights-host` global flag to configure Fairwinds Insights host (defaults to `https://insights.fairwinds.com`).
|
||||
* Add new `auth` sub-commands be able to authenticate on Polaris using Fairwinds Insights credentials
|
||||
|
||||
@@ -16,8 +16,8 @@ key | default | description
|
||||
`pullPolicyNotAlways` | `warning` | Fails when an image pull policy is not `always`.
|
||||
`priorityClassNotSet` | `warning` | Fails when a priorityClassName is not set for a pod.
|
||||
`deploymentMissingReplicas` | `warning` | Fails when there is only one replica for a deployment.
|
||||
`missingPodDisruptionBudget` | `warning`
|
||||
`metadataAndNameMismatched` | `warning`
|
||||
`missingPodDisruptionBudget` | `warning` | Fails when PDB is missing.
|
||||
`metadataAndInstanceMismatched` | `warning` | Fails when label `app.kubernetes.io/instance` and `metadata.name` mismatch
|
||||
`topologySpreadConstraint` | `warning` | Fails when there is no topology spread constraint on the pod
|
||||
|
||||
## Background
|
||||
@@ -70,4 +70,4 @@ spec:
|
||||
- [Kubernetes Docs: Configure Liveness and Readiness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/)
|
||||
- [Utilizing Kubernetes Liveness and Readiness Probes to Automatically Recover From Failure](https://medium.com/spire-labs/utilizing-kubernetes-liveness-and-readiness-probes-to-automatically-recover-from-failure-2fe0314f2b2e)
|
||||
- [Kubernetes Liveness and Readiness Probes: How to Avoid Shooting Yourself in the Foot](https://blog.colinbreck.com/kubernetes-liveness-and-readiness-probes-how-to-avoid-shooting-yourself-in-the-foot/)
|
||||
- [Topology Spread Cosntraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
- [Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
|
||||
+24
-2
@@ -9,14 +9,18 @@ meta:
|
||||
# top-level commands
|
||||
audit
|
||||
Runs a one-time audit.
|
||||
auth
|
||||
Authenticate polaris with Fairwinds Insights
|
||||
dashboard
|
||||
Runs the webserver for Polaris dashboard.
|
||||
fix
|
||||
Fix Infrastructure as code files.
|
||||
help
|
||||
Prints help, if you give it a command then it will print help for that command. Same as -h
|
||||
version
|
||||
Prints the version of Polaris
|
||||
webhook
|
||||
Runs the webhook webserver
|
||||
Runs the webhook webserver.
|
||||
|
||||
# global flags
|
||||
-c, --config string Location of Polaris configuration file.
|
||||
@@ -25,6 +29,7 @@ webhook
|
||||
--disallow-config-exemptions Disallow exemptions set within the configuration file.
|
||||
--disallow-annotation-exemptions Disallow any exemption defined as a controller annotation.
|
||||
--kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster.
|
||||
--insights-host string Fairwinds Insights host URL. (default "https://insights.fairwinds.com")
|
||||
--log-level string Logrus log level. (default "info")
|
||||
|
||||
# dashboard flags
|
||||
@@ -38,7 +43,8 @@ webhook
|
||||
|
||||
# audit flags
|
||||
--audit-path string If specified, audits one or more YAML files instead of a cluster.
|
||||
--checks stringArray Optional flag to specify specific checks to check
|
||||
--checks strings Optional flag to specify specific checks to check
|
||||
--cluster-name string Set --cluster-name to a descriptive name for the cluster you're auditing
|
||||
--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")
|
||||
@@ -52,10 +58,26 @@ webhook
|
||||
--resource string Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.
|
||||
--set-exit-code-below-score int Set an exit code of 4 when the score is below this threshold (1-100).
|
||||
--set-exit-code-on-danger Set an exit code of 3 when the audit contains danger-level issues.
|
||||
--severity string Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)
|
||||
--skip-ssl-validation Skip https certificate verification
|
||||
--upload-insights Upload scan results to Fairwinds Insights
|
||||
|
||||
# fix flags
|
||||
--checks strings Optional flag to specify specific checks to fix eg. checks=hostIPCSet,hostPIDSet and checks=all applies fix to all defined checks mutations
|
||||
--files-path string mutate and fix one or more YAML files in a specified folder
|
||||
-h, --help help for fix
|
||||
--template set to true when modifyng a YAML template, like a Helm chart (experimental)
|
||||
|
||||
|
||||
# webhook flags
|
||||
--disable-webhook-config-installer disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.
|
||||
-h, --help help for webhook
|
||||
-p, --port int Port for the dashboard webserver. (default 9876)
|
||||
|
||||
# auth sub-commands
|
||||
login Authenticate polaris with Fairwinds Insights.
|
||||
logout Log out of a Fairwinds Insights.
|
||||
status View authentication status.
|
||||
token Print the auth token gh is configured to use.
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ checks:
|
||||
topologySpreadConstraint: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
metadataAndNameMismatched: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
|
||||
@@ -6,7 +6,7 @@ checks:
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
metadataAndNameMismatched: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
topologySpreadConstraint: warning
|
||||
|
||||
@@ -3,22 +3,22 @@ module github.com/fairwindsops/polaris
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gobuffalo/packr/v2 v2.8.3
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qri-io/jsonschema v0.1.2
|
||||
github.com/sirupsen/logrus v1.9.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.27.2
|
||||
k8s.io/apimachinery v0.27.2
|
||||
k8s.io/client-go v0.27.2
|
||||
k8s.io/api v0.27.3
|
||||
k8s.io/apimachinery v0.27.4
|
||||
k8s.io/client-go v0.27.3
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
@@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
@@ -334,8 +334,8 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
|
||||
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -375,6 +375,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
@@ -400,6 +401,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -435,6 +437,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -472,6 +475,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -499,6 +503,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -546,14 +551,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
@@ -565,6 +570,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -624,6 +631,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -766,13 +774,13 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo=
|
||||
k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4=
|
||||
k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y=
|
||||
k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg=
|
||||
k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE=
|
||||
k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ=
|
||||
k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
|
||||
k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8=
|
||||
k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48=
|
||||
k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo=
|
||||
k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo=
|
||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ func HandleLogin(insightsHost string) error {
|
||||
|
||||
var user, token, organization string
|
||||
if answer == loginUsingBrowser {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
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{
|
||||
@@ -39,6 +38,7 @@ var (
|
||||
"memoryRequestsMissing",
|
||||
"cpuLimitsMissing",
|
||||
"cpuRequestsMissing",
|
||||
"cpuLimitsRange",
|
||||
"readinessProbeMissing",
|
||||
"livenessProbeMissing",
|
||||
"pullPolicyNotAlways",
|
||||
@@ -56,7 +56,7 @@ var (
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndNameMismatched",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
"missingNetworkPolicy",
|
||||
"sensitiveConfigmapContent",
|
||||
@@ -72,7 +72,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
schemaBox = packr.New("Schemas", "../../checks")
|
||||
schemaBox := packr.New("Schemas", "../../checks")
|
||||
for _, checkID := range checkOrder {
|
||||
contents, err := schemaBox.Find(checkID + ".yaml")
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRequiredFieldsOnBuiltInChecks(t *testing.T) {
|
||||
for _, v := range BuiltInChecks {
|
||||
assert.NotEmpty(t, v.SuccessMessage)
|
||||
assert.NotEmpty(t, v.FailureMessage)
|
||||
assert.NotEmpty(t, v.Category)
|
||||
assert.NotEmpty(t, v.Target)
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ package kube
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -53,6 +53,26 @@ type AuditData struct {
|
||||
Score uint
|
||||
}
|
||||
|
||||
// FilterResultsBySeverityLevel includes results according to the provided severity level:
|
||||
// 'danger' is the least verbose, 'warning' is medium verbosity, default behavior will
|
||||
// include all results, which currently also includes 'ignore'
|
||||
func (res AuditData) FilterResultsBySeverityLevel(severityLevel config.Severity) AuditData {
|
||||
resCopy := res
|
||||
resCopy.Results = []Result{}
|
||||
|
||||
filteredResults := funk.Map(res.Results, func(auditDataResult Result) Result {
|
||||
return auditDataResult.filterResultsBySeverityLevel(severityLevel)
|
||||
}).([]Result)
|
||||
|
||||
for _, result := range filteredResults {
|
||||
if result.isNotEmpty() {
|
||||
resCopy.Results = append(resCopy.Results, result)
|
||||
}
|
||||
}
|
||||
|
||||
return resCopy
|
||||
}
|
||||
|
||||
// RemoveSuccessfulResults removes all tests that have passed
|
||||
func (res AuditData) RemoveSuccessfulResults() AuditData {
|
||||
resCopy := res
|
||||
@@ -108,6 +128,25 @@ func (res ResultSet) removeSuccessfulResults() ResultSet {
|
||||
return newResults
|
||||
}
|
||||
|
||||
func (res ResultSet) filterResultsBySeverityLevel(severityLevel config.Severity) ResultSet {
|
||||
newResults := ResultSet{}
|
||||
for k, resultMessage := range res {
|
||||
switch severityLevel {
|
||||
case config.SeverityDanger:
|
||||
if resultMessage.Severity == config.SeverityDanger {
|
||||
newResults[k] = resultMessage
|
||||
}
|
||||
case config.SeverityWarning:
|
||||
if resultMessage.Severity == config.SeverityDanger || resultMessage.Severity == config.SeverityWarning {
|
||||
newResults[k] = resultMessage
|
||||
}
|
||||
default:
|
||||
return res
|
||||
}
|
||||
}
|
||||
return newResults
|
||||
}
|
||||
|
||||
// Result provides results for a Kubernetes object
|
||||
type Result struct {
|
||||
Name string
|
||||
@@ -128,6 +167,16 @@ func (res Result) removeSuccessfulResults() Result {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res Result) filterResultsBySeverityLevel(severityLevel config.Severity) Result {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
if res.PodResult != nil {
|
||||
podCopy := res.PodResult.filterResultsBySeverityLevel(severityLevel)
|
||||
resCopy.PodResult = &podCopy
|
||||
}
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res Result) isNotEmpty() bool {
|
||||
if res.PodResult != nil {
|
||||
return res.PodResult.isNotEmpty()
|
||||
@@ -151,6 +200,15 @@ func (res PodResult) removeSuccessfulResults() PodResult {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res PodResult) filterResultsBySeverityLevel(severityLevel config.Severity) PodResult {
|
||||
resCopy := PodResult{}
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
resCopy.ContainerResults = funk.Map(res.ContainerResults, func(containerResult ContainerResult) ContainerResult {
|
||||
return containerResult.filterResultsBySeverityLevel(severityLevel)
|
||||
}).([]ContainerResult)
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res PodResult) isNotEmpty() bool {
|
||||
for _, cr := range res.ContainerResults {
|
||||
if cr.isNotEmpty() {
|
||||
@@ -172,6 +230,12 @@ func (res ContainerResult) removeSuccessfulResults() ContainerResult {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res ContainerResult) filterResultsBySeverityLevel(severityLevel config.Severity) ContainerResult {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res ContainerResult) isNotEmpty() bool {
|
||||
return res.Results.isNotEmpty()
|
||||
}
|
||||
|
||||
+31
-5
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/fairwindsops/polaris/pkg/mutation"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
@@ -35,41 +36,66 @@ type Mutator struct {
|
||||
decoder *admission.Decoder
|
||||
}
|
||||
|
||||
var _ admission.Handler = &Mutator{}
|
||||
|
||||
// NewMutateWebhook creates a mutating admission webhook for the apiType.
|
||||
func NewMutateWebhook(mgr manager.Manager, mutator Mutator) {
|
||||
func NewMutateWebhook(mgr manager.Manager, c config.Configuration) {
|
||||
path := "/mutate"
|
||||
|
||||
mutator := Mutator{
|
||||
Client: mgr.GetClient(),
|
||||
decoder: admission.NewDecoder(runtime.NewScheme()),
|
||||
Config: c,
|
||||
}
|
||||
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &mutator})
|
||||
}
|
||||
|
||||
func (m *Mutator) mutate(req admission.Request) ([]jsonpatch.Operation, error) {
|
||||
results, kubeResources, err := GetValidatedResults(req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while validating resource: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if results == nil {
|
||||
logrus.Infof("Not mutating owned pod")
|
||||
return nil, nil
|
||||
}
|
||||
patches := mutation.GetMutationsFromResult(results)
|
||||
originalYaml, err := yaml.JSONToYAML(kubeResources.OriginalObjectJSON)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to convert JSON to YAML: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
mutatedYamlStr, err := mutation.ApplyAllMutations(string(originalYaml), patches)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to apply mutations: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return jsonpatch.CreatePatch(originalYaml, []byte(mutatedYamlStr))
|
||||
|
||||
mutatedJson, err := yaml.YAMLToJSON([]byte(mutatedYamlStr))
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to convert YAML to JSON: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ops, err := jsonpatch.CreatePatch(kubeResources.OriginalObjectJSON, mutatedJson)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create patch from mutation: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (m *Mutator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
logrus.Info("Starting request")
|
||||
logrus.Info("Starting mutation request")
|
||||
patches, err := m.mutate(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while getting mutations: %v", err)
|
||||
return admission.Errored(403, err)
|
||||
}
|
||||
if patches == nil {
|
||||
logrus.Infof("No patches generated")
|
||||
return admission.Allowed("Allowed")
|
||||
}
|
||||
logrus.Infof("Generated %d patches", len(patches))
|
||||
return admission.Patched("", patches...)
|
||||
}
|
||||
|
||||
+22
-21
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
@@ -38,19 +39,14 @@ type Validator struct {
|
||||
Config config.Configuration
|
||||
}
|
||||
|
||||
// InjectDecoder injects the decoder.
|
||||
func (v *Validator) InjectDecoder(d *admission.Decoder) error {
|
||||
logrus.Info("Injecting decoder")
|
||||
v.decoder = d
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ admission.Handler = &Validator{}
|
||||
|
||||
// NewValidateWebhook creates a validating admission webhook for the apiType.
|
||||
func NewValidateWebhook(mgr manager.Manager, validator Validator) {
|
||||
func NewValidateWebhook(mgr manager.Manager, c config.Configuration) {
|
||||
path := "/validate"
|
||||
|
||||
validator := Validator{
|
||||
Client: mgr.GetClient(),
|
||||
decoder: admission.NewDecoder(runtime.NewScheme()),
|
||||
Config: c,
|
||||
}
|
||||
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &validator})
|
||||
}
|
||||
|
||||
@@ -60,35 +56,40 @@ func (v *Validator) handleInternal(req admission.Request) (*validator.Result, ku
|
||||
|
||||
// GetValidatedResults returns the validated results.
|
||||
func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) {
|
||||
var controller kube.GenericResource
|
||||
var resource kube.GenericResource
|
||||
var err error
|
||||
if kind == "Pod" {
|
||||
if decoder == nil {
|
||||
panic("Decoder is nil!")
|
||||
}
|
||||
pod := corev1.Pod{}
|
||||
err := decoder.Decode(req, &pod)
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
logrus.Errorf("Failed to decode pod: %v", err)
|
||||
return nil, resource, err
|
||||
}
|
||||
if len(pod.ObjectMeta.OwnerReferences) > 0 {
|
||||
logrus.Infof("Allowing owned pod %s/%s to pass through webhook", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
return nil, controller, nil
|
||||
return nil, resource, nil
|
||||
}
|
||||
controller, err = kube.NewGenericResourceFromPod(pod, pod)
|
||||
resource, err = kube.NewGenericResourceFromPod(pod, pod)
|
||||
} else {
|
||||
controller, err = kube.NewGenericResourceFromBytes(req.Object.Raw)
|
||||
resource, err = kube.NewGenericResourceFromBytes(req.Object.Raw)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
logrus.Errorf("Failed to create resource: %v", err)
|
||||
return nil, resource, err
|
||||
}
|
||||
controllerResult, err := validator.ApplyAllSchemaChecks(&config, nil, controller)
|
||||
resourceResult, err := validator.ApplyAllSchemaChecks(&config, nil, resource)
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
return nil, resource, err
|
||||
}
|
||||
return &controllerResult, controller, nil
|
||||
return &resourceResult, resource, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
logrus.Info("Starting request")
|
||||
logrus.Info("Starting admission request")
|
||||
result, _, err := v.handleInternal(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error validating request: %v", err)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 128Mi
|
||||
cpu: 5
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 128Mi
|
||||
cpu: 1
|
||||
+1
-1
@@ -3,7 +3,7 @@ kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: not-nginx
|
||||
app.kubernetes.io/instance: not-nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
+1
-1
@@ -3,7 +3,7 @@ kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
app.kubernetes.io/instance: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
@@ -19,7 +19,7 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: ignore
|
||||
priorityClassNotSet: warning
|
||||
runAsRootAllowed: danger
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
|
||||
@@ -17,6 +17,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
securityContext:
|
||||
@@ -26,4 +27,4 @@ spec:
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
- ALL
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx-2
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
@@ -60,7 +60,7 @@ function clean_up() {
|
||||
echo "Uninstalling webhook and webhook config"
|
||||
kubectl delete validatingwebhookconfigurations polaris-webhook --wait=false || true
|
||||
kubectl delete validatingwebhookconfigurations polaris-validate-webhook --wait=false || true
|
||||
kubectl delete validatingwebhookconfigurations polaris-mutate-webhook --wait=false || true
|
||||
kubectl delete mutatingwebhookconfigurations polaris-mutate-webhook --wait=false || true
|
||||
kubectl -n polaris delete deploy -l app=polaris --wait=false || true
|
||||
echo -e "\n\nDone cleaning up\n\n"
|
||||
}
|
||||
@@ -82,11 +82,12 @@ kubectl create ns tests
|
||||
echo "Installing a bad deployment"
|
||||
kubectl apply -n scale-test -f ./test/webhook_cases/failing_test.deployment.yaml
|
||||
|
||||
echo "Installing the webhook"
|
||||
echo "Installing the webhook at version $CI_SHA1"
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
helm install polaris fairwinds-stable/polaris --namespace polaris --create-namespace \
|
||||
--set dashboard.enable=false \
|
||||
--set webhook.enable=true \
|
||||
--set webhook.mutate=true \
|
||||
--set image.tag=$CI_SHA1
|
||||
|
||||
echo "Waiting for the webhook to come online"
|
||||
@@ -105,6 +106,7 @@ for filename in test/webhook_cases/passing_test.*.yaml; do
|
||||
if ! kubectl apply -n tests -f $filename; then
|
||||
ALL_TESTS_PASSED=0
|
||||
echo -e "${RED}****Test Failed: Polaris prevented a resource with no configuration issues****${NC}"
|
||||
kubectl logs -n polaris deploy/polaris-webhook
|
||||
else
|
||||
echo -e "${GREEN}****Test Passed: Polaris correctly allowed this resource****${NC}"
|
||||
fi
|
||||
@@ -118,7 +120,7 @@ for filename in test/webhook_cases/failing_test.*.yaml; do
|
||||
if kubectl apply -n tests -f $filename; then
|
||||
ALL_TESTS_PASSED=0
|
||||
echo -e "${RED}****Test Failed: Polaris should have prevented this resource due to configuration issues.****${NC}"
|
||||
kubectl logs -n polaris $(kubectl get po -oname -n polaris | grep webhook)
|
||||
kubectl logs -n polaris deploy/polaris-webhook
|
||||
else
|
||||
echo -e "${GREEN}****Test Passed: Polaris correctly prevented this resource****${NC}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user