Skip to content

Commit 3982e4c

Browse files
committed
Add support for pods, statefulsets and daemonsets
- change command to "kubectl plugin scan [pod|deployment|statefulset|daemonset]/name" - ref #2
1 parent b68709c commit 3982e4c

File tree

3 files changed

+264
-149
lines changed

3 files changed

+264
-149
lines changed

README.md

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# kubectl-kubesec
22

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

55
### Install
66

77
Download and extract the scan plugin to `~/.kube/plugins/scan`:
88

99
```bash
1010
mkdir -p ~/.kube/plugins/scan && \
11-
curl -sL https://github.com/stefanprodan/kubectl-kubesec/releases/download/0.1.0/kubectl-kubesec_0.1.0_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
11+
curl -sL https://github.com/stefanprodan/kubectl-kubesec/releases/download/0.1.0/kubectl-kubesec_0.2.0_`uname -s`_amd64.tar.gz | tar xzvf - -C ~/.kube/plugins/scan
1212
```
1313

1414
### Usage
1515

16-
Scan a deployment:
16+
Scan a Deployment:
1717

1818
```bash
19-
kubectl -n kube-system plugin scan kubernetes-dashboard
19+
kubectl -n kube-system plugin scan deployment/kubernetes-dashboard
2020
```
2121

2222
Result:
@@ -36,3 +36,72 @@ Run as a high-UID user to avoid conflicts with the host's user table
3636
5. containers[] .securityContext .capabilities .drop | index("ALL")
3737
Drop all capabilities and add only those required to reduce syscall attack surface
3838
```
39+
40+
Scan a DaemonSet:
41+
42+
```bash
43+
kubectl -n weave plugin scan daemonset/weave-scope-agent
44+
```
45+
46+
Result:
47+
48+
```
49+
daemonset/weave-scope-agent kubesec.io score -54
50+
-----------------
51+
Critical
52+
1. containers[] .securityContext .privileged == true
53+
Privileged containers can allow almost completely unrestricted host access
54+
2. .spec .hostNetwork
55+
Sharing the host's network namespace permits processes in the pod to communicate with processes bound to the host's loopback adapter
56+
3. .spec .hostPID
57+
Sharing the host's PID namespace allows visibility of processes on the host, potentially leaking information such as environment variables and configuration
58+
4. .spec .volumes[] .hostPath .path == "/var/run/docker.sock"
59+
Mounting the docker.socket leaks information about other containers and can allow container breakout
60+
```
61+
62+
Scan a StatefulSet:
63+
64+
```bash
65+
kubectl plugin scan statefulset/memcached
66+
```
67+
68+
Result:
69+
70+
```
71+
statefulset/memcached kubesec.io score 2
72+
-----------------
73+
Advise
74+
1. .spec .volumeClaimTemplates[] .spec .accessModes | index("ReadWriteOnce")
75+
2. containers[] .securityContext .runAsNonRoot == true
76+
Force the running image to run as a non-root user to ensure least privilege
77+
3. containers[] .securityContext .capabilities .drop
78+
Reducing kernel capabilities available to a container limits its attack surface
79+
4. containers[] .securityContext .readOnlyRootFilesystem == true
80+
An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost
81+
5. containers[] .securityContext .runAsUser > 10000
82+
Run as a high-UID user to avoid conflicts with the host's user table
83+
```
84+
85+
Scan a Pod:
86+
87+
```bash
88+
kubectl -n kube-system plugin scan pod/tiller-deploy-5c688d5f9b-ztjbt
89+
```
90+
91+
Result:
92+
93+
```
94+
pod/tiller-deploy-5c688d5f9b-ztjbt kubesec.io score 3
95+
-----------------
96+
Advise
97+
1. containers[] .securityContext .runAsNonRoot == true
98+
Force the running image to run as a non-root user to ensure least privilege
99+
2. containers[] .securityContext .capabilities .drop
100+
Reducing kernel capabilities available to a container limits its attack surface
101+
3. containers[] .securityContext .readOnlyRootFilesystem == true
102+
An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost
103+
4. containers[] .securityContext .runAsUser > 10000
104+
Run as a high-UID user to avoid conflicts with the host's user table
105+
5. containers[] .securityContext .capabilities .drop | index("ALL")
106+
Drop all capabilities and add only those required to reduce syscall attack surface
107+
```

kubesec.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"io/ioutil"
10+
"mime/multipart"
11+
"net/http"
12+
)
13+
14+
type KubesecResult struct {
15+
Score int `json:"score"`
16+
Scoring struct {
17+
Critical []struct {
18+
Selector string `json:"selector"`
19+
Reason string `json:"reason"`
20+
Weight int `json:"weight"`
21+
} `json:"critical"`
22+
Advise []struct {
23+
Selector string `json:"selector"`
24+
Reason string `json:"reason"`
25+
Href string `json:"href,omitempty"`
26+
} `json:"advise"`
27+
} `json:"scoring"`
28+
}
29+
30+
func (r *KubesecResult) print(resource string) {
31+
fmt.Println(fmt.Sprintf("%v kubesec.io score %v", resource, r.Score))
32+
fmt.Println("-----------------")
33+
if len(r.Scoring.Critical) > 0 {
34+
fmt.Println("Critical")
35+
for i, el := range r.Scoring.Critical {
36+
fmt.Println(fmt.Sprintf("%v. %v", i+1, el.Selector))
37+
if len(el.Reason) > 0 {
38+
fmt.Println(el.Reason)
39+
}
40+
41+
}
42+
fmt.Println("-----------------")
43+
}
44+
if len(r.Scoring.Advise) > 0 {
45+
fmt.Println("Advise")
46+
for i, el := range r.Scoring.Advise {
47+
fmt.Println(fmt.Sprintf("%v. %v", i+1, el.Selector))
48+
if len(el.Reason) > 0 {
49+
fmt.Println(el.Reason)
50+
}
51+
}
52+
}
53+
}
54+
55+
func getResult(definition bytes.Buffer) (*KubesecResult, error) {
56+
57+
bodyBuf := &bytes.Buffer{}
58+
bodyWriter := multipart.NewWriter(bodyBuf)
59+
60+
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", "object.yaml")
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
_, err = io.Copy(fileWriter, &definition)
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
contentType := bodyWriter.FormDataContentType()
71+
bodyWriter.Close()
72+
73+
resp, err := http.Post("https://kubesec.io/", contentType, bodyBuf)
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
defer resp.Body.Close()
79+
80+
body, err := ioutil.ReadAll(resp.Body)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
if len(body) < 1 {
86+
return nil, errors.New("unknown result")
87+
}
88+
89+
var result KubesecResult
90+
err = json.Unmarshal(body, &result)
91+
if err != nil {
92+
return nil, err
93+
}
94+
95+
return &result, nil
96+
}

0 commit comments

Comments
 (0)