-
Notifications
You must be signed in to change notification settings - Fork 197
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Save historical reports of running kube-bench (#5)
Signed-off-by: Daniel Pacak <[email protected]>
- Loading branch information
1 parent
a4f150f
commit cc1deb2
Showing
8 changed files
with
201 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package kubebench | ||
|
||
import ( | ||
"encoding/json" | ||
starboard "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" | ||
"github.com/aquasecurity/starboard/pkg/ext" | ||
"io" | ||
meta "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
type Converter interface { | ||
Convert(reader io.Reader) (report starboard.CISKubernetesBenchmarkReport, err error) | ||
} | ||
|
||
var DefaultConverter Converter = &converter{ | ||
clock: ext.NewSystemClock(), | ||
} | ||
|
||
type converter struct { | ||
clock ext.Clock | ||
} | ||
|
||
func (c *converter) Convert(reader io.Reader) (report starboard.CISKubernetesBenchmarkReport, err error) { | ||
decoder := json.NewDecoder(reader) | ||
report = starboard.CISKubernetesBenchmarkReport{ | ||
GeneratedAt: meta.NewTime(c.clock.Now()), | ||
Scanner: starboard.Scanner{ | ||
Name: "kube-bench", | ||
Vendor: "Aqua Security", | ||
Version: "latest", | ||
}, | ||
Sections: []starboard.CISKubernetesBenchmarkSection{}, | ||
} | ||
|
||
for { | ||
var section starboard.CISKubernetesBenchmarkSection | ||
de := decoder.Decode(§ion) | ||
if de == io.EOF { | ||
break | ||
} | ||
if de != nil { | ||
err = de | ||
break | ||
} | ||
report.Sections = append(report.Sections, section) | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package kubebench |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,143 @@ | ||
package crd | ||
|
||
import ( | ||
"errors" | ||
"strings" | ||
"fmt" | ||
"strconv" | ||
|
||
sec "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" | ||
secapi "github.com/aquasecurity/starboard/pkg/generated/clientset/versioned" | ||
"github.com/aquasecurity/starboard/pkg/ext" | ||
"github.com/aquasecurity/starboard/pkg/kube" | ||
|
||
"k8s.io/klog" | ||
|
||
core "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/labels" | ||
"k8s.io/utils/pointer" | ||
|
||
starboard "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" | ||
starboardapi "github.com/aquasecurity/starboard/pkg/generated/clientset/versioned" | ||
"github.com/aquasecurity/starboard/pkg/kubebench" | ||
meta "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/rest" | ||
) | ||
|
||
const ( | ||
defaultHistoryLimit = 10 | ||
) | ||
|
||
type writer struct { | ||
client *secapi.Clientset | ||
clock ext.Clock | ||
clientset starboardapi.Interface | ||
} | ||
|
||
func NewWriter(config *rest.Config) (w kubebench.Writer, err error) { | ||
client, err := secapi.NewForConfig(config) | ||
func NewWriter(clock ext.Clock, clientset starboardapi.Interface) kubebench.Writer { | ||
return &writer{ | ||
clock: clock, | ||
clientset: clientset, | ||
} | ||
} | ||
|
||
func (w *writer) Write(report starboard.CISKubernetesBenchmarkReport, node *core.Node) (err error) { | ||
reports, err := w.getReportsByNodeName(node.GetName()) | ||
if err != nil { | ||
return | ||
} | ||
w = &writer{ | ||
client: client, | ||
err = w.removeHistoryLatestLabel(reports) | ||
if err != nil { | ||
return | ||
} | ||
return | ||
} | ||
|
||
func (w *writer) Write(report sec.CISKubernetesBenchmarkReport, node string) (err error) { | ||
if strings.TrimSpace(node) == "" { | ||
err = errors.New("node name must not be blank") | ||
err = w.removeReportsWithHistoryLimitExceeded(reports) | ||
if err != nil { | ||
return | ||
} | ||
// TODO Check if an instance of the report with the given name already exists. | ||
// TODO If exists just update it, create new instance otherwise | ||
_, err = w.client.AquasecurityV1alpha1().CISKubernetesBenchmarks().Create(&sec.CISKubernetesBenchmark{ | ||
|
||
_, err = w.clientset.AquasecurityV1alpha1().CISKubernetesBenchmarks().Create(&starboard.CISKubernetesBenchmark{ | ||
ObjectMeta: meta.ObjectMeta{ | ||
Name: node, | ||
Labels: map[string]string{}, | ||
Name: fmt.Sprintf("%s-%d", node.Name, w.clock.Now().Unix()), | ||
Labels: map[string]string{ | ||
kube.LabelResourceKind: "Node", // TODO Why node.Kind is nil? | ||
kube.LabelResourceName: node.Name, | ||
kube.LabelScannerName: "kube-bench", | ||
kube.LabelScannerVendor: "aqua", | ||
kube.LabelHistoryLatest: "true", | ||
}, | ||
Annotations: map[string]string{ | ||
// TODO Make this history limit configurable somehow, e.g. $ starboard kube-bench --history-limit=7 | ||
kube.AnnotationHistoryLimit: strconv.Itoa(10), | ||
}, | ||
OwnerReferences: []meta.OwnerReference{ | ||
{ | ||
APIVersion: "v1", // TODO Why node.APIVersion is nil? | ||
Kind: "Node", // TODO Why node.Kind is nil? | ||
Name: node.Name, | ||
UID: node.UID, | ||
Controller: pointer.BoolPtr(false), | ||
}, | ||
}, | ||
}, | ||
Report: report, | ||
}) | ||
return | ||
} | ||
|
||
func (w *writer) getReportsByNodeName(name string) (reports []starboard.CISKubernetesBenchmark, err error) { | ||
list, err := w.clientset.AquasecurityV1alpha1().CISKubernetesBenchmarks().List(meta.ListOptions{ | ||
LabelSelector: labels.Set{ | ||
kube.LabelResourceKind: "Node", | ||
kube.LabelResourceName: name, | ||
}.String(), | ||
}) | ||
if err != nil { | ||
return | ||
} | ||
reports = list.Items | ||
return | ||
} | ||
|
||
func (w *writer) removeHistoryLatestLabel(reports []starboard.CISKubernetesBenchmark) (err error) { | ||
for _, report := range reports { | ||
if value, ok := report.Labels[kube.LabelHistoryLatest]; !ok || value != "true" { | ||
continue | ||
} | ||
clone := report.DeepCopy() | ||
delete(clone.Labels, kube.LabelHistoryLatest) | ||
klog.V(3).Infof("Removing %s label from %s report", kube.LabelHistoryLatest, clone.Name) | ||
_, err = w.clientset.AquasecurityV1alpha1().CISKubernetesBenchmarks().Update(clone) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
return | ||
} | ||
|
||
func (w *writer) removeReportsWithHistoryLimitExceeded(reports []starboard.CISKubernetesBenchmark) (err error) { | ||
limit := w.getHistoryLimit(reports) | ||
diff := len(reports) - limit | ||
if diff < 0 { | ||
return | ||
} | ||
for _, r := range reports[0 : diff+1] { | ||
klog.V(3).Infof("Removing %s report which exceeded history limit of %d", r.GetName(), limit) | ||
err = w.clientset.AquasecurityV1alpha1().CISKubernetesBenchmarks().Delete(r.GetName(), &meta.DeleteOptions{ | ||
GracePeriodSeconds: pointer.Int64Ptr(0), | ||
}) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
return | ||
} | ||
|
||
func (w *writer) getHistoryLimit(reports []starboard.CISKubernetesBenchmark) int { | ||
if len(reports) == 0 { | ||
return defaultHistoryLimit | ||
} | ||
latestReport := reports[len(reports)-1] | ||
if value, ok := latestReport.Annotations[kube.AnnotationHistoryLimit]; ok { | ||
limit, err := strconv.Atoi(value) | ||
if err != nil { | ||
klog.V(3).Infof("Error while parsing value %s of %s annotation", value, kube.AnnotationHistoryLimit) | ||
return defaultHistoryLimit | ||
} | ||
return limit | ||
} | ||
return defaultHistoryLimit | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
package kubebench | ||
|
||
import ( | ||
sec "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" | ||
starboard "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1" | ||
core "k8s.io/api/core/v1" | ||
) | ||
|
||
type Writer interface { | ||
Write(report sec.CISKubernetesBenchmarkReport, node string) error | ||
Write(report starboard.CISKubernetesBenchmarkReport, node *core.Node) error | ||
} |