Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade plugin to kubesec v2 #20

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sudo: required
language: go

go:
- 1.11.x
- 1.12.x

script:
- make deploy
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ V?=0

deploy:
@mkdir -p ~/.kube/plugins/
@rm -rf ~/.kube/plugins/kubectl-scan || true
@go build -o ~/.kube/plugins/kubectl-scan


38 changes: 28 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# kubectl-kubesec

[![Build Status](https://travis-ci.org/stefanprodan/kubectl-kubesec.svg?branch=master)](https://travis-ci.org/stefanprodan/kubectl-kubesec)
[![Build Status](https://travis-ci.org/controlplaneio/kubectl-kubesec.svg?branch=master)](https://travis-ci.org/controlplaneio/kubectl-kubesec)

This is a kubectl plugin for scanning Kubernetes pods, deployments, daemonsets and statefulsets with [kubesec.io](https://kubesec.io)

For the admission controller see [kubesec-webhook](https://github.com/stefanprodan/kubesec-webhook)
For the admission controller see [kubesec-webhook](https://github.com/controlplaneio/kubesec-webhook)

### Install

Expand All @@ -21,7 +21,7 @@ For Kubernetes 1.12 or newer:

```bash
mkdir -p ~/.kube/plugins/scan && \
curl -sL https://github.com/stefanprodan/kubectl-kubesec/releases/download/1.0.0/kubectl-kubesec_1.0.0_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
curl -sL https://github.com/controlplaneio/kubectl-kubesec/releases/download/1.0.0/kubectl-kubesec_1.0.0_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
mv ~/.kube/plugins/scan/scan ~/.kube/plugins/scan/kubectl-scan
export PATH=$PATH:~/.kube/plugins/scan
```
Expand All @@ -30,7 +30,7 @@ For Kubernetes older than 1.12:

```bash
mkdir -p ~/.kube/plugins/scan && \
curl -sL https://github.com/stefanprodan/kubectl-kubesec/releases/download/0.3.1/kubectl-kubesec_0.3.1_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
curl -sL https://github.com/controlplaneio/kubectl-kubesec/releases/download/0.3.1/kubectl-kubesec_0.3.1_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
```

### Usage
Expand Down Expand Up @@ -68,17 +68,33 @@ kubectl scan -n weave daemonset weave-scope-agent
Result:

```
daemonset/weave-scope-agent kubesec.io score -54
kubesec.io score: -51
-----------------
Critical
1. containers[] .securityContext .privileged == true
Privileged containers can allow almost completely unrestricted host access
2. .spec .hostNetwork
1. .spec .hostNetwork == true
Sharing the host's network namespace permits processes in the pod to communicate with processes bound to the host's loopback adapter
3. .spec .hostPID
2. .spec .hostPID == true
Sharing the host's PID namespace allows visibility of processes on the host, potentially leaking information such as environment variables and configuration
4. .spec .volumes[] .hostPath .path == "/var/run/docker.sock"
3. containers[] .securityContext .privileged == true
Privileged containers can allow almost completely unrestricted host access
4. volumes[] .hostPath .path == /var/run/docker.sock
Mounting the docker.socket leaks information about other containers and can allow container breakout
-----------------
Advise
1. containers[] .securityContext .runAsUser -gt 10000
Run as a high-UID user to avoid conflicts with the host's user table
2. containers[] .securityContext .readOnlyRootFilesystem == true
An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost
3. containers[] .securityContext .runAsNonRoot == true
Force the running image to run as a non-root user to ensure least privilege
4. containers[] .resources .limits .cpu
Enforcing CPU limits prevents DOS via resource exhaustion
5. containers[] .securityContext .capabilities .drop
Reducing kernel capabilities available to a container limits its attack surface
6. .metadata .annotations ."container.seccomp.security.alpha.kubernetes.io/pod"
Seccomp profiles set minimum privilege and secure against unknown threats
7. containers[] .securityContext .capabilities .drop | index("ALL")
Drop all capabilities and add only those required to reduce syscall attack surface
```

Scan a StatefulSet:
Expand Down Expand Up @@ -127,3 +143,5 @@ Run as a high-UID user to avoid conflicts with the host's user table
5. containers[] .securityContext .capabilities .drop | index("ALL")
Drop all capabilities and add only those required to reduce syscall attack surface
```

Note that you can change the kubesec API address with the `KUBESEC_URL` env var.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package main
import (
"flag"
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/cmd"
_ "github.com/golang/glog"
"github.com/stefanprodan/kubectl-kubesec/pkg/cmd"
"os"
"strings"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"bytes"
"errors"
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/kubesec"
"github.com/spf13/cobra"
"github.com/stefanprodan/kubectl-kubesec/pkg/kubesec"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"bytes"
"errors"
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/kubesec"
"github.com/spf13/cobra"
"github.com/stefanprodan/kubectl-kubesec/pkg/kubesec"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"bytes"
"errors"
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/kubesec"
"github.com/spf13/cobra"
"github.com/stefanprodan/kubectl-kubesec/pkg/kubesec"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"bytes"
"errors"
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/kubesec"
"github.com/spf13/cobra"
"github.com/stefanprodan/kubectl-kubesec/pkg/kubesec"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package cmd

import (
"fmt"
"github.com/controlplaneio/kubectl-kubesec/pkg/version"
"github.com/spf13/cobra"
"github.com/stefanprodan/kubectl-kubesec/pkg/version"
)

var versionCmd = &cobra.Command{
Expand Down
27 changes: 10 additions & 17 deletions pkg/kubesec/kubesec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
)

// KubesecClient represent a client for kubesec.io.
Expand All @@ -22,20 +22,12 @@ func NewClient() *KubesecClient {

// ScanDefinition scans the provided resource definition.
func (kc *KubesecClient) ScanDefinition(def bytes.Buffer) (*KubesecResult, error) {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", "object.yaml")
if err != nil {
return nil, err
}
_, err = io.Copy(fileWriter, &def)
if err != nil {
return nil, err
url := os.Getenv("KUBESEC_URL")
if url == "" {
url = "https://v2.kubesec.io/scan"
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()

resp, err := http.Post("https://kubesec.io/", contentType, bodyBuf)
resp, err := http.Post(url, "application/yaml", bytes.NewBuffer(def.Bytes()))
if err != nil {
return nil, err
}
Expand All @@ -49,13 +41,14 @@ func (kc *KubesecClient) ScanDefinition(def bytes.Buffer) (*KubesecResult, error
return nil, errors.New("failed to scan definition")
}

var result KubesecResult
var result []KubesecResult
err = json.Unmarshal(body, &result)
if err != nil {
return nil, err
fmt.Println(string(body))
return nil, fmt.Errorf("json unmarshal error: %s", err.Error())
}

return &result, nil
return &result[0], nil
}

// KubesecResult represents a result returned by kubesec.io.
Expand Down Expand Up @@ -92,7 +85,7 @@ func (r *KubesecResult) Dump(w io.Writer) {
fmt.Fprintln(w, "-----------------")
}
if len(r.Scoring.Advise) > 0 {
fmt.Fprintf(w, "Advise")
fmt.Fprintln(w, "Advise")
for i, el := range r.Scoring.Advise {
fmt.Fprintf(w, "%v. %v\n", i+1, el.Selector)
if len(el.Reason) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/version/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package version

var VERSION = "1.0.0"
var VERSION = "2.0.0"
var REVISION = "unknown"