mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5b35fcb55 | ||
|
|
35449e3d4e | ||
|
|
9509c69d87 | ||
|
|
34170faae9 | ||
|
|
d5d0da8ac3 | ||
|
|
8b7a4b1e48 | ||
|
|
d5383fe218 | ||
|
|
46b9cf35ac | ||
|
|
329d341fbf | ||
|
|
6be692c66f | ||
|
|
3c062238ad | ||
|
|
954224e9f6 | ||
|
|
a5f99e0a8d | ||
|
|
d484aeb62c | ||
|
|
8c3eeab7ed | ||
|
|
cea8266734 | ||
|
|
eefaf7b23c | ||
|
|
bc61755f67 | ||
|
|
c462d1ec2f |
4
.github/workflows/build.yaml
vendored
4
.github/workflows/build.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Build
|
||||
run: mkdir build && go mod tidy && go build -ldflags "-w" -o build/kubescape
|
||||
run: mkdir build && go mod tidy && go build -ldflags "-w -s" -o build/kubescape
|
||||
|
||||
- name: Chmod
|
||||
run: chmod +x build/kubescape
|
||||
@@ -50,4 +50,4 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: build/kubescape
|
||||
asset_name: kubescape
|
||||
asset_content_type: application/octet-stream
|
||||
asset_content_type: application/octet-stream
|
||||
16
README.md
16
README.md
@@ -9,20 +9,27 @@ Tests are configured with YAML files, making this tool easy to update as test sp
|
||||
## Installation
|
||||
To install the tool locally, run this:
|
||||
|
||||
`curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh | /bin/bash`
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh | /bin/bash
|
||||
```
|
||||
|
||||
<img src="docs/install.jpeg">
|
||||
|
||||
## Run
|
||||
To get a fast check of the security posture of your Kubernetes cluster, run this:
|
||||
|
||||
`kubescape scan framework nsa`
|
||||
```
|
||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
||||
```
|
||||
|
||||
If you wish to scan all namespaces in your cluster, remove the `--exclude-namespaces` flag.
|
||||
|
||||
<img src="docs/run.jpeg">
|
||||
|
||||
|
||||
# Status
|
||||
[](https://github.com/armosec/kubescape/actions/workflows/build.yaml)
|
||||
[]()
|
||||
|
||||
# How to build
|
||||
`go mod tidy && go build -o kubescape` :zany_face:
|
||||
@@ -33,17 +40,14 @@ To get a fast check of the security posture of your Kubernetes cluster, run this
|
||||
Kubescape is running the following tests according to what is defined by [Kubernetes Hardening Guidance by to NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
||||
* Non-root containers
|
||||
* Immutable container filesystem
|
||||
* Building secure container images
|
||||
* Privileged containers
|
||||
* hostPID, hostIPC privileges
|
||||
* hostNetwork access
|
||||
* allowedHostPaths field
|
||||
* Protecting pod service account tokens
|
||||
* Pods in kube-system and kube-public
|
||||
* Resource policies
|
||||
* Control plane hardening
|
||||
* Encrypted secrets
|
||||
* Anonymous Requests
|
||||
* Exposed dashboard
|
||||
|
||||
|
||||
## Technology
|
||||
|
||||
@@ -16,6 +16,7 @@ var InfoDisplay = color.New(color.Bold, color.FgHiYellow).FprintfFunc()
|
||||
var InfoTextDisplay = color.New(color.Faint, color.FgHiYellow).FprintfFunc()
|
||||
var SimpleDisplay = color.New(color.Bold, color.FgHiWhite).FprintfFunc()
|
||||
var SuccessDisplay = color.New(color.Bold, color.FgHiGreen).FprintfFunc()
|
||||
var DescriptionDisplay = color.New(color.Faint, color.FgWhite).FprintfFunc()
|
||||
|
||||
var Spinner *spinner.Spinner
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ type ControlReport struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
type RuleReport struct {
|
||||
Name string `json:"name"`
|
||||
Remediation string `json:"remediation"`
|
||||
RuleStatus RuleStatus `json:"ruleStatus"`
|
||||
RuleResponses []RuleResponse `json:"ruleResponses"`
|
||||
NumOfResources int
|
||||
Name string `json:"name"`
|
||||
Remediation string `json:"remediation"`
|
||||
RuleStatus RuleStatus `json:"ruleStatus"`
|
||||
RuleResponses []RuleResponse `json:"ruleResponses"`
|
||||
ListInputResources []map[string]interface{} `json:"-"`
|
||||
}
|
||||
type RuleStatus struct {
|
||||
Status string `json:"status"`
|
||||
|
||||
@@ -70,7 +70,10 @@ func ParseRegoResult(regoResult *rego.ResultSet) ([]RuleResponse, error) {
|
||||
func (controlReport *ControlReport) GetNumberOfResources() int {
|
||||
sum := 0
|
||||
for i := range controlReport.RuleReports {
|
||||
sum += controlReport.RuleReports[i].NumOfResources
|
||||
if controlReport.RuleReports[i].ListInputResources == nil {
|
||||
continue
|
||||
}
|
||||
sum += len(controlReport.RuleReports[i].ListInputResources)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ func (flagHandler *FlagHandler) ParseFlag() {
|
||||
}
|
||||
|
||||
func (flagHandler *FlagHandler) Help() {
|
||||
fmt.Println("Run: kube-escape scan framework nsa")
|
||||
fmt.Println("Run: kubescape scan framework nsa --exclude-namespaces kube-system,kube-public")
|
||||
}
|
||||
|
||||
func (flagHandler *FlagHandler) Version() {
|
||||
fmt.Println("bla.bla.bla")
|
||||
fmt.Println("betav1")
|
||||
}
|
||||
|
||||
func (flagHandler *FlagHandler) Scan() {
|
||||
|
||||
@@ -6,14 +6,16 @@ echo
|
||||
|
||||
BASE_DIR=~/.kubescape
|
||||
KUBESCAPE_EXEC=kubescape
|
||||
RELEASE=v0.0.12
|
||||
|
||||
RELEASE=v0.0.29
|
||||
|
||||
DOWNLOAD_URL="https://github.com/armosec/kubescape/releases/download/$RELEASE/kubescape"
|
||||
|
||||
mkdir -p $BASE_DIR
|
||||
|
||||
OUTPUT=$BASE_DIR/$KUBESCAPE_EXEC
|
||||
|
||||
curl -sL $DOWNLOAD_URL -o $OUTPUT
|
||||
curl --progress-bar -L $DOWNLOAD_URL -o $OUTPUT
|
||||
echo -e "\033[32m[V] Downloaded Kubescape"
|
||||
|
||||
sudo chmod +x $OUTPUT
|
||||
@@ -24,5 +26,5 @@ rm -rf $BASE_DIR
|
||||
echo -e "[V] Finished Installation"
|
||||
echo
|
||||
|
||||
echo -e "\033[35m Usage: $ $KUBESCAPE_EXEC scan framework nsa"
|
||||
echo -e "\033[35m Usage: $ $KUBESCAPE_EXEC scan framework nsa --exclude-namespaces kube-system,kube-public"
|
||||
echo
|
||||
|
||||
@@ -87,7 +87,7 @@ func (opap *OPAProcessor) ProcessRulesHandler(opaSessionObj *cautils.OPASessionO
|
||||
} else {
|
||||
ruleReport.RuleStatus.Status = "success"
|
||||
}
|
||||
ruleReport.NumOfResources = len(k8sObjects)
|
||||
ruleReport.ListInputResources = k8sObjects
|
||||
ruleReports = append(ruleReports, ruleReport)
|
||||
}
|
||||
controlReport.RuleReports = ruleReports
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package policyhandler
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"kube-escape/cautils"
|
||||
|
||||
@@ -52,7 +53,11 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *opap
|
||||
// get k8s resources
|
||||
cautils.ProgressTextDisplay("Accessing Kubernetes objects")
|
||||
glog.Infof(fmt.Sprintf("Getting kubernetes objects. reportID: %s", notification.ReportID))
|
||||
k8sResources, err := policyHandler.getK8sResources(frameworks, ¬ification.Designators)
|
||||
excludedNamespaces := ""
|
||||
if flag.Arg(3) == "--exclude-namespaces" {
|
||||
excludedNamespaces = flag.Arg(4)
|
||||
}
|
||||
k8sResources, err := policyHandler.getK8sResources(frameworks, ¬ification.Designators, excludedNamespaces)
|
||||
if err != nil || len(*k8sResources) == 0 {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@ package policyhandler
|
||||
import (
|
||||
"fmt"
|
||||
"kube-escape/cautils"
|
||||
"strings"
|
||||
|
||||
"kube-escape/cautils/k8sinterface"
|
||||
|
||||
@@ -18,7 +19,7 @@ import (
|
||||
|
||||
const SelectAllResources = "*"
|
||||
|
||||
func (policyHandler *PolicyHandler) getK8sResources(frameworks []opapolicy.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) {
|
||||
func (policyHandler *PolicyHandler) getK8sResources(frameworks []opapolicy.Framework, designator *armotypes.PortalDesignator, excludedNamespaces string) (*cautils.K8SResources, error) {
|
||||
// build resources map
|
||||
k8sResourcesMap := setResourceMap(frameworks)
|
||||
|
||||
@@ -26,20 +27,20 @@ func (policyHandler *PolicyHandler) getK8sResources(frameworks []opapolicy.Frame
|
||||
_, namespace, labels := armotypes.DigestPortalDesignator(designator)
|
||||
|
||||
// pull k8s recourses
|
||||
if err := policyHandler.pullResources(k8sResourcesMap, namespace, labels); err != nil {
|
||||
if err := policyHandler.pullResources(k8sResourcesMap, namespace, labels, excludedNamespaces); err != nil {
|
||||
return k8sResourcesMap, err
|
||||
}
|
||||
|
||||
return k8sResourcesMap, nil
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string) error {
|
||||
func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error {
|
||||
|
||||
var errs error
|
||||
for groupResource := range *k8sResources {
|
||||
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(groupResource)
|
||||
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
|
||||
result, err := policyHandler.pullSingleResource(&gvr, namespace, labels)
|
||||
result, err := policyHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces)
|
||||
if err != nil {
|
||||
// handle error
|
||||
if errs == nil {
|
||||
@@ -55,10 +56,16 @@ func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResou
|
||||
return errs
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string) ([]unstructured.Unstructured, error) {
|
||||
func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) {
|
||||
|
||||
// set labels
|
||||
listOptions := metav1.ListOptions{}
|
||||
if excludedNamespaces != "" && k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
excludedNamespacesSlice := strings.Split(excludedNamespaces, ",")
|
||||
for _, excludedNamespace := range excludedNamespacesSlice {
|
||||
listOptions.FieldSelector += "metadata.namespace!=" + excludedNamespace + ","
|
||||
}
|
||||
}
|
||||
if labels != nil && len(labels) > 0 {
|
||||
set := k8slabels.Set(labels)
|
||||
listOptions.LabelSelector = set.AsSelector().String()
|
||||
@@ -66,7 +73,6 @@ func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVer
|
||||
|
||||
// set dynamic object
|
||||
var clientResource dynamic.ResourceInterface
|
||||
|
||||
if namespace != "" && k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
clientResource = policyHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"kube-escape/cautils"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"kube-escape/cautils/k8sinterface"
|
||||
"kube-escape/cautils/opapolicy"
|
||||
@@ -14,7 +13,7 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var INDENT = " "
|
||||
var INDENT = " "
|
||||
|
||||
type Printer struct {
|
||||
opaSessionObj *chan *cautils.OPASessionObj
|
||||
@@ -62,7 +61,7 @@ func (printer *Printer) SummerySetup(postureReport *opapolicy.PostureReport) {
|
||||
TotalResources: cr.GetNumberOfResources(),
|
||||
TotalFailed: len(workloadsSummery),
|
||||
WorkloadSummery: mapResources,
|
||||
Description: strings.ReplaceAll(cr.Description, ". ", fmt.Sprintf(".\n%s%s", INDENT, INDENT)),
|
||||
Description: cr.Description,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,9 +71,21 @@ func (printer *Printer) PrintResults() {
|
||||
for control, controlSummery := range printer.summery {
|
||||
printer.printTitle(control, &controlSummery)
|
||||
printer.printResult(control, &controlSummery)
|
||||
|
||||
if controlSummery.TotalResources > 0 {
|
||||
printer.printSummery(control, &controlSummery)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (print *Printer) printSummery(controlName string, controlSummery *ControlSummery) {
|
||||
cautils.SimpleDisplay(os.Stdout, "Summary - ")
|
||||
cautils.SuccessDisplay(os.Stdout, "Passed:%v ", controlSummery.TotalResources-controlSummery.TotalFailed)
|
||||
cautils.FailureDisplay(os.Stdout, "Failed:%v ", controlSummery.TotalFailed)
|
||||
cautils.InfoDisplay(os.Stdout, "Total:%v\n\n", controlSummery.TotalResources)
|
||||
}
|
||||
|
||||
func (printer *Printer) printTitle(controlName string, controlSummery *ControlSummery) {
|
||||
cautils.InfoDisplay(os.Stdout, "[control: %s] ", controlName)
|
||||
if controlSummery.TotalResources == 0 {
|
||||
@@ -85,7 +96,7 @@ func (printer *Printer) printTitle(controlName string, controlSummery *ControlSu
|
||||
cautils.FailureDisplay(os.Stdout, "failed %v\n", emoji.SadButRelievedFace)
|
||||
}
|
||||
|
||||
cautils.SimpleDisplay(os.Stdout, "%sDescription: %s\n", INDENT, controlSummery.Description)
|
||||
cautils.DescriptionDisplay(os.Stdout, "Description: %s\n", controlSummery.Description)
|
||||
|
||||
}
|
||||
func (printer *Printer) printResult(controlName string, controlSummery *ControlSummery) {
|
||||
@@ -93,8 +104,9 @@ func (printer *Printer) printResult(controlName string, controlSummery *ControlS
|
||||
indent := INDENT
|
||||
for ns, rsc := range controlSummery.WorkloadSummery {
|
||||
preIndent := indent
|
||||
indent += indent
|
||||
cautils.SimpleDisplay(os.Stdout, "%sNamespace %s\n", indent, ns)
|
||||
if ns != "" {
|
||||
cautils.SimpleDisplay(os.Stdout, "%sNamespace %s\n", indent, ns)
|
||||
}
|
||||
preIndent2 := indent
|
||||
for r := range rsc {
|
||||
indent += indent
|
||||
|
||||
@@ -14,7 +14,7 @@ type ControlSummery struct {
|
||||
TotalResources int
|
||||
TotalFailed int
|
||||
Description string
|
||||
WorkloadSummery map[string][]WorkloadSummery
|
||||
WorkloadSummery map[string][]WorkloadSummery // <namespace>:[<WorkloadSummery>]
|
||||
}
|
||||
|
||||
type WorkloadSummery struct {
|
||||
|
||||
Reference in New Issue
Block a user