Skip to content

Latest commit

 

History

History
300 lines (257 loc) · 9.88 KB

README.md

File metadata and controls

300 lines (257 loc) · 9.88 KB

klint 🧹

MIT license

A consolidated suite of kubernetes tools in a single container.

This is a portable solution to validate ☸️ manifests anywhere with a container runtime; local machine, CI, etc.

Reminders 🎗

  • The container is set with the permissions nobody:nobody
  • The container's workspace is set to /src
  • The container doesn't have curl, but it has wget
  • Any vulnerabilities are not my own; see tools 🔧

Tools 🔧

This swiss army knife stands on the shoulders of giants:

Name Version License Description
helm v3.7 Apache 2.0 The Kubernetes Package Manager
kubectl v1.23.3 Apache 2.0 The designated kubernetes CLI client
kubesec v2.11.4 Apache 2.0 Security risk analysis for Kubernetes resources
kubeval v0.15.0 Apache 2.0 Validate your Kubernetes configuration files, supports multiple Kubernetes versions
kube-score v1.13.0 Apache 2.0 Kubernetes object analysis with recommendations for improved reliability and security
kustomize v4.4.1 Apache 2.0 Customization of kubernetes YAML configurations

Usage 🐳

For local usage... first spin up the container

$ docker run --rm -it -v `pwd`:/src derekahn/klint

Kubescore

Example Usage
/src $ kube-score score *.yaml
Example Output
apps/v1/Deployment my-app in awesome                                    💥
  [CRITICAL] Pod Probes
    · Container has the same readiness and liveness probe
        Using the same probe for liveness and readiness is very likely dangerous. Generally it's better to avoid
        the livenessProbe than re-using the readinessProbe.
        More information: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
  [CRITICAL] Container Resources
    · scala-sample -> CPU limit is not set
        Resource limits are recommended to avoid resource DDOS. Set resources.limits.cpu
    · scala-sample -> Memory limit is not set
        Resource limits are recommended to avoid resource DDOS. Set resources.limits.memory
    · scala-sample -> CPU request is not set
        Resource requests are recommended to make sure that the application can start and run without crashing.
        Set resources.requests.cpu
    · scala-sample -> Memory request is not set
        Resource requests are recommended to make sure that the application can start and run without crashing.
        Set resources.requests.memory
  [CRITICAL] Container Image Tag
    · scala-sample -> Image with latest tag
        Using a fixed tag is recommended to avoid accidental upgrades
  [CRITICAL] Pod NetworkPolicy
    · The pod does not have a matching NetworkPolicy
        Create a NetworkPolicy that targets this pod to control who/what can communicate with this pod. Note, this
        feature needs to be supported by the CNI implementation used in the Kubernetes cluster to have an effect.
  [CRITICAL] Deployment has PodDisruptionBudget
    · No matching PodDisruptionBudget was found
        It's recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes
        maintenance operations, such as when draining a node.
  [WARNING] Deployment has host PodAntiAffinity
    · Deployment does not have a host podAntiAffinity set
        It's recommended to set a podAntiAffinity that stops multiple pods from a deployment from being scheduled
        on the same node. This increases availability in case the node becomes unavailable.
v1/Service my-app in awesome                                        ✅

Kubeval

Example Usage
/src $ kube-score score *.yaml
Example Output
PASS - my-app-deply.yaml contains a valid Deployment (awesome.my-app)
PASS - my-app-svc.yaml contains a valid Service (awesome.my-app-svc)

Kubesec

Example Usage
/src $ kubesec scan my-app-deploy.yaml
Example Output
[
  {
    "object": "Deployment/my-app.awesome",
    "valid": true,
    "fileName": "my-app-deploy.yaml",
    "message": "Passed with a score of 5 points",
    "score": 5,
    "scoring": {
      "passed": [
        {
          "id": "CapDropAny",
          "selector": "containers[] .securityContext .capabilities .drop",
          "reason": "Reducing kernel capabilities available to a container limits its attack surface",
          "points": 1
        },
        {
          "id": "CapDropAll",
          "selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")",
          "reason": "Drop all capabilities and add only those required to reduce syscall attack surface",
          "points": 1
        },
        {
          "id": "ReadOnlyRootFilesystem",
          "selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
          "reason": "An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost",
          "points": 1
        },
        {
          "id": "RunAsNonRoot",
          "selector": "containers[] .securityContext .runAsNonRoot == true",
          "reason": "Force the running image to run as a non-root user to ensure least privilege",
          "points": 1
        },
        {
          "id": "RunAsUser",
          "selector": "containers[] .securityContext .runAsUser -gt 10000",
          "reason": "Run as a high-UID user to avoid conflicts with the host's user table",
          "points": 1
        }
      ],
      "advise": [
        {
          "id": "ApparmorAny",
          "selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"",
          "reason": "Well defined AppArmor policies may provide greater protection from unknown threats. WARNING: NOT PRODUCTION READY",
          "points": 3
        },
        {
          "id": "ServiceAccountName",
          "selector": ".spec .serviceAccountName",
          "reason": "Service accounts restrict Kubernetes API access and should be configured with least privilege",
          "points": 3
        },
        {
          "id": "SeccompAny",
          "selector": ".metadata .annotations .\"container.seccomp.security.alpha.kubernetes.io/pod\"",
          "reason": "Seccomp profiles set minimum privilege and secure against unknown threats",
          "points": 1
        },
      ]
    }
  }
]

Kustomize

/src $ kustomize build overlays/development/
apiVersion: v1
kind: Namespace
metadata:
  annotations:
    kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity,container-security-context,deployment-has-poddisruptionbudget
  name: awesome
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity,container-security-context,deployment-has-poddisruptionbudget
  labels:
    app: my-app
  name: my-app
  namespace: awesome
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      annotations:
        kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity,container-security-context,deployment-has-poddisruptionbudget
      labels:
        app: my-app
    spec:
      containers:
      - image: k8s:5000/my-app
        imagePullPolicy: Always
        name: my-app
        ports:
        - containerPort: 5000
        resources:
          limits:
            cpu: 1
            memory: 2048Mi
          requests:
            cpu: 1
            memory: 512Mi

Continuous Integration

These are just simple examples of plugging this into your CI

Github Actions

jobs:
  container:
    runs-on: ubuntu-latest
    container: derekahn/klint:latest
    steps:
      - run: |
          kubeval *.yaml
        name: Run in container

Jenkins example using Docker agents

Jenkinsfile

pipeline {
  agent {
    docker { image 'derekahn/klint:latest' }
  }

  stages {
    stage('lint') {
      steps {
        sh 'kubeval *.yaml'
      }
    }
    stage('validate') {
      steps {
        sh 'kube-score score *.yaml'
      }
    }
    stage('security scan') {
      steps {
        sh 'kubeval *.yaml'
      }
    }
  }
}

Drone

.drone.yml

kind: pipeline
name: k8s validations

steps:
  - name: lint
    image: derekahn/klint:latest
    commands:
      - kubeval score *.yaml
  - name: validate
    image: derekahn/klint:latest
    commands:
      - kube-score score *.yaml
  - name: scan
    image: derekahn/klint:latest
    commands:
      - kubesec scan *.yaml

Circle-CI

.circleci/config.yml

version: 2.0
jobs:
  build:
    docker:
      - image: derekahn/klint:latest