Skip to content

Commit

Permalink
feat: prune the scanning pod
Browse files Browse the repository at this point in the history
  • Loading branch information
christasa committed Mar 12, 2023
1 parent 3443fe6 commit efaec20
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@
## improvements
- Add some rules for CAP checking
- Change the namespace checking of Secret and ConfigMap
- Improve the rules of `DeamonSet` scanning

# 1.0.5 (2023.2.13)
## features
Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -56,6 +56,7 @@ Vesta is a flexible toolkit which can run on physical machines in different type
|| Docker env password check | Check weak password in database. | high/medium | |
|| Image tag check | Image is not tagged or `latest`. | low | |
|| Docker History | Docker layers have some dangerous commands. | high/medium | |
| Pending | Docker Backdoor | Docker env command has malicious commands. | critical/high | |

---

Expand Down Expand Up @@ -84,7 +85,7 @@ Vesta is a flexible toolkit which can run on physical machines in different type
|| Sidecar configurations | Sidecar has some dangerous configurations. | critical/high/ medium/low | |
|| Pod annotation | Pod annotation has some unsafe configurations. | high/medium/ low/warning | [Ref](https://github.com/kvesta/vesta/wiki/Annotation-Checking-References) |
|| DaemonSet | DaemonSet has unsafe configurations. | critical/high/ medium/low | |
|| Backdoor | Backdoor Detection | critical | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection) |
|| Backdoor | Backdoor Detection | critical/high | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection) |



Expand Down
31 changes: 17 additions & 14 deletions README.zh-Hans.md
Expand Up @@ -56,19 +56,22 @@ vesta同时也是一个灵活,方便的工具,能够在各种系统上运行

> Docker检查
| Supported | Check Item | Description | Severity | Reference |
|-----------|---------------------------|----------------------------------|--------------------------|---------------------------------------------------------------------------------------------|
|| PrivilegeAllowed | 危险的特权模式 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |
|| Capabilities | 危险capabilities被设置 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |
|| Volume Mount | 敏感或危险目录被挂载 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References) |
|| Docker Unauthorized | 2375端口打开并且未授权 | critical | [Ref](https://github.com/vulhub/vulhub/blob/master/docker/unauthorized-rce/README.md) |
|| Kernel version | 当前内核版本存在逃逸漏洞 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References) |
|| Network Module | Net模式为`host`模式或同时在特定containerd版本下 | critical/medium | |
|| Pid Module | Pid模式被设置为`host` | high | |
|| Docker Server version | Docker Server版本存在漏洞 | critical/high/medium/low | |
|| Docker env password check | Docker env是否存在弱密码 | high/medium | |
|| Image tag check | Image没有被打tag或为默认latest | low | |
|| Docker history | Docker layers 存在不安全的命令 | high/medium | |
| Supported | Check Item | Description | Severity | Reference |
|-----------|---------------------------|------------------------------------|--------------------------|---------------------------------------------------------------------------------------------|
|| PrivilegeAllowed | 危险的特权模式 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |
|| Capabilities | 危险capabilities被设置 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |
|| Volume Mount | 敏感或危险目录被挂载 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References) |
|| Docker Unauthorized | 2375端口打开并且未授权 | critical | [Ref](https://github.com/vulhub/vulhub/blob/master/docker/unauthorized-rce/README.md) |
|| Kernel version | 当前内核版本存在逃逸漏洞 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References) |
|| Network Module | Net模式为`host`模式或同时在特定containerd版本下 | critical/medium | |
|| Pid Module | Pid模式被设置为`host` | high | |
|| Docker Server version | Docker Server版本存在漏洞 | critical/high/medium/low | |
|| Docker env password check | Docker env是否存在弱密码 | high/medium | |
|| Image tag check | Image没有被打tag或为默认latest | low | |
|| Docker history | Docker layers 存在不安全的命令 | high/medium | |
| Pending | Docker Backdoor | Docker env command 存在恶意命令 | critical/high | |



---

Expand Down Expand Up @@ -97,7 +100,7 @@ vesta同时也是一个灵活,方便的工具,能够在各种系统上运行
|| Sidecar configurations | Sidecar 安全配置检查以及Env环境检查 | critical/high/ medium/low | |
|| Pod annotation | Pod annotation 存在不安全配置 | high/medium/ low/warning | [Ref](https://github.com/kvesta/vesta/wiki/Annotation-Checking-References) |
|| DaemonSet | DaemonSet存在不安全配置 | critical/high/ medium/low | |
|| Backdoor | 检查k8s中是否有后门 | critical | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection) |
|| Backdoor | 检查k8s中是否有后门 | critical/high | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection) |


## 编译并使用vesta
Expand Down
2 changes: 1 addition & 1 deletion internal/analyzer/analyze.go
Expand Up @@ -141,7 +141,7 @@ func (ks *KScanner) checkKubernetesList(ctx context.Context) error {
log.Printf(config.Yellow("Begin Job and CronJob analyzing"))
log.Printf(config.Yellow("Begin DaemonSet analyzing"))

if ctx.Value("nameSpace") == "all" {
if ctx.Value("nameSpace") == "all" || ctx.Value("nameSpace") != "standard" {
namespaceWhileList = []string{}
}

Expand Down
8 changes: 4 additions & 4 deletions internal/analyzer/k8s_cni.go
Expand Up @@ -185,7 +185,7 @@ func checkEnvoy() (bool, []*threat) {
return vuln, tlist
}

func (ks KScanner) checkIstio(vulnCli vulnlib.Client) (bool, []*threat) {
func (ks *KScanner) checkIstio(vulnCli vulnlib.Client) (bool, []*threat) {
log.Printf(config.Yellow("Begin Istio analyzing"))

var vuln = false
Expand Down Expand Up @@ -251,7 +251,7 @@ func (ks KScanner) checkIstio(vulnCli vulnlib.Client) (bool, []*threat) {
return vuln, tlist
}

func (ks KScanner) checkIstioHeader(podname, ns, cname string) (bool, []*threat) {
func (ks *KScanner) checkIstioHeader(podname, ns, cname string) (bool, []*threat) {
var vuln = false
tlist := []*threat{}

Expand Down Expand Up @@ -317,7 +317,7 @@ func (ks KScanner) checkIstioHeader(podname, ns, cname string) (bool, []*threat)
return vuln, tlist
}

func (ks KScanner) checkCilium(vulnCli vulnlib.Client) (bool, []*threat) {
func (ks *KScanner) checkCilium(vulnCli vulnlib.Client) (bool, []*threat) {
log.Printf(config.Yellow("Begin cilium analyzing"))

var vuln = false
Expand Down Expand Up @@ -502,7 +502,7 @@ func checkKubectlProxy() (bool, []*threat) {
return vuln, tlist
}

func (ks KScanner) checkEtcd() (bool, []*threat) {
func (ks *KScanner) checkEtcd() (bool, []*threat) {
log.Printf(config.Yellow("Begin Etcd analyzing"))

var vuln = false
Expand Down
10 changes: 9 additions & 1 deletion internal/analyzer/k8s_configuration.go
Expand Up @@ -243,7 +243,6 @@ func (ks *KScanner) checkDaemonSet(ns string) error {

if len(daemonPod.Items) > 0 {
p = daemonPod.Items[0]

break
}
}
Expand All @@ -259,6 +258,11 @@ func (ks *KScanner) checkDaemonSet(ns string) error {
}
}

// Skip the low risk
if severity == "low" {
return nil
}

var containerImages string

for _, im := range da.Spec.Template.Spec.Containers {
Expand Down Expand Up @@ -288,6 +292,8 @@ func (ks *KScanner) checkDaemonSet(ns string) error {
}

if !isChecked && p.Name != "" {
sortSeverity(vList)

con := &container{
ContainerName: p.Name,
Namepsace: da.Namespace,
Expand Down Expand Up @@ -320,6 +326,7 @@ func (ks *KScanner) checkJobsOrCornJob(ns string) error {
return err
}

// TODO: add command checking in job
for _, job := range jobs.Items {
seccompProfile := job.Spec.Template.Spec.SecurityContext.SeccompProfile
selinuxProfile := job.Spec.Template.Spec.SecurityContext.SELinuxOptions
Expand Down Expand Up @@ -352,6 +359,7 @@ cronJob:
return err
}

// TODO: add command checking in cronjob
for _, cronjob := range cronjobs.Items {
seccompProfile := cronjob.Spec.JobTemplate.Spec.Template.Spec.SecurityContext.SeccompProfile
selinuxProfile := cronjob.Spec.JobTemplate.Spec.Template.Spec.SecurityContext.SELinuxOptions
Expand Down
143 changes: 137 additions & 6 deletions internal/analyzer/k8s_pod.go
@@ -1,17 +1,59 @@
package analyzer

import (
"context"
"fmt"
"math"
"regexp"
"sort"
"strings"
"time"

"github.com/kvesta/vesta/config"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (ks KScanner) podAnalyze(podSpec v1.PodSpec, rv RBACVuln, ns, podName string) []*threat {
func (ks *KScanner) podAnalyze(podSpec v1.PodSpec, rv RBACVuln, ns, podName string) []*threat {
vList := []*threat{}

for _, nswList := range namespaceWhileList {
if ns == nswList {
pruned, err := ks.prunePod(ns, podName)
if err != nil {
break
}

if pruned {
return vList
}

pod, err := ks.KClient.
CoreV1().
Pods(ns).
Get(context.TODO(), podName, metav1.GetOptions{})
if err != nil {
break
}

age := time.Since(pod.CreationTimestamp.Time)

if age.Hours() < 168 {
th := &threat{
Param: "replaced time",
Value: pod.CreationTimestamp.Time.Format("02/01/2006"),
Type: "Pod modify",
Describe: fmt.Sprintf("Pod has been modified %.2f hours ageo "+
"in crucial namespace: %s", age.Hours(), ns),
Severity: "medium",
}
vList = append(vList, th)
}

break
}
}

for _, v := range podSpec.Volumes {
if ok, tlist := checkPodVolume(v); ok {
vList = append(vList, tlist...)
Expand Down Expand Up @@ -155,7 +197,7 @@ func checkPodPrivileged(container v1.Container) (bool, []*threat) {
return vuln, tlist
}

func (ks KScanner) checkSidecarEnv(container v1.Container, ns string) (bool, []*threat) {
func (ks *KScanner) checkSidecarEnv(container v1.Container, ns string) (bool, []*threat) {
var vuln = false
tlist := []*threat{}

Expand Down Expand Up @@ -414,7 +456,7 @@ func checkPodAnnotation(ans map[string]string) (bool, []*threat) {
return vuln, tlist
}

func (ks KScanner) checkPodCommand(container v1.Container, ns string) (bool, []*threat) {
func (ks *KScanner) checkPodCommand(container v1.Container, ns string) (bool, []*threat) {
var vuln = false
tlist := []*threat{}

Expand Down Expand Up @@ -499,7 +541,7 @@ func (ks KScanner) checkPodCommand(container v1.Container, ns string) (bool, []*
return vuln, tlist
}

func (ks KScanner) findEnvValue(container v1.Container, name, ns string) string {
func (ks *KScanner) findEnvValue(container v1.Container, name, ns string) string {
var value string

for _, env := range container.Env {
Expand Down Expand Up @@ -528,7 +570,7 @@ func (ks KScanner) findEnvValue(container v1.Container, name, ns string) string
return value
}

func (ks KScanner) getRBACVulnType(ns string) RBACVuln {
func (ks *KScanner) getRBACVulnType(ns string) RBACVuln {
rbv := RBACVuln{
Severity: "warning",
}
Expand Down Expand Up @@ -574,7 +616,7 @@ func (ks KScanner) getRBACVulnType(ns string) RBACVuln {
return rbv
}

func (ks KScanner) checkConfigVulnType(ns, name, ty string, configReg *regexp.Regexp) (bool, *threat) {
func (ks *KScanner) checkConfigVulnType(ns, name, ty string, configReg *regexp.Regexp) (bool, *threat) {
var vuln = false
th := &threat{}

Expand All @@ -599,3 +641,92 @@ func (ks KScanner) checkConfigVulnType(ns, name, ty string, configReg *regexp.Re

return vuln, th
}

// prunePod assesses whether a pod need to check if namespace of pod in white list
func (ks *KScanner) prunePod(ns, podName string) (bool, error) {
pods, err := ks.KClient.
CoreV1().
Pods(ns).
List(context.TODO(), metav1.ListOptions{})
if err != nil {
return false, err
}

type PodStatus struct {
Age float64
Restarts int
}

p := PodStatus{}

podNumber := len(pods.Items)

ageWeight := make([]float64, podNumber-1)
restartWeight := make([]int, podNumber-1)

index := 0
for _, pod := range pods.Items {
age := time.Since(pod.CreationTimestamp.Time)
restarts := pod.Status.ContainerStatuses[0].RestartCount

if pod.Name == podName {
p.Age = math.Round(age.Hours())
p.Restarts = int(restarts)
continue
}

ageWeight[index] = math.Round(age.Hours())
restartWeight[index] = int(restarts)
index += 1
}

sort.Float64s(ageWeight)
sort.Ints(restartWeight)
ageDeviation := standardDeviation[float64](ageWeight)
restartDeviation := math.Sqrt(standardDeviation[int](restartWeight))

ageCount := map[float64]int{}
restartCount := map[int]int{}
for i := 0; i < podNumber-1; i++ {
age := ageWeight[i]
restarts := restartWeight[i]

if _, ok := ageCount[age]; ok {
ageCount[age] += 1
} else {
ageCount[age] = 1
}

if _, ok := restartCount[restarts]; ok {
restartCount[restarts] += 1
} else {
restartCount[restarts] = 1
}
}

score := 0.0

for number, count := range ageCount {
if math.Abs(p.Age-number) > ageDeviation {
score = math.Max(score, float64(count)/float64(podNumber-1))
}
}

// compare to the oldest operation
score += 0.2 * math.Abs(float64(p.Age)-ageWeight[podNumber-2]) / (ageWeight[podNumber-2] / 960)

rscore := 0.0
for number, count := range restartCount {
if math.Abs(float64(p.Restarts-number)) > restartDeviation {
rscore = math.Max(rscore, float64(count)/float64(podNumber-1))
}
}

score += rscore

if score < 0.7 {
return true, nil
}

return false, nil
}
6 changes: 3 additions & 3 deletions internal/analyzer/k8s_rbac.go
Expand Up @@ -556,7 +556,7 @@ func (ks *KScanner) checkSecret(ns string) error {
return nil
}

func (ks KScanner) checkSecretFromName(ns, key, seName, envName string) (bool, *threat) {
func (ks *KScanner) checkSecretFromName(ns, key, seName, envName string) (bool, *threat) {
var vuln = false
th := &threat{}

Expand All @@ -581,7 +581,7 @@ func (ks KScanner) checkSecretFromName(ns, key, seName, envName string) (bool, *
return vuln, th
}

func (ks KScanner) checkConfigFromName(ns, key, seName, envName string) (bool, *threat) {
func (ks *KScanner) checkConfigFromName(ns, key, seName, envName string) (bool, *threat) {
var vuln = false
th := &threat{}

Expand All @@ -606,7 +606,7 @@ func (ks KScanner) checkConfigFromName(ns, key, seName, envName string) (bool, *
return vuln, th
}

func (ks KScanner) findSecretOrConfigMapValue(name, com, ns string) string {
func (ks *KScanner) findSecretOrConfigMapValue(name, com, ns string) string {

switch com {
case "ConfigMap":
Expand Down

0 comments on commit efaec20

Please sign in to comment.