├── resources ├── DUMMY ├── codefresh │ ├── chart │ │ ├── Chart.yaml │ │ └── values.yaml │ ├── cf-gitops-runtime.yaml │ └── argo-example.yaml ├── app-projects │ └── cf-git-sources.yaml └── control-planes │ └── sealed-secrets │ ├── sealed-secrets-key66bh4-codefresh.sealing-key.yaml │ ├── sealed-secrets-keyn552s-codefresh.sealing-key.yaml │ └── sealed-secrets-keynds48-codefresh.sealing-key.yaml ├── pkg ├── README.md ├── images │ ├── beacon.gif │ ├── k8s_arch.png │ ├── k8s_audit.png │ └── beacon-gopher.png ├── utils │ ├── stringutil.go │ ├── stringutil_test.go │ ├── fileutil.go │ └── fileutil_test.go ├── models │ └── audit_results.go └── filters │ ├── predicate.go │ └── predicate_test.go ├── internal ├── README.md ├── hook │ ├── worker_test.go │ └── worker.go ├── cli │ ├── commands │ │ ├── help │ │ │ └── synopsis │ │ ├── fixtures │ │ │ ├── CheckMultiParamPass1stResultToNext.yml │ │ │ ├── CheckInClauseOppositeWithNum.yml │ │ │ ├── CheckTypeComparator.yml │ │ │ ├── CheckMultiParamNOK.yml │ │ │ ├── CheckMultiParamOK.yml │ │ │ ├── CheckMultiParamOKWithIN.yml │ │ │ ├── CheckMultiParamNOKWithIN.yml │ │ │ ├── CheckInClauseOpposite.yml │ │ │ ├── CheckInClauseOppositeEmptyReturn.yml │ │ │ ├── CheckTypeMultiProcessInClause.yml │ │ │ ├── CheckTypeMultiExprProcessParam.yml │ │ │ ├── CheckMultiParamComplex.yml │ │ │ ├── CheckTypeMultiExprEmptyProcessParam.yml │ │ │ └── CheckInClause4.2.13.yml │ │ ├── command-helper.go │ │ ├── k8s-audit.go │ │ ├── k8s-audit_test.go │ │ └── command-helper_test.go │ ├── cli_test.go │ └── cli.go ├── logger │ └── blogger.go ├── reports │ ├── processor.go │ └── processor_test.go ├── models │ ├── fixtures │ │ ├── CheckTypeBlaa.yml │ │ └── CheckTypeMultiProcessParam.yml │ └── audit.go ├── benchmark │ ├── benchutil.go │ ├── k8s │ │ └── v1.6.0 │ │ │ ├── 1.4_scheduler.yml │ │ │ ├── 3.0_control_plane_configuration.yml │ │ │ ├── 1.3_controller_manager.yml │ │ │ ├── 2.0_etcd.yml │ │ │ └── 1.1_master_node_configuration_files.yml │ └── gke │ │ └── v1.1.0 │ │ └── 2.0_control_plane_configuration.yml ├── startup │ ├── templates.go │ └── templates_test.go └── common │ └── globalconsts.go ├── configs └── README.md ├── cmd └── kube │ ├── kube-beacon.go │ └── README.md ├── scripts ├── lint.sh └── deb.sh ├── .gitignore ├── .github ├── workflows │ ├── test.yml │ ├── build.yml │ └── lint.yml └── dependabot.yml ├── Dockerfile ├── .dev ├── Vagrantfile ├── README.md └── setup.sh ├── .travis.yml ├── examples └── plugins │ └── K8s_bench_audit_result_hook.go ├── ui ├── banners.go └── console.go ├── runtimes └── codefresh │ └── in-cluster.yaml ├── jobs ├── k8s.yaml └── gke.yaml ├── Makefile ├── Makefile.old ├── go.mod ├── CODE_OF_CONDUCT.md ├── README.md ├── .golangci.yml └── LICENSE /resources/DUMMY: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/README.md: -------------------------------------------------------------------------------- 1 | # `/pkg` 2 | 3 | expose lib functions -------------------------------------------------------------------------------- /internal/README.md: -------------------------------------------------------------------------------- 1 | # `/internal` 2 | 3 | core project files -------------------------------------------------------------------------------- /configs/README.md: -------------------------------------------------------------------------------- 1 | # `/configs` 2 | 3 | project config files 4 | -------------------------------------------------------------------------------- /pkg/images/beacon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-keinan/kube-beacon/HEAD/pkg/images/beacon.gif -------------------------------------------------------------------------------- /pkg/images/k8s_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-keinan/kube-beacon/HEAD/pkg/images/k8s_arch.png -------------------------------------------------------------------------------- /pkg/images/k8s_audit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-keinan/kube-beacon/HEAD/pkg/images/k8s_audit.png -------------------------------------------------------------------------------- /pkg/images/beacon-gopher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-keinan/kube-beacon/HEAD/pkg/images/beacon-gopher.png -------------------------------------------------------------------------------- /cmd/kube/kube-beacon.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/cli" 5 | ) 6 | 7 | func main() { 8 | cli.StartCLI() 9 | } 10 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 3 | golangci-lint run -v > lint.xml -------------------------------------------------------------------------------- /resources/codefresh/chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 1.0.0 3 | description: Codefresh gitops runtime umbrella chart 4 | name: codefresh-gitops-runtime 5 | version: 0.15.0 6 | dependencies: 7 | - name: gitops-runtime 8 | repository: oci://quay.io/codefresh 9 | version: 0.15.0 10 | -------------------------------------------------------------------------------- /pkg/utils/stringutil.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | //GetAuditTestsList return processing function by specificTests 9 | func GetAuditTestsList(key, arg string) []string { 10 | values := strings.ReplaceAll(arg, fmt.Sprintf("%s=", key), "") 11 | return strings.Split(strings.ToLower(values), ",") 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .tmp 3 | .testCoverage.txt 4 | .idea 5 | statics 6 | .history 7 | client/node_modules 8 | .DS_Store 9 | lint.xml 10 | cp.out 11 | vendor/ 12 | */mocks* 13 | coverage.html 14 | a_startup-packr.go 15 | fmtcoverage.html 16 | coverage.md 17 | coverage.out 18 | kube-beacon.deb 19 | *.iml 20 | cf 21 | cf2 22 | tf 23 | tfr 24 | */mocks* 25 | *mocks* 26 | kube-beacon -------------------------------------------------------------------------------- /pkg/utils/stringutil_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | //Test_GetSpecificTestsToExecute test 9 | func Test_GetSpecificTestsToExecute(t *testing.T) { 10 | l := GetAuditTestsList("i", "i=1.2.3,1.4.5") 11 | assert.Equal(t, l[0], "1.2.3") 12 | assert.Equal(t, l[1], "1.4.5") 13 | l = GetAuditTestsList("e", "") 14 | assert.Equal(t, l[0], "") 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4.1.7 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Unit tests 22 | run: make test -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4.1.7 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Build 22 | run: make build 23 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4.1.7 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Lint check 22 | run: make lint 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official golang runtime as a parent image 2 | FROM golang:1.17-alpine as builder 3 | 4 | ENV GO111MODULE=on 5 | 6 | ADD . /src 7 | 8 | WORKDIR /src/cmd/kube 9 | 10 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o kube-beacon . 11 | 12 | FROM golang:1.15-alpine 13 | 14 | RUN apk --no-cache add ca-certificates 15 | 16 | WORKDIR /root/ 17 | 18 | COPY --from=builder /src/cmd/kube/kube-beacon . 19 | 20 | CMD ["./kube-beacon"] -------------------------------------------------------------------------------- /.dev/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "ubuntu/bionic64" 3 | config.vm.network "private_network", ip: "172.30.1.5" 4 | config.vm.provider "virtualbox" do |v| 5 | v.cpus = 2 6 | v.memory = 4048 7 | end 8 | config.vm.synced_folder ".", "/vagrant", type: "rsync" 9 | if Vagrant.has_plugin? "vagrant-vbguest" 10 | config.vbguest.auto_update = false 11 | end 12 | config.vm.provision "shell" do |s| 13 | s.path = "setup.sh" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /resources/app-projects/cf-git-sources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: AppProject 3 | metadata: 4 | name: cf-git-sources 5 | finalizers: 6 | - resources-finalizer.argocd.argoproj.io 7 | spec: 8 | destinations: 9 | - namespace: '*' 10 | server: '*' 11 | sourceRepos: 12 | - '*' 13 | namespaceResourceBlacklist: 14 | - group: codefresh.io 15 | kind: Product 16 | - group: codefresh.io 17 | kind: PromotionFlow 18 | - group: codefresh.io 19 | kind: PromotionPolicy 20 | -------------------------------------------------------------------------------- /internal/hook/worker_test.go: -------------------------------------------------------------------------------- 1 | package hook 2 | 3 | import ( 4 | m2 "github.com/chen-keinan/beacon/pkg/models" 5 | "github.com/stretchr/testify/assert" 6 | "go.uber.org/zap" 7 | "testing" 8 | ) 9 | 10 | func Test_NewPluginWorker(t *testing.T) { 11 | production, err := zap.NewProduction() 12 | assert.NoError(t, err) 13 | completedChan := make(chan bool) 14 | plChan := make(chan m2.KubeAuditResults) 15 | pw := NewPluginWorker(NewPluginWorkerData(plChan, K8sBenchAuditResultHook{}, completedChan), production) 16 | assert.True(t, len(pw.cmd.plugins.Plugins) == 0) 17 | } 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: github-actions 9 | directory: / 10 | schedule: 11 | interval: weekly 12 | - package-ecosystem: gomod 13 | directory: / 14 | schedule: 15 | interval: weekly -------------------------------------------------------------------------------- /internal/cli/commands/help/synopsis: -------------------------------------------------------------------------------- 1 | Usage: %s [--version] [--help] [] 2 | 3 | Available commands are: 4 | -r , --report : run audit tests and generate failure report 5 | -i , --include: execute only specific audit test, example -i=1.2.3,1.4.5 6 | -e , --exclude, ignore specific audit tests, example -e=1.2.3,1.4.5 7 | -n , --node, execute audit tests on specific node, example -n=master,-n=worker 8 | -s , --spec, execute specific audit tests spec, example -s=gke, default=k8s 9 | -v , --version, execute specific audit tests spec version, example -v=1.1.0,default=latest -------------------------------------------------------------------------------- /internal/logger/blogger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | ) 7 | 8 | //BLogger Object 9 | type BLogger struct { 10 | } 11 | 12 | var blogger *BLogger 13 | var blSync sync.Once 14 | 15 | //GetLog return native logger 16 | func GetLog() *BLogger { 17 | blSync.Do(func() { 18 | blogger = &BLogger{} 19 | }) 20 | return blogger 21 | } 22 | 23 | //Console print to console 24 | func (BLogger *BLogger) Console(str string) { 25 | log.SetFlags(0) 26 | log.Print(str) 27 | } 28 | 29 | //Table print to console 30 | func (BLogger *BLogger) Table(v interface{}) { 31 | log.SetFlags(0) 32 | log.Print(v) 33 | } 34 | -------------------------------------------------------------------------------- /scripts/deb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir builds/kube-beacon 4 | mkdir builds/kube-beacon/DEBIAN 5 | { 6 | echo package: kube-beacon 7 | echo Version: 0.1 8 | echo Section: custom 9 | echo Priority: optional 10 | echo Architecture: all 11 | echo Essential: no 12 | echo Installed-Size: 1024 13 | echo Maintainer: hen.keinan@gmail.com 14 | echo Description: k8s audit scan tool 15 | } >> builds/kube-beacon/DEBIAN/control 16 | mkdir -p builds/kube-beacon/usr/bin/ 17 | mv kube-beacon builds/kube-beacon/usr/bin/ 18 | dpkg-deb --build builds/kube-beacon 19 | mv builds/kube-beacon.deb builds/deb 20 | rm -rf builds/kube-beacon -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.17 4 | 5 | env: 6 | - "PATH=/home/travis/gopath/bin:$PATH" 7 | 8 | services: 9 | - docker 10 | 11 | before_install: 12 | - go get golang.org/x/tools/cmd/cover 13 | - go get github.com/mattn/goveralls 14 | 15 | script: 16 | - go get github.com/golang/mock/mockgen@latest 17 | - go install -v github.com/golang/mock/mockgen && export PATH=$GOPATH/bin:$PATH; 18 | - go generate ./... 19 | - goveralls -service=travis-pro 20 | - make test_travis 21 | - make build_travis 22 | - echo "$PASSWORD" | docker login -u "$USER" --password-stdin kubebcon.jfrog.io 23 | - make build_docker 24 | 25 | -------------------------------------------------------------------------------- /resources/codefresh/cf-gitops-runtime.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: cf-gitops-runtime 5 | labels: 6 | codefresh.io/entity: runtime 7 | codefresh.io/internal: 'true' 8 | spec: 9 | project: default 10 | source: 11 | repoURL: https://github.com/chen-keinan/kube-beacon.git 12 | targetRevision: HEAD 13 | path: resources/codefresh/chart 14 | helm: 15 | releaseName: cf-gitops-runtime 16 | destination: 17 | namespace: codefresh 18 | server: https://kubernetes.default.svc 19 | syncPolicy: 20 | automated: 21 | allowEmpty: true 22 | prune: true 23 | selfHeal: true 24 | -------------------------------------------------------------------------------- /.dev/README.md: -------------------------------------------------------------------------------- 1 | # kubernetes-vagrantfile 2 | 3 | vagrant file to be used for k8s associated programs developments, file include : 4 | - buntu/bionic64 5 | - k8s cluster 6 | - minikube 7 | - dlv for remote debug 8 | 9 | ## Quick Start 10 | 11 | ``` 12 | git clone git@github.com:chen-keinan/kubernetes-vagrantfile.git 13 | cd kubernetes-vagrantfile 14 | vagrant up 15 | 16 | ``` 17 | 18 | 19 | ### Compile binary with debug params 20 | ``` 21 | GOOS=linux GOARCH=amd64 go build -v -gcflags='-N -l' demo.go 22 | ``` 23 | ### Run debug on remote machine 24 | ``` 25 | dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./demo 26 | ``` 27 | 28 | ### Tear down 29 | ``` 30 | vagrant destroy 31 | -------------------------------------------------------------------------------- /resources/codefresh/argo-example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: argo-example 5 | labels: 6 | codefresh.io/entity: 'git-source' 7 | codefresh.io/internal: 'false' 8 | finalizers: 9 | - resources-finalizer.argocd.argoproj.io 10 | spec: 11 | project: cf-git-sources 12 | sources: 13 | - repoURL: https://github.com/chen-keinan/argocd-example-apps.git 14 | path: . 15 | targetRevision: main 16 | directory: 17 | include: '*' 18 | exclude: '' 19 | recurse: true 20 | destination: 21 | namespace: codefresh 22 | server: https://kubernetes.default.svc 23 | syncPolicy: 24 | automated: 25 | allowEmpty: true 26 | prune: true 27 | selfHeal: true 28 | -------------------------------------------------------------------------------- /resources/codefresh/chart/values.yaml: -------------------------------------------------------------------------------- 1 | gitops-runtime: 2 | app-proxy: 3 | config: 4 | cors: http://local.codefresh.io,https://chenk-platform.ngrok.app 5 | global: 6 | codefresh: 7 | accountId: 6107f96b1895b556b7835b26 8 | url: https://chenk-platform.ngrok.app 9 | userToken: 10 | secretKeyRef: 11 | name: codefresh-user-token 12 | key: token 13 | runtime: 14 | cluster: https://kubernetes.default.svc 15 | codefreshHosted: false 16 | ingress: 17 | enabled: false 18 | ingressUrl: http://host.docker.internal:8080 19 | isConfigurationRuntime: true 20 | name: codefresh 21 | installer: 22 | skipValidation: true 23 | internal-router: 24 | service: 25 | nodePort: 31243 26 | type: NodePort 27 | tunnel-client: 28 | enabled: false 29 | -------------------------------------------------------------------------------- /examples/plugins/K8s_bench_audit_result_hook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/chen-keinan/beacon/pkg/models" 8 | "net/http" 9 | "strings" 10 | ) 11 | 12 | //K8sBenchAuditResultHook this plugin method accept k8s audit bench results 13 | //event include test data , description , audit, remediation and result 14 | func K8sBenchAuditResultHook(k8sAuditResults models.KubeAuditResults) error { 15 | var sb = new(bytes.Buffer) 16 | err := json.NewEncoder(sb).Encode(k8sAuditResults) 17 | fmt.Print(k8sAuditResults) 18 | if err != nil { 19 | return err 20 | } 21 | req, err := http.NewRequest("POST", "http://localhost:8090/audit-results", strings.NewReader(sb.String())) 22 | if err != nil { 23 | return err 24 | } 25 | client := http.Client{} 26 | _, err = client.Do(req) 27 | if err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/models/audit_results.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | //KubeAuditResults encapsulate audit test results to be consumed by user plugin 4 | type KubeAuditResults struct { 5 | BenchmarkType string `yaml:"benchmark_type"` 6 | Categories []AuditBenchResult `yaml:"audit_bench_result"` 7 | } 8 | 9 | //AuditBenchResult data model 10 | type AuditBenchResult struct { 11 | Name string `yaml:"name"` 12 | ProfileApplicability string `yaml:"profile_applicability"` 13 | Category string `yaml:"category"` 14 | Description string `yaml:"description"` 15 | AuditCommand []string `json:"audit_command"` 16 | Remediation string `yaml:"remediation"` 17 | Impact string `yaml:"impact"` 18 | DefaultValue string `yaml:"default_value"` 19 | References []string `yaml:"references"` 20 | TestResult string `yaml:"test_result"` 21 | } 22 | -------------------------------------------------------------------------------- /ui/banners.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | //K8sAuditTest banner 4 | const K8sAuditTest = ` 5 | _ ___ _ _ _ _ _ 6 | | | / _ \ | (_) | | | | | 7 | | | _| (_) |___ __ _ _ _ __| |_| |_ | |_ ___ ___| |_ ___ 8 | | |/ /> _ - 18 | {all-runtimes-all-clusters/**/*.yaml,all-runtimes-all-clusters/*.yaml,codefresh/argo-example.yaml,codefresh/cf-gitops-runtime.yaml,configurations/**/*.yaml,control-planes/**/*.yaml,control-planes/*.yaml,runtimes/codefresh/**/*.yaml,runtimes/codefresh/*.yaml} 19 | recurse: true 20 | path: resources 21 | repoURL: https://github.com/chen-keinan/kube-beacon.git 22 | targetRevision: HEAD 23 | syncPolicy: 24 | automated: 25 | allowEmpty: true 26 | prune: true 27 | selfHeal: true 28 | syncOptions: 29 | - allowEmpty=true 30 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamPass1stResultToNext.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.34 Ensure that encryption providers are appropriately configured 9 | description: Where etcd encryption is used, appropriate providers should be 10 | configured. 11 | profile_applicability: Level 1 - Master Node 12 | audit: 13 | - aaa 14 | - bbb 15 | - 'ccc ${1}' 16 | remediation: Follow the Kubernetes documentation and configure a EncryptionConfig 17 | file. In this file, choose aescbc, kms or secretbox as the encryption provider. 18 | check_type: multi_param 19 | impact: None 20 | eval_expr: "'${0}' == '${1}'; && (('${2}' == '- aescbc:'; && ${3} == '- kms:';) || 21 | ${4} == '- secretbox:';)" 22 | default_value: By default, no encryption provider is set. 23 | references: 24 | - aaa 25 | - bbb 26 | - 'ccc ${1}' 27 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckInClauseOppositeWithNum.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 4.1.3 If proxy kubeconfig file exists ensure permissions are set to 644 9 | or more restrictive 10 | description: If kube-proxy is running, and if it is using a file-based kubeconfig 11 | file, ensure that the proxy kubeconfig file has permissions of 644 or more 12 | restrictive. 13 | profile_applicability: Level 1 - Worker Node 14 | audit: 15 | - aaa 16 | - 'bbb ${0}' 17 | remediation: |- 18 | Run the below command (based on the file location on your system) on the each worker node. For example, 19 | chmod 644 20 | check_type: multi_param 21 | impact: None 22 | eval_expr: "${1} <= 644;" 23 | default_value: By default, proxy file has permissions of 640. 24 | references: 25 | - https://kubernetes.io/docs/admin/kube-proxy/ 26 | -------------------------------------------------------------------------------- /cmd/kube/README.md: -------------------------------------------------------------------------------- 1 | ##Remote Debug 2 | ###Install dlv 3 | $ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve 4 | $ cd $GOPATH/src/github.com/go-delve/delve 5 | $ make install 6 | 7 | ### export dlv bin path 8 | export PATH=$PATH:/home/vagrant/go/bin 9 | export PATH=$PATH:/root/go/bin 10 | 11 | ### compile binary with debug params 12 | GOOS=linux GOARCH=amd64 go build -v -gcflags='-N -l' cmd/beacon/beacon.go 13 | 14 | ### run on remote machine 15 | 16 | dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./kube-beacon 17 | 18 | docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -v /*/cni/*:/*/cni/* -t beacon 19 | 20 | docker build ./ -t beacon -f Dockerfile 21 | 22 | export KUBECONFIG=/etc/kubernetes/admin.conf 23 | mkdir -p $HOME/.kube 24 | sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 25 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 26 | https://github.com/oracle/vagrant-projects 27 | 28 | kubectl taint nodes master-node node-role.kubernetes.io/master- 29 | kubectl create clusterrolebinding default-admin --clusterrole cluster-admin --serviceaccount=default:default 30 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckTypeComparator.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.20 Ensure that the --secure-port argument is not set to 0 9 | description: Do not disable the secure port. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'secure-port' | grep -o 'secure-port=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: 'Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and either remove the --secure-port parameter 16 | or set it to a different (non-zero) desired port. 17 | 18 | ' 19 | check_type: multi_param 20 | impact: You need to set the API Server up with the right TLS certificates. 21 | eval_expr: "${0} > 0; && ${0} < 65535;" 22 | default_value: By default, port 6443 is used as the secure port. 23 | references: 24 | - https://kubernetes.io/docs/admin/kube-apiserver/ 25 | -------------------------------------------------------------------------------- /ui/console.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "github.com/cheggaaa/pb" 5 | "github.com/chen-keinan/beacon/internal/logger" 6 | "github.com/chen-keinan/beacon/internal/models" 7 | "time" 8 | ) 9 | 10 | // OutputGenerator for audit results 11 | type OutputGenerator func(at []*models.SubCategory, log *logger.BLogger) 12 | 13 | //PrintOutput print audit test result to console 14 | func PrintOutput(auditTests []*models.SubCategory, outputGenerator OutputGenerator, log *logger.BLogger) { 15 | log.Console(auditResult) 16 | outputGenerator(auditTests, log) 17 | } 18 | 19 | //ShowProgressBar execute audit test and show progress bar 20 | func ShowProgressBar(a *models.SubCategory, execTestFunc func(ad *models.AuditBench) []*models.AuditBench, log *logger.BLogger) *models.SubCategory { 21 | if len(a.AuditTests) == 0 { 22 | return a 23 | } 24 | completedTest := make([]*models.AuditBench, 0) 25 | log.Console(a.Name) 26 | bar := pb.StartNew(len(a.AuditTests)) 27 | for _, test := range a.AuditTests { 28 | ar := execTestFunc(test) 29 | completedTest = append(completedTest, ar...) 30 | 31 | bar.Increment() 32 | time.Sleep(time.Millisecond * 20) 33 | } 34 | bar.Finish() 35 | return &models.SubCategory{Name: a.Name, AuditTests: completedTest} 36 | } 37 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamNOK.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - aaa 13 | - 'bbb ${0}' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "'${0}' != '${1}';" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamOK.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - aaa 13 | - 'bbb ${0}' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "'${0}' == '${1}';" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamOKWithIN.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - aaa 13 | - 'bbb ${0}' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "'${0}' IN (${1});" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamNOKWithIN.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - aaa 13 | - 'bbb ${0}' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "!('${0}' IN (${1}));" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/reports/processor_test.go: -------------------------------------------------------------------------------- 1 | package reports 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/models" 5 | "github.com/magiconair/properties/assert" 6 | "testing" 7 | ) 8 | 9 | //Test_GenerateAuditReport test 10 | func Test_GenerateAuditReport(t *testing.T) { 11 | ab := make([]*models.AuditBench, 0) 12 | ab = append(ab, &models.AuditBench{Name: "aaa", Description: "bbb", Impact: "ccc", Remediation: "ddd"}) 13 | tb := GenerateAuditReport(ab) 14 | s := tb.String() 15 | assert.Equal(t, s, "--------------\t-------------------------------------------------------------------------------------------\nStatus: \tFailed \nName: \taaa \nDescription: \tbbb \nImpact: \tccc \nRemediation: \tddd \nReferences: \t[] \n ") 16 | } 17 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckInClauseOpposite.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.34 Ensure that encryption providers are appropriately configured 9 | description: Where etcd encryption is used, appropriate providers should be 10 | configured. 11 | profile_applicability: Level 1 - Master Node 12 | audit: 13 | - aaa 14 | remediation: Follow the Kubernetes documentation and configure a EncryptionConfig 15 | file. In this file, choose aescbc, kms or secretbox as the encryption provider. 16 | check_type: multi_param 17 | impact: None 18 | eval_expr: "'${0}'; IN ('a','b','c');" 19 | default_value: By default, no encryption provider is set. 20 | references: 21 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/ 22 | - https://acotten.com/post/kube17-security 23 | - https://kubernetes.io/docs/admin/kube-apiserver/ 24 | - https://github.com/kubernetes/features/issues/92 25 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#providers 26 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckInClauseOppositeEmptyReturn.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.34 Ensure that encryption providers are appropriately configured 9 | description: Where etcd encryption is used, appropriate providers should be 10 | configured. 11 | profile_applicability: Level 1 - Master Node 12 | audit: 13 | - aaa 14 | - 'bbb ${0}' 15 | remediation: Follow the Kubernetes documentation and configure a EncryptionConfig 16 | file. In this file, choose aescbc, kms or secretbox as the encryption provider. 17 | check_type: multi_param 18 | impact: None 19 | eval_expr: "${0}; IN ('a','b','c'); || ${1}; IN ('a','b','c')" 20 | default_value: By default, no encryption provider is set. 21 | references: 22 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/ 23 | - https://acotten.com/post/kube17-security 24 | - https://kubernetes.io/docs/admin/kube-apiserver/ 25 | - https://github.com/kubernetes/features/issues/92 26 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#providers 27 | -------------------------------------------------------------------------------- /internal/models/fixtures/CheckTypeBlaa.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'authorization-mode' | grep -o 'authorization-mode=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: blaa 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "'RBAC' IN ($1)" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/models/fixtures/CheckTypeMultiProcessParam.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'authorization-mode' | grep -o 'authorization-mode=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "'RBAC' IN ($1)" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckTypeMultiProcessInClause.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: Ensure that the --authorization-mode argument includes RBAC (Automated) 9 | description: Turn on Role Based Access Control. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'authorization-mode' | grep -o 'authorization-mode=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and set the --authorization-mode parameter 16 | to a value that includes RBAC, for example:--authorization-mode=Node,RBAC 17 | check_type: multi_param 18 | impact: When RBAC is enabled you will need to ensure that appropriate RBAC settings 19 | (including Roles, RoleBindings and ClusterRoleBindings) are configured to 20 | allow appropriate access. 21 | eval_expr: "!('RBAC' IN (${0}));" 22 | default_value: By default, RBAC authorization is not enabled. 23 | references: 24 | - https://kubernetes.io/docs/reference/access-authn-authz/rbac/ 25 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckTypeMultiExprProcessParam.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.11 Ensure that the admission control plugin AlwaysAdmit is not set 9 | description: Do not allow all requests. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'enable-admission-plugins' | grep -o 'enable-admission-plugins=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: Edit the API server pod specification file /etc/kubernetes/manifests/kube- 15 | apiserver.yaml on the master node and either remove the --enable-admission-plugins 16 | parameter, or set it to a value that does not include AlwaysAdmit. 17 | check_type: multi_param 18 | impact: Only requests explicitly allowed by the admissions control plugins would 19 | be served. 20 | eval_expr: "'${0}' != ''; && !('AlwaysAdmit' IN (${0}));" 21 | default_value: AlwaysAdmit is not in the list of default admission plugins. 22 | references: 23 | - https://kubernetes.io/docs/admin/kube-apiserver/ 24 | - https://kubernetes.io/docs/admin/admission-controllers/#alwaysadmit 25 | -------------------------------------------------------------------------------- /jobs/k8s.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: kube-beacon 5 | spec: 6 | template: 7 | metadata: 8 | labels: 9 | app: kube-beacon 10 | spec: 11 | hostPID: true 12 | containers: 13 | - name: kube-beacon 14 | image: chenkeinan/kube-beacon:latest 15 | command: ["./kube-beacon"] 16 | volumeMounts: 17 | - name: var-lib-etcd 18 | mountPath: /var/lib/etcd 19 | readOnly: true 20 | - name: var-lib-kubelet 21 | mountPath: /var/lib/kubelet 22 | readOnly: true 23 | - name: etc-systemd 24 | mountPath: /etc/systemd 25 | readOnly: true 26 | - name: etc-kubernetes 27 | mountPath: /etc/kubernetes 28 | readOnly: true 29 | - name: usr-bin 30 | mountPath: /usr/bin 31 | readOnly: true 32 | restartPolicy: Never 33 | volumes: 34 | - name: var-lib-etcd 35 | hostPath: 36 | path: "/var/lib/etcd" 37 | - name: var-lib-kubelet 38 | hostPath: 39 | path: "/var/lib/kubelet" 40 | - name: etc-systemd 41 | hostPath: 42 | path: "/etc/systemd" 43 | - name: etc-kubernetes 44 | hostPath: 45 | path: "/etc/kubernetes" 46 | - name: usr-bin 47 | hostPath: 48 | path: "/usr/bin" 49 | tolerations: 50 | - operator: "Exists" -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckMultiParamComplex.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.34 Ensure that encryption providers are appropriately configured 9 | description: Where etcd encryption is used, appropriate providers should be 10 | configured. 11 | profile_applicability: Level 1 - Master Node 12 | audit: 13 | - aaa 14 | - bbb 15 | - ccc 16 | - ddd 17 | - eee 18 | remediation: Follow the Kubernetes documentation and configure a EncryptionConfig 19 | file. In this file, choose aescbc, kms or secretbox as the encryption provider. 20 | check_type: multi_param 21 | impact: None 22 | eval_expr: "'${0}' == '${1}'; && (('${2}' == 'aescbc:'; && '${3}' == 'kms';) || '${4}' 23 | == 'secretbox';)" 24 | default_value: By default, no encryption provider is set. 25 | references: 26 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/ 27 | - https://acotten.com/post/kube17-security 28 | - https://kubernetes.io/docs/admin/kube-apiserver/ 29 | - https://github.com/kubernetes/features/issues/92 30 | - https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#providers 31 | -------------------------------------------------------------------------------- /jobs/gke.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: kube-beacon 5 | spec: 6 | template: 7 | metadata: 8 | labels: 9 | app: kube-beacon 10 | spec: 11 | hostPID: true 12 | containers: 13 | - name: kube-beacon 14 | image: chenkeinan/kube-beacon:latest 15 | command: ["./kube-beacon"] 16 | args: ["-s=gke"] 17 | volumeMounts: 18 | - name: var-lib-etcd 19 | mountPath: /var/lib/etcd 20 | readOnly: true 21 | - name: var-lib-kubelet 22 | mountPath: /var/lib/kubelet 23 | readOnly: true 24 | - name: etc-systemd 25 | mountPath: /etc/systemd 26 | readOnly: true 27 | - name: etc-kubernetes 28 | mountPath: /etc/kubernetes 29 | readOnly: true 30 | - name: usr-bin 31 | mountPath: /usr/bin 32 | readOnly: true 33 | restartPolicy: Never 34 | volumes: 35 | - name: var-lib-etcd 36 | hostPath: 37 | path: "/var/lib/etcd" 38 | - name: var-lib-kubelet 39 | hostPath: 40 | path: "/var/lib/kubelet" 41 | - name: etc-systemd 42 | hostPath: 43 | path: "/etc/systemd" 44 | - name: etc-kubernetes 45 | hostPath: 46 | path: "/etc/kubernetes" 47 | - name: usr-bin 48 | hostPath: 49 | path: "/usr/bin" 50 | tolerations: 51 | - operator: "Exists" -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckTypeMultiExprEmptyProcessParam.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 1.2.14 Ensure that the admission control plugin ServiceAccount is set 9 | description: Automate service accounts management. 10 | profile_applicability: Level 1 - Master Node 11 | audit: 12 | - ps -ef | grep kube-apiserver |grep 'disable-admission-plugins' | grep -o 'disable-admission-plugins=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | remediation: Follow the documentation and create ServiceAccount objects as per 15 | your environment. Then, edit the API server pod specification file /etc/kubernetes/manifests/kube- 16 | apiserver.yaml on the master node and ensure that the --disable-admission-plugins 17 | parameter is set to a value that does not include ServiceAccount. 18 | check_type: multi_param 19 | impact: None 20 | eval_expr: "'${0}' != ''; && !('ServiceAccount' IN (${0}));" 21 | default_value: By default, ServiceAccount is set. 22 | references: 23 | - https://kubernetes.io/docs/admin/kube-apiserver/ 24 | - https://kubernetes.io/docs/admin/admission-controllers/#serviceaccount 25 | - https://kubernetes.io/docs/tasks/configure-pod-container/configure-service- 26 | account/ 27 | -------------------------------------------------------------------------------- /internal/benchmark/benchutil.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "github.com/chen-keinan/beacon/pkg/utils" 7 | "io/ioutil" 8 | ) 9 | 10 | // K8sFolder folder 11 | const K8sFolder = "k8s/v1.6.0" 12 | 13 | // GkeFolder folder 14 | const GkeFolder = "gke/v1.1.0" 15 | 16 | var ( 17 | //go:embed k8s/v1.6.0 18 | resK8s embed.FS 19 | 20 | //go:embed gke/v1.1.0 21 | resGke embed.FS 22 | ) 23 | 24 | //LoadK8sSpecs load specs 25 | func LoadK8sSpecs() ([]utils.FilesInfo, error) { 26 | dir, _ := resK8s.ReadDir(K8sFolder) 27 | specs := make([]utils.FilesInfo, 0) 28 | for _, r := range dir { 29 | file, err := resK8s.Open(fmt.Sprintf("%s/%s", K8sFolder, r.Name())) 30 | if err != nil { 31 | return specs, err 32 | } 33 | data, err := ioutil.ReadAll(file) 34 | spec := utils.FilesInfo{Name: r.Name(), Data: string(data)} 35 | if err != nil { 36 | return specs, err 37 | } 38 | if err != nil { 39 | return specs, err 40 | } 41 | specs = append(specs, spec) 42 | } 43 | return specs, nil 44 | } 45 | 46 | //LoadGkeSpecs load specs 47 | func LoadGkeSpecs() ([]utils.FilesInfo, error) { 48 | dir, _ := resGke.ReadDir(GkeFolder) 49 | specs := make([]utils.FilesInfo, 0) 50 | for _, r := range dir { 51 | file, err := resGke.Open(fmt.Sprintf("%s/%s", GkeFolder, r.Name())) 52 | if err != nil { 53 | return specs, err 54 | } 55 | data, err := ioutil.ReadAll(file) 56 | spec := utils.FilesInfo{Name: r.Name(), Data: string(data)} 57 | if err != nil { 58 | return specs, err 59 | } 60 | if err != nil { 61 | return specs, err 62 | } 63 | specs = append(specs, spec) 64 | } 65 | return specs, nil 66 | } 67 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | GOCMD=go 4 | MOVESANDBOX=mv ~/vms/kube/kube-beacon ~/vms-local/kube-beacon 5 | GOMOD=$(GOCMD) mod 6 | GOMOCKS=$(GOCMD) generate ./... 7 | GOBUILD=$(GOCMD) build 8 | GOTEST=$(GOCMD) test 9 | BINARY_NAME=kube-beacon 10 | GOCOPY=cp kube-beacon ~/vagrant_file/. 11 | 12 | all:test lint build 13 | 14 | fmt: 15 | $(GOCMD) fmt ./... 16 | lint: 17 | $(GOCMD) get -d github.com/golang/mock/mockgen@v1.6.0 18 | $(GOCMD) install -v github.com/golang/mock/mockgen 19 | export PATH=$HOME/go/bin:$PATH 20 | $(GOMOCKS) 21 | ./scripts/lint.sh 22 | tidy: 23 | $(GOMOD) tidy -v 24 | test: 25 | $(GOCMD) get -d github.com/golang/mock/mockgen@v1.6.0 26 | $(GOCMD) install -v github.com/golang/mock/mockgen && export PATH=$GOPATH/bin:$PATH; 27 | $(GOMOCKS) 28 | $(GOTEST) ./... -coverprofile coverage.md fmt 29 | $(GOCMD) tool cover -html=coverage.md -o coverage.html 30 | $(GOCMD) tool cover -func coverage.md 31 | build: 32 | export PATH=$GOPATH/bin:$PATH; 33 | export PATH=$PATH:/home/vagrant/go/bin 34 | export PATH=$PATH:/home/root/go/bin 35 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v ./cmd/kube; 36 | build_local: 37 | export PATH=$GOPATH/bin:$PATH; 38 | export PATH=$PATH:/home/vagrant/go/bin 39 | export PATH=$PATH:/home/root/go/bin 40 | $(GOBUILD) ./cmd/kube; 41 | 42 | build_docker_local: 43 | docker build -t chenkeinan/kube-beacon:latest . 44 | docker push chenkeinan/kube-beacon:latest 45 | dlv: 46 | dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./kube-beacon 47 | build_beb: 48 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v -gcflags='-N -l' cmd/kube/kube-beacon.go 49 | scripts/deb.sh 50 | .PHONY: all build install test 51 | -------------------------------------------------------------------------------- /internal/hook/worker.go: -------------------------------------------------------------------------------- 1 | package hook 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/pkg/models" 6 | "github.com/chen-keinan/go-user-plugins/uplugin" 7 | "go.uber.org/zap" 8 | "plugin" 9 | ) 10 | 11 | //K8sBenchAuditResultHook hold the plugin symbol for K8s bench audit result Hook 12 | type K8sBenchAuditResultHook struct { 13 | Plugins []plugin.Symbol 14 | Plug *uplugin.PluginLoader 15 | } 16 | 17 | //PluginWorker instance which match command data to specific pattern 18 | type PluginWorker struct { 19 | cmd *PluginWorkerData 20 | log *zap.Logger 21 | } 22 | 23 | //NewPluginWorker return new plugin worker instance 24 | func NewPluginWorker(commandMatchData *PluginWorkerData, log *zap.Logger) *PluginWorker { 25 | return &PluginWorker{cmd: commandMatchData, log: log} 26 | } 27 | 28 | //NewPluginWorkerData return new plugin worker instance 29 | func NewPluginWorkerData(plChan chan models.KubeAuditResults, hook K8sBenchAuditResultHook, completedChan chan bool) *PluginWorkerData { 30 | return &PluginWorkerData{plChan: plChan, plugins: hook, completedChan: completedChan} 31 | } 32 | 33 | //PluginWorkerData encapsulate plugin worker properties 34 | type PluginWorkerData struct { 35 | plChan chan models.KubeAuditResults 36 | completedChan chan bool 37 | plugins K8sBenchAuditResultHook 38 | } 39 | 40 | //Invoke invoke plugin accept audit bench results 41 | func (pm *PluginWorker) Invoke() { 42 | go func() { 43 | ae := <-pm.cmd.plChan 44 | if len(pm.cmd.plugins.Plugins) > 0 { 45 | for _, pl := range pm.cmd.plugins.Plugins { 46 | _, err := pm.cmd.plugins.Plug.Invoke(pl, ae) 47 | if err != nil { 48 | pm.log.Error(fmt.Sprintf("failed to execute plugins %s", err.Error())) 49 | } 50 | } 51 | } 52 | pm.cmd.completedChan <- true 53 | }() 54 | } 55 | -------------------------------------------------------------------------------- /internal/startup/templates.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/internal/benchmark" 6 | "github.com/chen-keinan/beacon/internal/common" 7 | "github.com/chen-keinan/beacon/pkg/utils" 8 | "github.com/gobuffalo/packr" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | //GenerateK8sBenchmarkFiles use packr to load benchmark audit test yaml 14 | func GenerateK8sBenchmarkFiles() ([]utils.FilesInfo, error) { 15 | return benchmark.LoadK8sSpecs() 16 | } 17 | 18 | //GenerateGkeBenchmarkFiles use packr to load benchmark audit test yaml 19 | func GenerateGkeBenchmarkFiles() ([]utils.FilesInfo, error) { 20 | return benchmark.LoadGkeSpecs() 21 | } 22 | 23 | //GetHelpSynopsis get help synopsis file 24 | func GetHelpSynopsis() string { 25 | box := packr.NewBox("./../cli/commands/help/") 26 | // Add Master Node Configuration tests 27 | hs, err := box.FindString(common.Synopsis) 28 | if err != nil { 29 | panic(fmt.Sprintf("faild to load cli help synopsis %s", err.Error())) 30 | } 31 | return hs 32 | } 33 | 34 | //SaveBenchmarkFilesIfNotExist create benchmark audit file if not exist 35 | func SaveBenchmarkFilesIfNotExist(spec, version string, filesData []utils.FilesInfo) error { 36 | fm := utils.NewKFolder() 37 | folder, err := utils.GetBenchmarkFolder(spec, version, fm) 38 | if err != nil { 39 | return err 40 | } 41 | for _, fileData := range filesData { 42 | filePath := filepath.Join(folder, fileData.Name) 43 | if _, err := os.Stat(filePath); os.IsNotExist(err) { 44 | f, err := os.Create(filePath) 45 | if err != nil { 46 | return fmt.Errorf(err.Error()) 47 | } 48 | _, err = f.WriteString(fileData.Data) 49 | if err != nil { 50 | return fmt.Errorf("failed to write benchmark file") 51 | } 52 | err = f.Close() 53 | if err != nil { 54 | return fmt.Errorf("faild to close file %s", filePath) 55 | } 56 | } 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /Makefile.old: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | GOCMD=go 4 | MOVESANDBOX=mv ~/vms/kube-beacon/kube-beacon ~/vms-local/kube-beacon 5 | GOMOD=$(GOCMD) mod 6 | GOMOCKS=$(GOCMD) generate ./... 7 | GOBUILD=$(GOCMD) build 8 | GOTEST=$(GOCMD) test 9 | BINARY_NAME=kube-beacon 10 | GOCOPY=cp kube-beacon ~/vagrant_file/. 11 | 12 | all:test lint build 13 | 14 | fmt: 15 | $(GOCMD) fmt ./... 16 | lint: 17 | ./scripts/lint.sh 18 | tidy: 19 | $(GOMOD) tidy -v 20 | test: 21 | $(GOCMD) install github.com/golang/mock/mockgen@latest 22 | $(GOCMD) install -v github.com/golang/mock/mockgen && export PATH=$GOPATH/bin:$PATH; 23 | $(GOMOCKS) 24 | $(GOTEST) ./... -coverprofile coverage.md fmt 25 | $(GOCMD) tool cover -html=coverage.md -o coverage.html 26 | $(GOCMD) tool cover -func coverage.md 27 | build: 28 | export PATH=$GOPATH/bin:$PATH; 29 | export PATH=$PATH:/home/vagrant/go/bin 30 | export PATH=$PATH:/home/root/go/bin 31 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v cmd/kube/kube-beacon.go; 32 | install:build_travis 33 | cp $(BINARY_NAME) $(GOPATH)/bin/$(BINARY_NAME) 34 | test_travis: 35 | $(GOCMD) install github.com/golang/mock/mockgen@latest 36 | $(GOCMD) install -v github.com/golang/mock/mockgen && export PATH=$GOPATH/bin:$PATH; 37 | $(GOMOCKS) 38 | $(GOTEST) ./... -coverprofile coverage.md fmt 39 | $(GOCMD) tool cover -html=coverage.md -o coverage.html 40 | build_travis: 41 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v cmd/kube/kube-beacon.go; 42 | build_remote: 43 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v -gcflags='-N -l' cmd/kube/kube-beacon.go 44 | mv kube-beacon /Users/chenkeinan/boxes/basic_box/kube-beacon 45 | build_docker: 46 | export PATH=$GOPATH/bin:$PATH; 47 | docker build -t chenkeinan/kube-beacon:latest . 48 | docker push chenkeinan/kube-beacon:latest 49 | build_docker_local: 50 | docker build -t chenkeinan/kube-beacon:latest . 51 | docker push chenkeinan/kube-beacon:latest 52 | dlv: 53 | dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./kube-beacon 54 | build_beb: 55 | GOOS=linux GOARCH=amd64 $(GOBUILD) -v -gcflags='-N -l' cmd/kube/kube-beacon.go 56 | scripts/deb.sh 57 | .PHONY: all build install test 58 | -------------------------------------------------------------------------------- /internal/common/globalconsts.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | //MasterNodeConfigurationFiles file name 5 | MasterNodeConfigurationFiles = "1.1_master_node_configuration_files.yml" 6 | //APIServer file name 7 | APIServer = "1.2_api_server.yml" 8 | //ControllerManager file name 9 | ControllerManager = "1.3_controller_manager.yml" 10 | //Scheduler file name 11 | Scheduler = "1.4_scheduler.yml" 12 | //Etcd file name 13 | Etcd = "2.0_etcd.yml" 14 | //WorkerNodes file name 15 | WorkerNodes = "4.0_worker_nodes.yml" 16 | //ControlPlaneConfiguration file name 17 | ControlPlaneConfiguration = "3.0_control_plane_configuration.yml" 18 | //GkeWorkerNodes worker spec 19 | GkeWorkerNodes = "3.0_worker_nodes.yml" 20 | //GkePolicies policies spec 21 | GkePolicies = "4.0_policies.yml" 22 | //GkeManagedServices manages service spec 23 | GkeManagedServices = "5.0_managed_services.yml" 24 | //GkeControlPlaneConfiguration file name 25 | GkeControlPlaneConfiguration = "2.0_control_plane_configuration.yml" 26 | //Policies file name 27 | Policies = "5.0_policies.yml" 28 | //GrepRegex for tests 29 | GrepRegex = "[^\"]\\S*'" 30 | //MultiValue for tests 31 | MultiValue = "MultiValue" 32 | //SingleValue for tests 33 | SingleValue = "SingleValue" 34 | //EmptyValue for test 35 | EmptyValue = "EmptyValue" 36 | //NotValidNumber value 37 | NotValidNumber = "10000" 38 | //Report arg 39 | Report = "r" 40 | //ReportFull arg 41 | ReportFull = "report" 42 | //Synopsis help 43 | Synopsis = "synopsis" 44 | //BeaconCli Name 45 | BeaconCli = "kube-beacon" 46 | //BeaconVersion version 47 | BeaconVersion = "0.1" 48 | //IncludeParam param 49 | IncludeParam = "i=" 50 | //ExcludeParam param 51 | ExcludeParam = "e=" 52 | //NodeParam param 53 | NodeParam = "n=" 54 | //BeaconHomeEnvVar Beacon Home env var 55 | BeaconHomeEnvVar = "BEACON_HOME" 56 | //KubeBeacon binary name 57 | KubeBeacon = "kube-beacon" 58 | //RootUser process user owner 59 | RootUser = "root" 60 | //NonApplicableTest test is not applicable 61 | NonApplicableTest = "non_applicable" 62 | //ManualTest test can only be manual executed 63 | ManualTest = "manual" 64 | //K8sBenchAuditResultHook hook name 65 | K8sBenchAuditResultHook = "K8sBenchAuditResultHook" 66 | ) 67 | -------------------------------------------------------------------------------- /internal/benchmark/k8s/v1.6.0/1.4_scheduler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: 1.4 Scheduler 7 | audit_tests: 8 | - name: 1.4.1 Ensure that the --profiling argument is set to false 9 | description: Disable profiling, if not needed. 10 | profile_applicability: Master 11 | audit: 12 | - ps -ef | grep kube-scheduler |grep ' --profiling' | grep -o ' --profiling=[^"]\S*' 13 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | - awk -v FS="--profiling=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-scheduler.yaml 15 | remediation: |- 16 | Edit the Scheduler pod specification file /etc/kubernetes/manifests/kube- scheduler.yaml file on the master node and set the below parameter. 17 | --profiling=false 18 | check_type: multi_param 19 | impact: Profiling information would not be available. 20 | eval_expr: "'${0}' == 'false'; && '${0}' == '${1}';" 21 | default_value: By default, profiling is enabled. 22 | references: 23 | - https://kubernetes.io/docs/admin/kube-scheduler/ 24 | - https://github.com/kubernetes/community/blob/master/contributors/devel/profiling.md 25 | - name: 1.4.2 Ensure that the --bind-address argument is set to 127.0.0.1 26 | description: Do not bind the scheduler service to non-loopback insecure addresses. 27 | profile_applicability: Master 28 | audit: 29 | - ps -ef | grep kube-scheduler |grep ' --bind-address' | grep -o ' --bind-address=[^"]\S*' 30 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 31 | - awk -v FS=" --bind-address=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-scheduler.yaml 32 | remediation: Edit the Scheduler pod specification file /etc/kubernetes/manifests/kube- 33 | scheduler.yaml on the master node and ensure the correct value for the --bind-address 34 | parameter 35 | check_type: multi_param 36 | impact: None 37 | eval_expr: "'${0}' == '127.0.0.1'; && '${0}' == '${1}';" 38 | default_value: By default, the --bind-address parameter is set to 0.0.0.0 39 | references: 40 | - https://kubernetes.io/docs/reference/command-line-tools-reference/kube- scheduler/ 41 | -------------------------------------------------------------------------------- /internal/models/audit.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/common" 5 | "github.com/mitchellh/mapstructure" 6 | ) 7 | 8 | //Audit data model 9 | type Audit struct { 10 | BenchmarkType string `yaml:"benchmark_type"` 11 | Categories []Category `yaml:"categories"` 12 | } 13 | 14 | //AuditTestTotals model 15 | type AuditTestTotals struct { 16 | Warn int 17 | Pass int 18 | Fail int 19 | } 20 | 21 | //Category data model 22 | type Category struct { 23 | Name string `yaml:"name"` 24 | SubCategory *SubCategory `yaml:"sub_category"` 25 | } 26 | 27 | //SubCategory data model 28 | type SubCategory struct { 29 | Name string `yaml:"name"` 30 | AuditTests []*AuditBench `yaml:"audit_tests"` 31 | } 32 | 33 | //AuditBench data model 34 | type AuditBench struct { 35 | Name string `mapstructure:"name" yaml:"name"` 36 | ProfileApplicability string `mapstructure:"profile_applicability" yaml:"profile_applicability"` 37 | Description string `mapstructure:"description" yaml:"description"` 38 | AuditCommand []string `mapstructure:"audit" json:"audit"` 39 | CheckType string `mapstructure:"check_type" yaml:"check_type"` 40 | Remediation string `mapstructure:"remediation" yaml:"remediation"` 41 | Impact string `mapstructure:"impact" yaml:"impact"` 42 | DefaultValue string `mapstructure:"default_value" yaml:"default_value"` 43 | References []string `mapstructure:"references" yaml:"references"` 44 | EvalExpr string `mapstructure:"eval_expr" yaml:"eval_expr"` 45 | TestSucceed bool 46 | CommandParams map[int][]string 47 | Category string 48 | NonApplicable bool 49 | TestType string `mapstructure:"type" yaml:"type"` 50 | } 51 | 52 | //AuditResult data 53 | type AuditResult struct { 54 | NumOfExec int 55 | NumOfSuccess int 56 | } 57 | 58 | //UnmarshalYAML over unmarshall to add logic 59 | func (at *AuditBench) UnmarshalYAML(unmarshal func(interface{}) error) error { 60 | var res map[string]interface{} 61 | if err := unmarshal(&res); err != nil { 62 | return err 63 | } 64 | err := mapstructure.Decode(res, &at) 65 | if err != nil { 66 | return err 67 | } 68 | at.CommandParams = make(map[int][]string) 69 | if at.TestType == common.NonApplicableTest || at.TestType == common.ManualTest { 70 | at.NonApplicable = true 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chen-keinan/beacon 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/cheggaaa/pb v2.0.7+incompatible 7 | github.com/chen-keinan/go-command-eval v0.0.2 8 | github.com/chen-keinan/go-user-plugins v0.0.5 9 | github.com/gobuffalo/packr v1.30.1 10 | github.com/golang/mock v1.6.0 11 | github.com/gosuri/uitable v0.0.4 12 | github.com/magiconair/properties v1.8.7 13 | github.com/mitchellh/cli v1.1.5 14 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db 15 | github.com/mitchellh/mapstructure v1.5.0 16 | github.com/stretchr/testify v1.9.0 17 | go.uber.org/fx v1.18.2 18 | go.uber.org/zap v1.27.0 19 | gopkg.in/yaml.v2 v2.4.0 20 | ) 21 | 22 | require ( 23 | github.com/Knetic/govaluate v3.0.0+incompatible // indirect 24 | github.com/Masterminds/goutils v1.1.1 // indirect 25 | github.com/Masterminds/semver/v3 v3.1.1 // indirect 26 | github.com/Masterminds/sprig/v3 v3.2.1 // indirect 27 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect 28 | github.com/bgentry/speakeasy v0.1.0 // indirect 29 | github.com/davecgh/go-spew v1.1.1 // indirect 30 | github.com/fatih/color v1.9.0 // indirect 31 | github.com/gobuffalo/envy v1.7.0 // indirect 32 | github.com/gobuffalo/packd v0.3.0 // indirect 33 | github.com/google/uuid v1.1.2 // indirect 34 | github.com/hashicorp/errwrap v1.0.0 // indirect 35 | github.com/hashicorp/go-multierror v1.0.0 // indirect 36 | github.com/huandu/xstrings v1.3.2 // indirect 37 | github.com/imdario/mergo v0.3.11 // indirect 38 | github.com/joho/godotenv v1.3.0 // indirect 39 | github.com/mattn/go-colorable v0.1.4 // indirect 40 | github.com/mattn/go-isatty v0.0.11 // indirect 41 | github.com/mattn/go-runewidth v0.0.4 // indirect 42 | github.com/mitchellh/copystructure v1.0.0 // indirect 43 | github.com/mitchellh/reflectwalk v1.0.0 // indirect 44 | github.com/pmezard/go-difflib v1.0.0 // indirect 45 | github.com/posener/complete v1.1.1 // indirect 46 | github.com/rogpeppe/go-internal v1.3.0 // indirect 47 | github.com/shopspring/decimal v1.2.0 // indirect 48 | github.com/spf13/cast v1.3.1 // indirect 49 | go.uber.org/dig v1.15.0 // indirect 50 | go.uber.org/multierr v1.10.0 // indirect 51 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect 52 | golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect 53 | gopkg.in/VividCortex/ewma.v1 v1.1.1 // indirect 54 | gopkg.in/cheggaaa/pb.v2 v2.0.7 // indirect 55 | gopkg.in/fatih/color.v1 v1.7.0 // indirect 56 | gopkg.in/mattn/go-colorable.v0 v0.1.0 // indirect 57 | gopkg.in/mattn/go-isatty.v0 v0.0.4 // indirect 58 | gopkg.in/mattn/go-runewidth.v0 v0.0.4 // indirect 59 | gopkg.in/yaml.v3 v3.0.1 // indirect 60 | ) 61 | -------------------------------------------------------------------------------- /pkg/filters/predicate.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/models" 5 | "github.com/chen-keinan/beacon/pkg/utils" 6 | "strings" 7 | ) 8 | 9 | // Predicate filter audit tests cmd criteria 10 | type Predicate func(tests *models.SubCategory, params string) *models.SubCategory 11 | 12 | // IncludeAudit include audit tests , only included tests will be executed 13 | var IncludeAudit Predicate = func(tests *models.SubCategory, params string) *models.SubCategory { 14 | sat := make([]*models.AuditBench, 0) 15 | spt := utils.GetAuditTestsList("i", params) 16 | // check if param include category 17 | for _, sp := range spt { 18 | if strings.HasPrefix(tests.Name, sp) { 19 | return tests 20 | } 21 | } 22 | // check tests 23 | for _, at := range tests.AuditTests { 24 | for _, sp := range spt { 25 | if strings.HasPrefix(at.Name, sp) { 26 | sat = append(sat, at) 27 | } 28 | } 29 | } 30 | if len(sat) == 0 { 31 | return &models.SubCategory{Name: tests.Name, AuditTests: make([]*models.AuditBench, 0)} 32 | } 33 | return &models.SubCategory{Name: tests.Name, AuditTests: sat} 34 | } 35 | 36 | // ExcludeAudit audit test from been executed 37 | var ExcludeAudit Predicate = func(tests *models.SubCategory, params string) *models.SubCategory { 38 | sat := make([]*models.AuditBench, 0) 39 | spt := utils.GetAuditTestsList("e", params) 40 | // if exclude category 41 | for _, sp := range spt { 42 | if strings.HasPrefix(tests.Name, sp) { 43 | return &models.SubCategory{Name: tests.Name, AuditTests: []*models.AuditBench{}} 44 | } 45 | } 46 | for _, at := range tests.AuditTests { 47 | var skipTest bool 48 | for _, sp := range spt { 49 | if strings.HasPrefix(at.Name, sp) { 50 | skipTest = true 51 | } 52 | } 53 | if skipTest { 54 | continue 55 | } 56 | sat = append(sat, at) 57 | } 58 | return &models.SubCategory{Name: tests.Name, AuditTests: sat} 59 | } 60 | 61 | // NodeAudit audit test from been executed 62 | var NodeAudit Predicate = func(tests *models.SubCategory, params string) *models.SubCategory { 63 | sat := make([]*models.AuditBench, 0) 64 | spt := utils.GetAuditTestsList("n", params) 65 | // check tests 66 | for _, at := range tests.AuditTests { 67 | for _, sp := range spt { 68 | if strings.ToLower(at.ProfileApplicability) == sp { 69 | sat = append(sat, at) 70 | } 71 | } 72 | } 73 | if len(sat) == 0 { 74 | return &models.SubCategory{Name: tests.Name, AuditTests: make([]*models.AuditBench, 0)} 75 | } 76 | return &models.SubCategory{Name: tests.Name, AuditTests: sat} 77 | } 78 | 79 | // Basic filter by specific audit tests as set in command 80 | var Basic Predicate = func(tests *models.SubCategory, params string) *models.SubCategory { 81 | return tests 82 | } 83 | -------------------------------------------------------------------------------- /.dev/setup.sh: -------------------------------------------------------------------------------- 1 | echo "start vagrant provioning..." 2 | 3 | sudo apt-get update 4 | 5 | echo "install docker..." 6 | sudo apt-get install -y docker.io 7 | 8 | echo "set docker launch at boot..." 9 | sudo systemctl enable docker 10 | 11 | echo "start docker..." 12 | sudo systemctl start docker 13 | 14 | echo "add signing key..." 15 | curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add 16 | 17 | echo "install curl pkg..." 18 | sudo apt-get install -y curl 19 | 20 | echo "Add ubuntu to default repository..." 21 | sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main" 22 | 23 | echo "install k8s tools..." 24 | sudo apt-get install -y kubeadm kubelet kubectl 25 | sudo apt-mark hold kubeadm kubelet kubectl 26 | 27 | echo "set master node..." 28 | sudo swapoff -a 29 | sudo hostnamectl set-hostname master-node 30 | 31 | echo "init master node..." 32 | sudo kubeadm init --pod-network-cidr=10.244.0.0/16 33 | mkdir -p $HOME/.kube 34 | sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 35 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 36 | 37 | echo "deploy pod network to cluster..." 38 | sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 39 | 40 | kubectl get pods --all-namespaces 41 | 42 | kubectl get nodes 43 | 44 | echo "install http transport..." 45 | sudo apt-get install apt-transport-https 46 | 47 | echo "install virtual box..." 48 | echo virtualbox-ext-pack virtualbox-ext-pack/license select true | sudo debconf-set-selections 49 | sudo apt install -y virtualbox virtualbox-ext-pack 50 | 51 | echo "install minikube..." 52 | wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 53 | sudo cp minikube-linux-amd64 /usr/local/bin/minikube 54 | sudo chmod 755 /usr/local/bin/minikube 55 | minikube version 56 | 57 | echo "install kubectl..." 58 | curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl 59 | 60 | chmod +x ./kubectl 61 | sudo mv ./kubectl /usr/local/bin/kubectl 62 | kubectl version -o json 63 | 64 | echo "start minikube..." 65 | usermod -aG docker $USER && newgrp docker 66 | minikube start --memory=1992mb --driver=docker --force 67 | 68 | echo "check minikube status..." 69 | kubectl config view 70 | kubectl cluster-info 71 | kubectl get nodes 72 | kubectl get pod 73 | minikube status 74 | 75 | echo "install golang pkg" 76 | sudo add-apt-repository ppa:longsleep/golang-backports 77 | sudo apt update -y 78 | sudo apt install -y golang-go 79 | 80 | echo "Install dlv pkg" 81 | git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve 82 | cd $GOPATH/src/github.com/go-delve/delve 83 | make install 84 | 85 | ### export dlv bin path 86 | export PATH=$PATH:/home/vagrant/go/bin 87 | 88 | echo "Finished provisioning." 89 | -------------------------------------------------------------------------------- /pkg/filters/predicate_test.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/models" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | //Test_IncludeTestPredict text 10 | func Test_IncludeTestPredict(t *testing.T) { 11 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc"}, {Name: "1.2.3 eft"}}} 12 | abp := IncludeAudit(ab, "1.2.1") 13 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.1 abc") 14 | assert.True(t, len(abp.AuditTests) == 1) 15 | } 16 | 17 | //Test_IncludeTestPredicateNoValidArg text 18 | func Test_IncludeTestPredicateNoValidArg(t *testing.T) { 19 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc"}, {Name: "1.2.3 eft"}}} 20 | abp := IncludeAudit(ab, "1.2.5") 21 | assert.True(t, len(abp.AuditTests) == 0) 22 | } 23 | 24 | //Test_ExcludeTestPredict text 25 | func Test_ExcludeTestPredict(t *testing.T) { 26 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc"}, {Name: "1.2.3 eft"}}} 27 | abp := ExcludeAudit(ab, "1.2.1") 28 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.3 eft") 29 | assert.True(t, len(abp.AuditTests) == 1) 30 | } 31 | 32 | //Test_ExcludeTestPredicateNoValidArg text 33 | func Test_ExcludeTestPredicateNoValidArg(t *testing.T) { 34 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc"}, {Name: "1.2.3 eft"}}} 35 | abp := ExcludeAudit(ab, "1.2.5") 36 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.1 abc") 37 | assert.Equal(t, abp.AuditTests[1].Name, "1.2.3 eft") 38 | assert.True(t, len(abp.AuditTests) == 2) 39 | } 40 | 41 | //Test_BasicPredicate text 42 | func Test_BasicPredicate(t *testing.T) { 43 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc"}, {Name: "1.2.3 eft"}}} 44 | abp := Basic(ab, "") 45 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.1 abc") 46 | assert.Equal(t, abp.AuditTests[1].Name, "1.2.3 eft") 47 | assert.True(t, len(abp.AuditTests) == 2) 48 | } 49 | 50 | //Test_NodePredicateMaster text 51 | func Test_NodePredicateMaster(t *testing.T) { 52 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc", ProfileApplicability: "Master"}, {Name: "1.2.3 eft", ProfileApplicability: "Worker"}}} 53 | abp := NodeAudit(ab, "n=master") 54 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.1 abc") 55 | assert.True(t, len(abp.AuditTests) == 1) 56 | } 57 | 58 | //Test_NodePredicateWorker text 59 | func Test_NodePredicateWorker(t *testing.T) { 60 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc", ProfileApplicability: "Master"}, {Name: "1.2.3 eft", ProfileApplicability: "Worker"}}} 61 | abp := NodeAudit(ab, "n=worker") 62 | assert.Equal(t, abp.AuditTests[0].Name, "1.2.3 eft") 63 | assert.True(t, len(abp.AuditTests) == 1) 64 | } 65 | 66 | //Test_NodePredicateNone text 67 | func Test_NodePredicateNone(t *testing.T) { 68 | ab := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 abc", ProfileApplicability: "Master"}, {Name: "1.2.3 eft", ProfileApplicability: "Worker"}}} 69 | abp := NodeAudit(ab, "n=abd") 70 | assert.Equal(t, len(abp.AuditTests), 0) 71 | } 72 | -------------------------------------------------------------------------------- /internal/cli/commands/fixtures/CheckInClause4.2.13.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: API Server 7 | audit_tests: 8 | - name: 4.2.13 Ensure that the Kubelet only makes use of Strong Cryptographic 9 | Ciphers 10 | description: Ensure that the Kubelet is configured to only use strong cryptographic 11 | ciphers. 12 | profile_applicability: Level 1 - Worker Node 13 | audit: 14 | - ps -ef | grep kubelet |grep ' --config' | grep -o ' --config=[^"]\S*' | awk 15 | -F "=" '{print $2}' |awk 'FNR <= 1' 16 | - 'sudo grep ''TLSCipherSuites'' ${0} |grep ''TLSCipherSuites:[^"]\S*''| awk -F 17 | ":" ''{print $2}'' |awk ''FNR <= 1''' 18 | - 'echo ${1} > tmp && sed ''s/,/\n/g'' tmp' 19 | - ps -ef | grep kubelet |grep 'TLSCipherSuites' | grep -o 'TLSCipherSuites=[^"]\S*' 20 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 21 | - 'echo ${3} > tmp && sed ''s/,/\n/g'' tmp' 22 | remediation: |- 23 | If using a Kubelet config file, edit the file to set TLSCipherSuites: to TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 24 | ,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256 or to a subset of these values. 25 | If using executable arguments, edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-kubeadm.conf on each worker node and set the --tls-cipher-suites parameter as follows, or to a subset of these values. 26 | --tls-cipher- suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM _SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM _SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM _SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256 27 | check_type: multi_param 28 | impact: Kubelet clients that cannot support modern cryptographic ciphers will 29 | not be able to make connections to the Kubelet API. 30 | eval_expr: "'${2}'; IN ('TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256','TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' 31 | ,'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305','TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' 32 | ,'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305','TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384','TLS_RSA_WITH_AES_256_GCM_SHA384','TLS_RSA_WITH_AES_128_GCM_SHA256'); 33 | || '${4}'; IN ('TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256','TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' 34 | ,'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305','TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384','TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305','TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384','TLS_RSA_WITH_AES_256_GCM_SHA384','TLS_RSA_WITH_AES_128_GCM_SHA256');" 35 | default_value: By default the Kubernetes API server supports a wide range of 36 | TLS ciphers 37 | references: 38 | - https://github.com/kubernetes/kubernetes/pull/45059 39 | - https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/#kubelet-configuration 40 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hen.keinan@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /internal/benchmark/gke/v1.1.0/2.0_control_plane_configuration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - 5 | name: Control Plane Components 6 | sub_category: 7 | name: 2.0 Control Plane Configuration 8 | audit_tests: 9 | - name: 2.1.1 Client certificate authentication should not be used for users (Not 10 | Scored) 11 | description: |- 12 | Kubernetes provides the option to use client certificates for user authentication. However 13 | as there is no way to revoke these certificates when a user leaves an organization or loses 14 | their credential, they are not suitable for this purpose. 15 | It is not possible to fully disable client certificate use within a cluster as it is used for 16 | component to component authentication. 17 | profile_applicability: Master 18 | audit: 19 | - ps -ef | grep kube-apiserver |grep ' --oidc-username-claim' | grep -o ' --oidc-username-claim=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 20 | - awk -v FS="--oidc-username-claim=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-apiserver.yaml 21 | remediation: Alternative mechanisms provided by Kubernetes such as the use of OIDC should be 22 | implemented in place of client certificates. 23 | You can remediate the availability of client certificates in your GKE cluster. See 24 | Recommendation 6.8.2. 25 | check_type: multi_param 26 | impact: External mechanisms for authentication generally require additional software to be 27 | deployed. 28 | eval_expr: "'${0}' != ''; && '${0}' == '${1}';" 29 | default_value: See the GKE documentation for the default value. 30 | references: 31 | - https://cloud.google.com/kubernetes-engine/docs/concepts/cis-benchmarks 32 | - name: 2.2.1 Ensure that a minimal audit policy is created (Not Scored) 33 | description: |- 34 | Kubernetes can audit the details of requests made to the API server. The --audit-policyfile 35 | flag must be set for this logging to be enabled. 36 | profile_applicability: Master 37 | audit: 38 | - This control cannot be audited in GKE. 39 | remediation: This control cannot be modified in GKE. 40 | check_type: multi_param 41 | impact: Audit logs will be created on the master nodes, which will consume disk space. Care should 42 | be taken to avoid generating too large volumes of log information as this could impact the 43 | available of the cluster nodes. 44 | eval_expr: 45 | default_value: See the GKE documentation for the default value. 46 | type: non_applicable 47 | references: 48 | - https://kubernetes.io/docs/tasks/debug-application-cluster/audit/ 49 | - https://cloud.google.com/kubernetes-engine/docs/concepts/cis-benchmarks 50 | - name: 2.2.2 Ensure that the audit policy covers key security concerns (Not Scored) 51 | description: |- 52 | Ensure that the audit policy created for the cluster covers key security concerns. 53 | profile_applicability: Master 54 | audit: 55 | - This control cannot be audited in GKE. 56 | remediation: This control cannot be modified in GKE. 57 | check_type: multi_param 58 | impact: Increasing audit logging will consume resources on the nodes or other log destination. 59 | eval_expr: 60 | default_value: See the GKE documentation for the default value. 61 | type: non_applicable 62 | references: 63 | - https://github.com/k8scop/k8s-securitydashboard/blob/master/configs/kubernetes/adv-audit.yaml 64 | - https://kubernetes.io/docs/tasks/debug-application-cluster/audit/#audit-policy 65 | - https://github.com/falcosecurity/falco/blob/master/examples/k8s_audit_config/audit-policy.yaml 66 | - https://github.com/kubernetes/kubernetes/blob/master/cluster/gce/gci/configure-helper.sh#L735 67 | - https://cloud.google.com/kubernetes-engine/docs/concepts/cis-benchmarks -------------------------------------------------------------------------------- /internal/cli/cli_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/cli/commands" 5 | m3 "github.com/chen-keinan/beacon/internal/cli/mocks" 6 | "github.com/chen-keinan/beacon/internal/common" 7 | "github.com/chen-keinan/beacon/internal/logger" 8 | "github.com/chen-keinan/beacon/internal/mocks" 9 | "github.com/chen-keinan/beacon/internal/models" 10 | m2 "github.com/chen-keinan/beacon/pkg/models" 11 | "github.com/chen-keinan/beacon/pkg/utils" 12 | "github.com/chen-keinan/go-command-eval/eval" 13 | "github.com/golang/mock/gomock" 14 | "github.com/mitchellh/cli" 15 | "github.com/stretchr/testify/assert" 16 | "os" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | //Test_StartCli tests 22 | func Test_StartCli(t *testing.T) { 23 | fm := utils.NewKFolder() 24 | initBenchmarkSpecData(fm, ArgsData{SpecType: "k8s", SpecVersion: "v1.6.0"}) 25 | files, err := utils.GetK8sBenchAuditFiles("k8s", "v1.6.0", fm) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | assert.Equal(t, len(files), 8) 30 | assert.Equal(t, files[0].Name, common.MasterNodeConfigurationFiles) 31 | assert.Equal(t, files[1].Name, common.APIServer) 32 | assert.Equal(t, files[2].Name, common.ControllerManager) 33 | assert.Equal(t, files[3].Name, common.Scheduler) 34 | assert.Equal(t, files[4].Name, common.Etcd) 35 | assert.Equal(t, files[5].Name, common.ControlPlaneConfiguration) 36 | assert.Equal(t, files[6].Name, common.WorkerNodes) 37 | assert.Equal(t, files[7].Name, common.Policies) 38 | } 39 | 40 | func Test_ArgsSanitizer(t *testing.T) { 41 | args := []string{"--a", "-b"} 42 | ad := ArgsSanitizer(args) 43 | assert.Equal(t, ad.Filters[0], "a") 44 | assert.Equal(t, ad.Filters[1], "b") 45 | assert.False(t, ad.Help) 46 | args = []string{} 47 | ad = ArgsSanitizer(args) 48 | assert.True(t, ad.Filters[0] == "") 49 | args = []string{"--help"} 50 | ad = ArgsSanitizer(args) 51 | assert.True(t, ad.Help) 52 | } 53 | 54 | //Test_BeaconHelpFunc test 55 | func Test_BeaconHelpFunc(t *testing.T) { 56 | cm := make(map[string]cli.CommandFactory) 57 | bhf := BeaconHelpFunc(common.KubeBeacon) 58 | helpFile := bhf(cm) 59 | assert.True(t, strings.Contains(helpFile, "Available commands are:")) 60 | assert.True(t, strings.Contains(helpFile, "Usage: kube-beacon [--version] [--help] []")) 61 | } 62 | 63 | //Test_createCliBuilderData test 64 | func Test_createCliBuilderData(t *testing.T) { 65 | cmdArgs := []string{"a"} 66 | ad := ArgsSanitizer(os.Args[1:]) 67 | cmdArgs = append(cmdArgs, ad.Filters...) 68 | cmds := make([]cli.Command, 0) 69 | completedChan := make(chan bool) 70 | plChan := make(chan m2.KubeAuditResults) 71 | // invoke cli 72 | evaluator := eval.NewEvalCmd() 73 | cmds = append(cmds, commands.NewK8sAudit(ad.Filters, plChan, completedChan, []utils.FilesInfo{}, logger.GetLog(), evaluator)) 74 | c := createCliBuilderData(cmdArgs, cmds) 75 | _, ok := c["a"] 76 | assert.True(t, ok) 77 | 78 | } 79 | 80 | //Test_InvokeCli test 81 | func Test_InvokeCli(t *testing.T) { 82 | ab := &models.AuditBench{} 83 | ab.AuditCommand = []string{"aaa"} 84 | ab.EvalExpr = "'$0' != '';" 85 | ab.CommandParams = map[int][]string{} 86 | ctrl := gomock.NewController(t) 87 | defer ctrl.Finish() 88 | evalCmd := m3.NewMockCmdEvaluator(ctrl) 89 | evalCmd.EXPECT().EvalCommand([]string{"aaa"}, ab.EvalExpr).Return(eval.CmdEvalResult{Match: true}).Times(1) 90 | tl := mocks.NewMockTestLoader(ctrl) 91 | tl.EXPECT().LoadAuditTests(nil).Return([]*models.SubCategory{{Name: "te", AuditTests: []*models.AuditBench{ab}}}) 92 | completedChan := make(chan bool) 93 | plChan := make(chan m2.KubeAuditResults) 94 | go func() { 95 | <-plChan 96 | completedChan <- true 97 | }() 98 | kb := &commands.K8sAudit{Evaluator: evalCmd, ResultProcessor: commands.GetResultProcessingFunction([]string{}), FileLoader: tl, OutputGenerator: commands.ConsoleOutputGenerator, PlChan: plChan, CompletedChan: completedChan} 99 | cmdArgs := []string{"a"} 100 | cmds := make([]cli.Command, 0) 101 | // invoke cli 102 | cmds = append(cmds, kb) 103 | c := createCliBuilderData(cmdArgs, cmds) 104 | a, err := invokeCommandCli(cmdArgs, c) 105 | assert.NoError(t, err) 106 | assert.True(t, a == 0) 107 | } 108 | 109 | func Test_InitPluginFolder(t *testing.T) { 110 | fm := utils.NewKFolder() 111 | initPluginFolders(fm) 112 | } 113 | 114 | func Test_InitPluginWorker(t *testing.T) { 115 | completedChan := make(chan bool) 116 | plChan := make(chan m2.KubeAuditResults) 117 | go func() { 118 | plChan <- m2.KubeAuditResults{} 119 | completedChan <- true 120 | }() 121 | initPluginWorker(plChan, completedChan) 122 | 123 | } 124 | -------------------------------------------------------------------------------- /internal/startup/templates_test.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "github.com/chen-keinan/beacon/internal/common" 5 | "github.com/chen-keinan/beacon/pkg/utils" 6 | "github.com/stretchr/testify/assert" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | //Test_CreateK8sBenchmarkFilesIfNotExist test 12 | func Test_CreateK8sBenchmarkFilesIfNotExist(t *testing.T) { 13 | bFiles, err := GenerateK8sBenchmarkFiles() 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | // generate test with packr 18 | assert.Equal(t, bFiles[0].Name, common.MasterNodeConfigurationFiles) 19 | assert.Equal(t, bFiles[1].Name, common.APIServer) 20 | assert.Equal(t, bFiles[2].Name, common.ControllerManager) 21 | assert.Equal(t, bFiles[3].Name, common.Scheduler) 22 | assert.Equal(t, bFiles[4].Name, common.Etcd) 23 | assert.Equal(t, bFiles[5].Name, common.ControlPlaneConfiguration) 24 | assert.Equal(t, bFiles[6].Name, common.WorkerNodes) 25 | assert.Equal(t, bFiles[7].Name, common.Policies) 26 | fm := utils.NewKFolder() 27 | err = utils.CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 28 | assert.NoError(t, err) 29 | // save benchmark files to folder 30 | err = SaveBenchmarkFilesIfNotExist("k8s", "v1.6.0", bFiles) 31 | assert.NoError(t, err) 32 | // fetch files from benchmark folder 33 | bFiles, err = utils.GetK8sBenchAuditFiles("k8s", "v1.6.0", fm) 34 | assert.Equal(t, bFiles[0].Name, common.MasterNodeConfigurationFiles) 35 | assert.Equal(t, bFiles[1].Name, common.APIServer) 36 | assert.Equal(t, bFiles[2].Name, common.ControllerManager) 37 | assert.Equal(t, bFiles[3].Name, common.Scheduler) 38 | assert.Equal(t, bFiles[4].Name, common.Etcd) 39 | assert.Equal(t, bFiles[5].Name, common.ControlPlaneConfiguration) 40 | assert.Equal(t, bFiles[6].Name, common.WorkerNodes) 41 | assert.Equal(t, bFiles[7].Name, common.Policies) 42 | assert.NoError(t, err) 43 | err = os.RemoveAll(utils.GetHomeFolder()) 44 | assert.NoError(t, err) 45 | } 46 | 47 | //Test_CreateGkeBenchmarkFilesIfNotExist test 48 | func Test_CreateGkeBenchmarkFilesIfNotExist(t *testing.T) { 49 | bFiles, err := GenerateGkeBenchmarkFiles() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | // generate test with packr 54 | assert.Equal(t, bFiles[0].Name, common.GkeControlPlaneConfiguration) 55 | assert.Equal(t, bFiles[1].Name, common.GkeWorkerNodes) 56 | assert.Equal(t, bFiles[2].Name, common.GkePolicies) 57 | assert.Equal(t, bFiles[3].Name, common.GkeManagedServices) 58 | fm := utils.NewKFolder() 59 | err = utils.CreateBenchmarkFolderIfNotExist("gke", "v1.1.0", fm) 60 | assert.NoError(t, err) 61 | // save benchmark files to folder 62 | err = SaveBenchmarkFilesIfNotExist("gke", "v1.1.0", bFiles) 63 | assert.NoError(t, err) 64 | // fetch files from benchmark folder 65 | bFiles, err = utils.GetK8sBenchAuditFiles("gke", "v1.1.0", fm) 66 | assert.Equal(t, bFiles[0].Name, common.GkeControlPlaneConfiguration) 67 | assert.Equal(t, bFiles[1].Name, common.GkeWorkerNodes) 68 | assert.Equal(t, bFiles[2].Name, common.GkePolicies) 69 | assert.Equal(t, bFiles[3].Name, common.GkeManagedServices) 70 | assert.NoError(t, err) 71 | err = os.RemoveAll(utils.GetHomeFolder()) 72 | assert.NoError(t, err) 73 | } 74 | 75 | //Test_GetHelpSynopsis test 76 | func Test_GetHelpSynopsis(t *testing.T) { 77 | hs := GetHelpSynopsis() 78 | assert.True(t, len(hs) != 0) 79 | } 80 | 81 | //Test_SaveBenchmarkFilesIfNotExist test 82 | func Test_SaveBenchmarkFilesIfNotExist(t *testing.T) { 83 | fm := utils.NewKFolder() 84 | folder, err2 := utils.GetBenchmarkFolder("k8s", "v1.6.0", fm) 85 | assert.NoError(t, err2) 86 | err := os.RemoveAll(folder) 87 | assert.NoError(t, err) 88 | filesData := make([]utils.FilesInfo, 0) 89 | err = utils.CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 90 | assert.NoError(t, err) 91 | filesData = append(filesData, utils.FilesInfo{Name: common.Scheduler, Data: "bbb"}) 92 | err = SaveBenchmarkFilesIfNotExist("k8s", "v1.6.0", filesData) 93 | assert.NoError(t, err) 94 | err = os.RemoveAll(utils.GetHomeFolder()) 95 | assert.NoError(t, err) 96 | } 97 | 98 | //Test_SaveBenchmarkFilesIfNotExist test 99 | func Test_SaveGkeBenchmarkFilesIfNotExist(t *testing.T) { 100 | fm := utils.NewKFolder() 101 | folder, err2 := utils.GetBenchmarkFolder("gke", "v1.1.0", fm) 102 | assert.NoError(t, err2) 103 | err := os.RemoveAll(folder) 104 | assert.NoError(t, err) 105 | filesData := make([]utils.FilesInfo, 0) 106 | err = utils.CreateBenchmarkFolderIfNotExist("gke", "v1.1.0", fm) 107 | assert.NoError(t, err) 108 | filesData = append(filesData, utils.FilesInfo{Name: common.Scheduler, Data: "bbb"}) 109 | err = SaveBenchmarkFilesIfNotExist("gke", "v1.1.0", filesData) 110 | assert.NoError(t, err) 111 | err = os.RemoveAll(utils.GetHomeFolder()) 112 | assert.NoError(t, err) 113 | } 114 | -------------------------------------------------------------------------------- /resources/control-planes/sealed-secrets/sealed-secrets-key66bh4-codefresh.sealing-key.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | creationTimestamp: 2024-12-23T09:46:13.000Z 5 | labels: 6 | sealedsecrets.bitnami.com/sealed-secrets-key: active 7 | codefresh.io/sealing-key-id: 6769329101d98c1169ba655c 8 | codefresh.io/sealing-key: 'true' 9 | codefresh.io/runtime-name: codefresh 10 | name: sealed-secrets-key66bh4 11 | data: 12 | tls.crt: >- 13 | LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUV6VENDQXJXZ0F3SUJBZ0lSQU5ySURkUkZ1OHg2cjFiaE1NYVJ5L013RFFZSktvWklodmNOQVFFTEJRQXcKQURBZUZ3MHlOREV5TWpNd09UUTJNVE5hRncwek5ERXlNakV3T1RRMk1UTmFNQUF3Z2dJaU1BMEdDU3FHU0liMwpEUUVCQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUUM2SUorczhXcDRkbXdBbXp4S3ZaQkdoZ1hPRjNEb0phRndXSHZoCktOdjcxWFpjdC9OWlhsbVQrNHRLS1ZCQlBManorcU5pZHlQQmVsZmZraFVMZW9lNERKTEJHTmhjV0VDcDdHNEYKbG5JdDRONU9GaURsQ3pkS1M2YlM4VERRNDRGUnp6dVNuSmVHZUNGN1kwM3BVdXFOM0J5TlYzVGdLU1V0RFF0aApLMzFsRTY0NU01SncxK1RKYnhSV2Q1dnJ6a1BOOHRqamtvV29SU1ArOXpWRW5JRXR2RkxMUEdEd1NhSDRvS2NyCnpxQ3R4cGdzZ1dYYzVvbXFnWC9DSkpRUitHVjVydDkwbnd5aWpyM1VHN0xtamJ6ZGRJb3pDUnNVV1IvRzZhKzgKRDdEWkV6MW5UcmxLcnZuSEtTcGY5bzBWTXFoQzRFMDkzMm1HOElqUEs0WGVLbCtJRHBna05xaitZSTg1dUNjTApuUExmWWxmMjZYb2lqNDFiblRmc0dxa1FUd2NvU3dJQWJNSCtXY2U0VmwxTk1aSGlMbDB3NXg1d0NaNzNTOU9jCnlnVnZ0TldkZmcrdHRYYzR3OGpBdTU4VFVKd29JV0JGa2RQZjlPQmFsT290NXpyRmRVcXlOc01Zd2cxOEZaN0MKQWI5bzYvRjQxUUQ4TG8vZ1QvREo4dVpxMXc1ZUhCc3AxY1JNMi9LdnhHYWRmU284OVo5QmZvR0JBbkxoa1BPQwpvVE45NkkzNkhjOWxYb2hlK0hGOUozOXBLempsWUZFVXo5aHVIZDR2MWNnYUF6YW9wMmMyQ2hNT3NNWFMyODJICjBjRXE1TlEzV0N1ckM4U2tidm5tV3UxRDUvaVRCWnFOTzZkMzJ0am4vQy9FQXFuTzJJY21maGk3eFFrS1gwZDkKdUM2L3R3SURBUUFCbzBJd1FEQU9CZ05WSFE4QkFmOEVCQU1DQUFFd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4VWpxWmJUWkNRUnBWUjluQ2Z2ZnllaXoyK3N3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCCkFBSUkvNzEyNEpMRzJFeVR3aWJPVGs4SnkvYzB3SHEyWHdGV3poc2NUUVQ3NVBET3lrY3dHalRsb0xBZXgzTSsKbTRXbHRlY3d4UUJPYkErMGgwOUlaOTY3SUJWNGV2b2hmdWtNZnZmbWtaYnJiRmVHNXFTR003aHJ1N1M4N3Irdwpvam1QR2M5QVg4MWovelNQaTJGaEVsK3ZrQWJ5NkxsaitibE12aXFVR3NYcm00TkZoMWYzdWs4YytTM1RGU1ZGCkM5NFN0WjllQ3BRbHlPU0p5bUx4Qi80S1N6QkNncXRuRjVIclE1eitoZ2RkRVJSWDVCQnJZYXYrS2NidlFlSHEKaTdBR1RYbHBmU044MWdGUVRDTXdVdHYvTStWSXYweXZyaHZVME84NnV3VXI1NXFyNnRvTUJuZGhoaXpRbFZsNwpvNzNJdU5xS1RkS2krK2RuNXRXaXZwZzZKelRGQlFaelBGamtEbk1sYkV2dzVGdEtsQTUydGJUWWtxSjJzNFowCktGTGE0N0ZxWmpxUGp5ck13MHFjTWdmdmFtVmhreWhxeUNReU1OWkthVDkxb3hpSEp6Tjd4UjBNQXR5OVZueWQKVnBtbW5mdUREeG92U2ZaZTJhcWMwQnZncy85Vk5zdGdDMFd5Y2FIMVlvdUtFSys2V3N0WlFENjQ4R2tOM2Q1YgpwYUE5S05rM0dBeEd3WXE0Y2R0a1Z5elBjZDdxejBKcjIzM0p4bDNZTE5iTVNOQXBha0pZS1dyRWJ4R0xTVVFnClphS244T0RKSFlFVzRHcU1jS2RTSzZLWVpWSG1DdEtCSU5hVG50NUl6WFNYUHJhNUdyZ3BMRGJlcFhGWThHV0cKZ1o4WC93UEdtandMajZVbisra2tIN2NDZDFiL0RTc3EzaVhwK1NBemhTNFQKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 14 | tls.key: >- 15 | ZTaW1BNkF4dlArVUZqU0ZQRUxrbUMrVUxYNGZlbnI0dHllCkdrcFcvRTZFYUlNcWxJa1M2L0NDMHpBQlJ4b0Zpekc4SjFaMTl6M3ErRWdobzJWYndMSVJjZXlYbGxVOFdGaWgKSTZ4YVF5QVViWDhaSHVqck1GcndYTlNRaHZOOEp5ZTJtNTJEWHhocUpYR25PRlpLQlJRazhiM1pkaXR2V2RqTQp0QUJQSGRyUjFqYWxGd0tDQVFFQTMrdGhNZGRxbUhwTlBmVjM5YmZobWdUVXNjbDhCNzZPQ016VDB6QysrYnZtCjRjU3N6Z0FLSEtNOXdpdGs3NHcvOHNYMnNhUGdaVWpjK1JzQW05ODk4VEYyTGI1TWRDSzVwUGdzcUhzY0F2NUsKTkhqc05kOFhTVTEwZ0djdUUzSU5qN25vQ2plMW1pUmdCLytFdmNTNVI3eHRZbUpVaDROVlpqQTdlU09jQUNwUwp0L1RYUkFFcjBkYTFNZkxYcFJESlhVdzR4TER5QUhjdGI3ZlcxK01MVnQ1WnppRmNEWHdRU1FucVlUQjlXWm42CjZ2cXl5WGhPVWIvYk16VUZzcFQrYU1UaEpud2hRMnJoalFKNjBTQzUwS0phbkRpY3VtOWFRTGRXR21YZU5lYVkKMjV5TThXWUQ0YlliR3J5VlRjZWZnWUxaSVNSS3dBRU55bWV2Y3RxZVlRS0NBUUJES010dDVEK0NxdGRQSmZ2eApCMFMzV0FnVmE0WWZyMktmSVk4N2xRWFpqQUhkNGwzOUpCRmdZOG44N012Nmw2blBES0VUMUR6WUM5Z1p0LzR2CmNuZXNUTUt6eXdCaUp0Q1R4RG55OThaYjN1YVBUKzZmYnNUQVEwM1NBbHExR29YZVF1Y3gyaVNnemxKNjZlK2EKNk5VT2NaMENpTFJxTEczVERucnRLYVNYY1J4YnJtNDVWWThZWk90T0M1S0ZUcWxhdEJWcXlFNXQ1Y0NHYVJHSgpHcnk1bkhtSVZhUTBteFlNSnI1UkxDVkNGUnV1S0xDY0k4ZmFGZ2RiVGRnTHFLdy9HaSt0Z210NUl0QnFGeG5iClpSMjhqNjZLMkdpOEk2cHByVGNsOUhiQnJuamo3b1N4eEtrT01zMnh4S1hVVmlMb0ZOVlQrZ1RCV3NlZG1JNVoKbVcySkFvSUJBUURGeG1PeG54L2lqMXQ4Rnp5ZVJveTMrZjVYRGF4RU50WERCZzh1WU1hOVUyN0pDR3JOQkpIcAoyM0VuK0NZTnd1V1VxNitLRlVHVWxEZkF1azNlWHQ2YUUyTGJZbEtxM1NUMWhkSUQrY0N6RUNGQW1YR2czMnYwCjdibXgwL0NKY2xweUVQVFhXWDlJSjBxK3BieDcxSzhGb25OR3k2ejNpRk84ajJ0b1FzQitFT1BBNFR2bEw5ZWgKWHEvcDd5RHpCWkdsbXpZazNGalRlbkFpVmRFb0c3eWZQSU03b3BGb3c2U3FybHhhL1hZcDF1d2Q4ZEpLU3IxNgpUU2Z4NGFvbm9wdldzOVpkcEpQSXRUV0UwNlRnNDdQYmlCcmFFdEt6aExqRm9ta2xwY1lQKzU0Vmt2K1dFOUhuCjRXMnRQdFhzdmgydlNZQTRXcEsvWmU3bFVKRlJzN0RoQW9JQkFDcmZvY29IRFZkanNFWDFVZVppSEkrL0NBRTcKZy9yQUFCdXJEZnB1THpDK0prUTR5ZndGTHJ5dHRmbVpiK29KV3hVZnVaWmgrUW5wM25TZEw4ZXoxMUgvNFY1YQpYZGEzN01ycUtnQm1VSm5FdWlXV004MUN0NmVlSS9IZTlTMEREdUZGZlhWaWhOdWVxcHRBbDZZSyt1ZzJ0bjNiCnl6N1lwNUtNQitLd1o2QmMvN21KUlM1eHVndXJuQTc2MjRENEphTW1UN3A1dE9Wb1I1MS9CVGh4ZlNXano5L3IKUWpwVHJhcFc5Mm5xTjdIcHVDeVdIeGQrenVIMEZxNXQyN1FkdFZ4NTYrWUNLSUtLejEwRm5hMUlsWjBDejh1SQpTSTE1T1pDUHdKUTF0NmxCQTVEbTJlK1NpZ3E0RENITTFrSTlpTkdxS2wyY3VqWnF3L2NDUFJCMk5lST0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K 16 | -------------------------------------------------------------------------------- /resources/control-planes/sealed-secrets/sealed-secrets-keyn552s-codefresh.sealing-key.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | creationTimestamp: 2024-12-23T13:04:54.000Z 5 | labels: 6 | sealedsecrets.bitnami.com/sealed-secrets-key: active 7 | codefresh.io/sealing-key-id: 676960773a28c009dd4f7fb6 8 | codefresh.io/sealing-key: 'true' 9 | codefresh.io/runtime-name: codefresh 10 | name: sealed-secrets-keyn552s 11 | data: 12 | tls.crt: >- 13 | LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUV6VENDQXJXZ0F3SUJBZ0lSQU02UWFsZGtmMVlTNXhLdkNOOTlhcWd3RFFZSktvWklodmNOQVFFTEJRQXcKQURBZUZ3MHlOREV5TWpNeE16QTBOVFJhRncwek5ERXlNakV4TXpBME5UUmFNQUF3Z2dJaU1BMEdDU3FHU0liMwpEUUVCQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUUM2ZWFnVlB3TDNvb0diMmFQcVNpRVN0NWUyd0dYdXNTaFVQbkZBCjdrWXVIbHV4elBWdXFsYllvZVJoMmR2ZkVGN2ZOMkhidUxFSkh5Q3NXL2hWazZFdGdtdW5lcHA5eGxmdEtHM0kKL1Z3SWxkQXJYMTBDQjlKV2tBMUR3c3QrY2svUG5UTXJkTkRGRWxFT2Z2WnBSdnRvcGxzdlJTSDZLMWVRT1BrdworTEd5aUR6Y1hmTElUU0Zzdzc5TExycnJNS0pDVUY2T1N1T3FnMjN1eXR1UGpiaTVqRS9HTzJ3Qnl0MjFjdGhoCnlHMUkwSXEva0N3MGFiZnp6enUvMUlGRWRXV3pVSUYyRjNPZTdjUHpQSm4xSWtkakt2ZW9NQ1ZQYkZJS2tzdloKcXNpRkhKV25sTXVDRE01bUZmbXlLVGt5QkZycFQ1QjQ0OEx6d3BwVFJEUFJGVmI0eTdtZTlJKzJLTjlzTkVkTQpLekV4VnFTR2RNSzBzRVUrL2Q3OVlYamxuTFMybzFwRnJkQ2VNcmdZcVljUkRiUXNTRzhlYVg5TW0vZTh4bWZGCm1vRkNwS1Fmb3JaYmNDN3E4MGIyREozeldsSGNZYm5jT3kxbk5meHEvQVB3TVpTSDRQeVluSVJGejVSYWZYSTMKQmQwQWcrZVVmVTJBVjNLaDJvdzVEcTYrYVRxUGZUZlRJa0FtRXBxSWVyRWdLa0duWnNwSlN1VjFLZGFzYllpbwpXUSt5eUJHeWYxVk4wMmc4bVFRd3lSM2RFd0dEWk9WYnZXK2NpZitkZlhEZ3luMURNUlFGWHRMVldrYWFraUlBCkNCWExYd1RBYlJoeXBXWGY2T21wQlBWZ2Vud0RSUXA1LzZkS3VlWVV0ZkxZRzJLSTNYTWJ3ZzUvNlZ4djdXV0wKQXhIZ3JRSURBUUFCbzBJd1FEQU9CZ05WSFE4QkFmOEVCQU1DQUFFd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVViRXJPNXEwU0lodmFjODhCbHpmMzZqL3cxV2d3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCCkFENHJhMkpzSm1NTnhlSytWaUs3VzZTSXNnWUNoMVIrR1Z5MjJKSjVVWEUzOSthSUUyZDM1ZThqcVgzZ0JjcTkKMFh5K2xuUm04REZYWnRCVWVXSlBiNzlpc1crZDR6d2plZXMyVnBnVVFITVAyVDZKVS9ndkZZOTlZTTdXV1dvQQpwRWhDc1EzeDVDNXdCVE9pTWxScEhKM3pYajNtWmp4M21lZFVIU3V4elJSeWVIdnRzTUFjT2VVQkptc3dUUGsxCkE2WW03VFBLanExMHF5VXhtaFdRR0xsM29pbWhuVU1WbThTb2hGWlBQQ1l2U0ZYa1duUUN2NmVTUXc5U0lSbzgKNW05NzVqblhJenMvc0VoTE1SdGM1RWwrUSs2RzkxMEZoek8zbFd6SXdvTzQvaXhyOFVXWko1c25SejA5K3pOVwpNTHcwTmdJVWpuL2lKOW9TWUxFWitxMFk2akFaYURaOW5TNGtBRzBidktCOTBmdm5DeWVNVzFGNGIwcStUUFllCnNwR2NiVlRYZlpWOG5ScHVYN2NBa3U2aXpsUXpFWDNVRC80S3d3SUpxek9LTWNGR3lYckMyZVNxd1IxUGZqV1YKQjRaTnJwNGJSam9FU2pnNjZkalJMY3A4QmhEcVlMdU9wUytscTlZaWtHQXZlNUJjVDBPTFl4cDRDYmU3WTlDTgpyL0h3R0tjb3F5dEZBSEhWNXEwcnRCRzdzcjdsSHFiMC9KZDI4NzROSVREM2tWeFhUc2h4VHV0Y1gzNXZIakZsCmhsaXI3MGowd0tlalRlZ1A2eWdXVExYeVlxNllSVDRERDRGZkNJS2VaRTE2WGhUSjZhbjlqNVZvanhCTmszR0kKWHd2VWVTaVNvekxjZjR6YjRyeklFZTE5bkhDamRVUXFlMThINWhnM2lOckQKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 14 | tls.key: >- 15 | NLVVFKa3crR3UzWm43TEMyUVZEZjlwMHYyeEFDQ1lsdzFSClpQOWhHSzFraGMzeHExbGM5QWFGL0Y1SFhqSTR0Vkc4VjJseUkxUEUwRWhQdzE4TTYzL21Sc0ZYcEdjZWs3bXcKYy9xL1RGM0xhNE5aOTY0eEI1bkx6MVozTHYrMUxOeS9jVWxRWXlxaTJCZTNWZUtsVWhxOGdHODJYaDNVRTNFawpiUzFBOEt4cVpCTGEyUUtDQVFFQTMzMlVCT2ZaWVhyTVRHaGQ5SDF5UUZqSWluZWh5dk14SGNTSHlSWXp2T1MxCkZKMDZuSmhLK3FzdzFzYk9rUi9GRXVxVERsOU5BZDZmdUVzTm05WllHVzQyUWhxaHZFSXF4UGhGdi9TQm8rR0QKcE1LOFdPZVRWWnNwV0I0aVZESjJxTWswUkI4SWRJYm16UW1VMFBqbnhnRmdoaStoaStFVU9tNVBDcE5CZDdFTAo4QzN4aUZ3a3JUSEI0NXNObUZSUUF0NkZiS2psQVRZbTlVZzVqSjBnOTduMmV3T3YzYUhaMDAvRU5vd0o5eldzCnZHTzNWR1B1RkIrNFRPT1RPVWtLem9MVVV5bkVuSng0aHoxQm1KUTlBSDlVQkdZR1JnT1Q1NGlOa1BmNGZlV28KR0FyeW1ESjFLUno1NGl4YnU2TCtDUXgzTG1HQkRDQ3JsU3lvR2M2SDlRS0NBUUVBdHBrNHhTT1BYQzJyWFYxQQpzeUJ5UjFNUXNHRnFjYTRpdTNkTDdIWHZXWUR0ME5qRzV4R2g2ZkdralA4bnJRUXVhK0RwbkZDRlcwSThreFI3CkZkSGoxRThsenNtdmptS1ZjczNYcUl1ZDcySFRBdXlRMUk5ZXZyWitoRU8vNTQyY09WSUVzck9EcEhsOEFNT1kKTjNxbjJQcytENjgrZU9ZQlJGMU9Edi9tTHNjR3h3ZUdkRGRtS21tM1dnMERrVnhRYU5SMHZZU2tGVnYvekZIRwpRbUNJL1R0YXJEMjdZci9SOE9PcitNMVQzY0lGMGM5MXk3eFZlNllRN0cyZEZOV0pyOEI3c2p1bWJhOW93OGJXCjlDVUl6eVI1UTQ4bFhTMEVuQWY3QU13QldMNGMyM21DVm9zSHJvTWZZdTM2UVMxUlkrbko5NGpjWS95d3JFNG8KY1Q1eEdRS0NBUUIzSWlMZ1JwTDdRcEZhL1p0djg5c09jQVZld296TmVidXA2WlNVWGJCd3RkM05RazczSUx0LwpTZmc3MVRoUjVnU1FvOWlHSmNrUUFSTlZFelVLNkVTSU1kYmNnQVBOTGR4TXFTZk5QbEpKQUxMREJrZU9nTXRxCks4N3JPUTBGb3V6YjVuMWsvc0hzRStrK1RTZzJDU2pMeFBvbUxZZkkzODkvb2tPaUdJTWlnWThpbGVzVnpFQ0UKem14VG50aXRaZXhNeXorR2Y3V3JZcWw3d09ka2pISUJlL0RtL0Qwb1FObXpxZWFuU1JrYTNWTGFEYkd4VzUrUwp2S0hxNHJrZEpWR01hZk15N2FYdjhmelI0cFp2OWoxYVlZVnczczNqVG83dU1qMThsS0t5d2RSTUZMSGk3YXJFCm9yeVZreDNITlFpRDNGQS91aHduVWRjRkt3dmt6ZEp0QW9JQkFEWjhTRW5pYW85dEtkdDBweTlVWHY5eU1FMm8KYjJacmYyWVhnbXdHL2dybmEzYzdjb0ZnVVJYWVlONU9WY0htMkVYNlNzS3pxRXVDTHJRR1ZFTnJEak9JdjUwcApmK1oxUGg1OWhoMkpNZE9qR2psNElkMmFhSERSaCtORE5RY1ZIdHpDUEtJMUhxc1liTW5qZ2xVaVFlbHRMRXhxClhoMkJIQ1VLT2IyQVM3MmRsM3VxN1F5YXZkWnVheHlZRGVTc0VlSzJkNEo4OGlHMG5nSjV6NnhJTEJFR0xNSUwKSUVBRWg1TlVOSXBnQ2szS29EeGpKUWR3cHZmV01HRlQ5ZUFDT1JUUENSdlRJNkExUFBFRVhlOUJoR0kzU3ZLYQpzbnBXMHdINldRYmJ4VmUwSVhRWW5GMEdFS041M2pGUmdZMUxqa1l1ZlRZbHhsdWxmMXNZN0VDbTFZTT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K 16 | -------------------------------------------------------------------------------- /resources/control-planes/sealed-secrets/sealed-secrets-keynds48-codefresh.sealing-key.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | creationTimestamp: 2024-12-24T06:03:44.000Z 5 | labels: 6 | sealedsecrets.bitnami.com/sealed-secrets-key: active 7 | codefresh.io/sealing-key-id: 676a51608938b4d79998482d 8 | codefresh.io/sealing-key: 'true' 9 | codefresh.io/runtime-name: codefresh 10 | name: sealed-secrets-keynds48 11 | data: 12 | tls.crt: >- 13 | LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUV6RENDQXJTZ0F3SUJBZ0lRRGxYdmFIekxINCtoOUZIZi9xcFo5ekFOQmdrcWhraUc5dzBCQVFzRkFEQUEKTUI0WERUSTBNVEl5TkRBMk1ETTBORm9YRFRNME1USXlNakEyTURNME5Gb3dBRENDQWlJd0RRWUpLb1pJaHZjTgpBUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBS1ZCejkvV2FCdWpWQ21ReW9CZmdaODJzaEs4MHhqbVlnK1labzVCCjExUFFHR2I3TWgvcnE5M3VpTFRNYXdFeGRvQUkrNDkxSFBvcEtkVXFXRzZPaEpaK3FIdVgrZERabEl0R1JycUUKZWZZanBQUzFHYWtQNTFmRDZIaS9JMzFsbWN0LzdsQnRmQ1lMWERnTit2QUI4QXdqY3hWeWxoVWlPNmo2bE5QUgp5SjRwc1hGcjZwSC9RQmZ1ajJHUzloV1hWR1FMbkErWElkWnBqdnVXcmNkbHoweVltQVE1aDRSZkJPVzlqaXpnCkc0bTNBT3hrSGJtREZ4Um9BNzd6UXpld0dSL293alpsME9xTEpVNFV6U3JuRHFoSlN3OFdWUnBiZkpJUm9LWlQKdkhpa2FBcHVScDNGSXZaY3hYUHRwSFBmV3IzZVhXNUhoeUx5OWRlWXZZZzF2Nlp6bGNidGwvNEF1SjUwcHJ6NAo2cmY2QUkvdm5tWUNBdWlNeWtHdEgycmFLdlpwdkhtWktjY3R4MnBHTXFsdnc3QlB0dnNKa04zSU8yMGdxdWxoCi9xWXlyTFBlOE5RK0xJNWxBT29aUU1TVFFZSDZIL29aSW0wQ1RCckY4SHpZOFQxejZYTENCSk1sTVJwOGpVb1kKUGhiMHBvbDYvSmdLTkRmZlVvUXVteWZRWTlOWE5GWXMvQU5kYlpEMFVYZ1VsVHo5bDd1RkdXN25FbTUzbnhxNgpPYmVGdFVBSlFWK0ZMVzJTR2k2Y1Z4alRWekcyLytiSks3ckhrcjMxN3hmbStNKy85MjFmT1N1VmZ2RzJUY0VCCjZZUnVWVDJRSEZINWFYTHZkL1F5eUdwOUkxZXExTnpQMXE3ZG5KRENrZnovbGJ5NFd5anFpNmVnQmFUVEs0bnoKV252eEFnTUJBQUdqUWpCQU1BNEdBMVVkRHdFQi93UUVBd0lBQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwRwpBMVVkRGdRV0JCU2pRQWllcll2M00yQ3REcUlRUkt0c0V0ZUJZREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBClgvdEZMLzZ2bFB0V002YXVnRWI0eTdaT1dCVkZyREowL0dRMHFlMVBSN3hJQWZxa1RjVUYrS2I1WDNJRFNoSmYKTEEzZWZjR1JyZzhwcGJMQnJzU1E2REtRYjN5Z1pXZUUxSFdWRDBPYkxQTVJBOVc4amM1eW5zcTNkWlBQbUpaVgpGdmVaKzFlRnVOSWpuSVUxSnBzTzNlWkcvejg0MTJweWVtekEwTHEzVCs4NUlZLytWeXA1NzVJWDUvSm5QRlR0Cm5JTEFGODlHUTU1ZEd2Q1N4VGdCVnQxbm54YmU3QWZzemNIenF6RFArUzIwa0Vwd3c2Yy9HclNINUJtV2JXQjQKZzhzV3hWVUFZaFZCWUU0OGdXdDFYbzVaM1Q5V1ZMTi85YlpHVllEY2R5WU5IQ0E4S3lXcG5XQzN2ZGxZYU5pdwowT2pxc2REejk3bkl3dDI3MCtacjhTbFJWUWhjLzc5VjdKYXdXeXFxY3c4aktJY2FLRWxuZllvVGVaNTg5VE1zCmozbXkyU3NWL3VZMWhRcWl3eGJtMTgzZWNoNTBZeFBFcmo5aWtxbW1tZ3FuNHU5RitWQXk5QkgzMVA5UDJzMFoKdTJtd1YyQWpCTU9PS1l3VktITytBT2RNcURINEl1b1hiaUswSzNDYmhtTjZVOVZGT2IxcklKTEI3cTJrZEc1NQp4SElRQXpoVXpTYU1VcVVUVForTW9Ndjdkbnh3NTd0MmptV3NwenNFOUlrazBiSzdwVXhhd0RObGFhZE9qWk5IClRhVStwMks4WGp1dzZSUEJkZ3plb0x6TUFoeWJpR1krK2ozMzJtbnBvRHArKzFxUnlZV1pmM3JuVGVidlljRTIKV3BzbXA5V01VWW4wVDJ1ZUptREJoWEcxSlZKa3BOVzdlRkFwUVlxM29VND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 14 | tls.key: >- 15 | owQytOb3BMcVNnL0JnOC9TOUMrNGJzTUVtY1djVzVCUjU0CmtsUVArSDVDYlpOOFJpWXluZ0ttVlVSQWE3VDlSdkZEZnJ6MXRIQ0pjWm5QQkMxVFJkQzl1T0E0NGJvN3IvdXYKRGRBSHRzZ1hTUUNNU1BqWExvRXY5MlV4bks5S2VRdXlLQjZGdnljWEplMm56NEovdGRNTldUem03YXB3MkxwQQpWUVlicVIwQmIrWEt5YTBDZ2dFQkFNT3dMSGZTOXZNeVBkbDVqalhFeDdsdkJUajhHNTI0U2FObjFTOGxLNkVOCkpXek5QM2NxenlMa0VvajgvM1NkdE9KOFk3QXdISmZ6ZGdiSWl6d3pSWDVGa3JKbE9XazFHNSt5ZTY1N3c3WVUKQWo2Y3lEMitBS3hxcFpyeWtmbS9uR1U3S2dhUENzNjNRY1FoUjZ2cjlwbnJVSmF1aDVTSEY4OWo1S3gwbldqYwplK1ZJN29HZWxCM3lNSnZjN3NsOFpEME9ZUllneFh3U2J3T3E4RDRDZDRqcDlPOEtLekFHTVRINjBGcUZMaVZkCmRGN3FiREgzSlI1WjVKVDA0QkliSFF0dllqZHFlOW5VYXlKTXBla1VBMVVPTEU5akdHRHNOWmZXa2lDT2xHeEQKVURDQTZMSVFqNTZZMGxrV3d6SzkzN2ZTUURSaFJrS1RlcjNObFdJWFM5VUNnZ0VCQU1WTVNleHhmaTduYTJySQpBaVpzR0VtSzdtYXJ4a2E3Mnh3TFJOUE5YMFM4T3NsSUx5Zi9wclpkdlRadjMxdE40OGsxTVh5R2k1TjYyOE0xCnh5bVI1UUd4ZEM2MGVVYlloMWtTbHlENFQzdmJKcm1NdkJrVG15L0I4RE9sWDhWMTI1cE9Rb2NmSDc4NWw3ak0KZ0EzMGlqTEE3dWtaTUdtZ3NPVnd0WmEzZWFpUDRQTjV2OWZYQVRFNnJoWktReDlLdzFzQktielBTS3ZURDNNYQo2S21xeHRKaGEzYjdtZUY1OFZIeHUrYS9ORkQ2NDd1NGs2Wk5HWURMK0hFeVZDQzlpTFBQZVNIMmRtS2VSNStBCk1LMkJXYTlQb2k5Z09TVnZDNUdtSTZZYTV5WkQ1Vm1sZElYWm1qVjhtOUdLNHlBRG1HN3VJYnlJclkrdmVyenYKN05FSUt0a0NnZ0VBVmJGaFY3dVhqdG9TZ1ppY2NPbFNPTjdvSDVNdkdMc3A3QmU5SUY4Ym92UVp6bkoyOTExVQp4Y2V3WWpMQVB6WU1qeDIvbmRrRHZIck4xWnllZEdkVGFEOVpSU3ZVbjBDMWJ4SiszT0t2eXAwcWVuYTNyV24xCjY0cnNLRlZQSFZvT1I3cEg1Z1ZmeXpRdTVZOVpUbTBQV1VuVnlaWjJJdEdoMkp5Njk5WTlWRDB4T0MvZTJmNHkKaUlhVlNMdlFDWVdpYzZaSFY2UlVOc2Z2dTJCd3dnYmNoNlZFTUNWNDBoZjBGUm5yZ2FoT2srZy8zSXkxZ28vaQp2azdKTFgycVhlTm5yYWxDRGxoZ0VmeldTWGZkTTVzaTltMDFkREpGNjdPa3FIQTdIMnl3akZSUjJsdXBDc2o3Cnk0ekttRjdDYmdiOTRBWi9QVERYenJHOGp6OW15a1MvWFFLQ0FRQlJLZERrN21Ya3F3YS9vTU9TYmFUMkdudUUKTTcwL1BkbzNFdzJGZXc3aTFOSk01SnRqRTgwMlZwNzFhV0pFYmFLbW8rcWlLYmtVb2xUK24vMXROZ3FwQ2lYOQpYeTJ3M1RkN3ZaMXNuTGhWd2pRVWU4MFU2RWZuN1lHN244eEE3L0xnbDE4L0F6d0JTQzZ2ck0wOXFJZ3MrWEpFCmpsNWtqbXZQMDVRcERwcGlOYndHUm1MUGdDWmhZTTZyUnF4QUt1RjVMdStxNFNkaXc4WSs2RWNQTXIxcFJwMWQKWEVyNmxnNVg1NnhuK1dGRjdOanlZWEcxL05tK3JDd3RnelM2K1pFcG9MT201aUhlR3RzUUZvSytXcXQrc25iSwpuS3B6RTdBWmoxRlJBbHBtb0VGWGg3WWFpczJRTUI5eE14ckpqZkdxMlRsckZHQ2IzaENrbWpDLzdVMjEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K 16 | -------------------------------------------------------------------------------- /internal/benchmark/k8s/v1.6.0/3.0_control_plane_configuration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: 3.0 Control Plane Configuration 7 | audit_tests: 8 | - name: 3.1.1 Client certificate authentication should not be used for users 9 | description: |- 10 | Kubernetes provides the option to use client certificates for user authentication. However as there is no way to revoke these certificates when a user leaves an organization or loses their credential, they are not suitable for this purpose. 11 | It is not possible to fully disable client certificate use within a cluster as it is used for component to component authentication. 12 | profile_applicability: Master 13 | audit: 14 | - ps -ef | grep kube-apiserver |grep ' --oidc-username-claim' | grep -o ' --oidc-username-claim=[^"]\S*' 15 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 16 | - awk -v FS="--oidc-username-claim=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-apiserver.yaml 17 | remediation: Alternative mechanisms provided by Kubernetes such as the use of 18 | OIDC should be implemented in place of client certificates. 19 | check_type: multi_param 20 | impact: External mechanisms for authentication generally require additional 21 | software to be deployed. 22 | eval_expr: "'${0}' != ''; && '${0}' == '${1}';" 23 | default_value: Client certificate authentication is enabled by default. 24 | references: 25 | - https://kubernetes.io/docs/reference/command-line-tools-reference/kube- scheduler/ 26 | - name: 3.2.1 Ensure that a minimal audit policy is created 27 | description: Kubernetes can audit the details of requests made to the API server. 28 | The --audit-policy- file flag must be set for this logging to be enabled. 29 | profile_applicability: Master 30 | audit: 31 | - ps -ef | grep kube-apiserver |grep ' --audit-policy-file' | grep -o ' --audit-policy-file=[^\"]\\S*' 32 | | awk -F \"=\" '{print $2}' |awk 'FNR <= 1' 33 | remediation: Create an audit policy file for your cluster. 34 | check_type: multi_param 35 | impact: Audit logs will be created on the master nodes, which will consume disk 36 | space. Care should be taken to avoid generating too large volumes of log information 37 | as this could impact the available of the cluster nodes. 38 | eval_expr: "'${0}' != '';" 39 | default_value: Unless the --audit-policy-file flag is specified, no auditing 40 | will be carried out. 41 | references: 42 | - https://kubernetes.io/docs/tasks/debug-application-cluster/audit/ 43 | - name: 3.2.2 Ensure that the audit policy covers key security concerns 44 | description: Ensure that the audit policy created for the cluster covers key 45 | security concerns. 46 | profile_applicability: Master 47 | audit: 48 | - ps -ef | grep kube-apiserver |grep ' --audit-policy-file' | grep -o ' --audit-policy-file=[^\"]\\S*' 49 | | awk -F \"=\" '{print $2}' |awk 'FNR <= 1' 50 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''secrets''' 51 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''configmaps''' 52 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''tokenreviews''' 53 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''pods/exec''' 54 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''pods/portforward''' 55 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''pods/proxy''' 56 | - 'grep -A10 ''level: Metadata'' ${0} | grep ''services/proxy''' 57 | remediation: |- 58 | Consider modification of the audit policy in use on the cluster to include these items, at a minimum.:Access to Secrets managed by the cluster. Care should be taken to only log Metadata for requests to Secrets, ConfigMaps, and TokenReviews, in order to avoid the risk of logging sensitive data. 59 | • Modification of pod and deployment objects. 60 | • Use of pods/exec, pods/portforward, pods/proxy and services/proxy. 61 | check_type: multi_param 62 | impact: Increasing audit logging will consume resources on the nodes or other 63 | log destination. 64 | eval_expr: "'${0}' != ''; && '${1}' != ''; && '${2}' != ''; && '${3}' != ''; && '${4}' 65 | != ''; && '${5}' != ''; && '${6}' != ''; && '${7}' != '';" 66 | default_value: By default Kubernetes clusters do not log audit information. 67 | references: 68 | - https://github.com/k8scop/k8s-security- dashboard/blob/master/configs/kubernetes/adv-audit.yaml 69 | - https://kubernetes.io/docs/tasks/debug-application-cluster/audit/#audit-policy 70 | - https://github.com/falcosecurity/falco/blob/master/examples/k8s_audit_config/audit-policy.yaml 71 | - https://github.com/kubernetes/kubernetes/blob/master/cluster/gce/gci/configure-helper.sh#L735 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Report Card](https://goreportcard.com/badge/github.com/chen-keinan/beacon)](https://goreportcard.com/report/github.com/chen-keinan/beacon) 2 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/chen-keinan/beacon/blob/main/LICENSE) 3 | [![Build Status](https://travis-ci.com/chen-keinan/kube-beacon.svg?branch=main)](https://travis-ci.com/chen-keinan/kube-beacon) 4 | [![Coverage Status](https://coveralls.io/repos/github/chen-keinan/kube-beacon/badge.svg?branch=main)](https://coveralls.io/github/chen-keinan/kube-beacon?branch=main) 5 | [![Gitter](https://badges.gitter.im/kube-beacon/community.svg)](https://gitter.im/kube-beacon/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 6 |
kube-beacon logo
7 | 8 | # Kube-Beacon Project 9 | ### Scan your kubernetes runtime !! 10 | Kube-Beacon is an open source audit scanner who perform audit check on a deployed kubernetes cluster and output a security report. 11 | 12 | The audit tests are the full implementation of [CIS Kubernetes Benchmark specification](https://www.cisecurity.org/benchmark/kubernetes/)
13 | 14 | NEW !! audit result now can be leveraged as webhook via user plugin(using go plugin) 15 | #### Audit checks are performed on master and worker nodes and the output audit report include : 16 | * root cause of the security issue 17 | * proposed remediation for security issue 18 | 19 | #### kubernetes cluster audit scan output: 20 | ![k8s audit](./pkg/images/beacon.gif) 21 | 22 | * [Installation](#installation) 23 | * [Quick Start](#quick-start) 24 | * [Kube-beacon as Docker](#Kube-beacon-as-Docker) 25 | * [Kube-beacon as pod in k8s](#Kube-beacon-as-pod-in-k8s) 26 | * [User Plugin Usage](#user-plugin-usage) 27 | * [Next steps](#Next-steps) 28 | 29 | 30 | 31 | ## Installation 32 | 33 | ```sh 34 | git clone https://github.com/chen-keinan/kube-beacon 35 | cd kube-beacon 36 | make build 37 | ``` 38 | 39 | - Note: kube-beacon require root user to be executed 40 | 41 | ## Quick Start 42 | 43 | Execute kube-eacon without any flags , execute all tests 44 | ``` 45 | ./kube-beacon 46 | 47 | ``` 48 | 49 | Execute kube-beacon with flags , execute test on demand 50 | 51 | ``` 52 | Usage: kube-Beacon [--version] [--help] [] 53 | 54 | Available commands are: 55 | -r , --report : run audit tests and generate failure report 56 | -i , --include: execute only specific audit test, example -i=1.2.3,1.4.5 57 | -e , --exclude: ignore specific audit tests, example -e=1.2.3,1.4.5 58 | -n , --node: execute audit tests on specific node, example -n=master,-n=worker 59 | -s , --spec: execute specific audit tests spec, example -s=gke, default=k8s 60 | -v , --version: execute specific audit tests spec version, example -v=1.1.0,default=1.6.0 61 | ``` 62 | 63 | Execute tests and generate failure tests report 64 | 65 | ``` 66 | ./kube-beacon -r 67 | ``` 68 | 69 | ## Kube-beacon as pod in k8s 70 | 71 | - Execute kube beacon as a pod in k8s cluster 72 | 73 | - Add cluster role binding with role=cluster-admin 74 | ``` 75 | kubectl create clusterrolebinding default-admin --clusterrole cluster-admin --serviceaccount=default:default 76 | ``` 77 | ``` 78 | cd jobs 79 | ``` 80 | - simple k8s cluster run following job 81 | 82 | ``` 83 | kubectl apply -f k8s.yaml 84 | ``` 85 | 86 | - gke cluster run the following job 87 | 88 | ``` 89 | kubectl apply -f gke.yaml 90 | ``` 91 | 92 | 93 | - Check k8s pod status 94 | ``` 95 | kubectl get pods --all-namespaces 96 | 97 | NAMESPACE NAME READY STATUS RESTARTS AGE 98 | default kube-beacon-sc8g9 0/1 Completed 0 111s 99 | kube-system event-exporter-gke-8489df9489-skcvv 2/2 Running 0 7m24s 100 | kube-system fluentd-gke-7d5sl 2/2 Running 0 7m6s 101 | kube-system fluentd-gke-f6q5d 2/2 Running 0 6m59s 102 | ``` 103 | 104 | - Check k8s pod audit output 105 | ``` 106 | kubectl logs kube-beacon-sc8g9 107 | ``` 108 | 109 | - cleanup (remove role and delete pod) 110 | ``` 111 | kubectl delete clusterrolebinding default-admin 112 | ``` 113 | ``` 114 | kubectl delete -f k8s.yaml 115 | ``` 116 | 117 | ## User Plugin Usage 118 | The Kube-Beacon expose hook for user plugins [Example](https://github.com/chen-keinan/kube-beacon/tree/master/examples/plugins) : 119 | - **K8sBenchAuditResultHook** - this hook accepts audit benchmark results as found by audit report 120 | 121 | ##### Compile user plugin 122 | ``` 123 | go build -buildmode=plugin -o=~//bench_plugin.so //bench_plugin.go 124 | ``` 125 | ##### Copy plugin to folder (.beacon folder is created on the 1st startup) 126 | ``` 127 | cp //bench_plugin.so ~/.beacon/plugins/compile/bench_plugin.so 128 | ``` 129 | Note: Plugin and binary must compile with the same linux env 130 | ## Next steps 131 | - Add support for Amazon EKS scanning 132 | 133 | -------------------------------------------------------------------------------- /pkg/utils/fileutil.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/internal/common" 6 | "io/ioutil" 7 | "os" 8 | "os/user" 9 | "path" 10 | "path/filepath" 11 | ) 12 | 13 | //PluginSourceSubFolder plugin source folder 14 | const PluginSourceSubFolder = "plugins/source" 15 | 16 | //CompilePluginSubFolder plugins complied folder 17 | const CompilePluginSubFolder = "plugins/compile" 18 | 19 | //FolderMgr defines the interface for kube-knark folder 20 | //fileutil.go 21 | //go:generate mockgen -destination=./mocks/mock_FolderMgr.go -package=mocks . FolderMgr 22 | type FolderMgr interface { 23 | CreateFolder(folderName string) error 24 | GetHomeFolder() (string, error) 25 | } 26 | 27 | //bFolder kube-beacon folder object 28 | type bFolder struct { 29 | } 30 | 31 | //NewKFolder return bFolder instance 32 | func NewKFolder() FolderMgr { 33 | return &bFolder{} 34 | } 35 | 36 | //CreateFolder create new kube beacon folder 37 | func (kf bFolder) CreateFolder(folderName string) error { 38 | _, err := os.Stat(folderName) 39 | if os.IsNotExist(err) { 40 | errDir := os.MkdirAll(folderName, 0750) 41 | if errDir != nil { 42 | return err 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | //GetHomeFolder return kube-beacon home folder 49 | func (kf bFolder) GetHomeFolder() (string, error) { 50 | usr, err := user.Current() 51 | if err != nil { 52 | return "", err 53 | } 54 | // User can set a custom KUBE_KNARK_HOME from environment variable 55 | usrHome := GetEnv(common.BeaconHomeEnvVar, usr.HomeDir) 56 | return path.Join(usrHome, ".beacon"), nil 57 | } 58 | 59 | //GetPluginSourceSubFolder return plugins source folder path 60 | func GetPluginSourceSubFolder(fm FolderMgr) (string, error) { 61 | folder, err := fm.GetHomeFolder() 62 | if err != nil { 63 | return "", err 64 | } 65 | return path.Join(folder, PluginSourceSubFolder), nil 66 | } 67 | 68 | //GetCompilePluginSubFolder return plugin compiled folder path 69 | func GetCompilePluginSubFolder(fm FolderMgr) (string, error) { 70 | folder, err := fm.GetHomeFolder() 71 | if err != nil { 72 | return "", err 73 | } 74 | return path.Join(folder, CompilePluginSubFolder), nil 75 | } 76 | 77 | //CreatePluginsCompiledFolderIfNotExist create plugins compiled folder if not exist 78 | func CreatePluginsCompiledFolderIfNotExist(fm FolderMgr) error { 79 | ebpfFolder, err := GetCompilePluginSubFolder(fm) 80 | if err != nil { 81 | return err 82 | } 83 | return fm.CreateFolder(ebpfFolder) 84 | } 85 | 86 | //CreatePluginsSourceFolderIfNotExist plugins source folder if not exist 87 | func CreatePluginsSourceFolderIfNotExist(fm FolderMgr) error { 88 | pluginfFolder, err := GetPluginSourceSubFolder(fm) 89 | if err != nil { 90 | return err 91 | } 92 | return fm.CreateFolder(pluginfFolder) 93 | } 94 | 95 | //GetHomeFolder return beacon home folder 96 | func GetHomeFolder() string { 97 | usr, err := user.Current() 98 | if err != nil { 99 | panic("Failed to fetch user home folder") 100 | } 101 | // User can set a custom BEACON_HOME from environment variable 102 | usrHome := GetEnv(common.BeaconHomeEnvVar, usr.HomeDir) 103 | return path.Join(usrHome, ".beacon") 104 | } 105 | 106 | //CreateHomeFolderIfNotExist create beacon home folder if not exist 107 | func CreateHomeFolderIfNotExist(fm FolderMgr) error { 108 | beaconFolder, err := fm.GetHomeFolder() 109 | if err != nil { 110 | return err 111 | } 112 | _, err = os.Stat(beaconFolder) 113 | if os.IsNotExist(err) { 114 | errDir := os.MkdirAll(beaconFolder, 0750) 115 | if errDir != nil { 116 | return fmt.Errorf("failed to create beacon home folder at %s", beaconFolder) 117 | } 118 | } 119 | return nil 120 | } 121 | 122 | //GetBenchmarkFolder return benchmark folder 123 | func GetBenchmarkFolder(spec, version string, fm FolderMgr) (string, error) { 124 | folder, err := fm.GetHomeFolder() 125 | if err != nil { 126 | return "", err 127 | } 128 | return filepath.Join(folder, fmt.Sprintf("benchmarks/%s/%s/", spec, version)), nil 129 | } 130 | 131 | //CreateBenchmarkFolderIfNotExist create beacon benchmark folder if not exist 132 | func CreateBenchmarkFolderIfNotExist(spec, version string, fm FolderMgr) error { 133 | benchmarkFolder, err := GetBenchmarkFolder(spec, version, fm) 134 | if err != nil { 135 | return err 136 | } 137 | return fm.CreateFolder(benchmarkFolder) 138 | } 139 | 140 | //GetK8sBenchAuditFiles return k8s benchmark file 141 | func GetK8sBenchAuditFiles(spec, version string, fm FolderMgr) ([]FilesInfo, error) { 142 | filesData := make([]FilesInfo, 0) 143 | folder, err := GetBenchmarkFolder(spec, version, fm) 144 | if err != nil { 145 | return filesData, err 146 | } 147 | filesInfo, err := ioutil.ReadDir(filepath.Join(folder)) 148 | if err != nil { 149 | return nil, err 150 | } 151 | for _, fileInfo := range filesInfo { 152 | filePath := filepath.Join(folder, filepath.Clean(fileInfo.Name())) 153 | fData, err := ioutil.ReadFile(filepath.Clean(filePath)) 154 | if err != nil { 155 | return nil, err 156 | } 157 | filesData = append(filesData, FilesInfo{fileInfo.Name(), string(fData)}) 158 | } 159 | return filesData, nil 160 | } 161 | 162 | //FilesInfo file data 163 | type FilesInfo struct { 164 | Name string 165 | Data string 166 | } 167 | 168 | //GetEnv Get Environment Variable value or return default 169 | func GetEnv(key, fallback string) string { 170 | if value, ok := os.LookupEnv(key); ok { 171 | return value 172 | } 173 | return fallback 174 | } 175 | -------------------------------------------------------------------------------- /internal/cli/commands/command-helper.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/internal/common" 6 | "github.com/chen-keinan/beacon/internal/logger" 7 | "github.com/chen-keinan/beacon/internal/models" 8 | "github.com/chen-keinan/beacon/pkg/filters" 9 | "github.com/chen-keinan/beacon/pkg/utils" 10 | "github.com/chen-keinan/beacon/ui" 11 | "github.com/mitchellh/colorstring" 12 | "gopkg.in/yaml.v2" 13 | "strings" 14 | ) 15 | 16 | func printTestResults(at []*models.AuditBench, log *logger.BLogger) models.AuditTestTotals { 17 | var ( 18 | warnCounter int 19 | passCounter int 20 | failCounter int 21 | ) 22 | for _, a := range at { 23 | if a.NonApplicable { 24 | warnTest := colorstring.Color("[yellow][Warn]") 25 | log.Console(fmt.Sprintf("%s %s\n", warnTest, a.Name)) 26 | warnCounter++ 27 | continue 28 | } 29 | if a.TestSucceed { 30 | passTest := colorstring.Color("[green][Pass]") 31 | log.Console(fmt.Sprintf("%s %s\n", passTest, a.Name)) 32 | passCounter++ 33 | } else { 34 | failTest := colorstring.Color("[red][Fail]") 35 | log.Console(fmt.Sprintf("%s %s\n", failTest, a.Name)) 36 | failCounter++ 37 | } 38 | } 39 | return models.AuditTestTotals{Fail: failCounter, Pass: passCounter, Warn: warnCounter} 40 | } 41 | 42 | //AddFailedMessages add failed audit test to report data 43 | func AddFailedMessages(at *models.AuditBench, match bool) []*models.AuditBench { 44 | av := make([]*models.AuditBench, 0) 45 | at.TestSucceed = match 46 | if !match || at.NonApplicable { 47 | av = append(av, at) 48 | } 49 | return av 50 | } 51 | 52 | //AddAllMessages add all audit test to report data 53 | func AddAllMessages(at *models.AuditBench, match bool) []*models.AuditBench { 54 | av := make([]*models.AuditBench, 0) 55 | at.TestSucceed = match 56 | av = append(av, at) 57 | return av 58 | } 59 | 60 | //TestLoader load tests from filesystem 61 | //command-helper.go 62 | //go:generate mockgen -destination=../../mocks/mock_TestLoader.go -package=mocks . TestLoader 63 | type TestLoader interface { 64 | LoadAuditTests(fi []utils.FilesInfo) []*models.SubCategory 65 | } 66 | 67 | //AuditTestLoader object 68 | type AuditTestLoader struct { 69 | } 70 | 71 | //NewFileLoader create new file loader 72 | func NewFileLoader() TestLoader { 73 | return &AuditTestLoader{} 74 | } 75 | 76 | //LoadAuditTests load audit test from benchmark folder 77 | func (tl AuditTestLoader) LoadAuditTests(auditFiles []utils.FilesInfo) []*models.SubCategory { 78 | auditTests := make([]*models.SubCategory, 0) 79 | audit := models.Audit{} 80 | for _, auditFile := range auditFiles { 81 | err := yaml.Unmarshal([]byte(auditFile.Data), &audit) 82 | if err != nil { 83 | panic("Failed to unmarshal audit test yaml file") 84 | } 85 | auditTests = append(auditTests, audit.Categories[0].SubCategory) 86 | } 87 | return auditTests 88 | } 89 | 90 | //FilterAuditTests filter audit tests by predicate chain 91 | func FilterAuditTests(predicates []filters.Predicate, predicateParams []string, at *models.SubCategory) *models.SubCategory { 92 | return RunPredicateChain(predicates, predicateParams, len(predicates), at) 93 | } 94 | 95 | //RunPredicateChain call every predicate in chain and filter tests 96 | func RunPredicateChain(predicates []filters.Predicate, predicateParams []string, size int, at *models.SubCategory) *models.SubCategory { 97 | if size == 0 { 98 | return at 99 | } 100 | return RunPredicateChain(predicates[1:size], predicateParams[1:size], size-1, predicates[size-1](at, predicateParams[size-1])) 101 | } 102 | 103 | // check weather are exist in array of specificTests 104 | func isArgsExist(args []string, name string) bool { 105 | for _, n := range args { 106 | if n == name { 107 | return true 108 | } 109 | } 110 | return false 111 | } 112 | 113 | //GetResultProcessingFunction return processing function by specificTests 114 | func GetResultProcessingFunction(args []string) ResultProcessor { 115 | if isArgsExist(args, common.Report) || isArgsExist(args, common.ReportFull) { 116 | return reportResultProcessor 117 | } 118 | return simpleResultProcessor 119 | } 120 | 121 | //getOutPutGeneratorFunction return output generator function 122 | func getOutputGeneratorFunction(args []string) ui.OutputGenerator { 123 | if isArgsExist(args, common.Report) || isArgsExist(args, common.ReportFull) { 124 | return ReportOutputGenerator 125 | } 126 | return ConsoleOutputGenerator 127 | } 128 | 129 | //buildPredicateChain build chain of filters based on command criteria 130 | func buildPredicateChain(args []string) []filters.Predicate { 131 | pc := make([]filters.Predicate, 0) 132 | for _, n := range args { 133 | switch { 134 | case strings.HasPrefix(n, common.IncludeParam): 135 | pc = append(pc, filters.IncludeAudit) 136 | case strings.HasPrefix(n, common.ExcludeParam): 137 | pc = append(pc, filters.ExcludeAudit) 138 | case strings.HasPrefix(n, common.NodeParam): 139 | pc = append(pc, filters.NodeAudit) 140 | case n == "a": 141 | pc = append(pc, filters.Basic) 142 | } 143 | } 144 | return pc 145 | } 146 | 147 | //buildPredicateParams build chain of filters params based on command criteria 148 | func buildPredicateChainParams(args []string) []string { 149 | pp := make([]string, 0) 150 | pp = append(pp, args...) 151 | return pp 152 | } 153 | 154 | func filteredAuditBenchTests(auditTests []*models.SubCategory, pc []filters.Predicate, pp []string) []*models.SubCategory { 155 | ft := make([]*models.SubCategory, 0) 156 | for _, adt := range auditTests { 157 | filteredAudit := FilterAuditTests(pc, pp, adt) 158 | if len(filteredAudit.AuditTests) == 0 { 159 | continue 160 | } 161 | ft = append(ft, filteredAudit) 162 | } 163 | return ft 164 | } 165 | 166 | func executeTests(ft []*models.SubCategory, execTestFunc func(ad *models.AuditBench) []*models.AuditBench, log *logger.BLogger) []*models.SubCategory { 167 | completedTest := make([]*models.SubCategory, 0) 168 | log.Console(ui.K8sAuditTest) 169 | for _, f := range ft { 170 | tr := ui.ShowProgressBar(f, execTestFunc, log) 171 | completedTest = append(completedTest, tr) 172 | } 173 | return completedTest 174 | } 175 | -------------------------------------------------------------------------------- /internal/cli/commands/k8s-audit.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/internal/logger" 6 | "github.com/chen-keinan/beacon/internal/models" 7 | "github.com/chen-keinan/beacon/internal/reports" 8 | "github.com/chen-keinan/beacon/internal/startup" 9 | "github.com/chen-keinan/beacon/pkg/filters" 10 | m2 "github.com/chen-keinan/beacon/pkg/models" 11 | "github.com/chen-keinan/beacon/pkg/utils" 12 | "github.com/chen-keinan/beacon/ui" 13 | "github.com/chen-keinan/go-command-eval/eval" 14 | "github.com/mitchellh/colorstring" 15 | ) 16 | 17 | //K8sAudit k8s benchmark object 18 | type K8sAudit struct { 19 | ResultProcessor ResultProcessor 20 | OutputGenerator ui.OutputGenerator 21 | FileLoader TestLoader 22 | PredicateChain []filters.Predicate 23 | PredicateParams []string 24 | PlChan chan m2.KubeAuditResults 25 | CompletedChan chan bool 26 | FilesInfo []utils.FilesInfo 27 | Evaluator eval.CmdEvaluator 28 | Log *logger.BLogger 29 | } 30 | 31 | // ResultProcessor process audit results 32 | type ResultProcessor func(at *models.AuditBench, match bool) []*models.AuditBench 33 | 34 | // ConsoleOutputGenerator print audit tests to stdout 35 | var ConsoleOutputGenerator ui.OutputGenerator = func(at []*models.SubCategory, log *logger.BLogger) { 36 | grandTotal := make([]models.AuditTestTotals, 0) 37 | for _, a := range at { 38 | log.Console(fmt.Sprintf("%s %s\n", "[Category]", a.Name)) 39 | categoryTotal := printTestResults(a.AuditTests, log) 40 | grandTotal = append(grandTotal, categoryTotal) 41 | } 42 | log.Console(printFinalResults(grandTotal)) 43 | } 44 | 45 | func printFinalResults(grandTotal []models.AuditTestTotals) string { 46 | finalTotal := calculateFinalTotal(grandTotal) 47 | passTest := colorstring.Color("[green]Pass:") 48 | failTest := colorstring.Color("[red]Fail:") 49 | warnTest := colorstring.Color("[yellow]Warn:") 50 | title := colorstring.Color("[blue]Test Result Total") 51 | return fmt.Sprintf("%s %s %d , %s %d , %s %d ", title, passTest, finalTotal.Pass, warnTest, finalTotal.Warn, failTest, finalTotal.Fail) 52 | } 53 | 54 | func calculateFinalTotal(granTotal []models.AuditTestTotals) models.AuditTestTotals { 55 | var ( 56 | warn int 57 | fail int 58 | pass int 59 | ) 60 | for _, total := range granTotal { 61 | warn = warn + total.Warn 62 | fail = fail + total.Fail 63 | pass = pass + total.Pass 64 | } 65 | return models.AuditTestTotals{Pass: pass, Fail: fail, Warn: warn} 66 | } 67 | 68 | // ReportOutputGenerator print failed audit test to human report 69 | var ReportOutputGenerator ui.OutputGenerator = func(at []*models.SubCategory, log *logger.BLogger) { 70 | for _, a := range at { 71 | log.Table(reports.GenerateAuditReport(a.AuditTests)) 72 | } 73 | } 74 | 75 | // simpleResultProcessor process audit results to stdout print only 76 | var simpleResultProcessor ResultProcessor = func(at *models.AuditBench, match bool) []*models.AuditBench { 77 | return AddAllMessages(at, match) 78 | } 79 | 80 | // ResultProcessor process audit results to std out and failure results 81 | var reportResultProcessor ResultProcessor = func(at *models.AuditBench, match bool) []*models.AuditBench { 82 | // append failed messages 83 | return AddFailedMessages(at, match) 84 | } 85 | 86 | //CmdEvaluator interface expose one method to evaluate command with evalExpr 87 | //k8s-audit.go 88 | //go:generate mockgen -destination=../mocks/mock_CmdEvaluator.go -package=mocks . CmdEvaluator 89 | type CmdEvaluator interface { 90 | EvalCommand(commands []string, evalExpr string) eval.CmdEvalResult 91 | } 92 | 93 | //NewK8sAudit new audit object 94 | func NewK8sAudit(filters []string, plChan chan m2.KubeAuditResults, completedChan chan bool, fi []utils.FilesInfo, log *logger.BLogger, evaluator CmdEvaluator) *K8sAudit { 95 | return &K8sAudit{ 96 | PredicateChain: buildPredicateChain(filters), 97 | PredicateParams: buildPredicateChainParams(filters), 98 | ResultProcessor: GetResultProcessingFunction(filters), 99 | OutputGenerator: getOutputGeneratorFunction(filters), 100 | FileLoader: NewFileLoader(), 101 | PlChan: plChan, 102 | FilesInfo: fi, 103 | Evaluator: evaluator, 104 | Log: log, 105 | CompletedChan: completedChan} 106 | } 107 | 108 | //Help return benchmark command help 109 | func (bk K8sAudit) Help() string { 110 | return startup.GetHelpSynopsis() 111 | } 112 | 113 | //Run execute the full k8s benchmark 114 | func (bk *K8sAudit) Run(args []string) int { 115 | // load audit tests fro benchmark folder 116 | auditTests := bk.FileLoader.LoadAuditTests(bk.FilesInfo) 117 | // filter tests by cmd criteria 118 | ft := filteredAuditBenchTests(auditTests, bk.PredicateChain, bk.PredicateParams) 119 | //execute audit tests and show it in progress bar 120 | completedTest := executeTests(ft, bk.runAuditTest, bk.Log) 121 | // generate output data 122 | ui.PrintOutput(completedTest, bk.OutputGenerator, bk.Log) 123 | // send test results to plugin 124 | sendResultToPlugin(bk.PlChan, bk.CompletedChan, completedTest) 125 | return 0 126 | } 127 | 128 | func sendResultToPlugin(plChan chan m2.KubeAuditResults, completedChan chan bool, auditTests []*models.SubCategory) { 129 | ka := m2.KubeAuditResults{BenchmarkType: "k8s", Categories: make([]m2.AuditBenchResult, 0)} 130 | for _, at := range auditTests { 131 | for _, ab := range at.AuditTests { 132 | var testResult = "FAIL" 133 | if ab.TestSucceed { 134 | testResult = "PASS" 135 | } 136 | abr := m2.AuditBenchResult{Category: at.Name, ProfileApplicability: ab.ProfileApplicability, Description: ab.Description, AuditCommand: ab.AuditCommand, Remediation: ab.Remediation, Impact: ab.Impact, DefaultValue: ab.DefaultValue, References: ab.References, TestResult: testResult} 137 | ka.Categories = append(ka.Categories, abr) 138 | } 139 | } 140 | plChan <- ka 141 | <-completedChan 142 | } 143 | 144 | // runAuditTest execute category of audit tests 145 | func (bk *K8sAudit) runAuditTest(at *models.AuditBench) []*models.AuditBench { 146 | auditRes := make([]*models.AuditBench, 0) 147 | if at.NonApplicable { 148 | auditRes = append(auditRes, at) 149 | return auditRes 150 | } 151 | // execute audit test command 152 | cmdEvalResult := bk.Evaluator.EvalCommand(at.AuditCommand, at.EvalExpr) 153 | // continue with result processing 154 | auditRes = append(auditRes, bk.ResultProcessor(at, cmdEvalResult.Match)...) 155 | return auditRes 156 | } 157 | 158 | //Synopsis for help 159 | func (bk *K8sAudit) Synopsis() string { 160 | return bk.Help() 161 | } 162 | -------------------------------------------------------------------------------- /internal/cli/commands/k8s-audit_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | m3 "github.com/chen-keinan/beacon/internal/cli/mocks" 6 | "github.com/chen-keinan/beacon/internal/logger" 7 | "github.com/chen-keinan/beacon/internal/models" 8 | m2 "github.com/chen-keinan/beacon/pkg/models" 9 | "github.com/chen-keinan/beacon/pkg/utils" 10 | "github.com/chen-keinan/go-command-eval/eval" 11 | "github.com/golang/mock/gomock" 12 | "github.com/stretchr/testify/assert" 13 | "gopkg.in/yaml.v2" 14 | "io/ioutil" 15 | "os" 16 | "testing" 17 | ) 18 | 19 | func TestRunAuditTests(t *testing.T) { 20 | tests := []struct { 21 | name string 22 | testFile string 23 | completedChan chan bool 24 | plChan chan m2.KubeAuditResults 25 | wantTestSucceeded bool 26 | }{ 27 | 28 | {name: "Test_MultiCommandParams_OK", testFile: "CheckMultiParamOK.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: true}, 29 | {name: "Test_MultiCommandParams_OK_With_IN", testFile: "CheckMultiParamOKWithIN.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: true}, 30 | {name: "Test_MultiCommandParams_NOKWith_IN", testFile: "CheckMultiParamNOKWithIN.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: false}, 31 | {name: "Test_MultiCommandParamsPass1stResultToNext", testFile: "CheckMultiParamPass1stResultToNext.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: false}, 32 | {name: "Test_MultiCommandParamsComplex", testFile: "CheckMultiParamComplex.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: true}, 33 | {name: "Test_MultiCommandParamsComplexOppositeEmptyReturn", testFile: "CheckInClauseOppositeEmptyReturn.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: false}, 34 | {name: "Test_MultiCommandParamsComplexOppositeWithNumber", testFile: "CheckInClauseOppositeWithNum.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: false}, 35 | {name: "Test_MultiCommand4_2_13", testFile: "CheckInClause4.2.13.yml", completedChan: make(chan bool), plChan: make(chan m2.KubeAuditResults), wantTestSucceeded: false}, 36 | } 37 | for _, tt := range tests { 38 | t.Run(tt.name, func(t *testing.T) { 39 | ab := models.Audit{} 40 | err := yaml.Unmarshal(readTestData(tt.testFile, t), &ab) 41 | if err != nil { 42 | t.Errorf("failed to Unmarshal test file %s error : %s", tt.testFile, err.Error()) 43 | } 44 | ctrl := gomock.NewController(t) 45 | defer ctrl.Finish() 46 | evalCmd := m3.NewMockCmdEvaluator(ctrl) 47 | testBench := ab.Categories[0].SubCategory.AuditTests[0] 48 | evalCmd.EXPECT().EvalCommand(testBench.AuditCommand, testBench.EvalExpr).Return(eval.CmdEvalResult{Match: tt.wantTestSucceeded, Error: nil}).Times(1) 49 | kb := K8sAudit{Evaluator: evalCmd, ResultProcessor: GetResultProcessingFunction([]string{}), PlChan: tt.plChan, CompletedChan: tt.completedChan} 50 | kb.runAuditTest(ab.Categories[0].SubCategory.AuditTests[0]) 51 | assert.Equal(t, ab.Categories[0].SubCategory.AuditTests[0].TestSucceed, tt.wantTestSucceeded) 52 | go func() { 53 | <-tt.plChan 54 | tt.completedChan <- true 55 | }() 56 | }) 57 | } 58 | } 59 | 60 | func readTestData(fileName string, t *testing.T) []byte { 61 | f, err := os.Open(fmt.Sprintf("./fixtures/%s", fileName)) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | b, err := ioutil.ReadAll(f) 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | f.Close() 70 | return b 71 | } 72 | 73 | //Test_NewK8sAudit test 74 | func Test_NewK8sAudit(t *testing.T) { 75 | args := []string{"a", "i=1.2.3"} 76 | completedChan := make(chan bool) 77 | plChan := make(chan m2.KubeAuditResults) 78 | evaluator := eval.NewEvalCmd() 79 | ka := NewK8sAudit(args, plChan, completedChan, []utils.FilesInfo{}, logger.GetLog(), evaluator) 80 | assert.True(t, len(ka.PredicateParams) == 2) 81 | assert.True(t, len(ka.PredicateChain) == 2) 82 | assert.True(t, ka.ResultProcessor != nil) 83 | go func() { 84 | <-plChan 85 | completedChan <- true 86 | }() 87 | } 88 | 89 | //Test_Help test 90 | func Test_Help(t *testing.T) { 91 | args := []string{"a", "i=1.2.3"} 92 | completedChan := make(chan bool) 93 | plChan := make(chan m2.KubeAuditResults) 94 | evaluator := eval.NewEvalCmd() 95 | ka := NewK8sAudit(args, plChan, completedChan, []utils.FilesInfo{}, logger.GetLog(), evaluator) 96 | help := ka.Help() 97 | assert.True(t, len(help) > 0) 98 | go func() { 99 | <-plChan 100 | completedChan <- true 101 | }() 102 | } 103 | 104 | //Test_reportResultProcessor test 105 | func Test_reportResultProcessor(t *testing.T) { 106 | ad := &models.AuditBench{Name: "1.2.1 aaa"} 107 | fm := reportResultProcessor(ad, true) 108 | assert.True(t, len(fm) == 0) 109 | fm = reportResultProcessor(ad, false) 110 | assert.True(t, len(fm) == 1) 111 | assert.Equal(t, fm[0].Name, "1.2.1 aaa") 112 | } 113 | 114 | //Test_K8sSynopsis test 115 | func Test_K8sSynopsis(t *testing.T) { 116 | args := []string{"a", "i=1.2.3"} 117 | completedChan := make(chan bool) 118 | plChan := make(chan m2.KubeAuditResults) 119 | evaluator := eval.NewEvalCmd() 120 | ka := NewK8sAudit(args, plChan, completedChan, []utils.FilesInfo{}, logger.GetLog(), evaluator) 121 | s := ka.Synopsis() 122 | assert.True(t, len(s) > 0) 123 | go func() { 124 | <-plChan 125 | completedChan <- true 126 | }() 127 | } 128 | 129 | func Test_sendResultToPlugin(t *testing.T) { 130 | pChan := make(chan m2.KubeAuditResults) 131 | cChan := make(chan bool) 132 | auditTests := make([]*models.SubCategory, 0) 133 | ab := make([]*models.AuditBench, 0) 134 | ats := &models.AuditBench{Name: "bbb", TestSucceed: true} 135 | atf := &models.AuditBench{Name: "ccc", TestSucceed: false} 136 | ab = append(ab, ats) 137 | ab = append(ab, atf) 138 | mst := &models.SubCategory{Name: "aaa", AuditTests: ab} 139 | auditTests = append(auditTests, mst) 140 | go func() { 141 | <-pChan 142 | cChan <- true 143 | }() 144 | sendResultToPlugin(pChan, cChan, auditTests) 145 | 146 | } 147 | func Test_calculateFinalTotal(t *testing.T) { 148 | att := make([]models.AuditTestTotals, 0) 149 | atOne := models.AuditTestTotals{Fail: 2, Pass: 3, Warn: 1} 150 | atTwo := models.AuditTestTotals{Fail: 1, Pass: 5, Warn: 7} 151 | att = append(att, atOne) 152 | att = append(att, atTwo) 153 | res := calculateFinalTotal(att) 154 | assert.Equal(t, res.Warn, 8) 155 | assert.Equal(t, res.Pass, 8) 156 | assert.Equal(t, res.Fail, 3) 157 | str := printFinalResults([]models.AuditTestTotals{res}) 158 | assert.Equal(t, str, "\u001B[34mTest Result Total\u001B[0m \u001B[32mPass:\u001B[0m 8 , \u001B[33mWarn:\u001B[0m 8 , \u001B[31mFail:\u001B[0m 3 ") 159 | } 160 | -------------------------------------------------------------------------------- /internal/cli/commands/command-helper_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | m3 "github.com/chen-keinan/beacon/internal/cli/mocks" 5 | "github.com/chen-keinan/beacon/internal/logger" 6 | "github.com/chen-keinan/beacon/internal/models" 7 | "github.com/chen-keinan/beacon/internal/startup" 8 | "github.com/chen-keinan/beacon/pkg/filters" 9 | m2 "github.com/chen-keinan/beacon/pkg/models" 10 | "github.com/chen-keinan/beacon/pkg/utils" 11 | "github.com/chen-keinan/go-command-eval/eval" 12 | "github.com/golang/mock/gomock" 13 | "github.com/stretchr/testify/assert" 14 | "os" 15 | "reflect" 16 | "runtime" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | //Test_AddFailedMessages text 22 | func Test_AddFailedMessages(t *testing.T) { 23 | atb1 := &models.AuditBench{TestSucceed: false} 24 | afm := AddFailedMessages(atb1, false) 25 | assert.True(t, len(afm) == 1) 26 | atb2 := &models.AuditBench{TestSucceed: true} 27 | afm = AddFailedMessages(atb2, true) 28 | assert.True(t, len(afm) == 0) 29 | } 30 | 31 | //Test_isArgsExist 32 | func Test_isArgsExist(t *testing.T) { 33 | args := []string{"aaa", "bbb"} 34 | exist := isArgsExist(args, "aaa") 35 | assert.True(t, exist) 36 | exist = isArgsExist(args, "ccc") 37 | assert.False(t, exist) 38 | } 39 | 40 | //Test_isArgsExist 41 | func Test_GetProcessingFunction(t *testing.T) { 42 | args := []string{"r"} 43 | a := GetResultProcessingFunction(args) 44 | name := GetFunctionName(a) 45 | assert.True(t, strings.Contains(name, "commands.glob..func4")) 46 | args = []string{} 47 | a = GetResultProcessingFunction(args) 48 | name = GetFunctionName(a) 49 | assert.True(t, strings.Contains(name, "commands.glob..func3")) 50 | } 51 | 52 | func GetFunctionName(i interface{}) string { 53 | return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() 54 | } 55 | 56 | //Test_getSpecificTestsToExecute test 57 | func Test_getSpecificTestsToExecute(t *testing.T) { 58 | test := utils.GetAuditTestsList("i", "i=1.2.4,1.2.5") 59 | assert.Equal(t, test[0], "1.2.4") 60 | assert.Equal(t, test[1], "1.2.5") 61 | } 62 | 63 | //Test_LoadAuditTest test 64 | func Test_LoadAuditTest(t *testing.T) { 65 | fm := utils.NewKFolder() 66 | folder, err2 := utils.GetBenchmarkFolder("k8s", "v1.6.0", fm) 67 | assert.NoError(t, err2) 68 | err := os.RemoveAll(folder) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | err = utils.CreateHomeFolderIfNotExist(fm) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | err = utils.CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | bFiles, err := startup.GenerateK8sBenchmarkFiles() 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | err = startup.SaveBenchmarkFilesIfNotExist("k8s", "v1.6.0", bFiles) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | at := NewFileLoader().LoadAuditTests(bFiles) 89 | assert.True(t, len(at) != 0) 90 | assert.True(t, strings.Contains(at[0].AuditTests[0].Name, "1.1.1")) 91 | } 92 | 93 | //Test_LoadGkeAuditTest test 94 | func Test_LoadGkeAuditTest(t *testing.T) { 95 | fm := utils.NewKFolder() 96 | folder, err2 := utils.GetBenchmarkFolder("gke", "v1.1.0", fm) 97 | assert.NoError(t, err2) 98 | err := os.RemoveAll(folder) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | err = utils.CreateHomeFolderIfNotExist(fm) 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | err = utils.CreateBenchmarkFolderIfNotExist("gke", "v1.1.0", fm) 107 | if err != nil { 108 | t.Fatal(err) 109 | } 110 | bFiles, err := startup.GenerateK8sBenchmarkFiles() 111 | if err != nil { 112 | t.Fatal(err) 113 | } 114 | err = startup.SaveBenchmarkFilesIfNotExist("gke", "v1.1.0", bFiles) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | at := NewFileLoader().LoadAuditTests(bFiles) 119 | assert.True(t, len(at) != 0) 120 | assert.True(t, strings.Contains(at[0].AuditTests[0].Name, "1.1.1")) 121 | } 122 | 123 | //Test_FilterAuditTests test 124 | func Test_FilterAuditTests(t *testing.T) { 125 | at := &models.SubCategory{AuditTests: []*models.AuditBench{{Name: "1.2.1 aaa"}, {Name: "2.2.2"}}} 126 | fab := FilterAuditTests([]filters.Predicate{filters.IncludeAudit}, []string{"1.2.1"}, at) 127 | assert.Equal(t, fab.AuditTests[0].Name, "1.2.1 aaa") 128 | assert.True(t, len(fab.AuditTests) == 1) 129 | } 130 | 131 | //Test_buildPredicateChain test 132 | func Test_buildPredicateChain(t *testing.T) { 133 | fab := buildPredicateChain([]string{"a", "i=1.2.1"}) 134 | assert.True(t, len(fab) == 2) 135 | fab = buildPredicateChain([]string{"a"}) 136 | assert.True(t, len(fab) == 1) 137 | fab = buildPredicateChain([]string{"i=1.2.1"}) 138 | assert.True(t, len(fab) == 1) 139 | } 140 | 141 | //Test_buildPredicateChainParams test 142 | func Test_buildPredicateChainParams(t *testing.T) { 143 | p := buildPredicateChainParams([]string{"a", "i=1.2.1"}) 144 | assert.True(t, len(p) == 2) 145 | assert.Equal(t, p[0], "a") 146 | assert.Equal(t, p[1], "i=1.2.1") 147 | } 148 | 149 | //Test_buildPredicateChainParamsExcludeNode test 150 | func Test_buildPredicateChainExcludeNode(t *testing.T) { 151 | p := buildPredicateChainParams([]string{"a", "n=master"}) 152 | assert.True(t, len(p) == 2) 153 | assert.Equal(t, p[0], "a") 154 | assert.Equal(t, p[1], "n=master") 155 | p = buildPredicateChainParams([]string{"a", "e=1.2.1"}) 156 | assert.True(t, len(p) == 2) 157 | assert.Equal(t, p[0], "a") 158 | assert.Equal(t, p[1], "e=1.2.1") 159 | } 160 | 161 | func Test_filteredAuditBenchTests(t *testing.T) { 162 | asc := []*models.SubCategory{{AuditTests: []*models.AuditBench{{Name: "1.1.0 bbb"}}}} 163 | fp := []filters.Predicate{filters.IncludeAudit, filters.ExcludeAudit} 164 | st := []string{"i=1.1.0", "e=1.1.0"} 165 | fr := filteredAuditBenchTests(asc, fp, st) 166 | assert.True(t, len(fr) == 0) 167 | } 168 | 169 | //Test_executeTests test 170 | func Test_executeTests(t *testing.T) { 171 | ab := &models.AuditBench{} 172 | ab.AuditCommand = []string{"aaa", "bbb"} 173 | ab.EvalExpr = "'${0}' == ''; && '${1}' == '';" 174 | ab.CommandParams = map[int][]string{} 175 | ctrl := gomock.NewController(t) 176 | defer ctrl.Finish() 177 | evalcmd := m3.NewMockCmdEvaluator(ctrl) 178 | evalcmd.EXPECT().EvalCommand([]string{"aaa", "bbb"}, ab.EvalExpr).Return(eval.CmdEvalResult{Match: false, CmdEvalExpr: ab.EvalExpr, Error: nil}) 179 | completedChan := make(chan bool) 180 | plChan := make(chan m2.KubeAuditResults) 181 | kb := K8sAudit{Evaluator: evalcmd, ResultProcessor: GetResultProcessingFunction([]string{}), PlChan: plChan, CompletedChan: completedChan} 182 | sc := []*models.SubCategory{{AuditTests: []*models.AuditBench{ab}}} 183 | executeTests(sc, kb.runAuditTest, logger.GetLog()) 184 | assert.False(t, ab.TestSucceed) 185 | go func() { 186 | <-plChan 187 | completedChan <- true 188 | }() 189 | } 190 | 191 | func Test_printTestResults(t *testing.T) { 192 | ab := make([]*models.AuditBench, 0) 193 | ats := &models.AuditBench{Name: "bbb", TestSucceed: true} 194 | atf := &models.AuditBench{Name: "ccc", TestSucceed: false} 195 | ata := &models.AuditBench{Name: "ddd", NonApplicable: true} 196 | ab = append(ab, ats) 197 | ab = append(ab, atf) 198 | ab = append(ab, ata) 199 | tr := printTestResults(ab, logger.GetLog()) 200 | assert.Equal(t, tr.Warn, 1) 201 | assert.Equal(t, tr.Pass, 1) 202 | assert.Equal(t, tr.Fail, 1) 203 | } 204 | -------------------------------------------------------------------------------- /pkg/utils/fileutil_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/chen-keinan/beacon/internal/common" 6 | "github.com/chen-keinan/beacon/pkg/utils/mocks" 7 | "github.com/golang/mock/gomock" 8 | "github.com/stretchr/testify/assert" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | //Test_GetHomeFolder test 16 | func Test_GetHomeFolder(t *testing.T) { 17 | a := GetHomeFolder() 18 | assert.True(t, strings.HasSuffix(a, ".beacon")) 19 | } 20 | 21 | //Test_CreateHomeFolderIfNotExist test 22 | func Test_CreateHomeFolderIfNotExist(t *testing.T) { 23 | fm := NewKFolder() 24 | err := CreateHomeFolderIfNotExist(fm) 25 | assert.NoError(t, err) 26 | _, err = os.Stat(GetHomeFolder()) 27 | if os.IsNotExist(err) { 28 | t.Fatal() 29 | } 30 | err = os.RemoveAll(GetHomeFolder()) 31 | assert.NoError(t, err) 32 | } 33 | 34 | //Test_GetBenchmarkFolder test 35 | func Test_GetBenchmarkFolder(t *testing.T) { 36 | fm := NewKFolder() 37 | a, err := GetBenchmarkFolder("k8s", "v1.6.0", fm) 38 | assert.NoError(t, err) 39 | assert.True(t, strings.HasSuffix(a, ".beacon/benchmarks/k8s/v1.6.0")) 40 | } 41 | 42 | //Test_CreateBenchmarkFolderIfNotExist test 43 | func Test_CreateBenchmarkFolderIfNotExist(t *testing.T) { 44 | fm := NewKFolder() 45 | err := CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 46 | assert.NoError(t, err) 47 | folder, err := GetBenchmarkFolder("k8s", "v1.6.0", fm) 48 | assert.NoError(t, err) 49 | _, err = os.Stat(folder) 50 | if os.IsNotExist(err) { 51 | t.Fatal() 52 | } 53 | err = os.RemoveAll(folder) 54 | assert.NoError(t, err) 55 | } 56 | 57 | //Test_GetK8sBenchAuditFiles test 58 | func Test_GetK8sBenchAuditFiles(t *testing.T) { 59 | fm := NewKFolder() 60 | err := CreateHomeFolderIfNotExist(fm) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | err = CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | err = saveFilesIfNotExist([]FilesInfo{{Name: "aaa", Data: "bbb"}, {Name: "ddd", Data: "ccc"}}) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | f, err := GetK8sBenchAuditFiles("k8s", "v1.6.0", fm) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | folder, err := GetBenchmarkFolder("k8s", "v1.6.0", fm) 77 | assert.NoError(t, err) 78 | err = os.RemoveAll(folder) 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | assert.Equal(t, f[0].Name, "aaa") 83 | assert.Equal(t, f[1].Name, "ddd") 84 | 85 | } 86 | 87 | //Test_GetK8sBenchAuditNoFolder test 88 | func Test_GetK8sBenchAuditNoFolder(t *testing.T) { 89 | fm := NewKFolder() 90 | _, err := GetK8sBenchAuditFiles("k8s", "v1.6.0", fm) 91 | assert.True(t, err != nil) 92 | } 93 | 94 | func saveFilesIfNotExist(filesData []FilesInfo) error { 95 | fm := NewKFolder() 96 | folder, err := GetBenchmarkFolder("k8s", "v1.6.0", fm) 97 | if err != nil { 98 | return err 99 | } 100 | for _, fileData := range filesData { 101 | filePath := filepath.Join(folder, fileData.Name) 102 | if _, err := os.Stat(filePath); os.IsNotExist(err) { 103 | f, err := os.Create(filePath) 104 | if err != nil { 105 | panic(err) 106 | } 107 | _, err = f.WriteString(fileData.Data) 108 | if err != nil { 109 | return fmt.Errorf("failed to write benchmark file") 110 | } 111 | err = f.Close() 112 | if err != nil { 113 | return fmt.Errorf("faild to close file %s", filePath) 114 | } 115 | } 116 | } 117 | return nil 118 | } 119 | 120 | //Test_GetEnv test getting home beacon folder 121 | func Test_GetEnv(t *testing.T) { 122 | os.Setenv(common.BeaconHomeEnvVar, "/home/beacon") 123 | homeFolder := GetEnv(common.BeaconHomeEnvVar, "/home/user") 124 | assert.Equal(t, homeFolder, "/home/beacon") 125 | os.Unsetenv(common.BeaconHomeEnvVar) 126 | homeFolder = GetEnv(common.BeaconHomeEnvVar, "/home/user") 127 | assert.Equal(t, homeFolder, "/home/user") 128 | } 129 | 130 | //Test_PluginsSourceFolder test 131 | func Test_PluginsSourceFolder(t *testing.T) { 132 | fm := NewKFolder() 133 | err := CreatePluginsSourceFolderIfNotExist(fm) 134 | assert.NoError(t, err) 135 | a, err := GetPluginSourceSubFolder(fm) 136 | assert.NoError(t, err) 137 | assert.True(t, strings.HasSuffix(a, PluginSourceSubFolder)) 138 | } 139 | 140 | //Test_PluginsCompiledFolder test 141 | func Test_PluginsCompiledFolder(t *testing.T) { 142 | fm := NewKFolder() 143 | err := CreatePluginsCompiledFolderIfNotExist(fm) 144 | assert.NoError(t, err) 145 | a, err := GetCompilePluginSubFolder(fm) 146 | assert.NoError(t, err) 147 | assert.True(t, strings.HasSuffix(a, CompilePluginSubFolder)) 148 | } 149 | 150 | func TestCreateBenchmarkFoldersErrorHomeFolder(t *testing.T) { 151 | ctl := gomock.NewController(t) 152 | fm := mocks.NewMockFolderMgr(ctl) 153 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 154 | err := CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm) 155 | assert.Error(t, err) 156 | fmr := NewKFolder() 157 | path, err := GetBenchmarkFolder("k8s", "v1.6.0", fmr) 158 | assert.NoError(t, err) 159 | rhfp := GetHomeFolder() 160 | fm2 := mocks.NewMockFolderMgr(ctl) 161 | fm2.EXPECT().GetHomeFolder().Return(rhfp, nil).Times(1) 162 | fm2.EXPECT().CreateFolder(path).Return(fmt.Errorf("error")).Times(1) 163 | err = CreateBenchmarkFolderIfNotExist("k8s", "v1.6.0", fm2) 164 | assert.Error(t, err) 165 | } 166 | 167 | func TestCreatePluginsCompiledFolderIfNotExist(t *testing.T) { 168 | ctl := gomock.NewController(t) 169 | fm := mocks.NewMockFolderMgr(ctl) 170 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 171 | err := CreatePluginsCompiledFolderIfNotExist(fm) 172 | assert.Error(t, err) 173 | fmr := NewKFolder() 174 | path, err := GetCompilePluginSubFolder(fmr) 175 | assert.NoError(t, err) 176 | rhfp := GetHomeFolder() 177 | fm2 := mocks.NewMockFolderMgr(ctl) 178 | fm2.EXPECT().GetHomeFolder().Return(rhfp, nil).Times(1) 179 | fm2.EXPECT().CreateFolder(path).Return(fmt.Errorf("error")).Times(1) 180 | err = CreatePluginsCompiledFolderIfNotExist(fm2) 181 | assert.Error(t, err) 182 | } 183 | 184 | func TestCreatePluginsSourcesFolderIfNotExist(t *testing.T) { 185 | ctl := gomock.NewController(t) 186 | fm := mocks.NewMockFolderMgr(ctl) 187 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 188 | err := CreatePluginsSourceFolderIfNotExist(fm) 189 | assert.Error(t, err) 190 | fmr := NewKFolder() 191 | path, err := GetPluginSourceSubFolder(fmr) 192 | assert.NoError(t, err) 193 | rhfp := GetHomeFolder() 194 | fm2 := mocks.NewMockFolderMgr(ctl) 195 | fm2.EXPECT().GetHomeFolder().Return(rhfp, nil).Times(1) 196 | fm2.EXPECT().CreateFolder(path).Return(fmt.Errorf("error")).Times(1) 197 | err = CreatePluginsSourceFolderIfNotExist(fm2) 198 | assert.Error(t, err) 199 | } 200 | 201 | func TestGetBenchmarkFoldersErrorHomeFolder(t *testing.T) { 202 | ctl := gomock.NewController(t) 203 | fm := mocks.NewMockFolderMgr(ctl) 204 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 205 | _, err := GetBenchmarkFolder("k8s", "v1.6.0", fm) 206 | assert.Error(t, err) 207 | } 208 | func TestGetSourcePluginFoldersErrorHomeFolder(t *testing.T) { 209 | ctl := gomock.NewController(t) 210 | fm := mocks.NewMockFolderMgr(ctl) 211 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 212 | _, err := GetPluginSourceSubFolder(fm) 213 | assert.Error(t, err) 214 | } 215 | func TestGetCompiledPluginFoldersErrorHomeFolder(t *testing.T) { 216 | ctl := gomock.NewController(t) 217 | fm := mocks.NewMockFolderMgr(ctl) 218 | fm.EXPECT().GetHomeFolder().Return("homePath", fmt.Errorf("error")).Times(1) 219 | _, err := GetCompilePluginSubFolder(fm) 220 | assert.Error(t, err) 221 | } 222 | -------------------------------------------------------------------------------- /internal/cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "github.com/chen-keinan/beacon/internal/cli/commands" 8 | "github.com/chen-keinan/beacon/internal/common" 9 | "github.com/chen-keinan/beacon/internal/hook" 10 | "github.com/chen-keinan/beacon/internal/logger" 11 | "github.com/chen-keinan/beacon/internal/startup" 12 | "github.com/chen-keinan/beacon/pkg/models" 13 | "github.com/chen-keinan/beacon/pkg/utils" 14 | "github.com/chen-keinan/go-command-eval/eval" 15 | "github.com/chen-keinan/go-user-plugins/uplugin" 16 | "github.com/mitchellh/cli" 17 | "go.uber.org/fx" 18 | "go.uber.org/zap" 19 | "os" 20 | "plugin" 21 | "strings" 22 | ) 23 | 24 | // StartCLI start kube-beacon audit tester 25 | func StartCLI() { 26 | app := fx.New( 27 | // dependency injection 28 | fx.NopLogger, 29 | fx.Provide(logger.GetLog), 30 | fx.Provide(NewK8sResultChan), 31 | fx.Provide(NewCompletionChan), 32 | fx.Provide(NewArgFunc), 33 | fx.Provide(NewCliArgs), 34 | fx.Provide(utils.NewKFolder), 35 | fx.Provide(initBenchmarkSpecData), 36 | fx.Provide(NewCliCommands), 37 | fx.Provide(NewCommandArgs), 38 | fx.Provide(createCliBuilderData), 39 | fx.Invoke(StartCLICommand), 40 | ) 41 | if err := app.Start(context.Background()); err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | //initBenchmarkSpecData initialize benchmark spec file and save if to file system 47 | func initBenchmarkSpecData(fm utils.FolderMgr, ad ArgsData) []utils.FilesInfo { 48 | err := utils.CreateHomeFolderIfNotExist(fm) 49 | if err != nil { 50 | panic(err) 51 | } 52 | err = utils.CreateBenchmarkFolderIfNotExist(ad.SpecType, ad.SpecVersion, fm) 53 | if err != nil { 54 | panic(err) 55 | } 56 | var filesData []utils.FilesInfo 57 | switch ad.SpecType { 58 | case "k8s": 59 | if ad.SpecVersion == "v1.6.0" { 60 | filesData, err = startup.GenerateK8sBenchmarkFiles() 61 | } 62 | case "gke": 63 | if ad.SpecVersion == "v1.1.0" { 64 | filesData, err = startup.GenerateGkeBenchmarkFiles() 65 | } 66 | } 67 | if err != nil { 68 | panic(err) 69 | } 70 | err = startup.SaveBenchmarkFilesIfNotExist(ad.SpecType, ad.SpecVersion, filesData) 71 | if err != nil { 72 | panic(err) 73 | } 74 | return filesData 75 | } 76 | 77 | //initBenchmarkSpecData initialize benchmark spec file and save if to file system 78 | func initPluginFolders(fm utils.FolderMgr) { 79 | err := utils.CreatePluginsSourceFolderIfNotExist(fm) 80 | if err != nil { 81 | panic(err) 82 | } 83 | err = utils.CreatePluginsCompiledFolderIfNotExist(fm) 84 | if err != nil { 85 | panic(err) 86 | } 87 | } 88 | 89 | //loadAuditBenchPluginSymbols load API call plugin symbols 90 | func loadAuditBenchPluginSymbols(log *zap.Logger) hook.K8sBenchAuditResultHook { 91 | fm := utils.NewKFolder() 92 | sourceFolder, err := utils.GetPluginSourceSubFolder(fm) 93 | if err != nil { 94 | panic("failed tpo get plugin source sourceFolder") 95 | } 96 | compliledFolder, err := utils.GetCompilePluginSubFolder(fm) 97 | if err != nil { 98 | panic("failed tpo get plugin compiled sourceFolder") 99 | } 100 | pl := uplugin.NewPluginLoader(sourceFolder, compliledFolder) 101 | names, err := pl.Plugins(uplugin.CompiledExt) 102 | if err != nil { 103 | panic(fmt.Sprintf("failed to get plugin compiled plugins %s", err.Error())) 104 | } 105 | apiPlugin := hook.K8sBenchAuditResultHook{Plugins: make([]plugin.Symbol, 0), Plug: pl} 106 | for _, name := range names { 107 | sym, err := pl.Load(name, common.K8sBenchAuditResultHook) 108 | if err != nil { 109 | log.Error(fmt.Sprintf("failed to load sym %s error %s", name, err.Error())) 110 | continue 111 | } 112 | apiPlugin.Plugins = append(apiPlugin.Plugins, sym) 113 | } 114 | return apiPlugin 115 | } 116 | 117 | // init new plugin worker , accept audit result chan and audit result plugin hooks 118 | func initPluginWorker(plChan chan models.KubeAuditResults, completedChan chan bool) { 119 | log, err := zap.NewProduction() 120 | if err != nil { 121 | panic(err) 122 | } 123 | k8sHooks := loadAuditBenchPluginSymbols(log) 124 | pluginData := hook.NewPluginWorkerData(plChan, k8sHooks, completedChan) 125 | worker := hook.NewPluginWorker(pluginData, log) 126 | worker.Invoke() 127 | } 128 | 129 | //StartCLICommand invoke cli k8s command beacon cli 130 | func StartCLICommand(fm utils.FolderMgr, plChan chan models.KubeAuditResults, completedChan chan bool, ad ArgsData, cmdArgs []string, commands map[string]cli.CommandFactory, log *logger.BLogger) { 131 | // init plugin folders 132 | initPluginFolders(fm) 133 | // init plugin worker 134 | initPluginWorker(plChan, completedChan) 135 | if ad.Help { 136 | cmdArgs = cmdArgs[1:] 137 | } 138 | status, err := invokeCommandCli(cmdArgs, commands) 139 | if err != nil { 140 | log.Console(err.Error()) 141 | } 142 | os.Exit(status) 143 | } 144 | 145 | //NewCommandArgs return new cli command args 146 | // accept cli args and return command args 147 | func NewCommandArgs(ad ArgsData) []string { 148 | cmdArgs := []string{"a"} 149 | cmdArgs = append(cmdArgs, ad.Filters...) 150 | return cmdArgs 151 | } 152 | 153 | //NewCliCommands return cli k8s obj commands 154 | // accept cli args data , completion chan , result chan , spec files and return artay of cli commands 155 | func NewCliCommands(ad ArgsData, plChan chan models.KubeAuditResults, completedChan chan bool, fi []utils.FilesInfo, log *logger.BLogger) []cli.Command { 156 | cmds := make([]cli.Command, 0) 157 | // invoke cli 158 | evaluator := eval.NewEvalCmd() 159 | // invoke cli 160 | cmds = append(cmds, commands.NewK8sAudit(ad.Filters, plChan, completedChan, fi, log, evaluator)) 161 | return cmds 162 | } 163 | 164 | //NewArgFunc return args func 165 | func NewArgFunc() SanitizeArgs { 166 | return ArgsSanitizer 167 | } 168 | 169 | //NewCliArgs return cli args 170 | func NewCliArgs(sa SanitizeArgs) ArgsData { 171 | ad := sa(os.Args[1:]) 172 | return ad 173 | } 174 | 175 | //NewCompletionChan return plugin Completion chan 176 | func NewCompletionChan() chan bool { 177 | completedChan := make(chan bool) 178 | return completedChan 179 | } 180 | 181 | //NewK8sResultChan return plugin test result chan 182 | func NewK8sResultChan() chan models.KubeAuditResults { 183 | plChan := make(chan models.KubeAuditResults) 184 | return plChan 185 | } 186 | 187 | //createCliBuilderData return cli params and commands 188 | func createCliBuilderData(ca []string, cmd []cli.Command) map[string]cli.CommandFactory { 189 | // read cli args 190 | cmdFactory := make(map[string]cli.CommandFactory) 191 | // build cli commands 192 | for index, a := range cmd { 193 | cmdFactory[ca[index]] = func() (cli.Command, error) { 194 | return a, nil 195 | } 196 | } 197 | return cmdFactory 198 | } 199 | 200 | // invokeCommandCli invoke cli command with params 201 | func invokeCommandCli(args []string, commands map[string]cli.CommandFactory) (int, error) { 202 | app := cli.NewCLI(common.BeaconCli, common.BeaconVersion) 203 | app.Args = append(app.Args, args...) 204 | app.Commands = commands 205 | app.HelpFunc = BeaconHelpFunc(common.BeaconCli) 206 | status, err := app.Run() 207 | return status, err 208 | } 209 | 210 | //ArgsSanitizer sanitize CLI arguments 211 | //nolint 212 | var ArgsSanitizer SanitizeArgs = func(str []string) ArgsData { 213 | ad := ArgsData{SpecType: "k8s"} 214 | args := make([]string, 0) 215 | if len(str) == 0 { 216 | args = append(args, "") 217 | } 218 | for _, arg := range str { 219 | arg = strings.Replace(arg, "--", "", -1) 220 | arg = strings.Replace(arg, "-", "", -1) 221 | switch { 222 | case arg == "help", arg == "h": 223 | ad.Help = true 224 | args = append(args, arg) 225 | case strings.HasPrefix(arg, "s="): 226 | ad.SpecType = arg[len("s="):] 227 | case strings.HasPrefix(arg, "spec="): 228 | ad.SpecType = arg[len("spec="):] 229 | case strings.HasPrefix(arg, "v="): 230 | ad.SpecVersion = fmt.Sprintf("v%s", arg[len("v="):]) 231 | case strings.HasPrefix(arg, "version="): 232 | ad.SpecVersion = fmt.Sprintf("v%s", arg[len("version="):]) 233 | default: 234 | args = append(args, arg) 235 | } 236 | } 237 | if ad.SpecType == "k8s" && len(ad.SpecVersion) == 0 { 238 | ad.SpecVersion = "v1.6.0" 239 | } 240 | if ad.SpecType == "gke" && len(ad.SpecVersion) == 0 { 241 | ad.SpecVersion = "v1.1.0" 242 | } 243 | ad.Filters = args 244 | return ad 245 | } 246 | 247 | //ArgsData hold cli args data 248 | type ArgsData struct { 249 | Filters []string 250 | Help bool 251 | SpecType string 252 | SpecVersion string 253 | } 254 | 255 | //SanitizeArgs sanitizer func 256 | type SanitizeArgs func(str []string) ArgsData 257 | 258 | // BeaconHelpFunc beacon Help function with all supported commands 259 | func BeaconHelpFunc(app string) cli.HelpFunc { 260 | return func(commands map[string]cli.CommandFactory) string { 261 | var buf bytes.Buffer 262 | buf.WriteString(fmt.Sprintf(startup.GetHelpSynopsis(), app)) 263 | return buf.String() 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /internal/benchmark/k8s/v1.6.0/1.3_controller_manager.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: 1.3 Controller Manager 7 | audit_tests: 8 | - name: 1.3.1 Ensure that the --terminated-pod-gc-threshold argument is set as 9 | appropriate 10 | description: Activate garbage collector on pod termination, as appropriate. 11 | profile_applicability: Master 12 | audit: 13 | - ps -ef | grep kube-controller-manager |grep ' --terminated-pod-gc-threshold' | grep -o ' --terminated-pod-gc-threshold=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 14 | - awk -v FS="--terminated-pod-gc-threshold=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 15 | remediation: |- 16 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node and set the --terminated-pod-gc- threshold to an appropriate threshold, for example: 17 | --terminated-pod-gc-threshold=10 18 | check_type: multi_param 19 | impact: None 20 | eval_expr: "'${0}' == '10'; && '${0}' == '${1}';" 21 | default_value: "--terminated-pod-gc-threshold is set to 12500." 22 | references: 23 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 24 | - https://github.com/kubernetes/kubernetes/issues/28484 25 | - name: 1.3.2 Ensure that the --profiling argument is set to false 26 | description: Disable profiling, if not needed. 27 | profile_applicability: Master 28 | audit: 29 | - ps -ef | grep kube-controller-manager |grep ' --profiling' | grep -o ' --profiling=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 30 | - awk -v FS="--profiling=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 31 | remediation: |- 32 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node and set the below parameter. 33 | --profiling=false 34 | check_type: multi_param 35 | impact: Profiling information would not be available. 36 | eval_expr: "'${0}' == 'false'; && '${0}' == '${1}';" 37 | default_value: By default, profiling is enabled. 38 | references: 39 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 40 | - https://github.com/kubernetes/community/blob/master/contributors/devel/profiling.md, 41 | - name: 1.3.3 Ensure that the --use-service-account-credentials argument is set 42 | to true 43 | description: Use individual service account credentials for each controller. 44 | profile_applicability: Master 45 | audit: 46 | - ps -ef | grep kube-controller-manager |grep ' --use-service-account-credentials' | grep -o ' --use-service-account-credentials=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 47 | - awk -v FS="--use-service-account-credentials=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 48 | remediation: |- 49 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node to set the below parameter. 50 | --use-service-account-credentials=true 51 | check_type: multi_param 52 | impact: |- 53 | Whatever authorizer is configured for the cluster, it must grant sufficient permissions to the service accounts to perform their intended tasks. When using the RBAC authorizer, those roles are created and bound to the appropriate service accounts in the kube-system namespace automatically with default roles and rolebindings that are auto-reconciled on startup. 54 | If using other authorization methods (ABAC, Webhook, etc), the cluster deployer is responsible for granting appropriate permissions to the service accounts (the required permissions can be seen by inspecting the controller-roles.yaml and controller-role- bindings.yaml files for the RBAC roles. 55 | eval_expr: "'${0}' == 'true'; && '${0}' == '${1}';" 56 | default_value: By default, --use-service-account-credentials is set to false. 57 | references: 58 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 59 | - https://kubernetes.io/docs/admin/service-accounts-admin/ 60 | - https://github.com/kubernetes/kubernetes/blob/release-1.6/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml 61 | - https://github.com/kubernetes/kubernetes/blob/release-1.6/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-role-bindings.yaml 62 | - https://kubernetes.io/docs/admin/authorization/rbac/#controller-roles 63 | - name: 1.3.4 Ensure that the --service-account-private-key-file argument is set 64 | as appropriate 65 | description: Explicitly set a service account private key file for service accounts 66 | on the controller manager. 67 | profile_applicability: Master 68 | audit: 69 | - ps -ef | grep kube-controller-manager |grep ' --service-account-private-key-file' | grep -o ' --service-account-private-key-file=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 70 | - awk -v FS="--service-account-private-key-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 71 | remediation: |- 72 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node and set the --service-account-private- key-file parameter to the private key file for service accounts. 73 | --service-account-private-key-file= 74 | check_type: multi_param 75 | impact: You would need to securely maintain the key file and rotate the keys based on your organization''s key rotation policy. 76 | eval_expr: "'${0}' != ''; && '${0}' == '${1}';" 77 | default_value: By default, --service-account-private-key-file it not set. 78 | references: 79 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 80 | - name: 1.3.5 Ensure that the --root-ca-file argument is set as appropriate 81 | description: Allow pods to verify the API server's serving certificate before 82 | establishing connections. 83 | profile_applicability: Master 84 | audit: 85 | - ps -ef | grep kube-controller-manager |grep ' --root-ca-file' | grep -o ' --root-ca-file=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 86 | - awk -v FS="--root-ca-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 87 | remediation: |- 88 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node and set the --root-ca-file parameter to the certificate bundle file`. 89 | --root-ca-file= 90 | check_type: multi_param 91 | impact: You need to setup and maintain root certificate authority file. 92 | eval_expr: "'${0}' != ''; && '${0}' == '${1}';" 93 | default_value: By default, --root-ca-file is not set. 94 | references: 95 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 96 | - https://github.com/kubernetes/kubernetes/issues/11000 97 | - name: 1.3.6 Ensure that the RotateKubeletServerCertificate argument is set to 98 | true 99 | description: Enable kubelet server certificate rotation on controller-manager. 100 | profile_applicability: Master 101 | audit: 102 | - ps -ef | grep kube-controller-manager |grep ' --RotateKubeletServerCertificate' | grep -o ' --RotateKubeletServerCertificate=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 103 | - awk -v FS="feature-gates=RotateKubeletServerCertificate=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 104 | remediation: |- 105 | Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- controller-manager.yaml on the master node and set the --feature-gates parameter to include RotateKubeletServerCertificate=true. 106 | --feature-gates=RotateKubeletServerCertificate=true 107 | check_type: multi_param 108 | impact: None 109 | eval_expr: "'${0}' != ''; && '${0}' == '${1}';" 110 | default_value: By default, RotateKubeletServerCertificate is set to "true" this 111 | recommendation verifies that it has not been disabled. 112 | references: 113 | - https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/#approval-controller 114 | - https://github.com/kubernetes/features/issues/267 115 | - https://github.com/kubernetes/kubernetes/pull/45059 116 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 117 | - name: 1.3.7 Ensure that the --bind-address argument is set to 127.0.0.1 118 | description: Do not bind the Controller Manager service to non-loopback insecure 119 | addresses. 120 | profile_applicability: Master 121 | audit: 122 | - ps -ef | grep kube-controller-manager |grep ' --bind-address' | grep -o ' --bind-address=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1' 123 | - awk -v FS="--bind-address=" 'NF>1{print $2}' /etc/kubernetes/manifests/kube-controller-manager.yaml 124 | remediation: Edit the Controller Manager pod specification file /etc/kubernetes/manifests/kube- 125 | controller-manager.yaml on the master node and ensure the correct value for 126 | the -- bind-address parameter 127 | check_type: multi_param 128 | impact: None 129 | eval_expr: "'${0}' == '127.0.0.1'; && '${0}' == '${1}';" 130 | default_value: By default, the --bind-address parameter is set to 0.0.0.0 131 | references: 132 | - https://kubernetes.io/docs/reference/command-line-tools-reference/kube- controller-manager/ 133 | -------------------------------------------------------------------------------- /internal/benchmark/k8s/v1.6.0/2.0_etcd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - name: Control Plane Components 5 | sub_category: 6 | name: 2.0 etcd 7 | audit_tests: 8 | - name: 2.1 Ensure that the --cert-file and --key-file arguments are set as appropriate 9 | description: Configure TLS encryption for the etcd service. 10 | profile_applicability: Master 11 | audit: 12 | - ps -ef | grep etcd |grep ' --cert-file' | grep -o ' --cert-file=[^"]\S*' | awk -F 13 | "=" '{print $2}' |awk 'FNR <= 1' 14 | - awk -v FS="--cert-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 15 | - ps -ef | grep etcd |grep ' --key-file' | grep -o ' --key-file=[^"]\S*' | awk -F 16 | "=" '{print $2}' |awk 'FNR <= 1' 17 | - awk -v FS="--key-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 18 | remediation: |- 19 | Follow the etcd service documentation and configure TLS encryption. 20 | Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --cert-file= --key-file= 21 | check_type: multi_param 22 | impact: Client connections only over TLS would be served. 23 | eval_expr: "'${0}' == '1'; && '${2}' == '${3}';" 24 | default_value: By default, TLS encryption is not set. 25 | references: 26 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 27 | - https://kubernetes.io/docs/admin/etcd/ 28 | - name: 2.2 Ensure that the --client-cert-auth argument is set to true 29 | description: Enable client authentication on etcd service. 30 | profile_applicability: Master 31 | audit: 32 | - ps -ef | grep etcd |grep ' --client-cert-auth' | grep -o ' --client-cert-auth=[^"]\S*' 33 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 34 | - awk -v FS="--client-cert-auth=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 35 | remediation: |- 36 | Edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameter. 37 | --client-cert-auth="true" 38 | check_type: multi_param 39 | impact: All clients attempting to access the etcd server will require a valid 40 | client certificate. 41 | eval_expr: "'${0}' == 'true'; && '${0}' == '${1}';" 42 | default_value: By default, the etcd service can be queried by unauthenticated 43 | clients. 44 | references: 45 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 46 | - https://kubernetes.io/docs/admin/etcd/ 47 | - name: 2.3 Ensure that the --auto-tls argument is not set to true 48 | description: Do not use self-signed certificates for TLS. 49 | profile_applicability: Master 50 | audit: 51 | - ps -ef | grep etcd |grep ' --auto-tls' | grep -o ' --auto-tls=[^"]\S*' | awk -F 52 | "=" '{print $2}' |awk 'FNR <= 1' 53 | - awk -v FS="--auto-tls=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 54 | remediation: |- 55 | Edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and either remove the --auto-tls parameter or set it to false. 56 | --auto-tls=false 57 | check_type: multi_param 58 | impact: Clients will not be able to use self-signed certificates for TLS. 59 | eval_expr: "'${0}' == 'true'; && '${0}' == '${1}';" 60 | default_value: By default, --auto-tls is set to false. 61 | references: 62 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 63 | - https://kubernetes.io/docs/admin/etcd/ 64 | - https://coreos.com/etcd/docs/latest/op-guide/configuration.html#auto-tls 65 | - name: 2.4 Ensure that the --peer-cert-file and --peer-key-file arguments are 66 | set as appropriate 67 | description: etcd should be configured to make use of TLS encryption for peer 68 | connections. 69 | profile_applicability: Master 70 | audit: 71 | - ps -ef | grep etcd |grep ' --peer-cert-file' | grep -o ' --peer-cert-file=[^"]\S*' 72 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 73 | - awk -v FS="--peer-cert-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 74 | - ps -ef | grep etcd |grep ' --peer-key-file' | grep -o ' --peer-key-file=[^"]\S*' 75 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 76 | - awk -v FS="--peer-key-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 77 | remediation: |- 78 | Follow the etcd service documentation and configure peer TLS encryption as appropriate for your etcd cluster. 79 | Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameters. --peer-client-file= --peer-key-file= 80 | check_type: multi_param 81 | impact: etcd cluster peers would need to set up TLS for their communication. 82 | eval_expr: "'${0}' == '${1}'; && '${2}' == '${3}';" 83 | default_value: "Note: This recommendation is applicable only for etcd clusters. 84 | If you are using only one\netcd server in your environment then this recommendation 85 | is not applicable.\n --peer-client-file= --peer-key-file=\n 86 | \ \nBy default, peer communication over TLS is not configured." 87 | references: 88 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 89 | - https://kubernetes.io/docs/admin/etcd/ 90 | - name: 2.5 Ensure that the --peer-client-cert-auth argument is set to true 91 | description: etcd should be configured for peer authentication. 92 | profile_applicability: Master 93 | audit: 94 | - ps -ef | grep etcd |grep ' --peer-client-cert-auth' | grep -o ' --peer-client-cert-auth=[^"]\S*' 95 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 96 | - awk -v FS="--peer-client-cert-auth=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 97 | remediation: |- 98 | Edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameter. 99 | --peer-client-cert-auth=true 100 | check_type: multi_param 101 | impact: All peers attempting to communicate with the etcd server will require 102 | a valid client certificate for authentication. 103 | eval_expr: "'${0}' == 'true'; && '${0}' == '${1}';" 104 | default_value: |- 105 | Note: This recommendation is applicable only for etcd clusters. If you are using only one 106 | etcd server in your environment then this recommendation is not applicable. By default, --peer-client-cert-auth argument is set to false. 107 | references: 108 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 109 | - https://kubernetes.io/docs/admin/etcd/ 110 | - https://coreos.com/etcd/docs/latest/op-guide/configuration.html#peer-client-cert-auth 111 | - name: 2.6 Ensure that the --peer-auto-tls argument is not set to true 112 | description: Do not use automatically generated self-signed certificates for 113 | TLS connections between peers. 114 | profile_applicability: Master 115 | audit: 116 | - ps -ef | grep etcd |grep ' --peer-auto-tls' | grep -o ' --peer-auto-tls=[^"]\S*' 117 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 118 | - awk -v FS="--peer-auto-tls=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 119 | remediation: |- 120 | Edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and either remove the --peer-auto-tls parameter or set it to false. 121 | --peer-auto-tls=false 122 | check_type: multi_param 123 | impact: All peers attempting to communicate with the etcd server will require 124 | a valid client certificate for authentication. 125 | eval_expr: "'${0}' == 'true'; && '${0}' == '${1}';" 126 | default_value: |- 127 | Note: This recommendation is applicable only for etcd clusters. If you are using only one etcd server in your environment then this recommendation is not applicable. 128 | By default, --peer-auto-tls argument is set to false. 129 | references: 130 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 131 | - https://kubernetes.io/docs/admin/etcd/ 132 | - https://coreos.com/etcd/docs/latest/op-guide/configuration.html#peer-auto-tls 133 | - name: 2.7 Ensure that a unique Certificate Authority is used for etcd 134 | description: Use a different certificate authority for etcd from the one used 135 | for Kubernetes. 136 | profile_applicability: Master 137 | audit: 138 | - ps -ef | grep etcd |grep ' --trusted-ca-file' | grep -o ' --trusted-ca-file=[^"]\S*' 139 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 140 | - awk -v FS="--trusted-ca-file=" 'NF>1{print $2}' /etc/kubernetes/manifests/etcd.yaml 141 | - ps -ef | grep apiserver |grep ' --client-ca-file' | grep -o ' --client-ca-file=[^"]\S*' 142 | | awk -F "=" '{print $2}' |awk 'FNR <= 1' 143 | remediation: |- 144 | Follow the etcd documentation and create a dedicated certificate authority setup for the etcd service. 145 | Then, edit the etcd pod specification file /etc/kubernetes/manifests/etcd.yaml on the master node and set the below parameter. 146 | --trusted-ca-file= 147 | check_type: multi_param 148 | impact: Additional management of the certificates and keys for the dedicated 149 | certificate authority will be required. 150 | eval_expr: "'${0}' != ''; && '${0}' == '${1}'; && '${0}' != '${2}';" 151 | default_value: By default, no etcd certificate is created and used. 152 | references: 153 | - https://coreos.com/etcd/docs/latest/op-guide/security.html 154 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # This file contains all available configuration options 2 | # with their default values. 3 | 4 | # options for analysis running 5 | run: 6 | # default concurrency is a available CPU number 7 | #concurrency: 4 8 | 9 | # timeout for analysis, e.g. 30s, 5m, default is 1m 10 | deadline: 1m 11 | 12 | # exit code when at least one issue was found, default is 1 13 | issues-exit-code: 1 14 | 15 | # include test files or not, default is true 16 | tests: true 17 | 18 | # list of build tags, all linters use it. Default is empty list. 19 | 20 | #build-tags: 21 | # - mytag 22 | 23 | # which dirs to skip: they won't be analyzed; 24 | # can use regexp here: generated.*, regexp is applied on full path; 25 | # default value is empty list, but next dirs are always skipped independently 26 | # from this option's value: 27 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ 28 | 29 | skip-dirs: 30 | - resources 31 | 32 | # which files to skip: they will be analyzed, but issues from them 33 | # won't be reported. Default value is empty list, but there is 34 | # no need to include all autogenerated files, we confidently recognize 35 | # autogenerated files. If it's not please let us know. 36 | 37 | #skip-files: 38 | # - ".*\\.my\\.go$" 39 | # - lib/bad.go 40 | 41 | # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": 42 | # If invoked with -mod=readonly, the go command is disallowed from the implicit 43 | # automatic updating of go.mod described above. Instead, it fails when any changes 44 | # to go.mod are needed. This setting is most useful to check that go.mod does 45 | # not need updates, such as in a continuous integration and testing system. 46 | # If invoked with -mod=vendor, the go command assumes that the vendor 47 | # directory holds the correct copies of dependencies and ignores 48 | # the dependency descriptions in go.mod. 49 | #modules-download-mode: (release|readonly|vendor) 50 | 51 | 52 | # output configuration options 53 | output: 54 | # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" 55 | format: colored-line-number 56 | 57 | # print lines of code with issue, default is true 58 | print-issued-lines: true 59 | 60 | # print linter name in the end of issue text, default is true 61 | print-linter-name: true 62 | 63 | 64 | # all available settings of specific linters 65 | linters-settings: 66 | errcheck: 67 | # report about not checking of errors in type assetions: `a := b.(MyStruct)`; 68 | # default is false: such cases aren't reported by default. 69 | check-type-assertions: true 70 | 71 | # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; 72 | # default is false: such cases aren't reported by default. 73 | check-blank: false 74 | 75 | # [deprecated] comma-separated list of pairs of the form pkg:regex 76 | # the regex is used to ignore names within pkg. (default "fmt:.*"). 77 | # see https://github.com/kisielk/errcheck#the-deprecated-method for details 78 | ignore: fmt:.*,io/ioutil:^Read.* 79 | 80 | # path to a file containing a list of functions to exclude from checking 81 | # see https://github.com/kisielk/errcheck#excluding-functions for details 82 | #exclude: /path/to/file.txt 83 | 84 | govet: 85 | # report about shadowed variables 86 | check-shadowing: true 87 | golint: 88 | # minimal confidence for issues, default is 0.8 89 | min-confidence: 0.8 90 | gofmt: 91 | # simplify code: gofmt with `-s` option, true by default 92 | simplify: true 93 | goimports: 94 | # put imports beginning with prefix after 3rd-party packages; 95 | # it's a comma-separated list of prefixes 96 | local-prefixes: github.com/org/project 97 | gocyclo: 98 | # minimal code complexity to report, 30 by default (but we recommend 10-20) 99 | min-complexity: 10 100 | maligned: 101 | # print struct with more effective memory layout or not, false by default 102 | suggest-new: true 103 | dupl: 104 | # tokens count to trigger issue, 150 by default 105 | threshold: 150 106 | goconst: 107 | # minimal length of string constant, 3 by default 108 | min-len: 3 109 | # minimal occurrences count to trigger, 3 by default 110 | min-occurrences: 3 111 | depguard: 112 | list-type: blacklist 113 | include-go-root: false 114 | packages: 115 | - github.com/davecgh/go-spew/spew 116 | misspell: 117 | # Correct spellings using locale preferences for US or UK. 118 | # Default is to use a neutral variety of English. 119 | # Setting locale to US will correct the British spelling of 'colour' to 'color'. 120 | locale: US 121 | ignore-words: 122 | - someword 123 | lll: 124 | # max line length, lines longer will be reported. Default is 120. 125 | # '\t' is counted as 1 character by default, and can be changed with the tab-width option 126 | line-length: 120 127 | # tab width in spaces. Default to 1. 128 | tab-width: 1 129 | unused: 130 | # treat code as a program (not a library) and report unused exported identifiers; default is false. 131 | # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: 132 | # if it's called for subdir of a project it can't find funcs usages. All text editor integrations 133 | # with golangci-lint call it on a directory with the changed file. 134 | check-exported: false 135 | unparam: 136 | # Inspect exported functions, default is false. Set to true if no external program/library imports your code. 137 | # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: 138 | # if it's called for subdir of a project it can't find external interfaces. All text editor integrations 139 | # with golangci-lint call it on a directory with the changed file. 140 | check-exported: false 141 | nakedret: 142 | # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 143 | max-func-lines: 30 144 | prealloc: 145 | # XXX: we don't recommend using this linter before doing performance profiling. 146 | # For most programs usage of prealloc will be a premature optimization. 147 | 148 | # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. 149 | # True by default. 150 | simple: true 151 | range-loops: true # Report preallocation suggestions on range loops, true by default 152 | for-loops: false # Report preallocation suggestions on for loops, false by default 153 | gocritic: 154 | # Which checks should be enabled; can't be combined with 'disabled-checks'; 155 | # See https://go-critic.github.io/overview#checks-overview 156 | # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` 157 | # By default list of stable checks is used. 158 | enabled-checks: 159 | #- rangeValCopy 160 | 161 | # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty 162 | disabled-checks: 163 | - regexpMust 164 | 165 | # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. 166 | # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". 167 | enabled-tags: 168 | - performance 169 | 170 | settings: # settings passed to gocritic 171 | captLocal: # must be valid enabled check name 172 | paramsOnly: true 173 | rangeValCopy: 174 | sizeThreshold: 32 175 | 176 | linters: 177 | enable: 178 | - ineffassign 179 | - deadcode 180 | - typecheck 181 | - unused 182 | - gosimple 183 | - structcheck 184 | - varcheck 185 | - errcheck 186 | - staticcheck 187 | - govet 188 | - golint 189 | - gosec 190 | - unconvert 191 | - dupl 192 | - goconst 193 | - gocyclo 194 | - gofmt 195 | enable-all: false 196 | disable: 197 | - scopelint 198 | # - prealloc 199 | disable-all: false 200 | #presets: 201 | # - bugs 202 | # - unused 203 | fast: false 204 | 205 | 206 | issues: 207 | # List of regexps of issue texts to exclude, empty list by default. 208 | # But independently from this option we use default exclude patterns, 209 | # it can be disabled by `exclude-use-default: false`. To list all 210 | # excluded by default patterns execute `golangci-lint run --help` 211 | exclude: 212 | - abcdef 213 | 214 | # Excluding configuration per-path and per-linter 215 | exclude-rules: 216 | # Exclude some linters from running on tests files. 217 | - path: _test\.go 218 | linters: 219 | - gocyclo 220 | - errcheck 221 | - dupl 222 | - gosec 223 | - goconst 224 | 225 | # Ease some gocritic warnings on test files. 226 | - path: _test\.go 227 | text: "(unnamedResult|exitAfterDefer)" 228 | linters: 229 | - gocritic 230 | 231 | # Exclude known linters from partially hard-vendored code, 232 | # which is impossible to exclude via "nolint" comments. 233 | - path: internal/hmac/ 234 | text: "weak cryptographic primitive" 235 | linters: 236 | - gosec 237 | - path: internal/hmac/ 238 | text: "Write\\` is not checked" 239 | linters: 240 | - errcheck 241 | 242 | # Ease linting on benchmarking code. 243 | - path: cmd/stun-bench/ 244 | linters: 245 | - gosec 246 | - errcheck 247 | 248 | # Independently from option `exclude` we use default exclude patterns, 249 | # it can be disabled by this option. To list all 250 | # excluded by default patterns execute `golangci-lint run --help`. 251 | # Default value for this option is true. 252 | exclude-use-default: false 253 | 254 | # Maximum issues count per one linter. Set to 0 to disable. Default is 50. 255 | max-per-linter: 0 256 | 257 | # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. 258 | max-same-issues: 0 259 | 260 | # Show only new issues: if there are unstaged changes or untracked files, 261 | # only those changes are analyzed, else only changes in HEAD~ are analyzed. 262 | # It's a super-useful option for integration of golangci-lint into existing 263 | # large codebase. It's not practical to fix all existing issues at the moment 264 | # of integration: much better don't allow issues in new code. 265 | # Default is false. 266 | new: false 267 | 268 | # Show only new issues created after git revision `REV` 269 | #new-from-rev: REV 270 | 271 | # Show only new issues created in git patch with set file path. 272 | #new-from-patch: path/to/patch/file -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /internal/benchmark/k8s/v1.6.0/1.1_master_node_configuration_files.yml: -------------------------------------------------------------------------------- 1 | --- 2 | benchmark_type: k8s 3 | categories: 4 | - 5 | name: Control Plane Components 6 | sub_category: 7 | name: 1.1 Master Node Configuration Files 8 | audit_tests: 9 | - 10 | name: '1.1.1 Ensure that the API server pod specification file permissions are 11 | set to 644 or more restrictive ' 12 | description: Ensure that the API server pod specification file has permissions 13 | of 644 or more restrictive. 14 | profile_applicability: Master 15 | audit: 16 | - stat -c %a /etc/kubernetes/manifests/kube-apiserver.yaml 17 | remediation: chmod 644 /etc/kubernetes/manifests/kube-apiserver.yaml 18 | check_type: multi_param 19 | impact: None 20 | eval_expr: "${0} <= 644" 21 | default_value: By default, the kube-apiserver.yaml file has permissions of 640. 22 | references: 23 | - https://kubernetes.io/docs/admin/kube-apiserver/ 24 | - 25 | name: '1.1.2 Ensure that the API server pod specification file ownership is 26 | set to root:root ' 27 | description: Ensure that the API server pod specification file ownership is 28 | set to root:root. 29 | profile_applicability: Master 30 | audit: 31 | - stat -c %U:%G /etc/kubernetes/manifests/kube-apiserver.yaml 32 | remediation: chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml 33 | check_type: multi_param 34 | impact: None 35 | eval_expr: "'${0}' == 'root:root';" 36 | default_value: By default, the kube-apiserver.yaml file ownership is set to 37 | root:root. 38 | references: 39 | - https://kubernetes.io/docs/admin/kube-apiserver/ 40 | - 41 | name: '1.1.3 Ensure that the controller manager pod specification file permissions 42 | are set to 644 or more restrictive ' 43 | description: Ensure that the controller manager pod specification file has permissions 44 | of 644 or more restrictive. 45 | profile_applicability: Master 46 | audit: 47 | - stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml 48 | remediation: chmod 644 /etc/kubernetes/manifests/kube-controller-manager.yaml 49 | check_type: multi_param 50 | impact: None 51 | eval_expr: "${0} <= 644;" 52 | default_value: By default, the kube-controller-manager.yaml file has permissions 53 | of 640. 54 | references: 55 | - https://kubernetes.io/docs/admin/kube-controller-manager 56 | - 57 | name: '1.1.4 Ensure that the controller manager pod specification file ownership 58 | is set to root:root ' 59 | description: Ensure that the controller manager pod specification file ownership 60 | is set to root:root. 61 | profile_applicability: Master 62 | audit: 63 | - stat -c %U:%G /etc/kubernetes/manifests/kube-controller-manager.yaml 64 | remediation: chown root:root /etc/kubernetes/manifests/kube-controller-manager.yaml 65 | check_type: multi_param 66 | impact: None 67 | eval_expr: "'${0}' == 'root:root';" 68 | default_value: By default, kube-controller-manager.yaml file ownership is set 69 | to root:root. 70 | references: 71 | - https://kubernetes.io/docs/admin/kube-controller-manager 72 | - 73 | name: '1.1.5 Ensure that the scheduler pod specification file permissions are 74 | set to 644 or more restrictive ' 75 | description: Ensure that the scheduler pod specification file has permissions 76 | of 644 or more restrictive. 77 | profile_applicability: Master 78 | audit: 79 | - stat -c %a /etc/kubernetes/manifests/kube-scheduler.yaml 80 | remediation: chmod 644 /etc/kubernetes/manifests/kube-scheduler.yaml 81 | check_type: multi_param 82 | impact: None 83 | eval_expr: "${0} <= 644;" 84 | default_value: By default, kube-scheduler.yaml file has permissions of 640. 85 | references: 86 | - https://kubernetes.io/docs/admin/kube-scheduler/ 87 | - 88 | name: '1.1.6 Ensure that the scheduler pod specification file ownership is set 89 | to root:root ' 90 | description: Ensure that the scheduler pod specification file ownership is set 91 | to root:root. 92 | profile_applicability: Master 93 | audit: 94 | - stat -c %U:%G /etc/kubernetes/manifests/kube-scheduler.yaml 95 | remediation: chown root:root /etc/kubernetes/manifests/kube-scheduler.yaml 96 | check_type: multi_param 97 | impact: None 98 | eval_expr: "'${0}' == 'root:root';" 99 | default_value: By default, kube-scheduler.yaml file ownership is set to root:root. 100 | references: 101 | - https://kubernetes.io/docs/admin/kube-scheduler/ 102 | - 103 | name: '1.1.7 Ensure that the etcd pod specification file permissions are set 104 | to 644 or more restrictive ' 105 | description: Ensure that the /etc/kubernetes/manifests/etcd.yaml file has permissions 106 | of 644 or more restrictive. 107 | profile_applicability: Master 108 | audit: 109 | - stat -c %a /etc/kubernetes/manifests/etcd.yaml 110 | remediation: chmod 644 /etc/kubernetes/manifests/etcd.yaml 111 | check_type: multi_param 112 | impact: None 113 | eval_expr: "${0} <= 644;" 114 | default_value: By default, /etc/kubernetes/manifests/etcd.yaml file has permissions 115 | of 640. 116 | references: 117 | - https://coreos.com/etcd 118 | - https://kubernetes.io/docs/admin/etcd/ 119 | - 120 | name: '1.1.8 Ensure that the etcd pod specification file ownership is set to 121 | root:root ' 122 | description: Ensure that the /etc/kubernetes/manifests/etcd.yaml file ownership 123 | is set to root:root. 124 | profile_applicability: Master 125 | audit: 126 | - stat -c %U:%G /etc/kubernetes/manifests/etcd.yaml 127 | remediation: chown root:root /etc/kubernetes/manifests/etcd.yaml 128 | check_type: multi_param 129 | eval_expr: "'${0}' == 'root:root';" 130 | impact: None 131 | default_value: By default, /etc/kubernetes/manifests/etcd.yaml file ownership 132 | is set to root:root. 133 | references: 134 | - https://coreos.com/etcd 135 | - https://kubernetes.io/docs/admin/etcd/ 136 | - 137 | name: '1.1.9 Ensure that the Container Network Interface file permissions are 138 | set to 644 or more restrictive ' 139 | description: Ensure that the Container Network Interface files have permissions 140 | of 644 or more restrictive. 141 | profile_applicability: Master 142 | audit: 143 | - stat -c %a /*/cni/* 144 | remediation: chmod 644 145 | check_type: multi_param 146 | impact: None 147 | eval_expr: "${0} <= 644;" 148 | default_value: N/A 149 | references: 150 | - https://kubernetes.io/docs/concepts/cluster-administration/networking/ 151 | - 152 | name: '1.1.10 Ensure that the Container Network Interface file ownership is 153 | set to root:root ' 154 | description: Ensure that the Container Network Interface files have ownership 155 | set to root:root. 156 | profile_applicability: Master 157 | audit: 158 | - stat -c %U:%G /*/cni/* 159 | remediation: chown root:root 160 | check_type: multi_param 161 | impact: None 162 | eval_expr: "'${0}' == 'root:root';" 163 | default_value: None 164 | references: 165 | - https://kubernetes.io/docs/concepts/cluster-administration/networking/ 166 | - 167 | name: '1.1.11 Ensure that the etcd data directory permissions are set to 700 168 | or more restrictive ' 169 | description: Ensure that the etcd data directory has permissions of 700 or more 170 | restrictive. 171 | profile_applicability: Master 172 | audit: 173 | - stat -c %a $(ps -ef | grep etcd |grep ' --data-dir' | grep -o ' --data-dir=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1') 2> /dev/null 174 | remediation: chmod 700 $(ps -ef | grep etcd |grep 'data-dir' | grep -o 'data-dir=[^"]\S*' 175 | | awk -F "=" '{print $2}') 176 | check_type: multi_param 177 | impact: None 178 | eval_expr: "${0} <= 700;" 179 | default_value: By default, etcd data directory has permissions of 755. 180 | references: 181 | - https://coreos.com/etcd/docs/latest/op-guide/configuration.html#data-dir 182 | - https://kubernetes.io/docs/admin/etcd/ 183 | - 184 | name: '1.1.12 Ensure that the etcd data directory ownership is set to etcd:etcd ' 185 | description: Ensure that the etcd data directory ownership is set to etcd:etcd. 186 | profile_applicability: Level 1 - Master Node 187 | audit: 188 | - stat -c %U:%G $(ps -ef | grep etcd |grep ' --data-dir' | grep -o ' --data-dir=[^"]\S*' | awk -F "=" '{print $2}' |awk 'FNR <= 1') 2> /dev/null 189 | remediation: chown etcd:etcd $(ps -ef | grep etcd |grep 'data-dir' | grep -o 190 | 'data-dir=[^"]\S*' | awk -F "=" '{print $2}') 191 | check_type: multi_param 192 | impact: None 193 | eval_expr: "'${0}' == 'etcd:etcd';" 194 | default_value: By default, etcd data directory ownership is set to etcd:etcd. 195 | references: 196 | - https://coreos.com/etcd/docs/latest/op-guide/configuration.html#data-dir 197 | - https://kubernetes.io/docs/admin/etcd/ 198 | - 199 | name: '1.1.13 Ensure that the admin.conf file permissions are set to 644 or 200 | more restrictive ' 201 | description: Ensure that the admin.conf file has permissions of 644 or more 202 | restrictive. 203 | profile_applicability: Master 204 | audit: 205 | - stat -c %a /etc/kubernetes/admin.conf 206 | remediation: chmod 644 /etc/kubernetes/admin.conf 207 | check_type: multi_param 208 | impact: None 209 | eval_expr: "${0} <= 644;" 210 | default_value: By default, admin.conf has permissions of 640. 211 | references: 212 | - https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 213 | - 214 | name: '1.1.14 Ensure that the admin.conf file ownership is set to root:root ' 215 | description: Ensure that the admin.conf file ownership is set to root:root. 216 | profile_applicability: Level 1 - Master Node 217 | audit: 218 | - stat -c %U:%G /etc/kubernetes/admin.conf 219 | remediation: chown root:root /etc/kubernetes/admin.conf 220 | check_type: multi_param 221 | impact: None 222 | eval_expr: "'${0}' == 'root:root';" 223 | default_value: By default, admin.conf file ownership is set to root:root. 224 | references: 225 | - https://kubernetes.io/docs/admin/kubeadm/ 226 | - 227 | name: '1.1.15 Ensure that the scheduler.conf file permissions are set to 644 228 | or more restrictive ' 229 | description: 'Ensure that the scheduler.conf file permissions are set to 644 230 | or more restrictive ' 231 | profile_applicability: Master 232 | audit: 233 | - stat -c %a /etc/kubernetes/scheduler.conf 234 | remediation: chmod 644 /etc/kubernetes/scheduler.conf 235 | check_type: multi_param 236 | impact: None 237 | eval_expr: "${0} <= 644;" 238 | default_value: By default, scheduler.conf has permissions of 640. 239 | references: 240 | - https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 241 | - 242 | name: '1.1.16 Ensure that the scheduler.conf file ownership is set to root:root ' 243 | description: Ensure that the scheduler.conf file ownership is set to root:root. 244 | profile_applicability: Master 245 | audit: 246 | - stat -c %U:%G /etc/kubernetes/scheduler.conf 247 | remediation: chown root:root /etc/kubernetes/scheduler.conf 248 | check_type: multi_param 249 | impact: None 250 | eval_expr: "'${0}' == 'root:root';" 251 | default_value: By default, scheduler.conf file ownership is set to root:root. 252 | references: 253 | - https://kubernetes.io/docs/admin/kubeadm/ 254 | - 255 | name: '1.1.17 Ensure that the controller-manager.conf file permissions are set 256 | to 644 or more restrictive ' 257 | description: Ensure that the controller-manager.conf file has permissions of 258 | 644 or more restrictive. 259 | profile_applicability: Master 260 | audit: 261 | - stat -c %a /etc/kubernetes/controller-manager.conf 262 | remediation: chmod 644 /etc/kubernetes/controller-manager.conf 263 | check_type: multi_param 264 | impact: None 265 | eval_expr: "${0} <= 644;" 266 | default_value: By default, controller-manager.conf has permissions of 640. 267 | references: 268 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 269 | - 270 | name: '1.1.18 Ensure that the controller-manager.conf file ownership is set 271 | to root:root ' 272 | description: Ensure that the controller-manager.conf file ownership is set to 273 | root:root. 274 | profile_applicability: Master 275 | audit: 276 | - stat -c %U:%G /etc/kubernetes/controller-manager.conf 277 | remediation: chown root:root /etc/kubernetes/controller-manager.conf 278 | check_type: multi_param 279 | impact: None 280 | eval_expr: "'${0}' == 'root:root';" 281 | default_value: By default, controller-manager.conf file ownership is set to 282 | root:root. 283 | references: 284 | - https://kubernetes.io/docs/admin/kube-controller-manager/ 285 | - 286 | name: '1.1.19 Ensure that the Kubernetes PKI directory and file ownership is 287 | set to root:root ' 288 | description: Ensure that the Kubernetes PKI directory and file ownership is 289 | set to root:root. 290 | profile_applicability: Master 291 | audit: 292 | - stat -c %U:%G $(ls -R /etc/kubernetes/pki/ | awk '/:$/&&f{s=$0;f=0}/:$/&&!f{sub(/:$/,"");s=$0;f=1;next}NF&&f{ 293 | print s"/"$0 }') 294 | remediation: chown -R root:root /etc/kubernetes/pki/ 295 | check_type: multi_param 296 | impact: None 297 | eval_expr: "'${0}' == 'root:root';" 298 | default_value: By default, the /etc/kubernetes/pki/ directory and all of the 299 | files and directories contained within it, are set to be owned by the root 300 | user. 301 | references: 302 | - https://kubernetes.io/docs/admin/kube-apiserver/ 303 | - 304 | name: '1.1.20 Ensure that the Kubernetes PKI certificate file permissions are 305 | set to 644 or more restrictive ' 306 | description: Ensure that Kubernetes PKI certificate files have permissions of 307 | 644 or more restrictive. 308 | profile_applicability: Master 309 | audit: 310 | - stat -c %a $(ls -aR /etc/kubernetes/pki/ | awk '/:$/&&f{s=$0;f=0}/:$/&&!f{sub(/:$/,"");s=$0;f=1;next}NF&&f{ 311 | print s"/"$0 }' | grep \.crt$) 312 | remediation: chmod -R 644 /etc/kubernetes/pki/*.crt 313 | check_type: multi_param 314 | impact: None 315 | eval_expr: "${0} <= 644;" 316 | default_value: By default, the certificates used by Kubernetes are set to have 317 | permissions of 644 318 | references: 319 | - https://kubernetes.io/docs/admin/kube-apiserver/ 320 | - 321 | name: '1.1.21 Ensure that the Kubernetes PKI key file permissions are set to 322 | 600 ' 323 | description: Ensure that Kubernetes PKI key files have permissions of 600. 324 | profile_applicability: Master 325 | audit: 326 | - stat -c %a $(ls -aR /etc/kubernetes/pki/ | awk '/:$/&&f{s=$0;f=0}/:$/&&!f{sub(/:$/,"");s=$0;f=1;next}NF&&f{ 327 | print s"/"$0 }' | grep \.key$) 328 | remediation: chmod -R 600 /etc/kubernetes/pki/*.key 329 | check_type: multi_param 330 | impact: None 331 | eval_expr: "${0} <= 600;" 332 | default_value: By default, the keys used by Kubernetes are set to have permissions 333 | of 600 334 | references: 335 | - https://kubernetes.io/docs/admin/kube-apiserver/ 336 | --------------------------------------------------------------------------------