Compare commits

...
7 Commits
Author SHA1 Message Date
Robert Brennan d55cd4bdf8 Add insights prompt (#968)
* add insights prompt

* switch to stderr

* adjust message

* adjust message
2023-06-29 12:07:26 -04:00
75997f0e93 Bump github.com/AlecAivazis/survey/v2 from 2.3.6 to 2.3.7 (#961)
Bumps [github.com/AlecAivazis/survey/v2](https://github.com/AlecAivazis/survey) from 2.3.6 to 2.3.7.
- [Release notes](https://github.com/AlecAivazis/survey/releases)
- [Commits](https://github.com/AlecAivazis/survey/compare/v2.3.6...v2.3.7)

---
updated-dependencies:
- dependency-name: github.com/AlecAivazis/survey/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Grace Gude <44000341+mggude@users.noreply.github.com>
2023-06-29 09:22:11 -06:00
John Slivka b0d86cdcd2 Add option to filter audit results by severity level (#969) 2023-06-28 08:38:47 -05:00
5595de4ef9 Bump k8s.io/client-go from 0.27.2 to 0.27.3 (#960)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.27.2 to 0.27.3.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.27.2...v0.27.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Grace Gude <44000341+mggude@users.noreply.github.com>
2023-06-27 11:46:13 -04:00
Robert Brennan 4ca4c8f0f5 Fix nil pointer issue with webhook (#966)
* update

* update go mod

* tidy

* revert go mod

* fix port

* move pod test case

* downgrade controller-runtime

* revert updates

* fix nil pointer

* add logs

* fix var

* remove test requirement

* fix decoder

* fix mutate

* fix test case

* fix logs

* fmt

* fix owned pods in mutate

* fix test

* add logs

* add mutations to tests

* convert to json for patch

* fix up tests

* remove nil check

* fix logs

* add logs

* add env vars to webhook tests
2023-06-22 13:22:19 -04:00
Vitor Rodrigo Vezani 4b1d6635e0 add test for required fields on builtin checks (#965) 2023-06-21 12:25:44 -04:00
Vitor Rodrigo Vezani 0a26f3f578 FWI-4307 - fix checks category (#964)
* fix checks category

* add changelog
2023-06-21 12:08:49 -04:00
23 changed files with 222 additions and 63 deletions
+1 -2
View File
@@ -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]+/
+1
View File
@@ -1,5 +1,6 @@
successMessage: Label app.kubernetes.io/name matches metadata.name
failureMessage: Label app.kubernetes.io/name must match metadata.name
category: Reliability
target: Controller
schema:
'$schema': http://json-schema.org/draft-07/schema
+1 -1
View File
@@ -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
+19 -4
View File
@@ -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"
@@ -51,6 +52,7 @@ var (
helmValues string
checks []string
auditNamespace string
severityLevel string
skipSslValidation bool
uploadInsights bool
clusterName string
@@ -72,6 +74,7 @@ func init() {
auditCmd.PersistentFlags().StringVar(&helmValues, "helm-values", "", "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/action-items\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()
@@ -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
View File
@@ -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")
}
},
}
+4 -3
View File
@@ -53,7 +53,8 @@ var webhookCmd = &cobra.Command{
CertDir: certDir,
Port: webhookPort,
WebhookServer: webhook.NewServer(webhook.Options{
CertDir: certDir,
CertDir: certDir,
Port: webhookPort,
CertName: "tls.crt",
KeyName: "tls.key",
}),
@@ -74,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 {
+1 -1
View File
@@ -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`
+4
View File
@@ -6,6 +6,10 @@ meta:
---
## 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
+3 -3
View File
@@ -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.
`metadataAndNameMismatched` | `warning` | Fails when label `app.kubernetes.io/name` 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/)
+3
View File
@@ -52,6 +52,9 @@ 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
# webhook flags
--disable-webhook-config-installer disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.
+4 -4
View File
@@ -3,7 +3,7 @@ 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
@@ -16,9 +16,9 @@ require (
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.3
k8s.io/client-go v0.27.3
sigs.k8s.io/controller-runtime v0.15.0
sigs.k8s.io/yaml v1.3.0
)
+18 -10
View File
@@ -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=
@@ -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.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM=
k8s.io/apimachinery v0.27.3/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 -2
View File
@@ -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{
@@ -72,7 +71,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 {
+16
View File
@@ -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)
}
}
+1 -1
View File
@@ -16,8 +16,8 @@ package kube
import (
"bytes"
"fmt"
"context"
"fmt"
"os"
"testing"
"time"
+64
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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
+16
View File
@@ -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
+5 -3
View File
@@ -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