Compare commits

...
7 Commits
Author SHA1 Message Date
Robert Brennan 91378c3d92 Bump version to 0.6 (#231)
* bump version to 0.6.0

* update changelog
2019-12-09 12:20:15 -05:00
Robert Brennan 207297c11d move to using fairwinds quay repo (#230) 2019-12-09 08:51:17 -05:00
Robert Brennan 0d86096f09 remove extensionsv1beta1 reference to support 1.16 (#229) 2019-12-06 11:03:38 -05:00
Robert Brennan 67ab987f7e Add support for annotation-based exemptions (#227)
* add controllers_to_scan to example config-full

* add support for annotation-based exemptions

* fix lint errors

* add docs
2019-12-06 08:29:30 -05:00
Robert Brennan 97457d71c0 Full support for validating webhook, now that tests are in place (#226) 2019-12-04 14:12:13 -05:00
Andrew Suderman 1159a380ba Adding an exception for flannel being privileged to example config (#225) 2019-11-22 12:02:35 -07:00
Robert Brennan ca6aa76729 Add default exemptions (#220)
* Update config.yaml

* Update config.yaml

* add a couple more exemptions
2019-11-15 14:45:58 -05:00
22 changed files with 146 additions and 16 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
DOCKERFILE='Dockerfile'
EXTERNAL_REGISTRY_BASE_DOMAIN=quay.io
REPOSITORY_NAME=reactiveops/polaris
REPOSITORY_NAME=fairwinds/polaris
DOCKERTAG=${EXTERNAL_REGISTRY_BASE_DOMAIN}/${REPOSITORY_NAME}
if [[ -n $CI_TAG ]]; then
ADDITIONAL_DOCKER_TAG_VERSIONS=()
+1 -1
View File
@@ -93,7 +93,7 @@ references:
docker-pull -f .circleci/build.config
docker-build -f .circleci/build.config
if [[ -z $CIRCLE_PR_NUMBER ]]; then
docker login quay.io -u="reactiveops+circleci" -p="${quay_token}"
docker login quay.io -u="${fairwinds_quay_user}" -p="${fairwinds_quay_token}"
docker-push -f .circleci/build.config
else
echo "Skipping docker push for forked PR"
+5
View File
@@ -1,5 +1,10 @@
# x.x.x (next release)
# 0.6.0
* Fixed webhook support in Kubernetes 1.16
* this also removes support for 1.8
* Added support for exemptions via controller annotations
# 0.5.2
* Fixed missing success messages for resource requests/limits
+2 -2
View File
@@ -5,7 +5,7 @@
[![Version][version-image]][version-link] [![CircleCI][circleci-image]][circleci-link] [![Go Report Card][goreport-image]][goreport-link]
</div>
[version-image]: https://img.shields.io/static/v1.svg?label=Version&message=0.5.2&color=239922
[version-image]: https://img.shields.io/static/v1.svg?label=Version&message=0.6.0&color=239922
[version-link]: https://github.com/FairwindsOps/polaris
[goreport-image]: https://goreportcard.com/badge/github.com/FairwindsOps/polaris
@@ -52,7 +52,7 @@ Our default standards in Polaris are rather high, so dont be surprised if you
## Webhook
> [View installation instructions](docs/usage.md#webhook)
Polaris includes experimental support for an optional validating webhook. This accepts the same configuration as the dashboard, and can run the same validations. This webhook will reject any workloads that trigger a validation error. This is indicative of the greater goal of Polaris, not just to encourage better configuration through dashboard visibility, but to actually enforce it with this webhook. *Although we are working towards greater stability and better test coverage, we do not currently consider this webhook component production ready.*
Polaris includes an optional validating webhook. This accepts the same configuration as the dashboard, and can run the same validations. This webhook will reject any workloads that trigger a validation error. This is indicative of the greater goal of Polaris, not just to encourage better configuration through dashboard visibility, but to actually enforce it with this webhook.
Unfortunately we have not found a way to display warnings as part of `kubectl` output unless we are rejecting a workload altogether. That means that any checks with a severity of `warning` will still pass webhook validation, and the only evidence of that warning will either be in the Polaris dashboard or the Polaris webhook logs.
+1 -1
View File
@@ -179,7 +179,7 @@ spec:
- --dashboard
- --config
- /opt/app/config.yaml
image: 'quay.io/reactiveops/polaris:0.5'
image: 'quay.io/fairwinds/polaris:0.6'
imagePullPolicy: 'Always'
name: dashboard
ports:
+1 -1
View File
@@ -239,7 +239,7 @@ spec:
- --webhook
- --config
- /opt/app/config.yaml
image: 'quay.io/reactiveops/polaris:0.5'
image: 'quay.io/fairwinds/polaris:0.6'
imagePullPolicy: 'Always'
ports:
- containerPort: 9876
+21 -1
View File
@@ -4,9 +4,12 @@ be run as a local binary, which will use your kubeconfig to connect to the clust
or run against local YAML files.
## Configuration
Polaris supports a wide range of validations covering a number of Kubernetes best practices.
Here's a [sample configuration file](/examples/config-full.yaml) that includes all currently supported checks.
The [default configuration](/examples/config.yaml) contains a number of those checks.
Polaris supports a wide range of validations covering a number of Kubernetes best practices. Here's a sample configuration file that includes all currently supported checks. The [default configuration](https://github.com/fairwindsops/polaris/blob/master/examples/config.yaml) contains a number of those checks. This repository also includes a sample [full configuration file](https://github.com/fairwindsops/polaris/blob/master/examples/config-full.yaml) that enables all available checks.
### Checks
Each check can be assigned a `severity`. Only checks with a severity of `error` 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 `error` will result in a change being rejected.
Polaris validation checks fall into several different categories:
@@ -17,6 +20,23 @@ Polaris validation checks fall into several different categories:
- [Resources](check-documentation/resources.md)
- [Security](check-documentation/security.md)
### Exemptions
Exemptions can be added two ways: by annotating a controller, or editing the Polaris config.
To exempt a controller via annotations, use the annotation `polaris.fairwinds.com/exempt=true`, e.g.
```
kubectl annotate deployment my-deployment polaris.fairwinds.com/exempt=true
```
To exempt a controller via the config, you have to specify a list of controller names and a list of rules, e.g.
```yaml
exemptions:
- controllerNames:
- dns-controller
rules:
- hostNetworkSet
```
# Installing
There are several ways to install and use Polaris. Below outline ways to install using `kubectl`, `helm` and `local binary`.
+7
View File
@@ -63,6 +63,13 @@ security:
warning:
ifAnyAddedBeyond:
- NONE
controllers_to_scan:
- Deployments
- StatefulSets
- DaemonSets
- CronJobs
- Jobs
- ReplicationControllers
exemptions:
- controllerNames:
- dns-controller
+14 -1
View File
@@ -109,6 +109,7 @@ exemptions:
- kubedns
- dnsmasq
- autoscaler
- insights-agent-goldilocks-vpa-install
rules:
- cpuRequestsMissing
- cpuLimitsMissing
@@ -116,6 +117,7 @@ exemptions:
- memoryLimitsMissing
- controllerNames:
- kube2iam
- kube-flannel-ds
rules:
- runAsPrivileged
- controllerNames:
@@ -126,5 +128,16 @@ exemptions:
- polaris
- kube-hunter
- goldilocks
- insights-agent-goldilocks-vpa-install
rules:
- readOnlyRootFilesystem
- notReadOnlyRootFileSystem
- controllerNames:
- insights-agent-goldilocks-controller
rules:
- livenessProbeMissing
- readinessProbeMissing
- controllerNames:
- insights-agent-goldilocks-vpa-install
- kube-hunter
rules:
- runAsRootAllowed
+1 -1
View File
@@ -41,7 +41,7 @@ import (
const (
// Version represents the current release version of Polaris
Version = "0.5.2"
Version = "0.6.0"
)
func main() {
-2
View File
@@ -10,7 +10,6 @@ import (
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -99,7 +98,6 @@ func (s SupportedController) ListSupportedAPIVersions() []runtime.Object {
case Deployments:
supportedVersions = []runtime.Object{
&appsv1.Deployment{},
&extensionsv1beta1.Deployment{},
}
case StatefulSets:
supportedVersions = []runtime.Object{
+13
View File
@@ -15,6 +15,8 @@
package validator
import (
"strings"
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
"github.com/fairwindsops/polaris/pkg/validator/controllers"
@@ -22,6 +24,8 @@ import (
"github.com/sirupsen/logrus"
)
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
// ValidateController validates a single controller, returns a ControllerResult.
func ValidateController(conf conf.Configuration, controller controller.Interface) ControllerResult {
controllerType := controller.GetType()
@@ -44,6 +48,9 @@ func ValidateControllers(config conf.Configuration, kubeResources *kube.Resource
}
for _, controller := range controllersToAudit {
if !config.DisallowExemptions && hasExemptionAnnotation(controller) {
continue
}
controllerResult := ValidateController(config, controller)
nsResult := nsResults.getNamespaceResult(controller.GetNamespace())
nsResult.Summary.appendResults(*controllerResult.PodResult.Summary)
@@ -52,3 +59,9 @@ func ValidateControllers(config conf.Configuration, kubeResources *kube.Resource
}
}
}
func hasExemptionAnnotation(ctrl controller.Interface) bool {
annot := ctrl.GetAnnotations()
val := annot[exemptionAnnotationKey]
return strings.ToLower(val) == "true"
}
+46 -3
View File
@@ -17,12 +17,14 @@ package validator
import (
"testing"
conf "github.com/fairwindsops/polaris/pkg/config"
controller "github.com/fairwindsops/polaris/pkg/validator/controllers"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
conf "github.com/fairwindsops/polaris/pkg/config"
"github.com/fairwindsops/polaris/pkg/kube"
controller "github.com/fairwindsops/polaris/pkg/validator/controllers"
"github.com/fairwindsops/polaris/test"
"github.com/stretchr/testify/assert"
)
func TestValidateController(t *testing.T) {
@@ -134,3 +136,44 @@ func TestSkipHealthChecks(t *testing.T) {
assert.EqualValues(t, &expectedSum, actualResult.PodResult.Summary)
assert.EqualValues(t, expectedMessages, actualResult.PodResult.ContainerResults[0].Messages)
}
func TestControllerExemptions(t *testing.T) {
c := conf.Configuration{
HealthChecks: conf.HealthChecks{
ReadinessProbeMissing: conf.SeverityError,
LivenessProbeMissing: conf.SeverityWarning,
},
ControllersToScan: []conf.SupportedController{
conf.Deployments,
},
}
resources := &kube.ResourceProvider{
Deployments: []appsv1.Deployment{test.MockDeploy()},
}
expectedSum := ResultSummary{
Totals: CountSummary{
Successes: uint(0),
Warnings: uint(1),
Errors: uint(1),
},
ByCategory: make(map[string]*CountSummary),
}
expectedSum.ByCategory["Health Checks"] = &CountSummary{
Successes: uint(0),
Warnings: uint(1),
Errors: uint(1),
}
nsResults := NamespacedResults{}
ValidateControllers(c, resources, &nsResults)
actualResult := nsResults[""].DeploymentResults[0]
assert.Equal(t, "Deployments", actualResult.Type)
assert.EqualValues(t, &expectedSum, actualResult.PodResult.Summary)
resources.Deployments[0].ObjectMeta.Annotations = map[string]string{
exemptionAnnotationKey: "true",
}
nsResults = NamespacedResults{}
ValidateControllers(c, resources, &nsResults)
assert.Equal(t, (*NamespaceResult)(nil), nsResults[""])
}
+5
View File
@@ -27,6 +27,11 @@ func (c CronJobController) GetType() config.SupportedController {
return config.CronJobs
}
// GetAnnotations returns the controller's annotations
func (c CronJobController) GetAnnotations() map[string]string {
return c.K8SResource.ObjectMeta.Annotations
}
// NewCronJobController builds a new controller interface for Deployments
func NewCronJobController(originalDeploymentResource kubeAPIBatchV1beta1.CronJob) Interface {
controller := CronJobController{}
+5
View File
@@ -22,6 +22,11 @@ func (d DaemonSetController) GetPodSpec() *kubeAPICoreV1.PodSpec {
return &d.K8SResource.Spec.Template.Spec
}
// GetAnnotations returns the controller's annotations
func (d DaemonSetController) GetAnnotations() map[string]string {
return d.K8SResource.ObjectMeta.Annotations
}
// GetType returns the supportedcontroller enum type
func (d DaemonSetController) GetType() config.SupportedController {
return config.DaemonSets
+5
View File
@@ -22,6 +22,11 @@ func (d DeploymentController) GetPodSpec() *kubeAPICoreV1.PodSpec {
return &d.K8SResource.Spec.Template.Spec
}
// GetAnnotations returns the controller's annotations
func (d DeploymentController) GetAnnotations() map[string]string {
return d.K8SResource.ObjectMeta.Annotations
}
// GetType returns the supportedcontroller enum type
func (d DeploymentController) GetType() config.SupportedController {
return config.Deployments
+1
View File
@@ -15,6 +15,7 @@ type Interface interface {
GetPodTemplate() *kubeAPICoreV1.PodTemplateSpec
GetPodSpec() *kubeAPICoreV1.PodSpec
GetType() config.SupportedController
GetAnnotations() map[string]string
}
// GenericController is a base implementation with some free methods for inherited structs
+5
View File
@@ -22,6 +22,11 @@ func (j JobController) GetPodSpec() *kubeAPICoreV1.PodSpec {
return &j.K8SResource.Spec.Template.Spec
}
// GetAnnotations returns the controller's annotations
func (j JobController) GetAnnotations() map[string]string {
return j.K8SResource.ObjectMeta.Annotations
}
// GetType returns the supportedcontroller enum type
func (j JobController) GetType() config.SupportedController {
return config.Jobs
@@ -24,6 +24,11 @@ func (r ReplicationControllerController) GetPodSpec() *kubeAPICoreV1.PodSpec {
return &r.K8SResource.Spec.Template.Spec
}
// GetAnnotations returns the controller's annotations
func (r ReplicationControllerController) GetAnnotations() map[string]string {
return r.K8SResource.ObjectMeta.Annotations
}
// GetType returns the supportedcontroller enum type
func (r ReplicationControllerController) GetType() config.SupportedController {
return config.ReplicationControllers
@@ -22,6 +22,11 @@ func (s StatefulSetController) GetPodSpec() *kubeAPICoreV1.PodSpec {
return &s.K8SResource.Spec.Template.Spec
}
// GetAnnotations returns the controller's annotations
func (s StatefulSetController) GetAnnotations() map[string]string {
return s.K8SResource.ObjectMeta.Annotations
}
// GetType returns the supportedcontroller enum type
func (s StatefulSetController) GetType() config.SupportedController {
return config.StatefulSets
+1 -1
View File
@@ -1,4 +1,4 @@
sed -ri "s|'(quay.io/reactiveops/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/dashboard.yaml
sed -ri "s|'(quay.io/fairwinds/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/dashboard.yaml
function check_dashboard_is_ready() {
+1 -1
View File
@@ -2,7 +2,7 @@
set -e
#sed is replacing the polaris version with this commit sha so we are testing exactly this verison.
sed -ri "s|'(quay.io/reactiveops/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/webhook.yaml
sed -ri "s|'(quay.io/fairwinds/polaris:).+'|'\1${CIRCLE_SHA1}'|" ./deploy/webhook.yaml
# Testing to ensure that the webhook starts up, allows a correct deployment to pass,
# and prevents a incorrectly formatted deployment.