Compare commits

...

303 Commits

Author SHA1 Message Date
Jonathan Rau
51aa10e354 Update EKS Config & Create EKS Guide (#489)
* Change EKS Readme

* Fix readme formatting

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md
2019-11-06 07:34:43 +01:00
Sebastian Ehmann
56fa231376 Remove nil check (#493)
As the length of a nil slice is defined as 0, the nil check is
redundand. (suggested by golanci-lint/gosimple)
2019-11-05 20:23:31 -05:00
Sebastian Ehmann
09fb3c4fe4 Check error before deferring db.Close() (#491) 2019-11-05 20:17:03 -05:00
Sebastian Ehmann
b9be7daa4a Directly convert buffer to string (#492)
Using `buf.String()` instead of `fmt.Sprintf` is simpler
2019-11-05 20:07:41 -05:00
Liz Rice
d7b5422e8a Fix detection of encryption-provider-config (#513)
Fixes: https://github.com/aquasecurity/kube-bench/issues/420

Signed-off-by: Manuel Rüger <manuel@rueg.eu>
2019-11-05 19:45:40 -05:00
Soumyadeep Sinha
8e4da53006 Fixed some typos (#446)
* Fixed some typos

* Fixed some typos

* Fixed typo and capitalization of Kubernetes

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update docs/README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update docs/README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update docs/README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* docs: trivial, reinstate capital K

* docs: trivial, reinstate backticks

* docs: trivial, reinstate "in order" for clarity

* docs: trivial, reinstate capital K
2019-11-05 14:59:29 -08:00
Roberto Rojas
7ca438b618 Fixes Issue 269 - Numbering to use CIS Versions (#511)
* starting benchmark flag

* Revert "starting benchmark flag"

This reverts commit 58fc948626.

* fixes issue #269

* add more unit tests

* fix bug

* Update cmd/common.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* fixes as per PR review

* fixes as per PR review

* adds more tests

* fixed tests

* changes as per PR Review

* changes as per PR Review

* updated README

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update README.md

Co-Authored-By: Liz Rice <liz@lizrice.com>

* changes are per PR review
2019-11-05 16:31:27 -05:00
mwwolters
8276e521d4 Changed 1.3.3 to check that --use-service-account-credentials isn't set to false, but the flag is set (#442) 2019-11-05 21:29:16 +01:00
Roberto Rojas
d5a02f7cb4 Fixes Issue #331: Changes the Error Message When Programs are Missing (#497)
* changed error description for missing kubectl/kubelet execs

* adds function to generate error message for missing components

* adds function to generate error message for missing components

* adds function to generate error message for missing components

* Update cmd/util.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update cmd/util.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update cmd/util.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update cmd/util.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* Update cmd/util.go

Co-Authored-By: Liz Rice <liz@lizrice.com>

* fixed error message

* changes are per PR review
2019-11-05 10:44:57 -05:00
Roberto Rojas
13fe1cdfb8 Fixes issue #501: specifying absolute path for both ps and cat (#508)
* fixes issue #501

* specify abolute path for ps and cat
2019-11-01 13:10:52 +00:00
Nando Theessen
91bd47f296 Fixes job-eks.yaml to not fail on startup (#461) 2019-10-25 20:31:57 -04:00
Kevin W Monroe
04946a48fb add snap component paths to default config (#414) 2019-10-25 20:19:56 -04:00
Prem Kumar
01ee110ac4 Fix repetitive flags in some ocp-3.11 tests (#462)
* fix flag repetition in ocp-3.11/node.yaml

* fix flag repetition in ocp-3.11/master.yaml
2019-10-25 20:12:56 -04:00
michizhou
b0abc74350 Fixed documentation errors (#450) 2019-10-25 11:58:41 -07:00
DarthSett
bea820bdfe Improve CONTRIBUTING.md (#483)
Fixed the grammar as per the issue [#472 ](https://github.com/aquasecurity/kube-bench/issues/472)
2019-10-24 14:20:22 -07:00
Arpit Pandey
ce0137a31a Fix few typos (#469) 2019-10-24 14:05:13 -07:00
Saiyam Pathak
39d9ef9d37 usr-bin volume mount not required (#424)
usr-bin volume mount not required as using kubelet version in command
2019-10-24 14:49:33 +01:00
Alexey Pyltsyn
7a2cc3f554 Improve docs (#437) 2019-10-24 09:15:29 +01:00
Sidhya Tikku
bf383ec1f7 Added .DS_Store and thumbs.db to .gitignore (#463)
* Delete .DS_Store

* Update .gitignore
2019-10-24 09:04:13 +01:00
PARAM MITTAL
5f647d6a36 Fix typo in Contributing file (#471) 2019-10-24 08:57:32 +01:00
John Schnake
2657c2f96f Use newer kind load docker-image command (#459)
Updates the logic for `kind-push` in the makefile to use
the new, simple command provided by kind.

Fixes #458
2019-10-23 12:15:02 -07:00
Mohan Sha
b009520ea3 Added table of contents for navigation (#455) 2019-10-23 19:08:04 +01:00
Nikita Titov
146de15c2e removed deprecated field in Travis config (#452) 2019-10-23 18:45:10 +01:00
Simarpreet Singh
d77eab2234 master.yaml: Add --audit-policy-file check for 1.1.37. (#440)
* master.yaml: Add --audit-policy-file check for 1.1.37.

Signed-off-by: Simarpreet Singh <simar@linux.com>

* fix-177: fix line endings

Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-10-18 13:23:23 -07:00
Itay Shakury
3964377a80 add contribution guidelines (#454) 2019-10-16 17:51:33 +03:00
Liz Rice
1b49050974 docs: Clarify the meaning of WARN state (#430)
* docs: Clarify the meaning of WARN state

* Update README.md
2019-10-15 10:04:18 -04:00
Simarpreet Singh
d12a45bba9 Properly initialize viper library when checking for master components (#434)
* common_test: Add a failing test to show the SISEGV

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: Go green by fixing isMaster() to instantiate viper

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: Inject a seam for getBinariesFunc to be patched-in.

Also adds additional tests to showcase unhappy behaviors.

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common_test: Rename TestIsMaster()

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: init viper with master config

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: Add a pre-check if valid yaml is passed but doesn't include master.

Also adds additional tests to showcase unhappy behaviors.

Signed-off-by: Simarpreet Singh <simar@linux.com>

* mod: Upgrade viper to v1.4.0

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: Refactor node only yaml to a file

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common: Log  when master components are not found

Signed-off-by: Simarpreet Singh <simar@linux.com>

* common_test: Refactor subtests into a table

Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-10-14 11:15:08 -04:00
Roberto Rojas
a6ee61fd08 Fixes issue #289: removed versions prior to 1.11 (#429)
* removed version prior to 1.11

* removed references to kubernetes versions prior to 1.11
2019-10-14 10:52:43 -04:00
Roberto Rojas
3aa41db166 Issue #353: Merges JSON and Exec Params files (#426)
* starts fixes #353

* new approach to minize duplications

* applied merged yaml files for v1.11 and v1.13

* yaml files json/params merged

* fixes to remove double quotes from numbers and booleans

* fixed bug

* fixed certificate check

* removed -json files

* changes based on PR review

* Update check/check_test.go

Yay more tests!

Co-Authored-By: Liz Rice <liz@lizrice.com>

* changes as PR review

* fixed bug when scored check is missing tests

* attempt to improve the code

* fixed list breaks

* removes handleError function

* Update check/check.go

Accepting suggested log level.

Co-Authored-By: Liz Rice <liz@lizrice.com>
2019-10-14 10:37:10 -04:00
Roberto Rojas
c22f81610d removes federated (#431) 2019-10-12 19:00:26 -04:00
Roberto Rojas
91dfeb7577 passes KUBEBENCH_VERSION down to Dockerfile (#428) 2019-10-12 18:53:17 -04:00
Roberto Rojas
4416e46967 Adds Unit Tests for check/toNumeric (#401)
* fixes issue #364

* fixed unit test error text
2019-10-12 18:46:19 -04:00
James George
050145f6b3 docs: minor tweak (#438) 2019-10-11 15:47:10 +01:00
yoavrotems
89afda1f63 Add [Manual test] to remediation in all the manual tests (#435) 2019-10-09 16:26:02 +01:00
Simarpreet Singh
37f626dce6 cfg: Make proxy checks optional (#436)
Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-10-08 11:53:39 +01:00
Liz Rice
16beb3e616 docs: note that you may need to be root (#412) 2019-09-21 15:07:16 +01:00
yoavrotems
27261d1d32 Change Kind version (#411)
Something with the old version was crashing. now using the most recent one 1.15.3 is working.
2019-09-03 13:42:07 +01:00
Roberto Rojas
41e0ae77de changes to use the "op: valid_elements" operation to manage list of items (#402) 2019-09-03 13:36:47 +01:00
yoavrotems
ea9089bd42 update the yaml according (#410)
The update is from the new cis version 1.4.1.
like been done in https://github.com/aquasecurity/kube-bench/issues/370
2019-09-02 16:40:45 +01:00
Roberto Rojas
ec3b1076c0 Fixes issue #407 (#409)
* fixes issue #407

* fixes issue #407
2019-08-30 17:33:14 +01:00
Roberto Rojas
13dfa15ad6 Fixes Issue #396 - Replaces $kubeletconf for $kubeletsvc (#399)
* fixes issue #396

* reverts remediation text change

* changes to 1.11-json and 1.13-json as per PR review

* Tiny typo
2019-08-30 15:21:41 +01:00
Liz Rice
a2466da4b0 Correct 1.1.13 to match CIS spec (#406)
Text should say Not Scored
2019-08-30 15:10:30 +01:00
Liz Rice
d0d4e95d93 Updated version support (#385)
Strictly, we don't have the changes in 1.13-json but we do have them in 1.13
2019-08-30 12:09:11 +01:00
Roberto Rojas
7a53806863 fixes issue #346 by explicitly only checking read-only property (#404) 2019-08-30 08:56:48 +01:00
yoavrotems
4b5a877f1f Remove some tests from been manual (#398)
* Remove some tests from been manual

* Remove some tests from been manual
2019-08-29 08:54:29 +01:00
Roberto Rojas
f343d36862 hyperkube v1.15 renamed "proxy" to "kube-proxy" (#400) 2019-08-28 16:53:48 +01:00
Roberto Rojas
3e5d02e920 fixes issue #386 (#397)
* fixes issue #386

* Correct typo
2019-08-28 09:27:56 +01:00
Abubakr-Sadik Nii Nai Davis
92df9cb36c Read kubernetes version from environment (#390)
* Read kubernetes version from environment

Set kubernetes version to the value of the environment variable `KUBE_BENCH_VERSION` if it is defined and the flag `--version` is not specified on the kube-bench command line.

The command line flag `--version` takes precedence of the environment variable `KUBE_BENCH_VERSION` if both are defined.

* Add info about KUBE_BENCH_VERSION to README
2019-08-27 09:04:11 +01:00
Abubakr-Sadik Nii Nai Davis
a3b8ba58ad Fix error converting from string to integer (#392)
Replace the `gt` with `eq` for string comparison of kube-bench check 2.1.6 in `cfg/1.6/node.yaml`.
2019-08-23 16:15:21 +01:00
Patrick Lieberg
0d81ef10d5 Update config.yaml to add Azure AKS file locations for kubelet (#383)
* testing Azure config locations

* "Updated default config.yaml to incorporate Azure AKS file locations for kubelet"

* "Adjusted order of new lines.  Removed unneeded lines."
2019-08-22 14:52:34 +01:00
Abubakr-Sadik Nii Nai Davis
3fba5f4dac Fix version command failing because of missing config file it does not need. (#377)
* Fix version command failing because of missing config file it does
not need.

* Fix typo

* Remove reference to github issue in comment
2019-08-22 13:43:09 +01:00
mwwolters
787bf6ca4d Updated check to pass if flag isn't set (#379) 2019-08-09 18:24:20 +01:00
Liz Rice
f8b2f6c841 Correct 1.4.21 text (#356)
1.4.21 is about the PKI key file not the certificate
2019-08-07 17:17:21 +01:00
yoavrotems
136e9cd731 Remove federated from ocp (#381)
* Delete federated.yaml

There is no federated tests in ocp

* Delete federated.yaml

There are no federated tests in OCP
2019-08-07 16:52:04 +01:00
Abubakr-Sadik Nii Nai Davis
2e27d681f7 Remove duplicate documentation. (#373)
* Remove duplicate documentation.

* Add test configuration header back in main README.

* Add missing regex operator in docs/README.

* Fix incorrect description of configuration options bins, confs etc.

* Move description of version auto-detection to main README.

* Use 1.13 in examples since cfg/1.12 doesn't exist

* Remove duplicate sentence about regex

This sentence is now in the docs/README

* Add link to the docs for test YAML definitions
2019-08-07 03:43:51 -07:00
Efrat Levitan
b8a463f051 Correction to 1.13 and 1.13-json test 2.1.5 (#380) 2019-08-07 03:33:09 -07:00
yoavrotems
22b971a633 fixes-according-kube-cis1.4.1 (#376)
* Update master.yaml

* Update node.yaml

Fix 2.1.11 - got DEPRECATED
2.1.14 changed to be a set of options, would be fixed by https://github.com/aquasecurity/kube-bench/pull/367

* Update master.yaml

* Update node.yaml

change 2.1.11 Title, and state to not scored
2019-08-06 06:19:29 -07:00
Roberto Rojas
0422368615 issue #369: fixes RotateKubeletServerCertificate tests in 1.13-json (#371) 2019-08-06 00:58:35 -07:00
mwwolters
893aa3588c Updated check to pass if flag isn't set (#375) 2019-07-30 10:09:24 -07:00
Roberto Rojas
937bfc7b2e issue #344: Adds support for array comparison. Every element in the s… (#367)
* issue #344: Adds support for array comparison. Every element in the source array must exist in the target array.

* issue #344: Fixed typo and found if condition based on code review

* adds unit tests for valid_elements comparison

* removes spaces from split strings
2019-07-26 11:11:59 -07:00
Roberto Rojas
dab5e92bb5 Issue #363: Adds Unit Tests for Test Comparisons (#366)
* issue #363: starts unit tests for Test Comparison.

* issue #363: Adds tests for "eq" operation

* changes test result message

* issue #363: Adds tests for "noteq" operation

* issue #363: Adds tests for "gt" operation

* issue #363: Adds tests for "lt" operation

* issue #363: Adds tests for "gte" operation

* issue #363: Adds tests for "lte" operation

* issue #363: Adds tests for "has" operation

* issue #363: Adds tests for "nothave" operation

* issue #363: Adds tests for "regex" operation
2019-07-17 10:08:11 -04:00
yoavrotems
7c97f6a490 Add codecov (#336)
* Update .gitignore

* Update .travis.yml

* Update makefile

* Update .travis.yml

* Update .travis.yml

* Update .travis.yml

* Update README.md

* Update README.md

* Update README.md

* Update makefile

* Update .travis.yml
2019-07-16 14:11:51 -04:00
Roberto Rojas
86e3456f33 issue #243: Changes condition so that score: false tests are performed (#357)
* issue #243: Changes condition so that score: false tests are performed

* issue #243: Changes comments.
2019-07-13 08:05:29 +01:00
zilard
b86dd92c91 Issue #348: Refactor get<Thing>Files into getFiles (#359)
* issue #348: replace everywhere get<Thing>Files with getFiles
2019-07-13 07:48:24 +01:00
Roberto Rojas
c87c5cfb51 Fixes bugs on tests 2.1.4 and 2.1.5 - 1.13-json (#365)
* Adds bin_op to Test 2.1.4

* Adds bin_op to Test 2.1.5
2019-07-13 07:35:44 +01:00
Roberto Rojas
b649588f46 turns Go Module on (#362) 2019-07-12 14:12:59 +01:00
Liz Rice
cb3d876ced Remove Darwin build from go-releaser (#361)
Should fix #360
2019-07-12 12:41:46 +01:00
Roberto Rojas
d43cdfdf01 Issue #355: Adds Unit Tests for JSONPath Parse & Execute (#358)
* issue #335: Adds json/yaml unmarshal Unit Tests.

* issue #335: Adds jsonpath Unit Tests.

* issue #335: Removes log package.
2019-07-12 07:09:27 +01:00
Roberto Rojas
3926ba3977 issue #337: Adds comment for properties detected thru parsing command line. Fixed Audit for test 2.1.8 (#354) 2019-07-11 17:05:24 +01:00
Roberto Rojas
d127512ab9 issue #349: changes test 2.2.8 (#351) 2019-07-10 15:54:09 +01:00
Roberto Rojas
336ca84998 fixes substitution variable (kubeletconf -> kubeletsvc). (#350) 2019-07-10 14:20:14 +01:00
zilard
d8528a1ec8 issue #234: implement test 2.2.8 (#343)
* implement test 2.2.8

* Nit: correct indentation

The indentation looked a bit wonky due to spaces vs tabs; hopefully this corrects it
2019-07-10 10:43:15 +01:00
Roberto Rojas
a0bed18054 Adds json version of config for k8s 1.13 (#342) 2019-07-10 09:26:37 +01:00
Liz Rice
25b2c5da5a Add comment about procps limitation (#333) 2019-07-08 22:29:37 +01:00
Liz Rice
08097d2211 Need credentials in order to run kubectl version (#332)
Without passing in kubeconfig credentials:

```bash
$ docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -v $(which kubectl):/usr/bin/kubectl -t lizrice/kube-bench:5e6cdfd master -v 1
I0628 16:52:06.591683    6099 util.go:367] Unable to get Kubernetes version from kubectl, using default version: 1.6
I0628 16:52:06.591822    6099 common.go:74] Using benchmark file: cfg/1.6/master.yaml
...
```
As updated in the README with this fix:

```bash
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -v $(which kubectl):/usr/bin/kubectl -v ~/.kube:/.kube -e KUBECONFIG=/.kube/config -t lizrice/kube-bench:5e6cdfd master -v 1
I0628 16:53:26.784122    7224 util.go:131] No test file found for 1.14 - using tests for Kubernetes 1.13
I0628 16:53:26.784961    7224 common.go:228] Using config file: cfg/1.13/config.yaml
...
```
2019-07-08 22:22:48 +01:00
Liz Rice
9a900db021 docs: update WIP to draft (#324) 2019-07-03 08:27:28 +01:00
patelpayal
e6e6333e6d add glog flush to write the output to a file (#329)
* add glog flush to write the output to a file

* add glog flush before exit on error and fix code comment
2019-07-01 09:49:46 +01:00
Manuel Rüger
5e6cdfdb0e Detect kube-controller in CMD (#326)
If kube-controller-manager is getting detected by older versions of
procps, it will only be detected if we're looking for kube-controller
(15 chars)

NOTE: "The command name is not the same as the command line. Previous versions of
       procps and the kernel truncated this command name to 15
       characters. This limitation is no longer present in both. If
       you depended on matching only 15 characters, you may no longer
       get a match."
2019-06-28 16:58:23 +01:00
patelpayal
e066ec69dd fix go.mod dependency (#330) 2019-06-28 09:48:52 +01:00
Manuel Rüger
f7e3257e3c Go modules / Alpine 3.10 update / Remove binary (#322)
* Remove binary that was accidentally added

911e9051dc

* Dockerfile: Update to alpine 3.10

* Switch to go 1.12 and go modules
2019-06-26 11:58:51 +01:00
Liz Rice
086df3dda1 Merge pull request #321 from simar7/remove-extra-whitespaces
cfg: remove erroneous whitespaces in yaml
2019-06-26 11:26:39 +01:00
Simarpreet Singh
dddc42f046 cfg: remove erroneous whitespaces in yaml
Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-06-25 07:18:46 -07:00
Liz Rice
07dfeb8e27 Merge pull request #319 from aquasecurity/contributing
Add github issue creation instructions.
2019-06-25 14:51:32 +01:00
Liz Rice
0ab09a85e8 Add pull requests section
Add pull requests section
Include instructions for kube-bench version
Other small wording changes
2019-06-25 14:44:02 +01:00
Abubakr-Sadik Nii Nai Davis
7affbc83d8 Add github issue creation instructions. 2019-06-24 20:33:24 +00:00
Liz Rice
ea7400aa4b Merge pull request #301 from wwwil/op-regex
Add regex compare op
2019-06-19 12:10:29 +02:00
Liz Rice
5e3ff51fa9 Merge branch 'master' into op-regex 2019-06-19 11:43:39 +02:00
Liz Rice
c379df19b0 Merge pull request #316 from cpt-redbeard/master
Adding OCP 3.11
2019-06-18 07:40:18 -07:00
pthomson
2275eea93f Adding OCP 3.11
Adding OCP 3.11
2019-06-17 13:44:35 -04:00
Liz Rice
ec9779f56e Merge pull request #313 from simar7/add-kube-bench-version
kube-bench: add version subcommand
2019-06-17 02:27:27 -07:00
Simarpreet Singh
3b7438e2f2 kube-bench: add version subcommand
Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-06-12 01:41:09 -07:00
Liz Rice
c76369fe2c Add missing quote 2019-06-10 20:29:58 -07:00
Liz Rice
7f2e9b5231 Merge branch 'master' into op-regex 2019-06-11 04:28:03 +01:00
Liz Rice
1d7449db34 Merge pull request #309 from simar7/fix-ocp-3.10-yaml
ocp-3.10: Fix malformed yaml and improve TestControls_RunChecks
2019-06-11 04:27:25 +01:00
Simarpreet Singh
5df39eed02 ocp-3.10: Fix malformed yaml and improve TestControls_RunChecks
This improves the TestControls_RunChecks() test by making
more comprehensive assertions on a more fully fledged input yaml

Fixes: https://github.com/aquasecurity/kube-bench/issues/304

Signed-off-by: Simarpreet Singh <simar@linux.com>
2019-06-10 13:39:43 -07:00
wwwil
7efa7b2c35 Add regex to list of compare ops 2019-06-05 15:29:40 +01:00
wwwil
83c7536c8a Add tests for regex test op 2019-06-05 15:29:15 +01:00
Liz Rice
46baf8f8b5 Merge pull request #296 from aquasecurity/Config-doc
Document version-specific config files
2019-06-05 12:52:32 +02:00
Liz Rice
4f79d62149 Merge branch 'master' into Config-doc 2019-06-05 12:45:27 +02:00
Liz Rice
268fafd495 Merge pull request #300 from danielsagi/add_kubelet_config_path
Added another kubelet config file to node:kubelet:confs
2019-06-05 12:45:07 +02:00
Liz Rice
bab1237a44 Merge branch 'master' into add_kubelet_config_path 2019-06-05 12:27:07 +02:00
Liz Rice
d44f865ef3 Merge pull request #256 from aquasecurity/fix-235
Rationalize and document config
2019-06-05 12:07:17 +02:00
Liz Rice
e3da299e0c Merge branch 'master' into fix-235 2019-06-05 11:42:13 +02:00
Liz Rice
81f0d9c6e3 Merge branch 'master' into Config-doc 2019-06-05 11:41:15 +02:00
Liz Rice
312cdb1c6d Merge pull request #297 from aquasecurity/Openshift-executables
Update openshift executables
2019-06-05 11:40:56 +02:00
Liz Rice
0f12dca76d Merge branch 'master' into Openshift-executables 2019-06-05 11:29:42 +02:00
Liz Rice
87820b9775 Remove duplicate versions section
That info is important enough that it needs to stay in the main README.
I also changed the file title
2019-06-05 10:28:11 +01:00
Abubakr-Sadik Nii Nai Davis
85849a3c1f Add detailed kube-bench config documentation. 2019-06-04 22:25:24 +00:00
Daniel Sagi
43caaab00a added another kubelet config file to paths, in the main config yaml file. default location for gke cluster 2019-06-04 17:16:05 +03:00
wwwil
e4f0f470ee Add regex op to test 2019-06-04 11:38:17 +01:00
Liz Rice
5efb3e3b00 Merge pull request #298 from 030/191-master-node-doc
[GH-191] explained that master nodes cannot be inspected in managed k8s
2019-06-01 17:26:26 +01:00
Liz Rice
27df1f60ed Clarification about worker nodes in managed k8s
Because we don’t want to put people off running kube-bench altogether in these environments
2019-06-01 18:17:09 +02:00
030
9d0e3491a0 [GH-191] explained that master nodes cannot be inspected in managed k8s 2019-06-01 16:40:50 +02:00
Liz Rice
9d577d94b4 Update openshift executables 2019-05-30 23:04:44 +01:00
Liz Rice
df3577519c Document version-specific config files
Values in the version-specific files override the main file
2019-05-30 22:55:48 +01:00
nshauli
e64f61fa7f Add --outputfile flag for writing json results to output file (#295) 2019-05-29 18:05:55 +03:00
Liz Rice
5e80f41066 Merge pull request #292 from aquasecurity/config-improvements
Config improvements
2019-05-28 10:00:34 +02:00
Liz Rice
a8c69b57e8 Merge branch 'master' into config-improvements 2019-05-27 13:10:40 +02:00
Liz Rice
ff6443e279 Merge pull request #284 from yoavAqua/expected-result
Genereate expected result automatically for each test
2019-05-26 18:06:27 +02:00
Yoav Hizkiahou
ddb677bc69 Generate expected result by strings join 2019-05-26 10:15:00 +03:00
Yoav Hizkiahou
d1c3e3163b Genereate expected result automatically for each test 2019-05-26 10:14:25 +03:00
Liz Rice
53ef773944 Merge pull request #281 from yoavAqua/bugfix-no-actual-result
The check's actual result property is now set to be the audit command…
2019-05-24 13:22:42 +02:00
Liz Rice
31019c44da Merge branch 'master' into bugfix-no-actual-result 2019-05-24 13:18:34 +02:00
Liz Rice
ff427f8b0c Merge pull request #282 from yoavAqua/print-actual-result-of-failed-tests
Printing the actual test result of failed tests - when a flag is raised
2019-05-19 17:33:30 +01:00
Liz Rice
91da82aaa8 Merge branch 'master' into print-actual-result-of-failed-tests 2019-05-19 17:28:09 +01:00
Liz Rice
c4be2ee03d Merge pull request #293 from yoavAqua/save-audit-command-in-json
Save the audit command when requesting json output under the "audit" key
2019-05-19 17:24:55 +01:00
Yoav Hizkiahou
e7a8c14715 Save the audit command when requesting json output under the "audit" key 2019-05-19 11:23:44 +03:00
Liz Rice
9f9514d8c6 Merge branch 'master' into print-actual-result-of-failed-tests 2019-05-17 14:49:21 +01:00
Liz Rice
e33e44b676 Correct debug messages 2019-05-17 14:22:04 +01:00
Liz Rice
12e48297a6 Config file improvements
Correct defaults in main config.yaml file
Remove unnecessary overrides in version-specific config.yaml
2019-05-17 14:21:42 +01:00
Yoav Hizkiahou
240c8ad5b0 The check's actual result property is now set to be the audit command's output
fix #280
2019-05-16 10:48:04 +03:00
Liz Rice
74fd7cd595 Merge pull request #290 from aquasecurity/config-improvements
Config improvements
2019-05-15 09:49:52 +01:00
Yoav Hizkiahou
3aa28c4c32 Printing the actual test result of failed tests - when a flag is raised
fix #110
2019-05-15 10:14:11 +03:00
Liz Rice
02d5654cc1 Correct 1.1.14 in 1.13/master.yaml 2019-05-14 19:37:44 +01:00
Liz Rice
caf3fbd0a0 Moving more config into master config file 2019-05-13 18:20:57 +01:00
Liz Rice
c152088254 Merge pull request #279 from leodotcloud/issue_278_remediation
Fixing remediation field for json result
2019-05-10 10:05:59 +01:00
Liz Rice
c361b9b82f Merge branch 'master' into issue_278_remediation 2019-05-10 09:47:57 +01:00
Liz Rice
b9b4d47b3e Merge pull request #286 from CognotektGmbH/dln/kops-paths
Dln/kops paths
2019-05-09 18:21:36 +01:00
daniellohausen
22e835f0f5 Reverted kubelet conf to original value 2019-05-08 13:55:45 +02:00
daniellohausen
7ec10211a5 Added KOPS-specific paths 2019-05-08 13:52:08 +02:00
Murali Paluru
7c6b9680b4 add remediation field 2019-05-05 16:06:13 -07:00
Liz Rice
442447851e Merge pull request #259 from aquasecurity/no-master-binaries
Don't assume master if 0 master binaries specified
2019-05-05 16:02:40 +01:00
Liz Rice
1f67c45fd6 Merge branch 'master' into no-master-binaries 2019-05-05 15:56:53 +01:00
Liz Rice
7d9089d376 Merge pull request #273 from danielpacak/issue-172-filter-cis-checks
Add flags to further filter CIS checks to run
2019-05-05 15:55:39 +01:00
Liz Rice
aebd35a5ab Update copyright date 2019-05-02 18:15:31 -07:00
Liz Rice
8c8ae7ce76 Update copyright date 2019-05-02 18:15:05 -07:00
Liz Rice
0d57a9dff3 Update copyright date 2019-05-02 18:13:25 -07:00
Daniel Pacak
5fb133cd02 Adjust the semantics of scored and unscored flags 2019-05-01 22:52:56 +02:00
Daniel Pacak
306e1960af Add flags to further filter CIS checks to run 2019-05-01 22:52:56 +02:00
Liz Rice
fc536b239b Merge pull request #275 from aquasecurity/fix-270
Fix failing check 1.5.2 in version 1.11
2019-05-01 08:11:21 -07:00
Abubakr-Sadik Nii Nai Davis
fbbf6b37c7 Change test_items in 1.11 master.yaml check 1.5.2 to fix issue with
check failing even when --client-cert-auth is set.
2019-04-30 16:51:10 +00:00
Liz Rice
e5b6603da5 Merge branch 'master' into no-master-binaries 2019-04-24 10:02:32 +01:00
Liz Rice
6d9a3b4888 Merge pull request #260 from aquasecurity/json-config
Json & YAML config, continued
2019-04-24 09:59:10 +01:00
Liz Rice
a800ac6ccc Merge branch 'master' into json-config 2019-04-24 09:29:18 +01:00
Liz Rice
331d64b294 Merge pull request #267 from aquasecurity/lizrice-patch-1
Add OCP info into the README
2019-04-23 17:15:46 +02:00
Liz Rice
ceb44583dd Tidy up a couple of things 2019-04-23 16:07:27 +01:00
Liz Rice
91c6ef2155 Merge branch 'master' into json-config 2019-04-23 13:51:30 +02:00
Liz Rice
f9d0f4acc1 Add OCP info into the README 2019-04-23 11:59:54 +01:00
Liz Rice
ab2001e393 Merge pull request #261 from aquasecurity/yoavrotems-patch-3
update files
2019-04-23 12:54:39 +02:00
Liz Rice
7e8dfbc6ea Fix invalid YAML 2019-04-23 11:41:48 +01:00
Liz Rice
b4419e810f Tiny typo 2019-04-23 11:01:38 +01:00
Liz Rice
d05d71553f Tiny typo 2019-04-23 10:57:15 +01:00
yoavrotems
e70f50b2b5 update files 2019-04-16 06:01:51 +00:00
Liz Rice
a613f6f028 Document job for EKS 2019-04-11 19:00:17 +01:00
Liz Rice
fa60fb68fd Add job for EKS 2019-04-11 18:45:16 +01:00
Liz Rice
27dc75fefa No need for unused master config file.
Better comments in config file
2019-04-11 18:36:30 +01:00
Liz Rice
de623220e1 No need to load config just to check if components are running.
This also allows for there to be no master.yaml file, for environments where such a thing doesn’t need to exist
2019-04-11 18:34:22 +01:00
Liz Rice
248942e2fa No need to load config just to check if components are running.
This also allows for there to be no master.yaml file, for environments where such a thing doesn’t need to exist
2019-04-11 18:31:26 +01:00
Liz Rice
596dae03d9 Don't assume master if 0 master binaries specified 2019-04-11 17:19:50 +01:00
Liz Rice
01179963ce Don't assume master if 0 master binaries specified 2019-04-11 17:15:50 +01:00
Liz Rice
902a10f1c7 Just have one path for both json and yaml 2019-04-11 17:09:33 +01:00
Liz Rice
9b034024a7 Complete merge where test numbers changes 2019-04-11 10:21:19 +01:00
Liz Rice
c887794807 Merge branch 'master' into feature/json-config 2019-04-11 10:03:07 +01:00
Liz Rice
d30786da4a Merge pull request #258 from aquasecurity/fix-241
Add ":" as a valid flag-value separator for tests
2019-04-11 09:37:39 +01:00
Liz Rice
c03e958311 Merge branch 'master' into fix-241 2019-04-11 09:34:02 +01:00
Liz Rice
241972c659 Merge pull request #249 from aquasecurity/document-output
Document output states
2019-04-11 09:18:34 +01:00
Liz Rice
d93ed0acca Merge branch 'master' into fix-241 2019-04-11 09:05:18 +01:00
Liz Rice
b5f3299e92 Merge branch 'master' into document-output 2019-04-11 09:04:04 +01:00
Liz Rice
588d75d20d Merge pull request #251 from aquasecurity/version-mapping
Add CIS & Kubernetes version mapping to README
2019-04-11 09:03:44 +01:00
Abubakr-Sadik Nii Nai Davis
4b8a7ffbe1 Add ":" as a valid flag-value separator for tests
This is useful for checking values in YAML (possibly JSON) kubernetes config files.
2019-04-10 22:47:26 +00:00
Liz Rice
651b72f7d1 Merge branch 'master' into document-output 2019-04-10 08:45:55 +01:00
Liz Rice
0c40532e76 Merge branch 'master' into version-mapping 2019-04-10 08:31:04 +01:00
Liz Rice
54502c5f75 Merge pull request #247 from aquasecurity/yoavrotems-patch-2
Update master.yaml
2019-03-27 14:24:03 +00:00
Liz Rice
df556c2f42 Add CIS & Kubernetes version mapping to README 2019-03-27 14:21:22 +00:00
Liz Rice
488f5221ef Document output states
Also describe how tests can be omitted by editing the YAML
2019-03-26 10:37:17 +00:00
Liz Rice
b1ce0a9a75 Merge branch 'master' into yoavrotems-patch-2 2019-03-26 09:51:03 +00:00
Liz Rice
0f86bfc060 Merge pull request #246 from aquasecurity/yoavrotems-patch-1
Update master.yaml
2019-03-26 09:41:40 +00:00
yoavrotems
d059196b71 Update master.yaml
Fix 1.1.23 to check *if* --service-account-lookup argument is set and if so then if it's equal to true
2019-03-25 14:41:06 +02:00
yoavrotems
a85e5a7759 Update master.yaml
Fix title of 1.4.21 from 644 to 600 according to cis benchmark
2019-03-25 14:33:52 +02:00
Florent Delannoy
abfc38d672 Update documentation after review 2019-03-21 15:05:20 +00:00
Florent Delannoy
4d3144ca21 Support JSON and YAML configuration
Support new configuration options besides --flags:
- JSON file through `jsonpath`
- YAML file through `yamlpath`

These new options are fully backwards-compatible with the existing
tests.

Added a new profile, 1.11-json, that expects a JSON kubelet
configuration file and scores accordingly. This profile is compatible
with EKS.
2019-03-21 12:13:31 +00:00
Liz Rice
573136a700 Merge pull request #238 from Kuqd/features/autodetect-nodetype
Adds master node detection - thanks @Kuqd!
2019-03-18 18:43:13 +00:00
Liz Rice
9246be924d Merge branch 'master' into features/autodetect-nodetype 2019-03-13 20:36:19 -07:00
Cyril Tovena
5baf81a70a Adds master node detection and a root command that automatically detect checks to run.
The root command will run node checks and if possible master checks.
I've also added some Makefile targets to improve local testing and improve the documentation.
2019-03-12 19:32:05 -04:00
Liz Rice
c4c0d911d4 Merge pull request #237 from aquasecurity/openshift
Update openshift executable config
2019-03-07 14:53:22 +00:00
Liz Rice
9b3628e76a Update openshift executable config for #236 2019-03-07 11:18:06 +00:00
Liz Rice
8745df170a Merge pull request #233 from aquasecurity/clean-ocp-configs
Clean up OCP benchmark config.
2019-03-07 09:30:18 +00:00
Liz Rice
1ead9e1d71 Merge branch 'master' into clean-ocp-configs 2019-03-07 09:22:47 +00:00
Liz Rice
772d2e26b4 Merge pull request #226 from aquasecurity/add-new-cfg-version1.4
add new config files from the new CIS Kubernetes Benchmark
2019-03-06 13:35:17 +00:00
Abubakr-Sadik Nii Nai Davis
53ed68a0b2 Clean up OCP benchmark config.
The OCP benchmarks uses configs for only binary component variable names.
This commit cleans up the OCP config by removing all configuration
except those component binaries required to run kube-bench on OCP
installations and adds missing ones.
2019-03-06 12:02:58 +00:00
yoavrotems
c6102f0a1b Fix the files
Fix the start from 1.11 to 1.13 and adding changes from pull #227, and pull #228.
2019-03-06 11:26:36 +00:00
yoavrotems
e534392525 Delete node.yaml
replace with the new node.yaml file
2019-03-06 13:24:14 +02:00
yoavrotems
5f09ecef44 Delete master.yaml
replace with the new master.yaml file
2019-03-06 13:23:49 +02:00
yoavrotems
a7d9e06c1b Delete config.yaml
replace with the new config.yaml file
2019-03-06 13:23:18 +02:00
yoavrotems
50f22e7f13 Merge branch 'master' into add-new-cfg-version1.4 2019-03-06 11:16:36 +00:00
Liz Rice
2d4019aabe Merge pull request #228 from aquasecurity/fix-208
Fix issues with checks for kubelet configuration files
2019-03-03 11:10:05 +00:00
Liz Rice
dd8e7ec874 Merge branch 'master' into fix-208 2019-03-03 09:45:16 +00:00
Abubakr-Sadik Nii Nai Davis
d255b49d4b Revert 1.8 config file. 2019-03-02 17:20:46 +00:00
Liz Rice
0a58805cdb Merge pull request #227 from aquasecurity/fix-false-detections
Only find flags on the process we really want
2019-02-28 10:48:23 +08:00
Liz Rice
c18d8a2234 Merge branch 'master' into fix-false-detections 2019-02-28 10:38:41 +08:00
Abubakr-Sadik Nii Nai Davis
a88b0703d8 Add kubeconfig variable substitution for kubelet and proxy.
There are checks for the kubeconfig for both kubelet and proxy which
the current kube-bench implementation does not check for properly.
kube-bench checks the wrong files.

This PR adds support for variable substitution for all the config file
types are that should be checked in the CIS benchmarks.

This PR also fixes a buggy in CIS 1.3.0 check 2.2.9, which checks for
ownership of the kubelet config file /var/lib/kubelet/config.yaml but
recommends changing ownership of kubelet kubeconfig file
/etc/kubernetes/kubelet.conf as remediation.
2019-02-27 22:15:14 +00:00
Abubakr-Sadik Nii Nai Davis
3f98c1def2 Fix wrong reference to kubelet.config in node checks.
This fix applies to only checks for kubernetes versions 1.8 and 1.11.
See https://github.com/aquasecurity/kube-bench/pull/208.
2019-02-27 22:14:19 +00:00
Liz Rice
d712db47a2 Only find flags on the process we really want 2019-02-28 01:33:21 +08:00
yoavrotems
82150fdc63 add new config files from the new CIS Kubernetes Benchmark
there is a new update at CIS_Kubernetes_Benchmark_v1.4.0 for Kubernetes 1.13
2019-02-27 10:39:32 +00:00
Liz Rice
c824daeb15 Merge pull request #222 from nshauli/search_for_kubelet_binary_when_not_in_path
search for the kubelet binary when it is not in the path
2019-02-19 16:07:20 +00:00
nshauli
e93bfc1aac search for the kubelet binary when it is not in the path 2019-02-19 16:38:10 +02:00
Liz Rice
da09e6513a Merge pull request #218 from yoavAqua/bugfix-log-warnings-instead-of-print
Bugfix: Logging warning instead of printing
2019-02-19 13:48:30 +00:00
Liz Rice
7626dc2705 Merge branch 'master' into bugfix-log-warnings-instead-of-print 2019-02-19 13:44:23 +00:00
Yoav Hizkiahou
082e9cf7e9 Bugfix: Logging warning instead of printing
Made all the warnings to be logged and not printed, so when using the json flag the output will be only in json format.

fix #217
2019-02-19 14:39:55 +02:00
Liz Rice
2d4c7e8b42 Merge pull request #212 from aquasecurity/ocp-configs
OCP benchmarks and configs
2019-02-18 09:31:45 +00:00
Liz Rice
cd231106cc Improve comment
Tests could easily be marked "skip" because the user doesn't want to run them in their environment, and in this common case the set of tests will be non-nil
2019-02-18 08:46:26 +00:00
Liz Rice
db962a0ad9 Fix merge of skip check 2019-02-18 08:40:57 +00:00
Abubakr-Sadik Nii Nai Davis
911e9051dc Merge remote-tracking branch 'origin/master' into ocp-configs 2019-02-15 19:48:53 +00:00
Abubakr-Sadik Nii Nai Davis
e899e941f7 Add OCP 3.10 benchmarks. 2019-02-15 19:44:39 +00:00
Weston Steimel
42ed8628de Only get runningVersion if --version has not been provided
Signed-off-by: Weston Steimel <weston.steimel@gmail.com>
2019-02-15 19:43:13 +00:00
Liz Rice
dc8dcfbf8c Merge pull request #211 from yoavAqua/support-skip-flag
Type skip and not scored checks
2019-01-29 23:14:05 +02:00
Yoav Hizkiahou
49f745af8e Support new check type - skip:
If a check is marked with type "skip", it will be marked as Info.

Support scored property:
If a check is not scored and is not marked with type skip, it will be marked as Warn.
2019-01-29 19:05:12 +02:00
Liz Rice
ba437d500a Merge pull request #206 from westonsteimel/no_runningversion_if_version_set
Only get runningVersion if --version has not been provided
2019-01-24 12:00:59 +01:00
Weston Steimel
42f4152058 Only get runningVersion if --version has not been provided
Signed-off-by: Weston Steimel <weston.steimel@gmail.com>
2019-01-24 00:34:09 +00:00
Liz Rice
8dabb7dc37 Merge pull request #201 from aquasecurity/yam-comment
Comment why we mount /usr/bin
2019-01-22 09:49:25 +01:00
Liz Rice
f2062e81a1 Comment why /usr/bin is mounted 2019-01-17 11:36:25 +00:00
Liz Rice
528bcfbffe Update job-node.yaml 2019-01-17 11:34:26 +00:00
Liz Rice
3422b9102f Add comment for why /usr/bin is mounted 2019-01-17 11:33:35 +00:00
Liz Rice
86b126ad2b Create NOTICE (#199)
* Create NOTICE

* Update NOTICE
2019-01-16 10:53:07 +02:00
Liz Rice
827945f7fb Merge pull request #200 from spuder/patch-1
warn osx limitation
2019-01-15 11:11:57 +00:00
Liz Rice
79427e185e Merge branch 'master' into patch-1 2019-01-15 11:05:27 +00:00
Liz Rice
6b9ceae9d4 True for Windows too 2019-01-15 11:05:04 +00:00
Liz Rice
fbd6eb8ff5 Merge pull request #198 from aquasecurity/mount-volumes
For #197 - create job YAML files that mount host volumes as needed
2019-01-15 11:03:06 +00:00
Spencer Owen
2a9a02f25b warn osx limitation 2019-01-14 10:41:19 -07:00
Liz Rice
8021610e46 For #197 - create job YAML files that mount host volumes as needed 2019-01-11 18:44:13 +00:00
Liz Rice
2eef3e8ad2 Merge pull request #193 from maxbischoff/patch-1
Changed 1.1.14 to not fail when flag is not set
2019-01-09 10:21:27 +00:00
Maximilian Bischoff
791fbba9e7 Changed 1.1.14 to not fail when flag is not set
Added another test item that checks whether --disable-admission-plugins is not set and an "or" bin_op. 
This causes check 1.1.14 to be successful when the flag is not set, while still failing when the flag is set and includes the value NamespaceLifecycle
2019-01-08 13:58:41 +01:00
Liz Rice
f6cab11357 Merge pull request #187 from martinmosegaard/doc-kubectl-host-pid
Document limitation of running with kubectl
2019-01-02 11:05:32 +00:00
Liz Rice
9f2899027e Merge branch 'master' into doc-kubectl-host-pid 2019-01-02 10:59:19 +00:00
Liz Rice
313fe038f6 Merge pull request #188 from martinmosegaard/rm-space-tls-cipher
Remove spaces in remediation command for tls-cipher-suites
2019-01-02 10:59:07 +00:00
Liz Rice
2d721ed4ad Merge branch 'master' into rm-space-tls-cipher 2019-01-02 10:53:29 +00:00
Liz Rice
799b928054 Merge pull request #189 from Congelli501/patch-1
Typo: trailing whitespace for rule text
2019-01-02 10:53:16 +00:00
Liz Rice
3a662b3ff6 Merge branch 'master' into doc-kubectl-host-pid 2019-01-02 10:53:04 +00:00
Liz Rice
f902b30110 Merge branch 'master' into rm-space-tls-cipher 2019-01-02 10:31:34 +00:00
Liz Rice
b52a88214f Merge branch 'master' into patch-1 2019-01-02 10:30:33 +00:00
Liz Rice
bfdd921f3d Merge pull request #190 from Congelli501/patch-2
Advise the use to mount /etc & /var read only for docker usage
2019-01-02 10:29:58 +00:00
Colin GILLE
af7ad90477 Advise the use to mount /etc & /var read only for docker usage 2018-12-31 16:39:31 +01:00
Colin GILLE
ffe7ffb3d3 Type: trailing whitespace for rule text 2018-12-31 16:36:15 +01:00
Martin Mosegaard Amdisen
fd120d0adf Remove spaces in remediation command for tls-cipher-suites
Makes it easier to copy-paste the remediation. Matches the other occurences
of tls-cipher-suites in the configuration.
2018-12-27 14:48:21 +01:00
Martin Mosegaard Amdisen
ba03d8f64b Document limitation of running with kubectl
Once the master node recommended check:

1.1.12 Ensure that the admission control plugin DenyEscalatingExec is set

has been followed, it is no longer possible to run kube-bench itself using kubectl.
2018-12-27 13:10:00 +01:00
Liz Rice
21f7902288 Merge pull request #183 from s1lv3r40/master
Fixing Node Check - 2.1.15 typos
2018-12-21 11:31:43 +00:00
Liz Rice
26e28b8897 Merge branch 'master' into master 2018-12-21 11:26:53 +00:00
Liz Rice
ae1812b4db Merge pull request #185 from maxbischoff/patch-1
Added missing "=" to master.yaml
2018-12-21 11:26:40 +00:00
Liz Rice
1534a4aea8 Merge branch 'master' into patch-1 2018-12-21 11:20:13 +00:00
Liz Rice
28a57ff1a3 Merge branch 'master' into master 2018-12-21 11:18:26 +00:00
Liz Rice
41fe066039 Merge pull request #186 from seslattery/seslattery-patch-1
Fix typo on README.md
2018-12-21 11:17:31 +00:00
Sean Slattery
5ca498cd50 Fix typo on README.md 2018-12-20 11:19:44 -08:00
Maximilian Bischoff
e81b785bf8 Added missing "=" to master.yaml
In the remediation of 1.1.11 the flag --enable-admission-plugins was missing a =
2018-12-19 18:20:23 +01:00
Vladimir Dimov
645d23e1ec fixing typos 2.1.15 2018-11-28 13:14:49 +02:00
Liz Rice
52d6ac717d Merge pull request #181 from aquasecurity/config-file-location-mount
read config files from host /etc
2018-11-20 19:49:37 +00:00
Liz Rice
bdbbe41b69 Also /var 2018-11-20 13:22:36 +00:00
Liz Rice
ba9985047c read config files from host /etc
I don't see how kube-bench can check the permissions on files unless it has access to them on the host, so I think we need to be mounting the /etc directory from the host
2018-11-20 10:18:06 +00:00
Liz Rice
5fe702edbe Merge pull request #175 from aquasecurity/fix-2.1.8
Fix node check 2.1.8
2018-11-08 12:22:17 +00:00
Liz Rice
6e80b6477a Merge branch 'master' into fix-2.1.8 2018-11-08 11:41:54 +00:00
Liz Rice
e1f5bb1ace Merge pull request #173 from aquasecurity/fix-1.1.37
Fix check 1.1.37.
2018-11-08 11:40:06 +00:00
Liz Rice
6d8788071f Merge branch 'master' into fix-2.1.8 2018-11-08 11:38:34 +00:00
Liz Rice
f42243e9b5 Merge branch 'master' into fix-1.1.37 2018-11-08 11:35:58 +00:00
Liz Rice
d004acdbba Merge pull request #174 from johscheuer/correct-readme
Correct readme for 1.11 example
2018-11-08 11:33:50 +00:00
Abubakr-Sadik Nii Nai Davis
0a5358665e By default --make-iptables-util-chain is true, so PASS if this flag is not set. 2018-11-07 23:57:38 +00:00
Abubakr-Sadik Nii Nai Davis
4f40a11e84 Change binary op from and to or. 2018-11-07 23:54:41 +00:00
Johannes M. Scheuermann
b3b3cb819a Correct readme for 1.11 example
Signed-off-by: Johannes M. Scheuermann <joh.scheuer@gmail.com>
2018-11-07 21:51:52 +01:00
Abubakr-Sadik Nii Nai Davis
c0f56e966a Fix check 1.1.37. 2018-11-06 14:35:45 +00:00
Liz Rice
ed7f6cf3fc Merge pull request #171 from nickperry/master
Fixes https://github.com/aquasecurity/kube-bench/issues/170
2018-11-01 09:57:14 +00:00
Nick Perry
e083c8f0a3 Fixes https://github.com/aquasecurity/kube-bench/issues/170
Correcting the logic of 1.1.14 for Kubernetes 1.11.
2018-10-30 23:40:41 +00:00
Liz Rice
77481e8739 Merge pull request #169 from mikekim/fix-1.3.7
Fixing 1.3.7 on 1.11 master.
2018-10-29 12:12:39 +00:00
Liz Rice
48489637c5 Merge branch 'master' into fix-1.3.7 2018-10-29 12:08:22 +00:00
Liz Rice
15537cb42b Merge pull request #168 from mikekim/fix-dollar-in-paths
Fixing checks 2.2.9 and 2.2.10 on 1.11 nodes.
2018-10-27 09:31:55 +01:00
Michal Jankowski
9988503223 Fixing 1.3.7 on 1.11 master.
With multiple test items operator defaults to "and". In case of 1.3.7
the tests check whether --address flag is either set to 127.0.0.1 or not
set at all. Those conditions cannot be met at the same time.
2018-10-25 15:32:41 -07:00
Michal Jankowski
5f254de415 Fixing checks 2.2.9 and 2.2.10 on 1.11 nodes.
Path to kubelet configuration was accidentally prefixed with a dollar
symbol (probably as a result of copying some other test that used
variable name).
After removing the dollar sign from paths both checks pass on conforming
deployment.
2018-10-24 17:06:21 -07:00
Liz Rice
64f4f638e9 Merge pull request #167 from aquasecurity/fix-issue-with-kubelet-config-and-unitfile-checks
Fix issue with kubelet config and unitfile checks
2018-10-23 14:45:19 +01:00
Abubakr-Sadik Nii Nai Davis
97623aea05 Update kubernetes node benchmark to check kubelet systemd unitfile.
Also clean up the config file for 1.11 a bit.
2018-10-23 02:30:08 +00:00
Abubakr-Sadik Nii Nai Davis
ed21839464 Add getServiceFiles function.
The CIS benchmark check for node checks 2 config files for kubelet:
  - kubelet config file (kubelet.conf)
  - kubelet systemd unitfile (10-kubeadm.conf)

The getServiceFiles function gets candidates for kubelet systemd
unitfile and returns valid untifiles.
2018-10-23 02:26:38 +00:00
Liz Rice
277ec9c823 Merge pull request #163 from noqcks/master
Update tests for Kubernetes 1.11 - thank you @noqcks!
2018-10-13 22:09:24 +01:00
Abubakr-Sadik Nii Nai Davis
b1369832bc A few corrections to node tests. (#2)
* Add a few corrections.

* Add a few corrections to node test file.
2018-10-13 15:48:50 -04:00
Abubakr-Sadik Nii Nai Davis
934b4aef96 Add a few corrections. (#1) 2018-10-12 10:22:08 -04:00
noqcks
e85de9e8af fix simple errors 2018-10-09 19:16:08 -04:00
noqcks
ded5aff482 update README 2018-10-09 18:58:30 -04:00
noqcks
b3a115963b adding 1.11 config and node checks 2018-10-09 18:57:37 -04:00
noqcks
e5c05a97f7 updating README with 1.11 updates 2018-10-09 18:56:48 -04:00
noqcks
ba5ec8d4be adding 1.11 master configuration 2018-10-09 18:34:52 -04:00
Liz Rice
d56afd4104 Merge pull request #159 from lukebond/master
Update README.md
2018-09-04 08:37:04 +01:00
Luke Bond
8894b1dc4f Update README.md
Specify `-t` to get colour in the Docker output.
Added a note about mounting kubectl or kubelet to get the version.
2018-09-03 23:05:48 +01:00
Liz Rice
ff59938f94 Merge pull request #155 from bvwells/cis-benchmark-link
Add link to CIS kubernetes benchmark
2018-08-20 09:14:37 +01:00
bvwells
cc43fcbb7e Add link to CIS kubernetes benchmark 2018-08-10 20:55:02 +01:00
Liz Rice
2f4f55a363 Merge pull request #149 from aquasecurity/itai_cis_results
Support actual result in json output.
2018-07-31 18:18:51 +01:00
Itai Ben-Natan
e9076233dd Support actual result in json output.
This commit adds the actual value of the result
of the value which was returned by the test.
2018-07-30 14:19:18 +00:00
Liz Rice
b1e41d345f Merge pull request #147 from aquasecurity/version-fix
Shouldn't need kubelet or kubectl if version specified
2018-07-28 14:53:56 +01:00
Liz Rice
ccc2b6c9ae Shouldn't need kubelet or kubectl if version specified 2018-07-26 12:03:09 +01:00
55 changed files with 8813 additions and 5079 deletions

9
.gitignore vendored
View File

@@ -2,3 +2,12 @@ kube-bench
*.swp
vendor
dist
.vscode/
hack/kind.test.yaml
coverage.txt
.idea/
# Directory junk file
.DS_Store
thumbs.db

View File

@@ -1,8 +1,9 @@
env:
- GO111MODULE=on
builds:
- main: main.go
binary: kube-bench
goos:
- darwin
- linux
goarch:
- amd64

View File

@@ -1,8 +1,6 @@
---
language: go
sudo: required
services:
- docker
@@ -13,21 +11,28 @@ before_install:
- sudo apt-get -qq update
- sudo apt-get install -y rpm
- gem install --no-ri --no-rdoc fpm
install:
- go get -v github.com/golang/dep/cmd/dep
- dep ensure -v -vendor-only
- go get -t -v ./...
script:
- go test ./...
- GO111MODULE=on go test ./...
- docker build --tag kube-bench .
- docker run -v `pwd`:/host kube-bench install
- test -d cfg
- test -f kube-bench
- make tests
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
- bash <(curl -s https://codecov.io/bash)
deploy:
- provider: script
skip_cleanup: true
script: curl -sL https://git.io/goreleaser | bash
on:
tags: true
condition: "$TRAVIS_OS_NAME = linux"
env:
global:
secure: mb8AYZKDo6hkKN+2F9ldXcw27Yn2AfxpXvKlD8GD7NdGOI+TaiSFbE0I+qqTa/1DqcRekCQwqN7OG/17s9JDkgzUXYuYUGlVUOM4WbeJoSlzJFIOh9r9R/JddluYJohypgkE20IBHIrEHq5sY0Nn1Pl9WgSQFaVcQjxkX009AOuVjN0o5HcoXsb5hAzvHrpoSPkcSSqq7VWab60TgUttVaRlZSGwGdSYQEqk5TdO0hWHuXyxaaEPybgFIyZLLbxPS4JmMz8n3Sngetpw9Jgc+V9Fc7wKXpjvZZ33SpArG5p5ZFFu2YQOXFLZth9qtQOjduQ2gU1kHN6WjWnJ8QX2s8vmU38Tk19kd5i+mz9dvc87IdBvmTIqVYSpM6AAYa2osBGP3f97Rj2S68lTad4ecSVyHdsjz56vdE3ZH4wskswmogbKkVdvO4biPHxT6odszBxYLEJuRJyZ7ckXd52MCzqAUPrw7YUuH8N1mLIlf7V5bW5R+q4DlKw774zxnHiWrymXGvlINSrB0qxBn8Fii6ib+Pacl3PuqSumCcgIHlVjqrzIXaqcTMn2/ABZYC99mralGvwA/EgNa8CBKB5evMCEwWa5Ntvcs2I2DFcO5Q2WzN4H0YScyAzzCzK7/3hWJE/rUIJntwiSXkV3MSa1yxWSGGH8F1lcz+lzgTBm/MU=
matrix:
- GO111MODULE=on

23
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,23 @@
Thank you for taking an interest in contributing to kube-bench !
## Issues
- Feel free to open an issue for any reason as long as you make it clear if the issue is about a bug/feature/question/comment.
- Please spend some time giving due diligence to the issue tracker. Your issue might be a duplicate. If it is, please add your comment to the existing issue.
- Remember, users might be searching for your issue in the future. So please give it a meaningful title to help others.
- The issue should clearly explain the reason for opening the proposal if you have any, along with any relevant technical information.
- For questions and bug reports, please include the following information:
- version of kube-bench you are running (from kube-bench version) along with the command line options you are using.
- version of Kubernetes you are running (from kubectl version or oc version for Openshift).
- Verbose log output, by setting the `-v 10` command line option.
## Pull Requests
1. Every Pull Request should have an associated Issue, unless you are fixing a trivial documentation issue.
1. Your PR is more likely to be accepted if it focuses on just one change.
1. Describe what the PR does. There's no convention enforced, but please try to be concise and descriptive. Treat the PR description as a commit message. Titles that start with "fix"/"add"/"improve"/"remove" are good examples.
1. Please add the associated Issue in the PR description.
1. There's no need to add or tag reviewers.
1. If a reviewer commented on your code or asked for changes, please remember to mark the discussion as resolved after you address it. PRs with unresolved issues should not be merged (even if the comment is unclear or requires no action from your side).
1. Please include a comment with the results before and after your change.
1. Your PR is more likely to be accepted if it includes tests (We have not historically been very strict about tests, but we would like to improve this!).

View File

@@ -1,13 +1,13 @@
FROM golang:1.9 AS build
FROM golang:1.12 AS build
WORKDIR /go/src/github.com/aquasecurity/kube-bench/
ADD Gopkg.toml Gopkg.lock ./
RUN go get -v github.com/golang/dep/cmd/dep && dep ensure -v -vendor-only
ADD go.mod go.sum ./
ADD main.go .
ADD check/ check/
ADD cmd/ cmd/
RUN CGO_ENABLED=0 go install -a -ldflags '-w'
ARG KUBEBENCH_VERSION
RUN GO111MODULE=on CGO_ENABLED=0 go install -a -ldflags "-X github.com/aquasecurity/kube-bench/cmd.KubeBenchVersion=${KUBEBENCH_VERSION} -w"
FROM alpine:3.7 AS run
FROM alpine:3.10 AS run
WORKDIR /opt/kube-bench/
# add GNU ps for -C, -o cmd, and --no-headers support
# https://github.com/aquasecurity/kube-bench/issues/109

153
Gopkg.lock generated
View File

@@ -1,153 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/fatih/color"
packages = ["."]
revision = "570b54cabe6b8eb0bc2dfce68d964677d63b5260"
version = "v1.5.0"
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
revision = "4da3e2cfbabc9f751898f250b49f2439785783a1"
[[projects]]
branch = "master"
name = "github.com/golang/glog"
packages = ["."]
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
]
revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
name = "github.com/jinzhu/gorm"
packages = [
".",
"dialects/postgres"
]
revision = "5174cc5c242a728b435ea2be8a2f7f998e15429b"
version = "v1.0"
[[projects]]
name = "github.com/jinzhu/inflection"
packages = ["."]
revision = "1c35d901db3da928c72a72d8458480cc9ade058f"
[[projects]]
name = "github.com/lib/pq"
packages = [
".",
"hstore",
"oid"
]
revision = "83612a56d3dd153a94a629cd64925371c9adad78"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "5411d3eea5978e6cdc258b30de592b60df6aba96"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "57fdcb988a5c543893cc61bce354a6e24ab70022"
[[projects]]
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "06020f85339e21b2478f756a78e295255ffa4d6a"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "0131db6d737cfbbfb678f8b7d92e55e27ce46224"
[[projects]]
name = "github.com/spf13/afero"
packages = [
".",
"mem"
]
revision = "57afd63c68602b63ed976de00dd066ccb3c319db"
[[projects]]
name = "github.com/spf13/cast"
packages = ["."]
revision = "acbeb36b902d72a7a4c18e8f3241075e7ab763e4"
version = "v1.1.0"
[[projects]]
name = "github.com/spf13/cobra"
packages = ["."]
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
[[projects]]
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "12bd96e66386c1960ab0f74ced1362f66f552f7b"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "4c012f6dcd9546820e378d0bdda4d8fc772cdfea"
[[projects]]
name = "github.com/spf13/viper"
packages = ["."]
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
version = "v1.0.0"
[[projects]]
name = "golang.org/x/sys"
packages = ["unix"]
revision = "e24f485414aeafb646f6fca458b0bf869c0880a1"
[[projects]]
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm"
]
revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "c95af922eae69f190717a0b7148960af8c55a072"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "8d9a1b665b338530deef434f168913ba1184f835aa5bfed3a213a14c613bc17e"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,23 +0,0 @@
[[constraint]]
name = "github.com/fatih/color"
version = "1.5.0"
[[constraint]]
branch = "master"
name = "github.com/golang/glog"
[[constraint]]
name = "github.com/jinzhu/gorm"
version = "1.0.0"
[[constraint]]
name = "github.com/spf13/cobra"
version = "0.0.1"
[[constraint]]
name = "github.com/spf13/viper"
version = "1.0.0"
[prune]
go-tests = true
unused-packages = true

5
NOTICE Normal file
View File

@@ -0,0 +1,5 @@
kube-bench
Copyright 2017-2019 Aqua Security Software Ltd.
This product includes software developed by Aqua Security (https://aquasec.com).

318
README.md
View File

@@ -1,67 +1,201 @@
[![Build Status](https://travis-ci.org/aquasecurity/kube-bench.svg?branch=master)](https://travis-ci.org/aquasecurity/kube-bench)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aquasecurity/kube-bench/blob/master/LICENSE)
[![Docker image](https://images.microbadger.com/badges/image/aquasec/kube-bench.svg)](https://microbadger.com/images/aquasec/kube-bench "Get your own image badge on microbadger.com")
[![Source commit](https://images.microbadger.com/badges/commit/aquasec/kube-bench.svg)](https://microbadger.com/images/aquasec/kube-bench)
[![Coverage Status][cov-img]][cov]
[cov-img]: https://codecov.io/github/aquasecurity/kube-bench/branch/master/graph/badge.svg
[cov]: https://codecov.io/github/aquasecurity/kube-bench
<img src="images/kube-bench.png" width="200" alt="kube-bench logo">
kube-bench is a Go application that checks whether Kubernetes is deployed securely by running the checks documented in the CIS Kubernetes Benchmark.
kube-bench is a Go application that checks whether Kubernetes is deployed securely by running the checks documented in the [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/).
Tests are configured with YAML files, making this tool easy to update as test specifications evolve.
Note that it is impossible to inspect the master nodes of managed clusters, e.g. GKE, EKS and AKS, using kube-bench as one does not have access to such nodes, although it is still possible to use kube-bench to check worker node configuration in these environments.
Tests are configured with YAML files, making this tool easy to update as test specifications evolve.
![Kubernetes Bench for Security](https://raw.githubusercontent.com/aquasecurity/kube-bench/master/images/output.png "Kubernetes Bench for Security")
Table of Contents
=================
- [Table of Contents](#table-of-contents)
- [CIS Kubernetes Benchmark support](#cis-kubernetes-benchmark-support)
- [Installation](#installation)
- [Running kube-bench](#running-kube-bench)
- [Running inside a container](#running-inside-a-container)
- [Running in a Kubernetes cluster](#running-in-a-kubernetes-cluster)
- [Running in an EKS cluster](#running-in-an-eks-cluster)
- [Installing from a container](#installing-from-a-container)
- [Installing from sources](#installing-from-sources)
- [Running on OpenShift](#running-on-openshift)
- [Output](#output)
- [Configuration](#configuration)
- [Test config YAML representation](#test-config-yaml-representation)
- [Omitting checks](#omitting-checks)
- [Roadmap](#roadmap)
- [Testing locally with kind](#testing-locally-with-kind)
- [Contributing](#contributing)
- [Bugs](#bugs)
- [Features](#features)
- [Pull Requests](#pull-requests)
## CIS Kubernetes Benchmark support
kube-bench supports the tests for multiple versions of Kubernetes (1.6, 1.7 and 1.8) as defined in the CIS Benchmarks 1.0.0, 1.1.0 and 1.2.0 respectively. It will determine the test set to run based on the Kubernetes version running on the machine.
kube-bench supports the tests for Kubernetes as defined in the CIS Benchmarks 1.3.0 to 1.4.1 respectively.
| CIS Kubernetes Benchmark | kube-bench config | Kubernetes versions |
|---|---|---|
| 1.3.0| cis-1.3 | 1.11-1.12 |
| 1.4.1| cis-1.4 | 1.13- |
By default, kube-bench will determine the test set to run based on the Kubernetes version running on the machine.
There is also preliminary support for Red Hat's OpenShift Hardening Guide for 3.10 and 3.11. Please note that kube-bench does not automatically detect OpenShift - see below.
## Installation
You can choose to
You can choose to
* run kube-bench from inside a container (sharing PID namespace with the host)
* run a container that installs kube-bench on the host, and then run kube-bench directly on the host
* install the latest binaries from the [Releases page](https://github.com/aquasecurity/kube-bench/releases),
* install the latest binaries from the [Releases page](https://github.com/aquasecurity/kube-bench/releases),
* compile it from source.
## Running kube-bench
If you run kube-bench directly from the command line you may need to be root / sudo to have access to all the config files.
kube-bench automatically selects which `controls` to use based on the detected
node type and the version of Kubernetes a cluster is running. This behavior
can be overridden by specifying the `master` or `node` subcommand and the
`--version` flag on the command line.
The Kubernetes version can also be set with the `KUBE_BENCH_VERSION` environment variable.
The value of `--version` takes precedence over the value of `KUBE_BENCH_VERSION`.
For example, run kube-bench against a master with version auto-detection:
```
kube-bench master
```
Or run kube-bench against a worker node using the tests for Kubernetes version 1.13:
```
kube-bench node --version 1.13
```
`kube-bench` will map the `--version` to the corresponding CIS Benchmark version as indicated by the mapping table above. For example, if you specify `--version 1.13`, this is mapped to CIS Benchmark version `cis-1.14`.
Alternatively, you can specify `--benchmark` to run a specific CIS Benchmark version:
```
kube-bench node --benchmark cis-1.4
```
`controls` for the various versions of CIS Benchmark can be found in directories
with same name as the CIS Benchmark versions under `cfg/`, for example `cfg/cis-1.4`.
**Note:** **`It is an error to specify both --version and --benchmark flags together`**
### Running inside a container
You can avoid installing kube-bench on the host by running it inside a container using the host PID namespace.
You can avoid installing kube-bench on the host by running it inside a container using the host PID namespace and mounting the `/etc` and `/var` directories where the configuration and other files are located on the host so that kube-bench can check their existence and permissions.
```
docker run --pid=host aquasec/kube-bench:latest <master|node>
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t aquasec/kube-bench:latest [master|node] --version 1.13
```
You can even use your own configs by mounting them over the default ones in `/opt/kube-bench/cfg/`
> Note: the tests require either the kubelet or kubectl binary in the path in order to auto-detect the Kubernetes version. You can pass `-v $(which kubectl):/usr/bin/kubectl` to resolve this. You will also need to pass in kubeconfig credentials. For example:
```
docker run --pid=host -v path/to/my-config.yaml:/opt/kube-bench/cfg/config.yaml aquasec/kube-bench:latest <master|node>
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -v $(which kubectl):/usr/bin/kubectl -v ~/.kube:/.kube -e KUBECONFIG=/.kube/config -t aquasec/kube-bench:latest [master|node]
```
### Running in a kubernetes cluster
Run the master check
You can use your own configs by mounting them over the default ones in `/opt/kube-bench/cfg/`
```
kubectl run --rm -i -t kube-bench-master --image=aquasec/kube-bench:latest --restart=Never --overrides="{ \"apiVersion\": \"v1\", \"spec\": { \"hostPID\": true, \"nodeSelector\": { \"kubernetes.io/role\": \"master\" }, \"tolerations\": [ { \"key\": \"node-role.kubernetes.io/master\", \"operator\": \"Exists\", \"effect\": \"NoSchedule\" } ] } }" -- master --version 1.8
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t -v path/to/my-config.yaml:/opt/kube-bench/cfg/config.yam -v $(which kubectl):/usr/bin/kubectl -v ~/.kube:/.kube -e KUBECONFIG=/.kube/config aquasec/kube-bench:latest [master|node]
```
Run the node check
### Running in a Kubernetes cluster
You can run kube-bench inside a pod, but it will need access to the host's PID namespace in order to check the running processes, as well as access to some directories on the host where config files and other files are stored.
Master nodes are automatically detected by kube-bench and will run master checks when possible.
The detection is done by verifying that mandatory components for master, as defined in the config files, are running (see [Configuration](#configuration)).
The supplied `job.yaml` file can be applied to run the tests as a job. For example:
```bash
$ kubectl apply -f job.yaml
job.batch/kube-bench created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kube-bench-j76s9 0/1 ContainerCreating 0 3s
# Wait for a few seconds for the job to complete
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kube-bench-j76s9 0/1 Completed 0 11s
# The results are held in the pod's logs
kubectl logs kube-bench-j76s9
[INFO] 1 Master Node Security Configuration
[INFO] 1.1 API Server
...
```
kubectl run --rm -i -t kube-bench-node --image=aquasec/kube-bench:latest --restart=Never --overrides="{ \"apiVersion\": \"v1\", \"spec\": { \"hostPID\": true } }" -- node --version 1.8
You can still force to run specific master or node checks using respectively `job-master.yaml` and `job-node.yaml`.
To run the tests on the master node, the pod needs to be scheduled on that node. This involves setting a nodeSelector and tolerations in the pod spec.
The default labels applied to master nodes has changed since Kubernetes 1.11, so if you are using an older version you may need to modify the nodeSelector and tolerations to run the job on the master node.
### Running in an EKS cluster
There is a `job-eks.yaml` file for running the kube-bench node checks on an EKS cluster. The significant difference on EKS is that it's not possible to schedule jobs onto the master node, so master checks can't be performed
1. To create an EKS Cluster refer to [Getting Started with Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html) in the *Amazon EKS User Guide*
- Information on configuring `eksctl`, `kubectl` and the AWS CLI is within
2. Create an [Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) repository to host the kube-bench container image
```
aws ecr create-repository --repository-name k8s/kube-bench --image-tag-mutability MUTABLE
```
3. Download, build and push the kube-bench container image to your ECR repo
```
git clone https://github.com/aquasecurity/kube-bench.git
$(aws ecr get-login --no-include-email --region <AWS_REGION>)
docker build -t k8s/kube-bench .
docker tag k8s/kube-bench:latest <AWS_ACCT_NUMBER>.dkr.ecr.<AWS_REGION>.amazonaws.com/k8s/kube-bench:latest
docker tag k8s/kube-bench:latest <AWS_ACCT_NUMBER>.dkr.ecr.<AWS_REGION>.amazonaws.com/k8s/kube-bench:latest
docker push <AWS_ACCT_NUMBER>.dkr.ecr.<AWS_REGION>.amazonaws.com/k8s/kube-bench:latest
```
4. Copy the URI of your pushed image, the URI format is like this: `<AWS_ACCT_NUMBER>.dkr.ecr.<AWS_REGION>.amazonaws.com/k8s/kube-bench:latest`
5. Replace the `image` value in `job-eks.yaml` with the URI from Step 4
6. Run the kube-bench job on a Pod in your Cluster: `kubectl apply -f job-eks.yaml`
7. Find the Pod that was created, it *should* be in the `default` namespace: `kubectl get pods --all-namespaces`
8. Retrieve the value of this Pod and output the report, note the Pod name will vary: `kubectl logs kube-bench-<value>`
- You can save the report for later reference: `kubectl logs kube-bench-<value> > kube-bench-report.txt`
### Installing from a container
This command copies the kube-bench binary and configuration files to your host from the Docker container:
** binaries compiled for linux-x86-64 only (so they won't run on macOS or Windows) **
```
docker run --rm -v `pwd`:/host aquasec/kube-bench:latest install
```
You can then run `./kube-bench <master|node>`.
You can then run `./kube-bench [master|node]`.
### Installing from sources
If Go is installed on the target machines, you can simply clone this repository and run as follows (assuming your [$GOPATH is set](https://github.com/golang/go/wiki/GOPATH)):
If Go is installed on the target machines, you can simply clone this repository and run as follows (assuming your [`GOPATH` is set](https://github.com/golang/go/wiki/GOPATH)):
```shell
go get github.com/aquasecurity/kube-bench
@@ -73,79 +207,109 @@ go build -o kube-bench .
# See all supported options
./kube-bench --help
# Run the all checks on a master node
./kube-bench master
# Run all checks
./kube-bench
```
## Running on OpenShift
| OpenShift Hardening Guide | kube-bench config |
|---|---|---|
| ocp-3.10| rh-0.7 |
| ocp-3.11| rh-0.7 |
kube-bench includes a set of test files for Red Hat's OpenShift hardening guide for OCP 3.10 and 3.11. To run this you will need to specify `--benchmark rh-07`, or `--version ocp-3.10` or `--version ocp-3.11`
when you run the `kube-bench` command (either directly or through YAML).
## Output
There are three output states:
- [PASS] and [FAIL] indicate that a test was run successfully, and it either passed or failed.
- [WARN] means this test needs further attention, for example it is a test that needs to be run manually.
- [INFO] is informational output that needs no further action.
Note:
- If the test is Manual, this always generates WARN (because the user has to run it manually)
- If the test is Scored, and kube-bench was unable to run the test, this generates FAIL (because the test has not been passed, and as a Scored test, if it doesn't pass then it must be considered a failure).
- If the test is Not Scored, and kube-bench was unable to run the test, this generates WARN.
- If the test is Scored, type is empty, and there are no `test_items` present, it generates a WARN.
## Configuration
Kubernetes config and binary file locations and names can vary from installation to installation, so these are configurable in the `cfg/config.yaml` file.
For each type of node (*master*, *node* or *federated*) there is a list of components, and for each component there is a set of binaries (*bins*) and config files (*confs*) that kube-bench will look for (in the order they are listed). If your installation uses a different binary name or config file location for a Kubernetes component, you can add it to `cfg/config.yaml`.
Kubernetes configuration and binary file locations and names can vary from installation to installation, so these are configurable in the `cfg/config.yaml` file.
* **bins** - If there is a *bins* list for a component, at least one of these binaries must be running. The tests will consider the parameters for the first binary in the list found to be running.
* **podspecs** - From version 1.2.0 of the benchmark (tests for Kubernetes 1.8), the remediation instructions were updated to assume that the configuration for several kubernetes components is defined in a pod YAML file, and podspec settings define where to look for that configuration.
* **confs** - If one of the listed config files is found, this will be considered for the test. Tests can continue even if no config file is found. If no file is found at any of the listed locations, and a *defaultconf* location is given for the component, the test will give remediation advice using the *defaultconf* location.
* **unitfiles** - From version 1.2.0 of the benchmark (tests for Kubernetes 1.8), the remediation instructions were updated to assume that kubelet configuration is defined in a service file, and this setting defines where to look for that configuration.
Any settings in the version-specific config file `cfg/<version>/config.yaml` take precedence over settings in the main `cfg/config.yaml` file.
You can read more about `kube-bench` configuration in our [documentation](docs/README.md#configuration-and-variables).
## Test config YAML representation
The tests are represented as YAML documents (installed by default into ./cfg).
An example is as listed below:
```
---
controls:
id: 1
text: "Master Checks"
type: "master"
groups:
- id: 1.1
text: "Kube-apiserver"
The tests (or "controls") are represented as YAML documents (installed by default into `./cfg`). There are different versions of these test YAML files reflecting different versions of the CIS Kubernetes Benchmark. You will find more information about the test file YAML definitions in our [documentation](docs/README.md).
### Omitting checks
If you decide that a recommendation is not appropriate for your environment, you can choose to omit it by editing the test YAML file to give it the check type `skip` as in this example:
```yaml
checks:
- id: 1.1.1
text: "Ensure that the --allow-privileged argument is set (Scored)"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--allow-privileged"
set: true
- flag: "--some-other-flag"
set: false
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 2.1.1
text: "Ensure that the --allow-privileged argument is set to false (Scored)"
type: "skip"
scored: true
```
Recommendations (called `checks` in this document) can run on Kubernetes Master, Node or Federated API Servers.
Checks are organized into `groups` which share similar controls (things to check for) and are grouped together in the section of the CIS Kubernetes document.
These groups are further organized under `controls` which can be of the type `master`, `node` or `federated apiserver` to reflect the various Kubernetes node types.
No tests will be run for this check and the output will be marked [INFO].
## Tests
Tests are the items we actually look for to determine if a check is successful or not. Checks can have multiple tests, which must all be successful for the check to pass.
## Roadmap
The syntax for tests:
```
tests:
- flag:
set:
compare:
op:
value:
...
```
Tests have various `operations` which are used to compare the output of audit commands for success.
These operations are:
Going forward we plan to release updates to kube-bench to add support for new releases of the Benchmark, which in turn we can anticipate being made for each new Kubernetes release.
- `eq`: tests if the flag value is equal to the compared value.
- `noteq`: tests if the flag value is unequal to the compared value.
- `gt`: tests if the flag value is greater than the compared value.
- `gte`: tests if the flag value is greater than or equal to the compared value.
- `lt`: tests if the flag value is less than the compared value.
- `lte`: tests if the flag value is less than or equal to the compared value.
- `has`: tests if the flag value contains the compared value.
- `nothave`: tests if the flag value does not contain the compared value.
We welcome PRs and issue reports.
# Roadmap
Going forward we plan to release updates to kube-bench to add support for new releases of the Benchmark, which in turn we can anticipate being made for each new Kubernetes release.
## Testing locally with kind
We welcome PRs and issue reports.
Our makefile contains targets to test your current version of kube-bench inside a [Kind](https://kind.sigs.k8s.io/) cluster. This can be very handy if you don't want to run a real Kubernetes cluster for development purposes.
First, you'll need to create the cluster using `make kind-test-cluster` this will create a new cluster if it cannot be found on your machine. By default, the cluster is named `kube-bench` but you can change the name by using the environment variable `KIND_PROFILE`.
*If kind cannot be found on your system the target will try to install it using `go get`*
Next, you'll have to build the kube-bench docker image using `make build-docker`, then we will be able to push the docker image to the cluster using `make kind-push`.
Finally, we can use the `make kind-run` target to run the current version of kube-bench in the cluster and follow the logs of pods created. (Ctrl+C to exit)
Every time you want to test a change, you'll need to rebuild the docker image and push it to cluster before running it again. ( `make build-docker kind-push kind-run` )
## Contributing
### Bugs
If you think you have found a bug please follow the instructions below.
- Please spend a small amount of time giving due diligence to the issue tracker. Your issue might be a duplicate.
- Open a [new issue](https://github.com/aquasecurity/kube-bench/issues/new) if a duplicate doesn't already exist.
- Note the version of kube-bench you are running (from `kube-bench version`) and the command line options you are using.
- Note the version of Kubernetes you are running (from `kubectl version` or `oc version` for OpenShift).
- Set `-v 10` command line option and save the log output. Please paste this into your issue.
- Remember users might be searching for your issue in the future, so please give it a meaningful title to help others.
### Features
We also use the GitHub issue tracker to track feature requests. If you have an idea to make kube-bench even more awesome follow the steps below.
- Open a [new issue](https://github.com/aquasecurity/kube-bench/issues/new).
- Remember users might be searching for your issue in the future, so please give it a meaningful title to helps others.
- Clearly define the use case, using concrete examples. For example, I type `this` and kube-bench does `that`.
- If you would like to include a technical design for your feature please feel free to do so.
### Pull Requests
We welcome pull requests!
- Your PR is more likely to be accepted if it focuses on just one change.
- Please include a comment with the results before and after your change.
- Your PR is more likely to be accepted if it includes tests. (We have not historically been very strict about tests, but we would like to improve this!).
- You're welcome to submit a draft PR if you would like early feedback on an idea or an approach.
- Happy coding!

View File

@@ -1,285 +0,0 @@
---
controls:
version: 1.6
id: 3
text: "Federated Deployments"
type: "federated"
groups:
- id: 3.1
text: "Federation API Server"
checks:
- id: 3.1.1
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set --anonymous-auth=false.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.2
text: "Ensure that the --basic-auth-file argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--basic-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for authentication.
Then, edit the deployment specs and remove \"--basic-auth-file=<filename>\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.3
text: "Ensure that the --insecure-allow-any-token argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-allow-any-token"
set: false
remediation: "Edit the deployment specs and remove --insecure-allow-any-token.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.4
text: "Ensure that the --insecure-bind-address argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-bind-address"
set: false
remediation: "Edit the deployment specs and remove --insecure-bind-address.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.5
text: "Ensure that the --insecure-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the deployment specs and set --insecure-port=0.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.6
text: "Ensure that the --secure-port argument is not set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--secure-port"
compare:
op: gt
value: 0
set: true
- flag: "--secure-port"
set: false
remediation: "Edit the deployment specs and set the --secure-port argument to the desired port.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.7
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set \"--profiling=false\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
score: true
- id: 3.1.8
text: "Ensure that the admission control policy is not set to AlwaysAdmit (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: nothave
value: AlwaysAdmit
set: true
remediation: "Edit the deployment specs and set --admission-control argument to a value that does
not include AlwaysAdmit.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.9
text: "Ensure that the admission control policy is set to NamespaceLifecycle (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "admission-control"
compare:
op: has
value: "NamespaceLifecycle"
set: true
remediation: "Edit the deployment specs and set --admission-control argument to a value that includes NamespaceLifecycle.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.10
text: "Ensure that the --audit-log-path argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-path"
set: true
remediation: "Edit the deployment specs and set --audit-log-path argument as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.11
text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxage"
compare:
op: gte
value: 30
set: true
remediation: "Edit the deployment specs and set --audit-log-maxage to 30 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.12
text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxbackup"
compare:
op: gte
value: 10
set: true
remediation: "Edit the deployment specs and set --audit-log-maxbackup to 10 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.13
text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxsize"
compare:
op: gte
value: 100
set: true
remediation: "Edit the deployment specs and set --audit-log-maxsize=100 to 100 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.14
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: "Edit the deployment specs and set --authorization-mode argument to a value other than AlwaysAllow.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.15
text: "Ensure that the --token-auth-file parameter is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--token-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for authentication.
Then, edit the deployment specs and remove the --token-auth-file=<filename> argument.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.16
text: "Ensure that the --service-account-lookup argument is set to true (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-lookup"
compare:
op: eq
value: true
set: true
remediation: "Edit the deployment specs and set \"--service-account-lookup=true\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.17
text: "Ensure that the --service-account-key-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-key-file"
set: true
remediation: "Edit the deployment specs and set --service-account-key-file argument as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.18
text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Scored"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--etcd-certfile"
set: true
- flag: "--etcd-keyfile"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection between the
federation apiserver and etcd. Then, edit the deployment specs and set \"--etcd-
certfile=<path/to/client-certificate-file>\" and \"--etcd-
keyfile=<path/to/client-key-file>\" arguments.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.19
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the federation
apiserver. Then, edit the deployment specs and set \"--tls-cert-file=<path/to/tls-
certificate-file>\" and \"--tls-private-key-file=<path/to/tls-key-file>\" :
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.2
text: "Federation Controller Manager"
checks:
- id: 3.2.1
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedcontrollermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set \"--profiling=false\".\n
kubectl edit deployments federation-controller-manager-deployment --namespace=federation-system"
scored: true

View File

@@ -1,966 +0,0 @@
---
controls:
version: 1.6
id: 1
text: "Master Node Security Configuration"
type: "master"
groups:
- id: 1.1
text: "API Server"
checks:
- id: 1.1.1
text: "Ensure that the --allow-privileged argument is set to false (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "allow-privileged"
compare:
op: eq
value: false
set: true
remediation: "Edit the $apiserverconf file on the master node and set
the KUBE_ALLOW_PRIV parameter to \"--allow-privileged=false\""
scored: true
- id: 1.1.2
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: "Edit the $apiserverconf file on the master node and set
the KUBE_API_ARGS parameter to \"--anonymous-auth=false\""
scored: true
- id: 1.1.3
text: "Ensure that the --basic-auth-file argument is not set (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--basic-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for
authentication. Then, edit the $apiserverconf file on the master
node and remove the \"--basic-auth-file=<filename>\" argument from the
KUBE_API_ARGS parameter."
scored: true
- id: 1.1.4
text: "Ensure that the --insecure-allow-any-token argument is not set (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-allow-any-token"
set: false
remediation: "Edit the $apiserverconf file on the master node and remove
the --insecure-allow-any-token argument from the KUBE_API_ARGS parameter."
scored: true
- id: 1.1.5
text: "Ensure that the --kubelet-https argument is set to true (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--kubelet-https"
compare:
op: eq
value: true
set: true
- flag: "--kubelet-https"
set: false
remediation: "Edit the $apiserverconf file on the master node and remove
the --kubelet-https argument from the KUBE_API_ARGS parameter."
scored: true
- id: 1.1.6
text: "Ensure that the --insecure-bind-address argument is not set (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-bind-address"
set: false
remediation: "Edit the $apiserverconf file on the master node and remove
the --insecure-bind-address argument from the KUBE_API_ADDRESS parameter."
scored: true
- id: 1.1.7
text: "Ensure that the --insecure-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $apiserverconf file on the master node and set
--insecure-port=0 in the KUBE_API_PORT parameter."
scored: true
- id: 1.1.8
text: "Ensure that the --secure-port argument is not set to 0 (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--secure-port"
compare:
op: gt
value: 0
set: true
- flag: "--secure-port"
set: false
remediation: "Edit the $apiserverconf file on the master node and either
remove the --secure-port argument from the KUBE_API_ARGS parameter or set
it to a different desired port."
scored: true
- id: 1.1.9
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--profiling=false\""
scored: true
- id: 1.1.10
text: "Ensure that the --repair-malformed-updates argument is set to false (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--repair-malformed-updates"
compare:
op: eq
value: false
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--repair-malformed-updates=false\""
scored: true
- id: 1.1.11
text: "Ensure that the admission control policy is not set to AlwaysAdmit (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: nothave
value: AlwaysAdmit
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to a value that does not include AlwaysAdmit"
scored: true
- id: 1.1.12
text: "Ensure that the admission control policy is set to AlwaysPullImages (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: has
value: "AlwaysPullImages"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to \"--admission-control=...,AlwaysPullImages,...\""
scored: true
- id: 1.1.13
text: "Ensure that the admission control policy is set to DenyEscalatingExec (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: has
value: "DenyEscalatingExec"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to \"--admission-control=...,DenyEscalatingExec,...\""
scored: true
- id: 1.1.14
text: "Ensure that the admission control policy is set to SecurityContextDeny (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: has
value: "SecurityContextDeny"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to \"--admission-control=...,SecurityContextDeny,...\""
scored: true
- id: 1.1.15
text: "Ensure that the admission control policy is set to NamespaceLifecycle (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "admission-control"
compare:
op: has
value: "NamespaceLifecycle"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to \"--admission-control=NamespaceLifecycle,...\""
scored: true
- id: 1.1.16
text: "Ensure that the --audit-log-path argument is set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-path"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--audit-log-path=<filename>\""
scored: true
- id: 1.1.17
text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxage"
compare:
op: gte
value: 30
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--audit-log-maxage=30\""
scored: true
- id: 1.1.18
text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxbackup"
compare:
op: gte
value: 10
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--audit-log-maxbackup=10\""
scored: true
- id: 1.1.19
text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxsize"
compare:
op: gte
value: 100
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--audit-log-maxsize=100\""
scored: true
- id: 1.1.20
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to values other than \"--authorization-mode=AlwaysAllow\""
scored: true
- id: 1.1.21
text: "Ensure that the --token-auth-file parameter is not set (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--token-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for authentication.
Then, edit the $apiserverconf file on the master node and remove the
\"--tokenauth-file=<filename>\" argument from the KUBE_API_ARGS parameter."
scored: true
- id: 1.1.22
text: "Ensure that the --kubelet-certificate-authority argument is set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--kubelet-certificate-authority"
set: true
remediation: "Follow the Kubernetes documentation and setup the TLS connection between
the apiserver and kubelets. Then, edit the $apiserverconf file on the
master node and set the KUBE_API_ARGS parameter to
\"--kubelet-certificate-authority=<ca-string>\""
scored: true
- id: 1.1.23
text: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--kubelet-client-certificate"
set: true
- flag: "--kubelet-client-key"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection between the apiserver
and kubelets. Then, edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to \"--kubelet-clientcertificate=<path/to/client-certificate-file>\"
and \"--kubelet-clientkey=<path/to/client-key-file>\""
scored: true
- id: 1.1.24
text: "Ensure that the --service-account-lookup argument is set to true (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-lookup"
compare:
op: eq
value: true
set: true
remediation: "Edit the $apiserverconf file on the master node and set the KUBE_API_ARGS parameter
to \"--service-account-lookup=true\""
scored: true
- id: 1.1.25
text: "Ensure that the admission control policy is set to PodSecurityPolicy (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: has
value: "PodSecurityPolicy"
set: true
remediation: "Follow the documentation and create Pod Security Policy objects as per your environment.
Then, edit the $apiserverconf file on the master node and set the KUBE_ADMISSION_CONTROL
parameter to \"--admission-control=...,PodSecurityPolicy,...\""
scored: true
- id: 1.1.26
text: "Ensure that the --service-account-key-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-key-file"
set: true
remediation: "Edit the $apiserverconf file on the master node and set the KUBE_API_ARGS
parameter to \"--service-account-key-file=<filename>\""
scored: true
- id: 1.1.27
text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Scored"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--etcd-certfile"
set: true
- flag: "--etcd-keyfile"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection between the apiserver
and etcd. Then, edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to include \"--etcd-certfile=<path/to/clientcertificate-file>\"
and \"--etcd-keyfile=<path/to/client-key-file>\""
scored: true
- id: 1.1.28
text: "Ensure that the admission control policy is set to ServiceAccount (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: has
value: "ServiceAccount"
set: true
remediation: "Follow the documentation and create ServiceAccount objects as per your environment.
Then, edit the $apiserverconf file on the master node and set the
KUBE_ADMISSION_CONTROL parameter to \"--admissioncontrol=...,ServiceAccount,...\""
scored: true
- id: 1.1.29
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the apiserver.
Then, edit the $apiserverconf file on the master node and set the KUBE_API_ARGS parameter to
include \"--tls-cert-file=<path/to/tls-certificatefile>\" and
\"--tls-private-key-file=<path/to/tls-key-file>\""
scored: true
- id: 1.1.30
text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--client-ca-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the apiserver.
Then, edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to include \"--client-ca-file=<path/to/client-ca-file>\""
scored: true
- id: 1.1.31
text: "Ensure that the --etcd-cafile argument is set as appropriate (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
tests:
test_items:
- flag: "--etcd-cafile"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection between the apiserver
and etcd. Then, edit the $apiserverconf file on the master node and set the
KUBE_API_ARGS parameter to include \"--etcd-cafile=<path/to/ca-file>\""
scored: true
- id: 1.2
text: "Scheduler"
checks:
- id: 1.2.1
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $schedulerbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the $schedulerconf file on the master node and set the KUBE_SCHEDULER_ARGS
parameter to \"--profiling=false\""
scored: true
- id: 1.3
text: "Controller Manager"
checks:
- id: 1.3.1
text: "Ensure that the --terminated-pod-gc-threshold argument is set as appropriate (Scored)"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--terminated-pod-gc-threshold"
set: true
remediation: "Edit the $controllermanagerconf file on the master node and set the
KUBE_CONTROLLER_MANAGER_ARGS parameter to \"--terminated-pod-gcthreshold=<appropriate-number>\""
scored: true
- id: 1.3.2
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the $controllermanagerconf file on the master node and set the
KUBE_CONTROLLER_MANAGER_ARGS parameter to \"--profiling=false\""
scored: true
- id: 1.3.3
text: "Ensure that the --insecure-experimental-approve-all-kubelet-csrs-for-group argument is not set (Scored)"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-experimental-approve-all-kubelet-csrs-for-group"
set: false
remediation: "Edit the /etc/kubernetes/controller-manager file on the master node and remove the
--insecure-experimental-approve-all-kubelet-csrs-for-group argument from the
KUBE_CONTROLLER_MANAGER_ARGS parameter."
scored: true
- id: 1.3.4
text: "Ensure that the --use-service-account-credentials argument is set"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--use-service-account-credentials"
compare:
op: eq
value: true
set: true
remediation: "Edit the $controllermanagerconf file on the master node and set the
KUBE_CONTROLLER_MANAGER_ARGS parameter to --use-service-account-credentials=true"
scored: true
- id: 1.3.5
text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-private-key-file"
set: true
remediation: "Edit the $controllermanagerconf file on the master node and set the
KUBE_CONTROLLER_MANAGER_ARGS parameter to --service-account-private-keyfile=<filename>"
scored: true
- id: 1.3.6
text: "Ensure that the --root-ca-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $controllermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--root-ca-file"
set: true
remediation: "Edit the $controllermanagerconf file on the master node and set the
KUBE_CONTROLLER_MANAGER_ARGS parameter to include --root-ca-file=<file>"
scored: true
- id: 1.4
text: "Configure Files"
checks:
- id: 1.4.1
text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)"
# audit: "/bin/bash -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'"
audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $apiserverconf"
scored: true
- id: 1.4.2
text: "Ensure that the apiserver file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $apiserverconf"
scored: true
- id: 1.4.3
text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $config; then stat -c %a $config; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $config"
scored: true
- id: 1.4.4
text: "Ensure that the config file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $config; then stat -c %U:%G $config; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $config"
scored: true
- id: 1.4.5
text: "Ensure that the scheduler file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %a $schedulerconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $schedulerconf"
scored: true
- id: 1.4.6
text: "Ensure that the scheduler file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $schedulerconf"
scored: true
- id: 1.4.7
text: "Ensure that the etcd.conf file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %a $etcdconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $etcdconf"
scored: true
- id: 1.4.8
text: "Ensure that the etcd.conf file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $etcdconf"
scored: true
- id: 1.4.9
text: "Ensure that the flanneld file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $flanneldconf; then stat -c %a $flanneldconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $flanneldconf"
scored: true
- id: 1.4.10
text: "Ensure that the flanneld file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $flanneldconf; then stat -c %U:%G $flanneldconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $flanneldconf"
scored: true
- id: 1.4.11
text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Scored)"
audit: ps -ef | grep $etcdbin | grep -v grep | sed 's%.*data-dir[= ]\(\S*\)%\1%' | xargs stat -c %a
tests:
test_items:
- flag: "700"
compare:
op: eq
value: "700"
set: true
remediation: "On the etcd server node, get the etcd data directory, passed as an argument --data-dir ,
from the below command:\n
ps -ef | grep $etcdbin\n
Run the below command (based on the etcd data directory found above). For example,\n
chmod 700 /var/lib/etcd/default.etcd"
scored: true
- id: 1.4.12
text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Scored)"
audit: ps -ef | grep $etcdbin | grep -v grep | sed 's%.*data-dir[= ]\(\S*\)%\1%' | xargs stat -c %U:%G
tests:
test_items:
- flag: "etcd:etcd"
set: true
remediation: "On the etcd server node, get the etcd data directory, passed as an argument --data-dir ,
from the below command:\n
ps -ef | grep etcd\n
Run the below command (based on the etcd data directory found above). For example,\n
chown etcd:etcd /var/lib/etcd/default.etcd"
scored: true
- id: 1.5
text: "etcd"
checks:
- id: 1.5.1
text: "Ensure that the --cert-file and --key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--cert-file"
set: true
- flag: "--key-file"
set: true
remediation: "Follow the etcd service documentation and configure TLS encryption."
scored: true
- id: 1.5.2
text: "Ensure that the --client-cert-auth argument is set to true (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--client-cert-auth"
compare:
op: eq
value: true
set: true
remediation: "Edit the etcd envrironment file (for example, $etcdconf) on the
etcd server node and set the ETCD_CLIENT_CERT_AUTH parameter to \"true\".
Edit the etcd startup file (for example, /etc/systemd/system/multiuser.target.wants/etcd.service)
and configure the startup parameter for --clientcert-auth and set it to \"${ETCD_CLIENT_CERT_AUTH}\""
scored: true
- id: 1.5.3
text: "Ensure that the --auto-tls argument is not set to true (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--auto-tls"
set: false
- flag: "--auto-tls"
compare:
op: neq
value: true
remediation: "Edit the etcd environment file (for example, $etcdconf) on the etcd server
node and comment out the ETCD_AUTO_TLS parameter. Edit the etcd startup file (for example,
/etc/systemd/system/multiuser.target.wants/etcd.service) and remove the startup parameter
for --auto-tls."
scored: true
- id: 1.5.4
text: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--peer-cert-file"
set: true
- flag: "--peer-key-file"
set: true
remediation: "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.
Follow the etcd service documentation and configure peer TLS encryption as appropriate for
your etcd cluster."
scored: true
- id: 1.5.5
text: "Ensure that the --peer-client-cert-auth argument is set to true (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--peer-client-cert-auth"
compare:
op: eq
value: true
set: true
remediation: "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.
Edit the etcd environment file (for example, $etcdconf) on the etcd server node
and set the ETCD_PEER_CLIENT_CERT_AUTH parameter to \"true\". Edit the etcd startup file
(for example, /etc/systemd/system/multiuser.target.wants/etcd.service) and configure the
startup parameter for --peer-client-cert-auth and set it to \"${ETCD_PEER_CLIENT_CERT_AUTH}\""
scored: true
- id: 1.5.6
text: "Ensure that the --peer-auto-tls argument is not set to true (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--peer-auto-tls"
set: false
- flag: "--peer-auto-tls"
compare:
op: eq
value: false
set: true
remediation: "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. Edit the etcd environment file (for example, $etcdconf)
on the etcd server node and comment out the ETCD_PEER_AUTO_TLS parameter.
Edit the etcd startup file (for example, /etc/systemd/system/multiuser.target.wants/etcd.service)
and remove the startup parameter for --peer-auto-tls."
scored: true
- id: 1.5.7
text: "Ensure that the --wal-dir argument is set as appropriate (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--wal-dir"
set: true
remediation: "Edit the etcd environment file (for example, $etcdconf) on the etcd server node
and set the ETCD_WAL_DIR parameter as appropriate. Edit the etcd startup file (for example,
/etc/systemd/system/multiuser.target.wants/etcd.service) and configure the startup parameter for
--wal-dir and set it to \"${ETCD_WAL_DIR}\""
scored: true
- id: 1.5.8
text: "Ensure that the --max-wals argument is set to 0 (Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--max-wals"
compare:
op: eq
value: 0
set: true
remediation: "Edit the etcd environment file (for example, $etcdconf) on the etcd server node
and set the ETCD_MAX_WALS parameter to 0. Edit the etcd startup file (for example,
/etc/systemd/system/multiuser.target.wants/etcd.service) and configure the startup parameter
for --max-wals and set it to \"${ETCD_MAX_WALS}\"."
scored: true
- id: 1.5.9
text: "Ensure that a unique Certificate Authority is used for etcd (Not Scored)"
audit: "ps -ef | grep $etcdbin | grep -v grep"
tests:
test_items:
- flag: "--trusted-ca-file"
set: true
remediation: "Follow the etcd documentation and create a dedicated certificate authority setup for the
etcd service."
scored: false
- id: 1.6
text: "General Security Primitives"
checks:
- id: 1.6.1
text: "Ensure that the cluster-admin role is only used where required (Not Scored)"
type: "manual"
remediation: "Remove any unneeded clusterrolebindings: kubectl delete clusterrolebinding [name]"
scored: false
- id: 1.6.2
text: "Create Pod Security Policies for your cluster (Not Scored)"
type: "manual"
remediation: "Follow the documentation and create and enforce Pod Security Policies for your cluster.
Additionally, you could refer the \"CIS Security Benchmark for Docker\" and follow the
suggested Pod Security Policies for your environment."
scored: false
- id: 1.6.3
text: "Create administrative boundaries between resources using namespaces (Not Scored)"
type: "manual"
remediation: "Follow the documentation and create namespaces for objects in your deployment as you
need them."
scored: false
- id: 1.6.4
text: "Create network segmentation using Network Policies (Not Scored)"
type: "manual"
remediation: "Follow the documentation and create NetworkPolicy objects as you need them."
scored: false
- id: 1.6.5
text: "Avoid using Kubernetes Secrets (Not Scored)"
type: "manual"
remediation: "Use other mechanisms such as vaults to manage your cluster secrets."
scored: false
- id: 1.6.6
text: "Ensure that the seccomp profile is set to docker/default in your pod definitions (Not Scored)"
type: "manual"
remediation: "Seccomp is an alpha feature currently. By default, all alpha features are disabled. So, you
would need to enable alpha features in the apiserver by passing \"--feature-
gates=AllAlpha=true\" argument.\n
Edit the $apiserverconf file on the master node and set the KUBE_API_ARGS
parameter to \"--feature-gates=AllAlpha=true\"
KUBE_API_ARGS=\"--feature-gates=AllAlpha=true\""
scored: false
- id: 1.6.7
text: "Apply Security Context to Your Pods and Containers (Not Scored)"
type: "manual"
remediation: "Follow the Kubernetes documentation and apply security contexts to your pods. For a
suggested list of security contexts, you may refer to the CIS Security Benchmark for Docker
Containers."
scored: false
- id: 1.6.8
text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Not Scored)"
type: "manual"
remediation: "Follow the Kubernetes documentation and setup image provenance."
scored: false

View File

@@ -1,304 +0,0 @@
---
controls:
version: 1.6
id: 2
text: "Worker Node Security Configuration"
type: "node"
groups:
- id: 2.1
text: "Kubelet"
checks:
- id: 2.1.1
text: "Ensure that the --allow-privileged argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--allow-privileged"
compare:
op: eq
value: false
set: true
remediation: "Edit the $config file on each node and set the KUBE_ALLOW_PRIV
parameter to \"--allow-privileged=false\""
scored: true
- id: 2.1.2
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: "Edit the $kubeletconf file on the master node and set the
KUBELET_ARGS parameter to \"--anonymous-auth=false\""
scored: true
- id: 2.1.3
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: "Edit the $kubeletconf file on each node and set the
KUBELET_ARGS parameter to \"--authorization-mode=Webhook\""
scored: true
- id: 2.1.4
text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--client-ca-file"
set: true
remediation: "Follow the Kubernetes documentation and setup the TLS connection between
the apiserver and kubelets. Then, edit the $kubeletconf file on each node
and set the KUBELET_ARGS parameter to \"--client-ca-file=<path/to/client-ca-file>\""
scored: true
- id: 2.1.5
text: "Ensure that the --read-only-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--read-only-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--read-only-port=0\""
scored: true
- id: 2.1.6
text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--streaming-connection-idle-timeout"
compare:
op: gt
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--streaming-connection-idle-timeout=<appropriate-timeout-value>\""
scored: true
- id: 2.1.7
text: "Ensure that the --protect-kernel-defaults argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--protect-kernel-defaults"
compare:
op: eq
value: true
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--protect-kernel-defaults=true\""
scored: true
- id: 2.1.8
text: "Ensure that the --make-iptables-util-chains argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--make-iptables-util-chains"
compare:
op: eq
value: true
set: true
- flag: "--make-iptables-util-chains"
set: false
remediation: "Edit the $kubeletconf file on each node and remove the
--make-iptables-util-chains argument from the KUBELET_ARGS parameter."
scored: true
- id: 2.1.9
text: "Ensure that the --keep-terminated-pod-volumes argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--keep-terminated-pod-volumes"
compare:
op: eq
value: false
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--keep-terminated-pod-volumes=false\""
scored: true
- id: 2.1.10
text: "Ensure that the --hostname-override argument is not set (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--hostname-override"
set: false
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_HOSTNAME
parameter to \"\""
scored: true
- id: 2.1.11
text: "Ensure that the --event-qps argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--event-qps"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--event-qps=0\""
scored: true
- id: 2.1.12
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the Kubelet.
Then, edit the $kubeletconf file on the master node and set the KUBELET_ARGS
parameter to include \"--tls-cert-file=<path/to/tls-certificate-file>\" and
\"--tls-private-key-file=<path/to/tls-key-file>\""
scored: true
- id: 2.1.13
text: "Ensure that the --cadvisor-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--cadvisor-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS parameter
to \"--cadvisor-port=0\""
scored: true
- id: 2.2
text: "Configuration Files"
checks:
- id: 2.2.1
text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $config; then stat -c %a $config; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $config"
scored: true
- id: 2.2.2
text: "Ensure that the config file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $config; then stat -c %U:%G $config; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $config"
scored: true
- id: 2.2.3
text: "Ensure that the kubelet file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: 644
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $kubeletconf"
scored: true
- id: 2.2.4
text: "Ensure that the kubelet file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $kubeletconf"
scored: true
- id: 2.2.5
text: "Ensure that the proxy file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %a $proxyconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $proxyconf"
scored: true
- id: 2.2.6
text: "Ensure that the proxy file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %U:%G $proxyconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $proxyconf"
scored: true

View File

@@ -1,285 +0,0 @@
---
controls:
version: 1.7
id: 3
text: "Federated Deployments"
type: "federated"
groups:
- id: 3.1
text: "Federation API Server"
checks:
- id: 3.1.1
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set --anonymous-auth=false.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.2
text: "Ensure that the --basic-auth-file argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--basic-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for authentication.
Then, edit the deployment specs and remove \"--basic-auth-file=<filename>\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.3
text: "Ensure that the --insecure-allow-any-token argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-allow-any-token"
set: false
remediation: "Edit the deployment specs and remove --insecure-allow-any-token.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.4
text: "Ensure that the --insecure-bind-address argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-bind-address"
set: false
remediation: "Edit the deployment specs and remove --insecure-bind-address.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.5
text: "Ensure that the --insecure-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the deployment specs and set --insecure-port=0.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.6
text: "Ensure that the --secure-port argument is not set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--secure-port"
compare:
op: gt
value: 0
set: true
- flag: "--secure-port"
set: false
remediation: "Edit the deployment specs and set the --secure-port argument to the desired port.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.7
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set \"--profiling=false\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
score: true
- id: 3.1.8
text: "Ensure that the admission control policy is not set to AlwaysAdmit (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: nothave
value: AlwaysAdmit
set: true
remediation: "Edit the deployment specs and set --admission-control argument to a value that does
not include AlwaysAdmit.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.9
text: "Ensure that the admission control policy is set to NamespaceLifecycle (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "admission-control"
compare:
op: has
value: "NamespaceLifecycle"
set: true
remediation: "Edit the deployment specs and set --admission-control argument to a value that includes NamespaceLifecycle.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.10
text: "Ensure that the --audit-log-path argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-path"
set: true
remediation: "Edit the deployment specs and set --audit-log-path argument as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.11
text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxage"
compare:
op: gte
value: 30
set: true
remediation: "Edit the deployment specs and set --audit-log-maxage to 30 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.12
text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxbackup"
compare:
op: gte
value: 10
set: true
remediation: "Edit the deployment specs and set --audit-log-maxbackup to 10 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.13
text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxsize"
compare:
op: gte
value: 100
set: true
remediation: "Edit the deployment specs and set --audit-log-maxsize=100 to 100 or as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.14
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: "Edit the deployment specs and set --authorization-mode argument to a value other than AlwaysAllow.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.15
text: "Ensure that the --token-auth-file parameter is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--token-auth-file"
set: false
remediation: "Follow the documentation and configure alternate mechanisms for authentication.
Then, edit the deployment specs and remove the --token-auth-file=<filename> argument.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.16
text: "Ensure that the --service-account-lookup argument is set to true (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-lookup"
compare:
op: eq
value: true
set: true
remediation: "Edit the deployment specs and set \"--service-account-lookup=true\".\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.17
text: "Ensure that the --service-account-key-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-key-file"
set: true
remediation: "Edit the deployment specs and set --service-account-key-file argument as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.18
text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Scored"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--etcd-certfile"
set: true
- flag: "--etcd-keyfile"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection between the
federation apiserver and etcd. Then, edit the deployment specs and set \"--etcd-
certfile=<path/to/client-certificate-file>\" and \"--etcd-
keyfile=<path/to/client-key-file>\" arguments.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.19
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the federation
apiserver. Then, edit the deployment specs and set \"--tls-cert-file=<path/to/tls-
certificate-file>\" and \"--tls-private-key-file=<path/to/tls-key-file>\" :
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.2
text: "Federation Controller Manager"
checks:
- id: 3.2.1
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedcontrollermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: "Edit the deployment specs and set \"--profiling=false\".\n
kubectl edit deployments federation-controller-manager-deployment --namespace=federation-system"
scored: true

File diff suppressed because it is too large Load Diff

View File

@@ -1,370 +0,0 @@
---
controls:
version: 1.7
id: 2
text: "Worker Node Security Configuration"
type: "node"
groups:
- id: 2.1
text: "Kubelet"
checks:
- id: 2.1.1
text: "Ensure that the --allow-privileged argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--allow-privileged"
compare:
op: eq
value: false
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBE_ALLOW_PRIV
parameter to \"--allow-privileged=false\""
scored: true
- id: 2.1.2
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: "Edit the $kubeletconf file on the master node and set the
KUBELET_ARGS parameter to \"--anonymous-auth=false\""
scored: true
- id: 2.1.3
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: "Edit the $kubeletconf file on each node and set the
KUBELET_ARGS parameter to \"--authorization-mode=Webhook\""
scored: true
- id: 2.1.4
text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--client-ca-file"
set: true
remediation: "Follow the Kubernetes documentation and setup the TLS connection between
the apiserver and kubelets. Then, edit the $kubeletconf file on each node
and set the KUBELET_ARGS parameter to \"--client-ca-file=<path/to/client-ca-file>\""
scored: true
- id: 2.1.5
text: "Ensure that the --read-only-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--read-only-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--read-only-port=0\""
scored: true
- id: 2.1.6
text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--streaming-connection-idle-timeout"
compare:
op: noteq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--streaming-connection-idle-timeout=<appropriate-timeout-value>\""
scored: true
- id: 2.1.7
text: "Ensure that the --protect-kernel-defaults argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--protect-kernel-defaults"
compare:
op: eq
value: true
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--protect-kernel-defaults=true\""
scored: true
- id: 2.1.8
text: "Ensure that the --make-iptables-util-chains argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--make-iptables-util-chains"
compare:
op: eq
value: true
set: true
- flag: "--make-iptables-util-chains"
set: false
remediation: "Edit the $kubeletconf file on each node and remove the
--make-iptables-util-chains argument from the KUBELET_ARGS parameter."
scored: true
- id: 2.1.9
text: "Ensure that the --keep-terminated-pod-volumes argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--keep-terminated-pod-volumes"
compare:
op: eq
value: false
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--keep-terminated-pod-volumes=false\""
scored: true
- id: 2.1.10
text: "Ensure that the --hostname-override argument is not set (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--hostname-override"
set: false
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_HOSTNAME
parameter to \"\""
scored: true
- id: 2.1.11
text: "Ensure that the --event-qps argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--event-qps"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS
parameter to \"--event-qps=0\""
scored: true
- id: 2.1.12
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: "Follow the Kubernetes documentation and set up the TLS connection on the Kubelet.
Then, edit the $kubeletconf file on the master node and set the KUBELET_ARGS
parameter to include \"--tls-cert-file=<path/to/tls-certificate-file>\" and
\"--tls-private-key-file=<path/to/tls-key-file>\""
scored: true
- id: 2.1.13
text: "Ensure that the --cadvisor-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--cadvisor-port"
compare:
op: eq
value: 0
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS parameter
to \"--cadvisor-port=0\""
scored: true
- id: 2.1.14
text: "Ensure that the RotateKubeletClientCertificate argument is set to true"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "RotateKubeletClientCertificate"
compare:
op: eq
value: true
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS parameter
to a value to include \"--feature-gates=RotateKubeletClientCertificate=true\"."
scored: true
- id: 2.1.15
text: "Ensure that the RotateKubeletServerCertificate argument is set to true"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "RotateKubeletServerCertificate"
compare:
op: eq
value: true
set: true
remediation: "Edit the $kubeletconf file on each node and set the KUBELET_ARGS parameter
to a value to include \"--feature-gates=RotateKubeletServerCertificate=true\"."
scored: true
- id: 2.2
text: "Configuration Files"
checks:
- id: 2.2.1
text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubernetesconf; then stat -c %a $kubernetesconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $kubernetesconf"
scored: true
- id: 2.2.2
text: "Ensure that the config file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $kubernetesconf; then stat -c %U:%G $kubernetesconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $kubernetesconf"
scored: true
- id: 2.2.3
text: "Ensure that the kubelet file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: 644
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $kubeletconf"
scored: true
- id: 2.2.4
text: "Ensure that the kubelet file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $kubeletconf"
scored: true
- id: 2.2.5
text: "Ensure that the proxy file permissions are set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %a $proxyconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $proxyconf"
scored: true
- id: 2.2.6
text: "Ensure that the proxy file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %U:%G $proxyconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $proxyconf"
scored: true
- id: 2.2.7
text: "Ensure that the certificate authorities file permissions are set to
644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $ca-file; then stat -c %a $ca-file; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: "Run the following command to modify the file permissions of the --client-ca-file
\nchmod 644 <filename>"
scored: true
- id: 2.2.8
text: "Ensure that the client certificate authorities file ownership is set to root:root"
audit: "/bin/sh -c 'if test -e $ca-file; then stat -c %U:%G $ca-file; fi'"
tests:
test_items:
- flag: "notexist:notexist"
set: true
remediation: "Run the following command to modify the ownership of the --client-ca-file.
\nchown root:root <filename>"
scored: true

View File

@@ -1,44 +0,0 @@
---
## Controls Files.
# These are YAML files that hold all the details for running checks.
#
## Uncomment to use different control file paths.
# masterControls: ./cfg/master.yaml
# nodeControls: ./cfg/node.yaml
# federatedControls: ./cfg/federated.yaml
master:
apiserver:
confs:
- /etc/kubernetes/manifests/kube-apiserver.yaml
- /etc/kubernetes/manifests/kube-apiserver.manifest
defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml
scheduler:
confs:
- /etc/kubernetes/manifests/kube-scheduler.yaml
- /etc/kubernetes/manifests/kube-scheduler.manifest
defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml
controllermanager:
confs:
- /etc/kubernetes/manifests/kube-controller-manager.yaml
- /etc/kubernetes/manifests/kube-controller-manager.manifest
defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml
etcd:
confs:
- /etc/kubernetes/manifests/etcd.yaml
- /etc/kubernetes/manifests/etcd.manifest
defaultconf: /etc/kubernetes/manifests/etcd.yaml
node:
kubelet:
confs:
- /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
defaultconf: /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
proxy:
confs:
- /etc/kubernetes/addons/kube-proxy-daemonset.yaml
defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml

View File

@@ -1,309 +0,0 @@
---
controls:
version: 1.8
id: 3
text: "Federated Deployments"
type: "federated"
groups:
- id: 3.1
text: "Federation API Server"
checks:
- id: 3.1.1
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: |
Edit the deployment specs and set --anonymous-auth=false.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.2
text: "Ensure that the --basic-auth-file argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--basic-auth-file"
set: false
remediation: |
Follow the documentation and configure alternate mechanisms for authentication. Then,
edit the deployment specs and remove "--basic-auth-file=<filename>".
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.3
text: "Ensure that the --insecure-allow-any-token argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-allow-any-token"
set: false
remediation: |
Edit the deployment specs and remove --insecure-allow-any-token.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.4
text: "Ensure that the --insecure-bind-address argument is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-bind-address"
set: false
remediation: |
Edit the deployment specs and remove --insecure-bind-address.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.5
text: "Ensure that the --insecure-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--insecure-port"
compare:
op: eq
value: 0
set: true
remediation: |
Edit the deployment specs and set --insecure-port=0.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.6
text: "Ensure that the --secure-port argument is not set to 0 (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--secure-port"
compare:
op: gt
value: 0
set: true
- flag: "--secure-port"
set: false
remediation: |
Edit the deployment specs and set the --secure-port argument to the desired port.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.7
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: |
Edit the deployment specs and set "--profiling=false":
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
score: true
- id: 3.1.8
text: "Ensure that the admission control policy is not set to AlwaysAdmit (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--admission-control"
compare:
op: nothave
value: AlwaysAdmit
set: true
remediation: |
Edit the deployment specs and set --admission-control argument to a value that does not
include AlwaysAdmit .
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.9
text: "Ensure that the admission control policy is set to NamespaceLifecycle (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "admission-control"
compare:
op: has
value: "NamespaceLifecycle"
set: true
remediation: |
Edit the deployment specs and set --admission-control argument to a value that includes
NamespaceLifecycle.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.10
text: "Ensure that the --audit-log-path argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-path"
set: true
remediation: "Edit the deployment specs and set --audit-log-path argument as appropriate.\n
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system"
scored: true
- id: 3.1.11
text: "Ensure that the --audit-log-maxage argument is set to 30 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxage"
compare:
op: gte
value: 30
set: true
remediation: |
Edit the deployment specs and set --audit-log-maxage to 30 or as appropriate.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.12
text: "Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxbackup"
compare:
op: gte
value: 10
set: true
remediation: |
Edit the deployment specs and set --audit-log-maxbackup to 10 or as appropriate.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.13
text: "Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--audit-log-maxsize"
compare:
op: gte
value: 100
set: true
remediation: |
Edit the deployment specs and set --audit-log-maxsize=100 to 100 or as appropriate.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.14
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: |
Edit the deployment specs and set --authorization-mode argument to a value other than
AlwaysAllow
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.15
text: "Ensure that the --token-auth-file parameter is not set (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--token-auth-file"
set: false
remediation: |
Follow the documentation and configure alternate mechanisms for authentication. Then,
edit the deployment specs and remove the --token-auth-file=<filename> argument.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.16
text: "Ensure that the --service-account-lookup argument is set to true (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-lookup"
compare:
op: eq
value: true
set: true
remediation: |
Edit the deployment specs and set "--service-account-lookup=true" .
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.17
text: "Ensure that the --service-account-key-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
test_items:
- flag: "--service-account-key-file"
set: true
remediation: |
Edit the deployment specs and set --service-account-key-file argument as appropriate.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.18
text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as
appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--etcd-certfile"
set: true
- flag: "--etcd-keyfile"
set: true
remediation: |
Follow the Kubernetes documentation and set up the TLS connection between the
federation apiserver and etcd. Then, edit the deployment specs and set
"--etcd-certfile=<path/to/client-certificate-file>" and
"--etcd-keyfile=<path/to/client-key-file>" arguments.
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.1.19
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as
appropriate (Scored)"
audit: "ps -ef | grep $fedapiserverbin | grep -v grep"
tests:
bin_op: and
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: |
Follow the Kubernetes documentation and set up the TLS connection on the federation
apiserver. Then, edit the deployment specs and set
"--tls-cert-file=<path/to/tls-certificate-file>" and
"--tls-private-key-file=<path/to/tls-key-file>":
kubectl edit deployments federation-apiserver-deployment --namespace=federation-system
scored: true
- id: 3.2
text: "Federation Controller Manager"
checks:
- id: 3.2.1
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep $fedcontrollermanagerbin | grep -v grep"
tests:
test_items:
- flag: "--profiling"
compare:
op: eq
value: false
set: true
remediation: |
Edit the deployment specs and set "--profiling=false":
kubectl edit deployments federation-controller-manager-deployment --namespace=federation-system
scored: true

View File

@@ -1,440 +0,0 @@
---
controls:
version: 1.8
id: 2
text: "Worker Node Security Configuration"
type: "node"
groups:
- id: 2.1
text: "Kubelet"
checks:
- id: 2.1.1
text: "Ensure that the --allow-privileged argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--allow-privileged"
compare:
op: eq
value: false
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--allow-privileged=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.2
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--anonymous-auth=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.3
text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--authorization-mode"
compare:
op: nothave
value: "AlwaysAllow"
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable.
--authorization-mode=Webhook
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.4
text: "Ensure that the --client-ca-file argument is set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--client-ca-file"
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_AUTHZ_ARGS variable.
--client-ca-file=<path/to/client-ca-file>
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.5
text: "Ensure that the --read-only-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--read-only-port"
compare:
op: eq
value: 0
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--read-only-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.6
text: "Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--streaming-connection-idle-timeout"
compare:
op: noteq
value: 0
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--streaming-connection-idle-timeout=5m
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.7
text: "Ensure that the --protect-kernel-defaults argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--protect-kernel-defaults"
compare:
op: eq
value: true
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--protect-kernel-defaults=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.8
text: "Ensure that the --make-iptables-util-chains argument is set to true (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--make-iptables-util-chains"
compare:
op: eq
value: true
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and remove the --make-iptables-util-chains argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.9
text: "Ensure that the --keep-terminated-pod-volumes argument is set to false (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--keep-terminated-pod-volumes"
compare:
op: eq
value: false
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--keep-terminated-pod-volumes=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.10
text: "Ensure that the --hostname-override argument is not set (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--hostname-override"
set: false
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and remove the --hostname-override argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.11
text: "Ensure that the --event-qps argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--event-qps"
compare:
op: eq
value: 0
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--event-qps=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.12
text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--tls-cert-file"
set: true
- flag: "--tls-private-key-file"
set: true
remediation: |
Follow the Kubernetes documentation and set up the TLS connection on the Kubelet.
Then edit the kubelet service file /etc/systemd/system/kubelet.service.d/10-
kubeadm.conf on each worker node and set the below parameters in
KUBELET_CERTIFICATE_ARGS variable.
--tls-cert-file=<path/to/tls-certificate-file>
file=<path/to/tls-key-file>
--tls-private-key-
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.13
text: "Ensure that the --cadvisor-port argument is set to 0 (Scored)"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "--cadvisor-port"
compare:
op: eq
value: 0
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_CADVISOR_ARGS variable.
--cadvisor-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.14
text: "Ensure that the RotateKubeletClientCertificate argument is set to true"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "RotateKubeletClientCertificate"
compare:
op: eq
value: true
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and remove the --feature-
gates=RotateKubeletClientCertificate=false argument from the
KUBELET_CERTIFICATE_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.15
text: "Ensure that the RotateKubeletServerCertificate argument is set to true"
audit: "ps -ef | grep $kubeletbin | grep -v grep"
tests:
test_items:
- flag: "RotateKubeletServerCertificate"
compare:
op: eq
value: true
set: true
remediation: |
Edit the kubelet service file $kubeletconf
on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable.
--feature-gates=RotateKubeletServerCertificate=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.2
text: "Configuration Files"
checks:
- id: 2.2.1
text: "Ensure that the kubelet.conf file permissions are set to 644 or
more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $kubeletconf
scored: true
- id: 2.2.2
text: "Ensure that the kubelet.conf file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root /etc/kubernetes/kubelet.conf
scored: true
- id: 2.2.3
text: "Ensure that the kubelet service file permissions are set to 644 or
more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: 644
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 755 $kubeletconf
scored: true
- id: 2.2.4
text: "2.2.4 Ensure that the kubelet service file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $kubeletconf
scored: true
- id: 2.2.5
text: "Ensure that the proxy kubeconfig file permissions are set to 644 or more
restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %a $proxyconf; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $proxyconf
scored: true
- id: 2.2.6
text: "Ensure that the proxy kubeconfig file ownership is set to root:root (Scored)"
audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %U:%G $proxyconf; fi'"
tests:
test_items:
- flag: "root:root"
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $proxyconf
scored: true
- id: 2.2.7
text: "Ensure that the certificate authorities file permissions are set to
644 or more restrictive (Scored)"
type: manual
remediation: |
Run the following command to modify the file permissions of the --client-ca-file
chmod 644 <filename>
scored: true
- id: 2.2.8
text: "Ensure that the client certificate authorities file ownership is set to root:root"
audit: "/bin/sh -c 'if test -e $ca-file; then stat -c %U:%G $ca-file; fi'"
type: manual
remediation: |
Run the following command to modify the ownership of the --client-ca-file .
chown root:root <filename>
scored: true

2
cfg/cis-1.3/config.yaml Normal file
View File

@@ -0,0 +1,2 @@
---
## Version-specific settings that override the values in cfg/config.yaml

File diff suppressed because it is too large Load Diff

541
cfg/cis-1.3/node.yaml Normal file
View File

@@ -0,0 +1,541 @@
---
controls:
version: "1.11"
id: "2"
text: Worker Node Security Configuration
type: "node"
groups:
- id: "2.1"
text: Kubelet
checks:
- id: 2.1.1
text: Ensure that the --allow-privileged argument is set to false (Scored)
audit: "/bin/ps -fC $kubeletbin "
tests:
test_items:
- flag: --allow-privileged
set: true
compare:
op: eq
value: false
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--allow-privileged=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.2
text: Ensure that the --anonymous-auth argument is set to false (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --anonymous-auth
path: '{.authentication.anonymous.enabled}'
set: true
compare:
op: eq
value: false
remediation: |
If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to
false .
If using executable arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--anonymous-auth=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.3
text: Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --authorization-mode
path: '{.authorization.mode}'
set: true
compare:
op: nothave
value: AlwaysAllow
remediation: |
If using a Kubelet config file, edit the file to set authorization: mode to Webhook.
If using executable arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_AUTHZ_ARGS variable.
--authorization-mode=Webhook
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.4
text: Ensure that the --client-ca-file argument is set as appropriate (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --client-ca-file
path: '{.authentication.x509.clientCAFile}'
set: true
remediation: |
If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to
the location of the client CA file.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_AUTHZ_ARGS variable.
--client-ca-file=<path/to/client-ca-file>
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.5
text: Ensure that the --read-only-port argument is set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --read-only-port
path: '{.readOnlyPort}'
set: true
compare:
op: eq
value: 0
remediation: |
If using a Kubelet config file, edit the file to set readOnlyPort to 0 .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--read-only-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.6
text: Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --streaming-connection-idle-timeout
path: '{.streamingConnectionIdleTimeout}'
set: true
compare:
op: noteq
value: 0
- flag: --streaming-connection-idle-timeout
path: '{.streamingConnectionIdleTimeout}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a
value other than 0.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--streaming-connection-idle-timeout=5m
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.7
text: Ensure that the --protect-kernel-defaults argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --protect-kernel-defaults
path: '{.protectKernelDefaults}'
set: true
compare:
op: eq
value: true
remediation: |
If using a Kubelet config file, edit the file to set protectKernelDefaults: true .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--protect-kernel-defaults=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.8
text: Ensure that the --make-iptables-util-chains argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --make-iptables-util-chains
path: '{.makeIPTablesUtilChains}'
set: true
compare:
op: eq
value: true
- flag: --make-iptables-util-chains
path: '{.makeIPTablesUtilChains}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
remove the --make-iptables-util-chains argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.9
text: Ensure that the --hostname-override argument is not set (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --hostname-override
path: '{.hostnameOverride}'
set: false
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and remove the --hostname-override argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.10
text: Ensure that the --event-qps argument is set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --event-qps
path: '{.eventRecordQPS}'
set: true
compare:
op: eq
value: 0
remediation: |
If using a Kubelet config file, edit the file to set eventRecordQPS: 0 .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--event-qps=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.11
text: Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --tls-cert-file
path: '{.tlsCertFile}'
set: true
- flag: --tls-private-key-file
path: '{.tlsPrivateKeyFile}'
set: true
bin_op: and
remediation: |
If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate
file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the
corresponding private key file.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameters in KUBELET_CERTIFICATE_ARGS variable.
--tls-cert-file=<path/to/tls-certificate-file>
file=<path/to/tls-key-file>
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.12
text: Ensure that the --cadvisor-port argument is set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --cadvisor-port
path: '{.cadvisorPort}'
set: true
compare:
op: eq
value: 0
- flag: --cadvisor-port
path: '{.cadvisorPort}'
set: false
bin_op: or
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_CADVISOR_ARGS variable.
--cadvisor-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.13
text: Ensure that the --rotate-certificates argument is not set to false (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --rotate-certificates
path: '{.rotateCertificates}'
set: true
compare:
op: eq
value: true
- flag: --rotate-certificates
path: '{.rotateCertificates}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to add the line rotateCertificates: true.
If using command line arguments, edit the kubelet service file $kubeletsvc
on each worker node and add --rotate-certificates=true argument to the KUBELET_CERTIFICATE_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.14
text: Ensure that the RotateKubeletServerCertificate argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: RotateKubeletServerCertificate
path: '{.featureGates.RotateKubeletServerCertificate}'
set: true
compare:
op: eq
value: true
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable.
--feature-gates=RotateKubeletServerCertificate=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.15
text: Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Not Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --tls-cipher-suites
path: '{.tlsCipherSuites}'
set: true
compare:
op: valid_elements
value: 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
remediation: |
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,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256
If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter.
--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
scored: false
- id: "2.2"
text: Configuration Files
checks:
- id: 2.2.1
text: Ensure that the kubelet.conf file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %a $kubeletkubeconfig; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $kubeletkubeconfig
scored: true
- id: 2.2.2
text: Ensure that the kubelet.conf file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' '
tests:
test_items:
- flag: root:root
set: true
compare:
op: eq
value: root:root
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $kubeletkubeconfig
scored: true
- id: 2.2.3
text: Ensure that the kubelet service file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %a $kubeletsvc; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 755 $kubeletsvc
scored: true
- id: 2.2.4
text: Ensure that the kubelet service file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $kubeletsvc
scored: true
- id: 2.2.5
text: Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %a $proxykubeconfig; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $proxykubeconfig
scored: true
- id: 2.2.6
text: Ensure that the proxy kubeconfig file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $proxykubeconfig
scored: true
- id: 2.2.7
text: Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Scored)
type: manual
remediation: |
Run the following command to modify the file permissions of the --client-ca-file
chmod 644 <filename>
scored: true
- id: 2.2.8
text: Ensure that the client certificate authorities file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletcafile; then stat -c %U:%G $kubeletcafile; fi'' '
tests:
test_items:
- flag: root:root
set: true
compare:
op: eq
value: root:root
remediation: |
Run the following command to modify the ownership of the --client-ca-file .
chown root:root <filename>
scored: true
- id: 2.2.9
text: Ensure that the kubelet configuration file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the following command (using the config file location identied in the Audit step)
chown root:root $kubeletconf
scored: true
- id: 2.2.10
text: Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the following command (using the config file location identied in the Audit step)
chmod 644 $kubeletconf
scored: true

2
cfg/cis-1.4/config.yaml Normal file
View File

@@ -0,0 +1,2 @@
---
## Version-specific settings that override the values in cfg/config.yaml

1549
cfg/cis-1.4/master.yaml Normal file

File diff suppressed because it is too large Load Diff

525
cfg/cis-1.4/node.yaml Normal file
View File

@@ -0,0 +1,525 @@
---
controls:
version: "1.13"
id: "2"
text: Worker Node Security Configuration
type: "node"
groups:
- id: "2.1"
text: Kubelet
checks:
- id: 2.1.1
text: Ensure that the --anonymous-auth argument is set to false (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: "--anonymous-auth"
path: '{.authentication.anonymous.enabled}'
set: true
compare:
op: eq
value: false
remediation: |
If using a Kubelet config file, edit the file to set authentication: anonymous: enabled to
false .
If using executable arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--anonymous-auth=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.2
text: Ensure that the --authorization-mode argument is not set to AlwaysAllow (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --authorization-mode
path: '{.authorization.mode}'
set: true
compare:
op: nothave
value: AlwaysAllow
remediation: |
If using a Kubelet config file, edit the file to set authorization: mode to Webhook.
If using executable arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_AUTHZ_ARGS variable.
--authorization-mode=Webhook
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.3
text: Ensure that the --client-ca-file argument is set as appropriate (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --client-ca-file
path: '{.authentication.x509.clientCAFile}'
set: true
remediation: |
If using a Kubelet config file, edit the file to set authentication: x509: clientCAFile to
the location of the client CA file.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_AUTHZ_ARGS variable.
--client-ca-file=<path/to/client-ca-file>
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.4
text: Ensure that the --read-only-port argument is set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: "--read-only-port"
path: '{.readOnlyPort}'
set: true
compare:
op: eq
value: 0
remediation: |
If using a Kubelet config file, edit the file to set readOnlyPort to 0 .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--read-only-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.5
text: Ensure that the --streaming-connection-idle-timeout argument is not set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --streaming-connection-idle-timeout
path: '{.streamingConnectionIdleTimeout}'
set: true
compare:
op: noteq
value: 0
- flag: --streaming-connection-idle-timeout
path: '{.streamingConnectionIdleTimeout}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to set streamingConnectionIdleTimeout to a
value other than 0.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--streaming-connection-idle-timeout=5m
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.6
text: Ensure that the --protect-kernel-defaults argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --protect-kernel-defaults
path: '{.protectKernelDefaults}'
set: true
compare:
op: eq
value: true
remediation: |
If using a Kubelet config file, edit the file to set protectKernelDefaults: true .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--protect-kernel-defaults=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.7
text: Ensure that the --make-iptables-util-chains argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --make-iptables-util-chains
path: '{.makeIPTablesUtilChains}'
set: true
compare:
op: eq
value: true
- flag: --make-iptables-util-chains
path: '{.makeIPTablesUtilChains}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to set makeIPTablesUtilChains: true .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
remove the --make-iptables-util-chains argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.8
text: Ensure that the --hostname-override argument is not set (Scored)
# This is one of those properties that can only be set as a command line argument.
# To check if the property is set as expected, we need to parse the kubelet command
# instead reading the Kubelet Configuration file.
audit: "/bin/ps -fC $kubeletbin "
tests:
test_items:
- flag: --hostname-override
set: false
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and remove the --hostname-override argument from the
KUBELET_SYSTEM_PODS_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.9
text: Ensure that the --event-qps argument is set to 0 (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --event-qps
path: '{.eventRecordQPS}'
set: true
compare:
op: eq
value: 0
remediation: |
If using a Kubelet config file, edit the file to set eventRecordQPS: 0 .
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--event-qps=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.10
text: Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --tls-cert-file
path: '{.tlsCertFile}'
set: true
- flag: --tls-private-key-file
path: '{.tlsPrivateKeyFile}'
set: true
bin_op: and
remediation: |
If using a Kubelet config file, edit the file to set tlsCertFile to the location of the certificate
file to use to identify this Kubelet, and tlsPrivateKeyFile to the location of the
corresponding private key file.
If using command line arguments, edit the kubelet service file
$kubeletsvc on each worker node and
set the below parameters in KUBELET_CERTIFICATE_ARGS variable.
--tls-cert-file=<path/to/tls-certificate-file>
file=<path/to/tls-key-file>
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.11
text: '[DEPRECATED] Ensure that the --cadvisor-port argument is set to 0'
# This is one of those properties that can only be set as a command line argument.
# To check if the property is set as expected, we need to parse the kubelet command
# instead reading the Kubelet Configuration file.
audit: "/bin/ps -fC $kubeletbin "
type: skip
tests:
test_items:
- flag: --cadvisor-port
set: true
compare:
op: eq
value: 0
- flag: --cadvisor-port
set: false
bin_op: or
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_CADVISOR_ARGS variable.
--cadvisor-port=0
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: false
- id: 2.1.12
text: Ensure that the --rotate-certificates argument is not set to false (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --rotate-certificates
path: '{.rotateCertificates}'
set: true
compare:
op: eq
value: true
- flag: --rotate-certificates
path: '{.rotateCertificates}'
set: false
bin_op: or
remediation: |
If using a Kubelet config file, edit the file to add the line rotateCertificates: true.
If using command line arguments, edit the kubelet service file $kubeletsvc
on each worker node and add --rotate-certificates=true argument to the KUBELET_CERTIFICATE_ARGS variable.
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.13
text: Ensure that the RotateKubeletServerCertificate argument is set to true (Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: RotateKubeletServerCertificate
path: '{.featureGates.RotateKubeletServerCertificate}'
set: true
compare:
op: eq
value: true
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_CERTIFICATE_ARGS variable.
--feature-gates=RotateKubeletServerCertificate=true
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
scored: true
- id: 2.1.14
text: Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers (Not Scored)
audit: "/bin/ps -fC $kubeletbin"
audit_config: "/bin/cat $kubeletconf"
tests:
test_items:
- flag: --tls-cipher-suites
path: '{range .tlsCipherSuites[:]}{}{'',''}{end}'
set: true
compare:
op: valid_elements
value: 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
remediation: |
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,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256
If using executable arguments, edit the kubelet service file $kubeletsvc on each worker node and set the below parameter.
--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
scored: false
- id: "2.2"
text: Configuration Files
checks:
- id: 2.2.1
text: Ensure that the kubelet.conf file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %a $kubeletkubeconfig; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $kubeletkubeconfig
scored: true
- id: 2.2.2
text: Ensure that the kubelet.conf file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletkubeconfig; then stat -c %U:%G $kubeletkubeconfig; fi'' '
tests:
test_items:
- flag: root:root
set: true
compare:
op: eq
value: root:root
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $kubeletkubeconfig
scored: true
- id: 2.2.3
text: Ensure that the kubelet service file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %a $kubeletsvc; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 755 $kubeletsvc
scored: true
- id: 2.2.4
text: Ensure that the kubelet service file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletsvc; then stat -c %U:%G $kubeletsvc; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $kubeletsvc
scored: true
- id: 2.2.5
text: Ensure that the proxy kubeconfig file permissions are set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %a $proxykubeconfig; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chmod 644 $proxykubeconfig
scored: true
- id: 2.2.6
text: Ensure that the proxy kubeconfig file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the below command (based on the file location on your system) on the each worker
node. For example,
chown root:root $proxykubeconfig
scored: true
- id: 2.2.7
text: Ensure that the certificate authorities file permissions are set to 644 or more restrictive (Scored)
type: manual
tests: {}
remediation: |
Run the following command to modify the file permissions of the --client-ca-file
chmod 644 <filename>
scored: true
- id: 2.2.8
text: Ensure that the client certificate authorities file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletcafile; then stat -c %U:%G $kubeletcafile; fi'' '
tests:
test_items:
- flag: root:root
set: true
compare:
op: eq
value: root:root
remediation: |
Run the following command to modify the ownership of the --client-ca-file .
chown root:root <filename>
scored: true
- id: 2.2.9
text: Ensure that the kubelet configuration file ownership is set to root:root (Scored)
audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' '
tests:
test_items:
- flag: root:root
set: true
remediation: |
Run the following command (using the config file location identied in the Audit step)
chown root:root $kubeletconf
scored: true
- id: 2.2.10
text: Ensure that the kubelet configuration file has permissions set to 644 or more restrictive (Scored)
audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'' '
tests:
test_items:
- flag: "644"
set: true
compare:
op: eq
value: "644"
- flag: "640"
set: true
compare:
op: eq
value: "640"
- flag: "600"
set: true
compare:
op: eq
value: "600"
bin_op: or
remediation: |
Run the following command (using the config file location identied in the Audit step)
chmod 644 $kubeletconf
scored: true

View File

@@ -1,18 +1,17 @@
---
## Controls Files.
## Controls Files.
# These are YAML files that hold all the details for running checks.
#
## Uncomment to use different control file paths.
# masterControls: ./cfg/master.yaml
# nodeControls: ./cfg/node.yaml
# federatedControls: ./cfg/federated.yaml
master:
components:
- apiserver
- scheduler
- controllermanager
- etcd
- etcd
- flanneld
# kubernetes is a component to cover the config file /etc/kubernetes/config that is referred to in the benchmark
- kubernetes
@@ -24,39 +23,49 @@ master:
bins:
- "kube-apiserver"
- "hyperkube apiserver"
- "hyperkube kube-apiserver"
- "apiserver"
confs:
- /etc/kubernetes/apiserver.conf
- /etc/kubernetes/apiserver
defaultconf: /etc/kubernetes/apiserver
- /etc/kubernetes/manifests/kube-apiserver.yaml
- /etc/kubernetes/manifests/kube-apiserver.manifest
- /var/snap/kube-apiserver/current/args
defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml
scheduler:
bins:
- "kube-scheduler"
- "hyperkube scheduler"
- "hyperkube kube-scheduler"
- "scheduler"
confs:
- /etc/kubernetes/scheduler.conf
- /etc/kubernetes/scheduler
defaultconf: /etc/kubernetes/scheduler
confs:
- /etc/kubernetes/manifests/kube-scheduler.yaml
- /etc/kubernetes/manifests/kube-scheduler.manifest
- /var/snap/kube-scheduler/current/args
defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml
controllermanager:
bins:
- "kube-controller-manager"
- "kube-controller"
- "hyperkube controller-manager"
- "hyperkube kube-controller-manager"
- "controller-manager"
confs:
- /etc/kubernetes/controller-manager.conf
- /etc/kubernetes/controller-manager
defaultconf: /etc/kubernetes/controller-manager
- /etc/kubernetes/manifests/kube-controller-manager.yaml
- /etc/kubernetes/manifests/kube-controller-manager.manifest
- /var/snap/kube-controller-manager/current/args
defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml
etcd:
optional: true
bins:
- "etcd"
confs:
- /etc/kubernetes/manifests/etcd.yaml
- /etc/kubernetes/manifests/etcd.manifest
- /etc/etcd/etcd.conf
defaultconf: /etc/etcd/etcd.conf
- /var/snap/etcd/common/etcd.conf.yml
defaultconf: /etc/kubernetes/manifests/etcd.yaml
flanneld:
optional: true
@@ -72,40 +81,71 @@ node:
- kubernetes
kubernetes:
defaultconf: /etc/kubernetes/config
defaultconf: "/etc/kubernetes/config"
kubelet:
cafile:
- "/etc/kubernetes/pki/ca.crt"
- "/etc/kubernetes/certs/ca.crt"
- "/etc/kubernetes/cert/ca.pem"
svc:
# These paths must also be included
# in the 'confs' property below
- "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
- "/etc/systemd/system/kubelet.service"
- "/lib/systemd/system/kubelet.service"
- "/etc/systemd/system/snap.kubelet.daemon.service"
bins:
- "hyperkube kubelet"
- "kubelet"
kubeconfig:
- "/etc/kubernetes/kubelet.conf"
- "/var/lib/kubelet/kubeconfig"
- "/etc/kubernetes/kubelet-kubeconfig"
confs:
- /etc/kubernetes/kubelet.conf
- /etc/kubernetes/kubelet
defaultconf: "/etc/kubernetes/kubelet.conf"
- "/var/lib/kubelet/config.yaml"
- "/etc/kubernetes/kubelet/kubelet-config.json"
- "/home/kubernetes/kubelet-config.yaml"
- "/etc/default/kubelet"
- "/var/lib/kubelet/kubeconfig"
- "/var/snap/kubelet/current/args"
## Due to the fact that the kubelet might be configured
## without a kubelet-config file, we use a work-around
## of pointing to the systemd service file (which can also
## hold kubelet configuration).
## Note: The following paths must match the one under 'svc'
- "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
- "/etc/systemd/system/kubelet.service"
- "/lib/systemd/system/kubelet.service"
- "/etc/systemd/system/snap.kubelet.daemon.service"
defaultconf: "/var/lib/kubelet/config.yaml"
defaultsvc: "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
defaultkubeconfig: "/etc/kubernetes/kubelet.conf"
defaultcafile: "/etc/kubernetes/pki/ca.crt"
proxy:
optional: true
bins:
- "kube-proxy"
- "hyperkube proxy"
- "hyperkube kube-proxy"
- "proxy"
confs:
- /etc/kubernetes/proxy.conf
- /etc/kubernetes/proxy
- /etc/kubernetes/addons/kube-proxy-daemonset.yaml
- /var/snap/kube-proxy/current/args
kubeconfig:
- /etc/kubernetes/kubelet-kubeconfig
svc:
- "/lib/systemd/system/kube-proxy.service"
defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
defaultkubeconfig: "/etc/kubernetes/proxy.conf"
federated:
components:
- fedapiserver
- fedcontrollermanager
fedapiserver:
bins:
- "hyperkube federation-apiserver"
- "kube-federation-apiserver"
- "federation-apiserver"
fedcontrollermanager:
bins:
- "hyperkube federation-controller-manager"
- "kube-federation-controller-manager"
- "federation-controller-manager"
version_mapping:
"1.11": "cis-1.3"
"1.12": "cis-1.3"
"1.13": "cis-1.4"
"1.14": "cis-1.4"
"1.15": "cis-1.4"
"ocp-3.10": "rh-0.7"
"ocp-3.11": "rh-0.7"

62
cfg/node_only.yaml Normal file
View File

@@ -0,0 +1,62 @@
---
node:
components:
- kubelet
- proxy
# kubernetes is a component to cover the config file /etc/kubernetes/config that is referred to in the benchmark
- kubernetes
kubernetes:
defaultconf: "/etc/kubernetes/config"
kubelet:
cafile:
- "/etc/kubernetes/pki/ca.crt"
- "/etc/kubernetes/certs/ca.crt"
- "/etc/kubernetes/cert/ca.pem"
svc:
# These paths must also be included
# in the 'confs' property below
- "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
- "/etc/systemd/system/kubelet.service"
- "/lib/systemd/system/kubelet.service"
bins:
- "hyperkube kubelet"
- "kubelet"
kubeconfig:
- "/etc/kubernetes/kubelet.conf"
- "/var/lib/kubelet/kubeconfig"
- "/etc/kubernetes/kubelet-kubeconfig"
confs:
- "/var/lib/kubelet/config.yaml"
- "/etc/kubernetes/kubelet/kubelet-config.json"
- "/home/kubernetes/kubelet-config.yaml"
- "/etc/default/kubelet"
## Due to the fact that the kubelet might be configured
## without a kubelet-config file, we use a work-around
## of pointing to the systemd service file (which can also
## hold kubelet configuration).
## Note: The following paths must match the one under 'svc'
- "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
- "/etc/systemd/system/kubelet.service"
- "/lib/systemd/system/kubelet.service"
defaultconf: "/var/lib/kubelet/config.yaml"
defaultsvc: "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
defaultkubeconfig: "/etc/kubernetes/kubelet.conf"
defaultcafile: "/etc/kubernetes/pki/ca.crt"
proxy:
bins:
- "kube-proxy"
- "hyperkube proxy"
- "hyperkube kube-proxy"
- "proxy"
confs:
- /etc/kubernetes/proxy
- /etc/kubernetes/addons/kube-proxy-daemonset.yaml
kubeconfig:
- /etc/kubernetes/kubelet-kubeconfig
svc:
- "/lib/systemd/system/kube-proxy.service"
defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
defaultkubeconfig: "/etc/kubernetes/proxy.conf"

27
cfg/rh-0.7/config.yaml Normal file
View File

@@ -0,0 +1,27 @@
---
## Version-specific settings that override the values in cfg/config.yaml
master:
apiserver:
bins:
- openshift start master api
- hypershift openshift-kube-apiserver
scheduler:
bins:
- "openshift start master controllers"
confs:
- /etc/origin/master/scheduler.json
controllermanager:
bins:
- "openshift start master controllers"
etcd:
bins:
- openshift start etcd
node:
proxy:
bins:
- openshift start network

1464
cfg/rh-0.7/master.yaml Normal file

File diff suppressed because it is too large Load Diff

376
cfg/rh-0.7/node.yaml Normal file
View File

@@ -0,0 +1,376 @@
---
controls:
id: 2
text: "Worker Node Security Configuration"
type: "node"
groups:
- id: 7
text: "Kubelet"
checks:
- id: 7.1
text: "Use Security Context Constraints to manage privileged containers as needed"
type: "skip"
scored: true
- id: 7.2
text: "Ensure anonymous-auth is not disabled"
type: "skip"
scored: true
- id: 7.3
text: "Verify that the --authorization-mode argument is set to WebHook"
audit: "grep -A1 authorization-mode /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "authorization-mode"
set: false
- flag: "authorization-mode"
compare:
op: has
value: "Webhook"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove authorization-mode under
kubeletArguments in /etc/origin/node/node-config.yaml or set it to "Webhook".
scored: true
- id: 7.4
text: "Verify the OpenShift default for the client-ca-file argument"
audit: "grep -A1 client-ca-file /etc/origin/node/node-config.yaml"
tests:
test_items:
- flag: "client-ca-file"
set: false
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove any configuration returned by the following:
grep -A1 client-ca-file /etc/origin/node/node-config.yaml
Reset to the OpenShift default.
See https://github.com/openshift/openshift-ansible/blob/release-3.10/roles/openshift_node_group/templates/node-config.yaml.j2#L65
The config file does not have this defined in kubeletArgument, but in PodManifestConfig.
scored: true
- id: 7.5
text: "Verify the OpenShift default setting for the read-only-port argument"
audit: "grep -A1 read-only-port /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "read-only-port"
set: false
- flag: "read-only-port"
compare:
op: has
value: "0"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and removed so that the OpenShift default is applied.
scored: true
- id: 7.6
text: "Adjust the streaming-connection-idle-timeout argument"
audit: "grep -A1 streaming-connection-idle-timeout /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "streaming-connection-idle-timeout"
set: false
- flag: "5m"
set: false
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and set the streaming-connection-timeout
value like the following in node-config.yaml.
kubeletArguments:
 streaming-connection-idle-timeout:
   - "5m"
scored: true
- id: 7.7
text: "Verify the OpenShift defaults for the protect-kernel-defaults argument"
type: "skip"
scored: true
- id: 7.8
text: "Verify the OpenShift default value of true for the make-iptables-util-chains argument"
audit: "grep -A1 make-iptables-util-chains /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "make-iptables-util-chains"
set: false
- flag: "make-iptables-util-chains"
compare:
op: has
value: "true"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and reset make-iptables-util-chains to the OpenShift
default value of true.
scored: true
- id: 7.9
text: "Verify that the --keep-terminated-pod-volumes argument is set to false"
audit: "grep -A1 keep-terminated-pod-volumes /etc/origin/node/node-config.yaml"
tests:
test_items:
- flag: "keep-terminated-pod-volumes"
compare:
op: has
value: "false"
set: true
remediation: |
Reset to the OpenShift defaults
scored: true
- id: 7.10
text: "Verify the OpenShift defaults for the hostname-override argument"
type: "skip"
scored: true
- id: 7.11
text: "Set the --event-qps argument to 0"
audit: "grep -A1 event-qps /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "event-qps"
set: false
- flag: "event-qps"
compare:
op: has
value: "0"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml set the event-qps argument to 0 in
the kubeletArguments section of.
scored: true
- id: 7.12
text: "Verify the OpenShift cert-dir flag for HTTPS traffic"
audit: "grep -A1 cert-dir /etc/origin/node/node-config.yaml"
tests:
test_items:
- flag: "/etc/origin/node/certificates"
compare:
op: has
value: "/etc/origin/node/certificates"
set: true
remediation: |
Reset to the OpenShift default values.
scored: true
- id: 7.13
text: "Verify the OpenShift default of 0 for the cadvisor-port argument"
audit: "grep -A1 cadvisor-port /etc/origin/node/node-config.yaml"
tests:
bin_op: or
test_items:
- flag: "cadvisor-port"
set: false
- flag: "cadvisor-port"
compare:
op: has
value: "0"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and remove the cadvisor-port flag
if it is set in the kubeletArguments section.
scored: true
- id: 7.14
text: "Verify that the RotateKubeletClientCertificate argument is set to true"
audit: "grep -B1 RotateKubeletClientCertificate=true /etc/origin/node/node-config.yaml"
tests:
test_items:
- flag: "RotateKubeletClientCertificate=true"
compare:
op: has
value: "true"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and set RotateKubeletClientCertificate to true.
scored: true
- id: 7.15
text: "Verify that the RotateKubeletServerCertificate argument is set to true"
audit: "grep -B1 RotateKubeletServerCertificate=true /etc/origin/node/node-config.yaml"
tests:
test_items:
- flag: "RotateKubeletServerCertificate=true"
compare:
op: has
value: "true"
set: true
remediation: |
Edit the Openshift node config file /etc/origin/node/node-config.yaml and set RotateKubeletServerCertificate to true.
scored: true
- id: 8
text: "Configuration Files"
checks:
- id: 8.1
text: "Verify the OpenShift default permissions for the kubelet.conf file"
audit: "stat -c %a /etc/origin/node/node.kubeconfig"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command on each worker node.
chmod 644 /etc/origin/node/node.kubeconfig
scored: true
- id: 8.2
text: "Verify the kubeconfig file ownership of root:root"
audit: "stat -c %U:%G /etc/origin/node/node.kubeconfig"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: |
Run the below command on each worker node.
chown root:root /etc/origin/node/node.kubeconfig
scored: true
- id: 8.3
text: "Verify the kubelet service file permissions of 644"
audit: "stat -c %a /etc/systemd/system/atomic-openshift-node.service"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command on each worker node.
chmod 644 /etc/systemd/system/atomic-openshift-node.service
scored: true
- id: 8.4
text: "Verify the kubelet service file ownership of root:root"
audit: "stat -c %U:%G /etc/systemd/system/atomic-openshift-node.service"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: |
Run the below command on each worker node.
chown root:root /etc/systemd/system/atomic-openshift-node.service
scored: true
- id: 8.5
text: "Verify the OpenShift default permissions for the proxy kubeconfig file"
audit: "stat -c %a /etc/origin/node/node.kubeconfig"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command on each worker node.
chmod 644 /etc/origin/node/node.kubeconfig
scored: true
- id: 8.6
text: "Verify the proxy kubeconfig file ownership of root:root"
audit: "stat -c %U:%G /etc/origin/node/node.kubeconfig"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: |
Run the below command on each worker node.
chown root:root /etc/origin/node/node.kubeconfig
scored: true
- id: 8.7
text: "Verify the OpenShift default permissions for the certificate authorities file."
audit: "stat -c %a /etc/origin/node/client-ca.crt"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true
remediation: |
Run the below command on each worker node.
chmod 644 /etc/origin/node/client-ca.crt
scored: true
- id: 8.8
text: "Verify the client certificate authorities file ownership of root:root"
audit: "stat -c %U:%G /etc/origin/node/client-ca.crt"
tests:
test_items:
- flag: "root:root"
compare:
op: eq
value: root:root
set: true
remediation: |
Run the below command on each worker node.
chown root:root /etc/origin/node/client-ca.crt
scored: true

View File

@@ -26,7 +26,7 @@ import (
"github.com/golang/glog"
)
// NodeType indicates the type of node (master, node, federated).
// NodeType indicates the type of node (master, node).
type NodeType string
// State is the state of a control check.
@@ -36,11 +36,11 @@ const (
// PASS check passed.
PASS State = "PASS"
// FAIL check failed.
FAIL = "FAIL"
FAIL State = "FAIL"
// WARN could not carry out check.
WARN = "WARN"
WARN State = "WARN"
// INFO informational message
INFO = "INFO"
INFO State = "INFO"
// MASTER a master node
MASTER NodeType = "master"
@@ -48,131 +48,146 @@ const (
NODE NodeType = "node"
// FEDERATED a federated deployment.
FEDERATED NodeType = "federated"
// MANUAL Check Type
MANUAL string = "manual"
)
func handleError(err error, context string) (errmsg string) {
if err != nil {
errmsg = fmt.Sprintf("%s, error: %s\n", context, err)
}
return
// Check contains information about a recommendation in the
// CIS Kubernetes document.
type Check struct {
ID string `yaml:"id" json:"test_number"`
Text string `json:"test_desc"`
Audit string `json:"audit"`
AuditConfig string `yaml:"audit_config"`
Type string `json:"type"`
Commands []*exec.Cmd `json:"omit"`
ConfigCommands []*exec.Cmd `json:"omit"`
Tests *tests `json:"omit"`
Set bool `json:"omit"`
Remediation string `json:"remediation"`
TestInfo []string `json:"test_info"`
State `json:"status"`
ActualValue string `json:"actual_value"`
Scored bool `json:"scored"`
ExpectedResult string `json:"expected_result"`
}
// Check contains information about a recommendation in the
// CIS Kubernetes 1.6+ document.
type Check struct {
ID string `yaml:"id" json:"test_number"`
Text string `json:"test_desc"`
Audit string `json:"omit"`
Type string `json:"type"`
Commands []*exec.Cmd `json:"omit"`
Tests *tests `json:"omit"`
Set bool `json:"omit"`
Remediation string `json:"-"`
TestInfo []string `json:"test_info"`
State `json:"status"`
// Runner wraps the basic Run method.
type Runner interface {
// Run runs a given check and returns the execution state.
Run(c *Check) State
}
// NewRunner constructs a default Runner.
func NewRunner() Runner {
return &defaultRunner{}
}
type defaultRunner struct{}
func (r *defaultRunner) Run(c *Check) State {
return c.run()
}
// Run executes the audit commands specified in a check and outputs
// the results.
func (c *Check) Run() {
// If check type is manual, force result to WARN.
if c.Type == "manual" {
func (c *Check) run() State {
// Since this is an Scored check
// without tests return a 'WARN' to alert
// the user that this check needs attention
if c.Scored && len(strings.TrimSpace(c.Type)) == 0 && c.Tests == nil {
c.State = WARN
return
return c.State
}
var out bytes.Buffer
var errmsgs string
// If check type is skip, force result to INFO
if c.Type == "skip" {
c.State = INFO
return c.State
}
// Check if command exists or exit with WARN.
for _, cmd := range c.Commands {
if !isShellCommand(cmd.Path) {
// If check type is manual force result to WARN
if c.Type == MANUAL {
c.State = WARN
return c.State
}
lastCommand := c.Audit
hasAuditConfig := c.ConfigCommands != nil
state, finalOutput, retErrmsgs := performTest(c.Audit, c.Commands, c.Tests)
if len(state) > 0 {
c.State = state
return c.State
}
errmsgs := retErrmsgs
// If something went wrong with the 'Audit' command
// and an 'AuditConfig' command was provided, use it to
// execute tests
if (finalOutput == nil || !finalOutput.testResult) && hasAuditConfig {
lastCommand = c.AuditConfig
nItems := len(c.Tests.TestItems)
// The reason we're creating a copy of the "tests"
// is so that tests can executed
// with the AuditConfig command
// against the Path only
currentTests := &tests{
BinOp: c.Tests.BinOp,
TestItems: make([]*testItem, nItems),
}
for i := 0; i < nItems; i++ {
ti := c.Tests.TestItems[i]
nti := &testItem{
// Path is used to test Command Param values
// AuditConfig ==> Path
Path: ti.Path,
Set: ti.Set,
Compare: ti.Compare,
}
currentTests.TestItems[i] = nti
}
state, finalOutput, retErrmsgs = performTest(c.AuditConfig, c.ConfigCommands, currentTests)
if len(state) > 0 {
c.State = state
return c.State
}
errmsgs += retErrmsgs
}
if finalOutput != nil && finalOutput.testResult {
c.State = PASS
c.ActualValue = finalOutput.actualResult
c.ExpectedResult = finalOutput.ExpectedResult
glog.V(3).Infof("Check.ID: %s Command: %q TestResult: %t Score: %q \n", c.ID, lastCommand, finalOutput.testResult, c.State)
} else {
if c.Scored {
c.State = FAIL
} else {
c.State = WARN
return
}
}
// Run commands.
n := len(c.Commands)
if n == 0 {
// Likely a warning message.
c.State = WARN
return
}
// Each command runs,
// cmd0 out -> cmd1 in, cmd1 out -> cmd2 in ... cmdn out -> os.stdout
// cmd0 err should terminate chain
cs := c.Commands
// Initialize command pipeline
cs[n-1].Stdout = &out
i := 1
var err error
errmsgs = ""
for i < n {
cs[i-1].Stdout, err = cs[i].StdinPipe()
errmsgs += handleError(
err,
fmt.Sprintf("failed to run: %s\nfailed command: %s",
c.Audit,
cs[i].Args,
),
)
i++
}
// Start command pipeline
i = 0
for i < n {
err := cs[i].Start()
errmsgs += handleError(
err,
fmt.Sprintf("failed to run: %s\nfailed command: %s",
c.Audit,
cs[i].Args,
),
)
i++
}
// Complete command pipeline
i = 0
for i < n {
err := cs[i].Wait()
errmsgs += handleError(
err,
fmt.Sprintf("failed to run: %s\nfailed command:%s",
c.Audit,
cs[i].Args,
),
)
if i < n-1 {
cs[i].Stdout.(io.Closer).Close()
}
i++
if finalOutput == nil {
glog.V(3).Infof("Check.ID: %s Command: %q TestResult: <<EMPTY>> \n", c.ID, lastCommand)
}
if errmsgs != "" {
glog.V(2).Info(errmsgs)
}
res := c.Tests.execute(out.String())
if res {
c.State = PASS
} else {
c.State = FAIL
}
return c.State
}
// textToCommand transforms an input text representation of commands to be
// run into a slice of commands.
// TODO: Make this more robust.
func textToCommand(s string) []*exec.Cmd {
glog.V(3).Infof("textToCommand: %q\n", s)
cmds := []*exec.Cmd{}
cp := strings.Split(s, "|")
@@ -229,3 +244,86 @@ func isShellCommand(s string) bool {
}
return false
}
func performTest(audit string, commands []*exec.Cmd, tests *tests) (State, *testOutput, string) {
if len(strings.TrimSpace(audit)) == 0 {
return "", failTestItem("missing command"), ""
}
var out bytes.Buffer
state, retErrmsgs := runExecCommands(audit, commands, &out)
if len(state) > 0 {
return state, nil, ""
}
errmsgs := retErrmsgs
finalOutput := tests.execute(out.String())
if finalOutput == nil {
errmsgs += fmt.Sprintf("Final output is <<EMPTY>>. Failed to run: %s\n", audit)
}
return "", finalOutput, errmsgs
}
func runExecCommands(audit string, commands []*exec.Cmd, out *bytes.Buffer) (State, string) {
var err error
errmsgs := ""
// Check if command exists or exit with WARN.
for _, cmd := range commands {
if !isShellCommand(cmd.Path) {
return WARN, errmsgs
}
}
// Run commands.
n := len(commands)
if n == 0 {
// Likely a warning message.
return WARN, errmsgs
}
// Each command runs,
// cmd0 out -> cmd1 in, cmd1 out -> cmd2 in ... cmdn out -> os.stdout
// cmd0 err should terminate chain
cs := commands
// Initialize command pipeline
cs[n-1].Stdout = out
i := 1
for i < n {
cs[i-1].Stdout, err = cs[i].StdinPipe()
if err != nil {
errmsgs += fmt.Sprintf("failed to run: %s, command: %s, error: %s\n", audit, cs[i].Args, err)
}
i++
}
// Start command pipeline
i = 0
for i < n {
err := cs[i].Start()
if err != nil {
errmsgs += fmt.Sprintf("failed to run: %s, command: %s, error: %s\n", audit, cs[i].Args, err)
}
i++
}
// Complete command pipeline
i = 0
for i < n {
err := cs[i].Wait()
if err != nil {
errmsgs += fmt.Sprintf("failed to run: %s, command: %s, error: %s\n", audit, cs[i].Args, err)
}
if i < n-1 {
cs[i].Stdout.(io.Closer).Close()
}
i++
}
glog.V(3).Infof("Command %q - Output:\n\n %s\n", audit, out.String())
return "", errmsgs
}

95
check/check_test.go Normal file
View File

@@ -0,0 +1,95 @@
// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"testing"
)
func TestCheck_Run(t *testing.T) {
type TestCase struct {
check Check
Expected State
}
testCases := []TestCase{
{check: Check{Type: MANUAL}, Expected: WARN},
{check: Check{Type: "skip"}, Expected: INFO},
{check: Check{Type: "", Scored: false}, Expected: WARN}, // Not scored checks with no type should be marked warn
{check: Check{Type: "", Scored: true}, Expected: WARN}, // If there are no tests in the check, warn
{check: Check{Type: MANUAL, Scored: false}, Expected: WARN},
{check: Check{Type: "skip", Scored: false}, Expected: INFO},
}
for _, testCase := range testCases {
testCase.check.run()
if testCase.check.State != testCase.Expected {
t.Errorf("test failed, expected %s, actual %s\n", testCase.Expected, testCase.check.State)
}
}
}
func TestCheckAuditConfig(t *testing.T) {
cases := []struct {
*Check
expected State
}{
{
controls.Groups[1].Checks[0],
"PASS",
},
{
controls.Groups[1].Checks[1],
"FAIL",
},
{
controls.Groups[1].Checks[2],
"FAIL",
},
{
controls.Groups[1].Checks[3],
"PASS",
},
{
controls.Groups[1].Checks[4],
"FAIL",
},
{
controls.Groups[1].Checks[5],
"PASS",
},
{
controls.Groups[1].Checks[6],
"FAIL",
},
{
controls.Groups[1].Checks[7],
"PASS",
},
{
controls.Groups[1].Checks[8],
"FAIL",
},
}
for _, c := range cases {
c.run()
if c.State != c.expected {
t.Errorf("%s, expected:%v, got:%v\n", c.Text, c.expected, c.State)
}
}
}

View File

@@ -18,14 +18,15 @@ import (
"encoding/json"
"fmt"
yaml "gopkg.in/yaml.v2"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
)
// Controls holds all controls to check for master nodes.
type Controls struct {
ID string `yaml:"id" json:"id"`
Version string `json:"version"`
Text string `json:"text"`
ID string `yaml:"id" json:"id"`
Version string `json:"version"`
Text string `json:"text"`
Type NodeType `json:"node_type"`
Groups []*Group `json:"tests"`
Summary
@@ -37,17 +38,22 @@ type Group struct {
Pass int `json:"pass"`
Fail int `json:"fail"`
Warn int `json:"warn"`
Info int `json:"info"`
Text string `json:"desc"`
Checks []*Check `json:"results"`
}
// Summary is a summary of the results of control checks run.
type Summary struct {
Pass int `json:"total_pass"`
Fail int `json:"total_fail"`
Warn int `json:"total_warn"`
Pass int `json:"total_pass"`
Fail int `json:"total_fail"`
Warn int `json:"total_warn"`
Info int `json:"total_info"`
}
// Predicate a predicate on the given Group and Check arguments.
type Predicate func(group *Group, check *Check) bool
// NewControls instantiates a new master Controls object.
func NewControls(t NodeType, in []byte) (*Controls, error) {
c := new(Controls)
@@ -64,83 +70,56 @@ func NewControls(t NodeType, in []byte) (*Controls, error) {
// Prepare audit commands
for _, group := range c.Groups {
for _, check := range group.Checks {
glog.V(3).Infof("Check.ID %s", check.ID)
check.Commands = textToCommand(check.Audit)
if len(check.AuditConfig) > 0 {
glog.V(3).Infof("Check.ID has audit_config %s", check.ID)
check.ConfigCommands = textToCommand(check.AuditConfig)
}
}
}
return c, nil
}
// RunGroup runs all checks in a group.
func (controls *Controls) RunGroup(gids ...string) Summary {
g := []*Group{}
controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn = 0, 0, 0
// If no groupid is passed run all group checks.
if len(gids) == 0 {
gids = controls.getAllGroupIDs()
}
for _, group := range controls.Groups {
for _, gid := range gids {
if gid == group.ID {
for _, check := range group.Checks {
check.Run()
check.TestInfo = append(check.TestInfo, check.Remediation)
summarize(controls, check)
summarizeGroup(group, check)
}
g = append(g, group)
}
}
}
controls.Groups = g
return controls.Summary
}
// RunChecks runs the checks with the supplied IDs.
func (controls *Controls) RunChecks(ids ...string) Summary {
g := []*Group{}
// RunChecks runs the checks with the given Runner. Only checks for which the filter Predicate returns `true` will run.
func (controls *Controls) RunChecks(runner Runner, filter Predicate) Summary {
var g []*Group
m := make(map[string]*Group)
controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn = 0, 0, 0
// If no groupid is passed run all group checks.
if len(ids) == 0 {
ids = controls.getAllCheckIDs()
}
controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn, controls.Info = 0, 0, 0, 0
for _, group := range controls.Groups {
for _, check := range group.Checks {
for _, id := range ids {
if id == check.ID {
check.Run()
check.TestInfo = append(check.TestInfo, check.Remediation)
summarize(controls, check)
// Check if we have already added this checks group.
if v, ok := m[group.ID]; !ok {
// Create a group with same info
w := &Group{
ID: group.ID,
Text: group.Text,
Checks: []*Check{},
}
// Add this check to the new group
w.Checks = append(w.Checks, check)
// Add to groups we have visited.
m[w.ID] = w
g = append(g, w)
} else {
v.Checks = append(v.Checks, check)
}
}
if !filter(group, check) {
continue
}
state := runner.Run(check)
check.TestInfo = append(check.TestInfo, check.Remediation)
// Check if we have already added this checks group.
if v, ok := m[group.ID]; !ok {
// Create a group with same info
w := &Group{
ID: group.ID,
Text: group.Text,
Checks: []*Check{},
}
// Add this check to the new group
w.Checks = append(w.Checks, check)
summarizeGroup(w, state)
// Add to groups we have visited.
m[w.ID] = w
g = append(g, w)
} else {
v.Checks = append(v.Checks, check)
summarizeGroup(v, state)
}
summarize(controls, state)
}
}
@@ -153,45 +132,32 @@ func (controls *Controls) JSON() ([]byte, error) {
return json.Marshal(controls)
}
func (controls *Controls) getAllGroupIDs() []string {
var ids []string
for _, group := range controls.Groups {
ids = append(ids, group.ID)
}
return ids
}
func (controls *Controls) getAllCheckIDs() []string {
var ids []string
for _, group := range controls.Groups {
for _, check := range group.Checks {
ids = append(ids, check.ID)
}
}
return ids
}
func summarize(controls *Controls, check *Check) {
switch check.State {
func summarize(controls *Controls, state State) {
switch state {
case PASS:
controls.Summary.Pass++
case FAIL:
controls.Summary.Fail++
case WARN:
controls.Summary.Warn++
case INFO:
controls.Summary.Info++
default:
glog.Warningf("Unrecognized state %s", state)
}
}
func summarizeGroup(group *Group, check *Check) {
switch check.State {
func summarizeGroup(group *Group, state State) {
switch state {
case PASS:
group.Pass++
case FAIL:
group.Fail++
case WARN:
group.Warn++
case INFO:
group.Info++
default:
glog.Warningf("Unrecognized state %s", state)
}
}

View File

@@ -1,41 +1,169 @@
// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
yaml "gopkg.in/yaml.v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"gopkg.in/yaml.v2"
)
const cfgDir = "../cfg/"
type mockRunner struct {
mock.Mock
}
func (m *mockRunner) Run(c *Check) State {
args := m.Called(c)
return args.Get(0).(State)
}
// validate that the files we're shipping are valid YAML
func TestYamlFiles(t *testing.T) {
// TODO: make this list dynamic
dirs := []string{"1.6/", "1.7/"}
for _, dir := range dirs {
dir = cfgDir + dir
files, err := ioutil.ReadDir(dir)
err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatalf("error reading %s directory: %v", dir, err)
t.Fatalf("failure accessing path %q: %v\n", path, err)
}
for _, file := range files {
fileName := file.Name()
in, err := ioutil.ReadFile(dir + fileName)
if !info.IsDir() {
t.Logf("reading file: %s", path)
in, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("error opening file %s: %v", fileName, err)
t.Fatalf("error opening file %s: %v", path, err)
}
c := new(Controls)
err = yaml.Unmarshal(in, c)
if err != nil {
t.Fatalf("failed to load YAML from %s: %v", fileName, err)
if err == nil {
t.Logf("YAML file successfully unmarshalled: %s", path)
} else {
t.Fatalf("failed to load YAML from %s: %v", path, err)
}
}
return nil
})
if err != nil {
t.Fatalf("failure walking cfg dir: %v\n", err)
}
}
func TestNewControls(t *testing.T) {
t.Run("Should return error when node type is not specified", func(t *testing.T) {
// given
in := []byte(`
---
controls:
type: # not specified
groups:
`)
// when
_, err := NewControls(MASTER, in)
// then
assert.EqualError(t, err, "non-master controls file specified")
})
t.Run("Should return error when input YAML is invalid", func(t *testing.T) {
// given
in := []byte("BOOM")
// when
_, err := NewControls(MASTER, in)
// then
assert.EqualError(t, err, "failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `BOOM` into check.Controls")
})
}
func TestControls_RunChecks(t *testing.T) {
t.Run("Should run checks matching the filter and update summaries", func(t *testing.T) {
// given
runner := new(mockRunner)
// and
in := []byte(`
---
type: "master"
groups:
- id: G1
checks:
- id: G1/C1
- id: G2
checks:
- id: G2/C1
text: "Verify that the SomeSampleFlag argument is set to true"
audit: "grep -B1 SomeSampleFlag=true /this/is/a/file/path"
tests:
test_items:
- flag: "SomeSampleFlag=true"
compare:
op: has
value: "true"
set: true
remediation: |
Edit the config file /this/is/a/file/path and set SomeSampleFlag to true.
scored: true
`)
// and
controls, err := NewControls(MASTER, in)
assert.NoError(t, err)
// and
runner.On("Run", controls.Groups[0].Checks[0]).Return(PASS)
runner.On("Run", controls.Groups[1].Checks[0]).Return(FAIL)
// and
var runAll Predicate = func(group *Group, c *Check) bool {
return true
}
// when
controls.RunChecks(runner, runAll)
// then
assert.Equal(t, 2, len(controls.Groups))
// and
G1 := controls.Groups[0]
assert.Equal(t, "G1", G1.ID)
assert.Equal(t, "G1/C1", G1.Checks[0].ID)
assertEqualGroupSummary(t, 1, 0, 0, 0, G1)
// and
G2 := controls.Groups[1]
assert.Equal(t, "G2", G2.ID)
assert.Equal(t, "G2/C1", G2.Checks[0].ID)
assert.Equal(t, "has", G2.Checks[0].Tests.TestItems[0].Compare.Op)
assert.Equal(t, "true", G2.Checks[0].Tests.TestItems[0].Compare.Value)
assert.Equal(t, true, G2.Checks[0].Tests.TestItems[0].Set)
assert.Equal(t, "SomeSampleFlag=true", G2.Checks[0].Tests.TestItems[0].Flag)
assert.Equal(t, "Edit the config file /this/is/a/file/path and set SomeSampleFlag to true.\n", G2.Checks[0].Remediation)
assert.Equal(t, true, G2.Checks[0].Scored)
assertEqualGroupSummary(t, 0, 1, 0, 0, G2)
// and
assert.Equal(t, 1, controls.Summary.Pass)
assert.Equal(t, 1, controls.Summary.Fail)
assert.Equal(t, 0, controls.Summary.Info)
assert.Equal(t, 0, controls.Summary.Warn)
// and
runner.AssertExpectations(t)
})
}
func assertEqualGroupSummary(t *testing.T, pass, fail, info, warn int, actual *Group) {
t.Helper()
assert.Equal(t, pass, actual.Pass)
assert.Equal(t, fail, actual.Fail)
assert.Equal(t, info, actual.Info)
assert.Equal(t, warn, actual.Warn)
}

View File

@@ -17,7 +17,7 @@ groups:
- id: 1
text: "flag is not set"
tests:
test_item:
test_items:
- flag: "--basic-auth"
set: false
@@ -157,4 +157,266 @@ groups:
value: Something
set: true
- id: 14
text: "check that flag some-arg is set to some-val with ':' separator"
tests:
test_items:
- flag: "some-arg"
compare:
op: eq
value: some-val
set: true
- id: 15
text: "jsonpath correct value on field"
tests:
test_items:
- path: "{.readOnlyPort}"
compare:
op: eq
value: 15000
set: true
- path: "{.readOnlyPort}"
compare:
op: gte
value: 15000
set: true
- path: "{.readOnlyPort}"
compare:
op: lte
value: 15000
set: true
- id: 16
text: "jsonpath correct case-sensitive value on string field"
tests:
test_items:
- path: "{.stringValue}"
compare:
op: noteq
value: "None"
set: true
- path: "{.stringValue}"
compare:
op: noteq
value: "webhook,Something,RBAC"
set: true
- path: "{.stringValue}"
compare:
op: eq
value: "WebHook,Something,RBAC"
set: true
- id: 17
text: "jsonpath correct value on boolean field"
tests:
test_items:
- path: "{.trueValue}"
compare:
op: noteq
value: somethingElse
set: true
- path: "{.trueValue}"
compare:
op: noteq
value: false
set: true
- path: "{.trueValue}"
compare:
op: eq
value: true
set: true
- id: 18
text: "jsonpath field absent"
tests:
test_items:
- path: "{.notARealField}"
set: false
- id: 19
text: "jsonpath correct value on nested field"
tests:
test_items:
- path: "{.authentication.anonymous.enabled}"
compare:
op: eq
value: "false"
set: true
- id: 20
text: "yamlpath correct value on field"
tests:
test_items:
- path: "{.readOnlyPort}"
compare:
op: gt
value: 14999
set: true
- id: 21
text: "yamlpath field absent"
tests:
test_items:
- path: "{.fieldThatIsUnset}"
set: false
- id: 22
text: "yamlpath correct value on nested field"
tests:
test_items:
- path: "{.authentication.anonymous.enabled}"
compare:
op: eq
value: "false"
set: true
- id: 23
text: "path on invalid json"
tests:
test_items:
- path: "{.authentication.anonymous.enabled}"
compare:
op: eq
value: "false"
set: true
- id: 24
text: "path with broken expression"
tests:
test_items:
- path: "{.missingClosingBrace"
set: true
- id: 25
text: "yamlpath on invalid yaml"
tests:
test_items:
- path: "{.authentication.anonymous.enabled}"
compare:
op: eq
value: "false"
set: true
- id: 26
text: "check regex op matches"
tests:
test_items:
- path: "{.currentMasterVersion}"
compare:
op: regex
value: '^1\.12.*$'
set: true
- id: 2.1
text: "audit and audit_config commands"
checks:
- id: 0
text: "audit finds flag and passes, audit_config doesn't exist -> pass"
audit: "echo flag=correct"
tests:
test_items:
- flag: "flag"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 1
text: "audit finds flag and fails, audit_config doesn't exist -> fail"
audit: "echo flag=wrong"
tests:
test_items:
- flag: "flag"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 2
text: "audit doesn't find flag, audit_config doesn't exist -> fail"
audit: "echo somethingElse=correct"
tests:
test_items:
- flag: "flag"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 3
text: "audit doesn't find flag, audit_config has correct setting -> pass"
audit: "echo somethingElse=correct"
audit_config: "echo 'flag: correct'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 4
text: "audit doesn't find flag, audit_config has wrong setting -> fail"
audit: "echo somethingElse=correct"
audit_config: "echo 'flag: wrong'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 5
text: "audit finds correct flag, audit_config has wrong setting -> pass"
audit: "echo flag=correct"
audit_config: "echo 'flag: wrong'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 6
text: "neither audit nor audit_config has correct setting -> fail"
audit: "echo flag=wrong"
audit_config: "echo 'flag: wrong'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 7
text: "audit isn't present, superfluous flag field,audit_config is correct -> pass"
audit_config: "echo 'flag: correct'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true
- id: 8
text: "audit isn't present, superfluous flag field,audit_config is wrong -> fail"
audit_config: "echo 'flag: wrong'"
tests:
test_items:
- flag: "flag"
path: "{.flag}"
compare:
op: eq
value: "correct"
set: true
scored: true

View File

@@ -15,11 +15,16 @@
package check
import (
"bytes"
"encoding/json"
"fmt"
"os"
"regexp"
"strconv"
"strings"
yaml "gopkg.in/yaml.v2"
"k8s.io/client-go/util/jsonpath"
)
// test:
@@ -32,12 +37,14 @@ import (
type binOp string
const (
and binOp = "and"
or = "or"
and binOp = "and"
or = "or"
defaultArraySeparator = ","
)
type testItem struct {
Flag string
Path string
Output string
Value string
Set bool
@@ -49,86 +56,235 @@ type compare struct {
Value string
}
func (t *testItem) execute(s string) (result bool) {
result = false
match := strings.Contains(s, t.Flag)
type testOutput struct {
testResult bool
actualResult string
ExpectedResult string
}
func failTestItem(s string) *testOutput {
return &testOutput{testResult: false, actualResult: s}
}
func (t *testItem) execute(s string) *testOutput {
result := &testOutput{}
var match bool
var flagVal string
if t.Flag != "" {
// Flag comparison: check if the flag is present in the input
match = strings.Contains(s, t.Flag)
} else {
// Path != "" - we don't know whether it's YAML or JSON but
// we can just try one then the other
var jsonInterface interface{}
if t.Path != "" {
err := unmarshal(s, &jsonInterface)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load YAML or JSON from provided input \"%s\": %v\n", s, err)
return failTestItem("failed to load YAML or JSON")
}
}
jsonpathResult, err := executeJSONPath(t.Path, &jsonInterface)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to parse path expression \"%s\": %v\n", t.Path, err)
return failTestItem("error executing path expression")
}
match = (jsonpathResult != "")
flagVal = jsonpathResult
}
if t.Set {
var flagVal string
isset := match
if isset && t.Compare.Op != "" {
// Expects flags in the form;
// --flag=somevalue
// --flag
// somevalue
//pttn := `(` + t.Flag + `)(=)*([^\s,]*) *`
pttn := `(` + t.Flag + `)(=)*([^\s]*) *`
flagRe := regexp.MustCompile(pttn)
vals := flagRe.FindStringSubmatch(s)
if t.Flag != "" {
// Expects flags in the form;
// --flag=somevalue
// flag: somevalue
// --flag
// somevalue
pttn := `(` + t.Flag + `)(=|: *)*([^\s]*) *`
flagRe := regexp.MustCompile(pttn)
vals := flagRe.FindStringSubmatch(s)
if len(vals) > 0 {
if vals[3] != "" {
flagVal = vals[3]
if len(vals) > 0 {
if vals[3] != "" {
flagVal = vals[3]
} else {
flagVal = vals[1]
}
} else {
flagVal = vals[1]
fmt.Fprintf(os.Stderr, "invalid flag in testitem definition")
os.Exit(1)
}
} else {
fmt.Fprintf(os.Stderr, "invalid flag in testitem definition")
os.Exit(1)
}
switch t.Compare.Op {
case "eq":
value := strings.ToLower(flagVal)
// Do case insensitive comparaison for booleans ...
if value == "false" || value == "true" {
result = value == t.Compare.Value
} else {
result = flagVal == t.Compare.Value
}
case "noteq":
value := strings.ToLower(flagVal)
// Do case insensitive comparaison for booleans ...
if value == "false" || value == "true" {
result = !(value == t.Compare.Value)
} else {
result = !(flagVal == t.Compare.Value)
}
case "gt":
a, b := toNumeric(flagVal, t.Compare.Value)
result = a > b
case "gte":
a, b := toNumeric(flagVal, t.Compare.Value)
result = a >= b
case "lt":
a, b := toNumeric(flagVal, t.Compare.Value)
result = a < b
case "lte":
a, b := toNumeric(flagVal, t.Compare.Value)
result = a <= b
case "has":
result = strings.Contains(flagVal, t.Compare.Value)
case "nothave":
result = !strings.Contains(flagVal, t.Compare.Value)
}
result.ExpectedResult, result.testResult = compareOp(t.Compare.Op, flagVal, t.Compare.Value)
} else {
result = isset
result.ExpectedResult = fmt.Sprintf("'%s' is present", t.Flag)
result.testResult = isset
}
} else {
result.ExpectedResult = fmt.Sprintf("'%s' is not present", t.Flag)
notset := !match
result.testResult = notset
}
return result
}
func compareOp(tCompareOp string, flagVal string, tCompareValue string) (string, bool) {
expectedResultPattern := ""
testResult := false
switch tCompareOp {
case "eq":
expectedResultPattern = "'%s' is equal to '%s'"
value := strings.ToLower(flagVal)
// Do case insensitive comparaison for booleans ...
if value == "false" || value == "true" {
testResult = value == tCompareValue
} else {
testResult = flagVal == tCompareValue
}
} else {
notset := !match
result = notset
case "noteq":
expectedResultPattern = "'%s' is not equal to '%s'"
value := strings.ToLower(flagVal)
// Do case insensitive comparaison for booleans ...
if value == "false" || value == "true" {
testResult = !(value == tCompareValue)
} else {
testResult = !(flagVal == tCompareValue)
}
case "gt", "gte", "lt", "lte":
a, b, err := toNumeric(flagVal, tCompareValue)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
switch tCompareOp {
case "gt":
expectedResultPattern = "%s is greater than %s"
testResult = a > b
case "gte":
expectedResultPattern = "%s is greater or equal to %s"
testResult = a >= b
case "lt":
expectedResultPattern = "%s is lower than %s"
testResult = a < b
case "lte":
expectedResultPattern = "%s is lower or equal to %s"
testResult = a <= b
}
case "has":
expectedResultPattern = "'%s' has '%s'"
testResult = strings.Contains(flagVal, tCompareValue)
case "nothave":
expectedResultPattern = " '%s' not have '%s'"
testResult = !strings.Contains(flagVal, tCompareValue)
case "regex":
expectedResultPattern = " '%s' matched by '%s'"
opRe := regexp.MustCompile(tCompareValue)
testResult = opRe.MatchString(flagVal)
case "valid_elements":
expectedResultPattern = "'%s' contains valid elements from '%s'"
s := splitAndRemoveLastSeparator(flagVal, defaultArraySeparator)
target := splitAndRemoveLastSeparator(tCompareValue, defaultArraySeparator)
testResult = allElementsValid(s, target)
}
return
if expectedResultPattern == "" {
return expectedResultPattern, testResult
}
return fmt.Sprintf(expectedResultPattern, flagVal, tCompareValue), testResult
}
func unmarshal(s string, jsonInterface *interface{}) error {
data := []byte(s)
err := json.Unmarshal(data, jsonInterface)
if err != nil {
err := yaml.Unmarshal(data, jsonInterface)
if err != nil {
return err
}
}
return nil
}
func executeJSONPath(path string, jsonInterface interface{}) (string, error) {
j := jsonpath.New("jsonpath")
j.AllowMissingKeys(true)
err := j.Parse(path)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = j.Execute(buf, jsonInterface)
if err != nil {
return "", err
}
jsonpathResult := buf.String()
return jsonpathResult, nil
}
func allElementsValid(s, t []string) bool {
sourceEmpty := len(s) == 0
targetEmpty := len(t) == 0
if sourceEmpty && targetEmpty {
return true
}
// XOR comparison -
// if either value is empty and the other is not empty,
// not all elements are valid
if (sourceEmpty || targetEmpty) && !(sourceEmpty && targetEmpty) {
return false
}
for _, sv := range s {
found := false
for _, tv := range t {
if sv == tv {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func splitAndRemoveLastSeparator(s, sep string) []string {
cleanS := strings.TrimRight(strings.TrimSpace(s), sep)
if len(cleanS) == 0 {
return []string{}
}
ts := strings.Split(cleanS, sep)
for i := range ts {
ts[i] = strings.TrimSpace(ts[i])
}
return ts
}
type tests struct {
@@ -136,13 +292,28 @@ type tests struct {
BinOp binOp `yaml:"bin_op"`
}
func (ts *tests) execute(s string) (result bool) {
res := make([]bool, len(ts.TestItems))
func (ts *tests) execute(s string) *testOutput {
finalOutput := &testOutput{}
for i, t := range ts.TestItems {
res[i] = t.execute(s)
// If no tests are defined return with empty finalOutput.
// This may be the case for checks of type: "skip".
if ts == nil {
return finalOutput
}
res := make([]testOutput, len(ts.TestItems))
if len(res) == 0 {
return finalOutput
}
expectedResultArr := make([]string, len(res))
for i, t := range ts.TestItems {
res[i] = *(t.execute(s))
expectedResultArr[i] = res[i].ExpectedResult
}
var result bool
// If no binary operation is specified, default to AND
switch ts.BinOp {
default:
@@ -151,30 +322,39 @@ func (ts *tests) execute(s string) (result bool) {
case and, "":
result = true
for i := range res {
result = result && res[i]
result = result && res[i].testResult
}
// Generate an AND expected result
finalOutput.ExpectedResult = strings.Join(expectedResultArr, " AND ")
case or:
result = false
for i := range res {
result = result || res[i]
result = result || res[i].testResult
}
// Generate an OR expected result
finalOutput.ExpectedResult = strings.Join(expectedResultArr, " OR ")
}
return
finalOutput.testResult = result
finalOutput.actualResult = res[0].actualResult
if finalOutput.actualResult == "" {
finalOutput.actualResult = s
}
return finalOutput
}
func toNumeric(a, b string) (c, d int) {
var err error
c, err = strconv.Atoi(a)
func toNumeric(a, b string) (c, d int, err error) {
c, err = strconv.Atoi(strings.TrimSpace(a))
if err != nil {
fmt.Fprintf(os.Stderr, "error converting %s: %s\n", a, err)
os.Exit(1)
return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", a, err)
}
d, err = strconv.Atoi(b)
d, err = strconv.Atoi(strings.TrimSpace(b))
if err != nil {
fmt.Fprintf(os.Stderr, "error converting %s: %s\n", b, err)
os.Exit(1)
return -1, -1, fmt.Errorf("toNumeric - error converting %s: %s", b, err)
}
return c, d
return c, d, nil
}

View File

@@ -110,12 +110,584 @@ func TestTestExecute(t *testing.T) {
controls.Groups[0].Checks[13],
"2:45 ../kubernetes/kube-apiserver --option --admission-control=Something ---audit-log-maxage=40",
},
{
// check for ':' as argument-value separator, with space between arg and val
controls.Groups[0].Checks[14],
"2:45 kube-apiserver some-arg: some-val --admission-control=Something ---audit-log-maxage=40",
},
{
// check for ':' as argument-value separator, with no space between arg and val
controls.Groups[0].Checks[14],
"2:45 kube-apiserver some-arg:some-val --admission-control=Something ---audit-log-maxage=40",
},
{
controls.Groups[0].Checks[15],
"{\"readOnlyPort\": 15000}",
},
{
controls.Groups[0].Checks[16],
"{\"stringValue\": \"WebHook,Something,RBAC\"}",
},
{
controls.Groups[0].Checks[17],
"{\"trueValue\": true}",
},
{
controls.Groups[0].Checks[18],
"{\"readOnlyPort\": 15000}",
},
{
controls.Groups[0].Checks[19],
"{\"authentication\": { \"anonymous\": {\"enabled\": false}}}",
},
{
controls.Groups[0].Checks[20],
"readOnlyPort: 15000",
},
{
controls.Groups[0].Checks[21],
"readOnlyPort: 15000",
},
{
controls.Groups[0].Checks[22],
"authentication:\n anonymous:\n enabled: false",
},
{
controls.Groups[0].Checks[26],
"currentMasterVersion: 1.12.7",
},
}
for _, c := range cases {
res := c.Tests.execute(c.str)
res := c.Tests.execute(c.str).testResult
if !res {
t.Errorf("%s, expected:%v, got:%v\n", c.Text, true, res)
}
}
}
func TestTestExecuteExceptions(t *testing.T) {
cases := []struct {
*Check
str string
}{
{
controls.Groups[0].Checks[23],
"this is not valid json {} at all",
},
{
controls.Groups[0].Checks[24],
"{\"key\": \"value\"}",
},
{
controls.Groups[0].Checks[25],
"broken } yaml\nenabled: true",
},
{
controls.Groups[0].Checks[26],
"currentMasterVersion: 1.11",
},
{
controls.Groups[0].Checks[26],
"currentMasterVersion: ",
},
}
for _, c := range cases {
res := c.Tests.execute(c.str).testResult
if res {
t.Errorf("%s, expected:%v, got:%v\n", c.Text, false, res)
}
}
}
func TestTestUnmarshal(t *testing.T) {
type kubeletConfig struct {
Kind string
ApiVersion string
Address string
}
cases := []struct {
content string
jsonInterface interface{}
expectedToFail bool
}{
{
`{
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1",
"address": "0.0.0.0"
}
`,
kubeletConfig{},
false,
}, {
`
kind: KubeletConfiguration
address: 0.0.0.0
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
tlsCipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
`,
kubeletConfig{},
false,
},
{
`
kind: ddress: 0.0.0.0
apiVersion: kubelet.config.k8s.io/v1beta
`,
kubeletConfig{},
true,
},
}
for _, c := range cases {
err := unmarshal(c.content, &c.jsonInterface)
if err != nil {
if !c.expectedToFail {
t.Errorf("%s, expectedToFail:%v, got:%v\n", c.content, c.expectedToFail, err)
}
} else {
if c.expectedToFail {
t.Errorf("%s, expectedToFail:%v, got:Did not fail\n", c.content, c.expectedToFail)
}
}
}
}
func TestExecuteJSONPath(t *testing.T) {
type kubeletConfig struct {
Kind string
ApiVersion string
Address string
}
cases := []struct {
jsonPath string
jsonInterface kubeletConfig
expectedResult string
expectedToFail bool
}{
{
// JSONPath parse works, results don't match
"{.Kind}",
kubeletConfig{
Kind: "KubeletConfiguration",
ApiVersion: "kubelet.config.k8s.io/v1beta1",
Address: "127.0.0.0",
},
"blah",
true,
},
{
// JSONPath parse works, results match
"{.Kind}",
kubeletConfig{
Kind: "KubeletConfiguration",
ApiVersion: "kubelet.config.k8s.io/v1beta1",
Address: "127.0.0.0",
},
"KubeletConfiguration",
false,
},
{
// JSONPath parse fails
"{.ApiVersion",
kubeletConfig{
Kind: "KubeletConfiguration",
ApiVersion: "kubelet.config.k8s.io/v1beta1",
Address: "127.0.0.0",
},
"",
true,
},
}
for _, c := range cases {
result, err := executeJSONPath(c.jsonPath, c.jsonInterface)
if err != nil && !c.expectedToFail {
t.Fatalf("jsonPath:%q, expectedResult:%q got:%v\n", c.jsonPath, c.expectedResult, err)
}
if c.expectedResult != result && !c.expectedToFail {
t.Errorf("jsonPath:%q, expectedResult:%q got:%q\n", c.jsonPath, c.expectedResult, result)
}
}
}
func TestAllElementsValid(t *testing.T) {
cases := []struct {
source []string
target []string
valid bool
}{
{
source: []string{},
target: []string{},
valid: true,
},
{
source: []string{"blah"},
target: []string{},
valid: false,
},
{
source: []string{},
target: []string{"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"},
valid: false,
},
{
source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
target: []string{"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"},
valid: true,
},
{
source: []string{"blah"},
target: []string{"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"},
valid: false,
},
{
source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "blah"},
target: []string{"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"},
valid: false,
},
}
for _, c := range cases {
if !allElementsValid(c.source, c.target) && c.valid {
t.Errorf("Not All Elements in %q are found in %q \n", c.source, c.target)
}
}
}
func TestSplitAndRemoveLastSeparator(t *testing.T) {
cases := []struct {
source string
valid bool
elementCnt int
}{
{
source: "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",
valid: true,
elementCnt: 8,
},
{
source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,",
valid: true,
elementCnt: 2,
},
{
source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,",
valid: true,
elementCnt: 2,
},
{
source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ",
valid: true,
elementCnt: 2,
},
{
source: " TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,",
valid: true,
elementCnt: 2,
},
}
for _, c := range cases {
as := splitAndRemoveLastSeparator(c.source, defaultArraySeparator)
if len(as) == 0 && c.valid {
t.Errorf("Split did not work with %q \n", c.source)
}
if c.elementCnt != len(as) {
t.Errorf("Split did not work with %q expected: %d got: %d\n", c.source, c.elementCnt, len(as))
}
}
}
func TestCompareOp(t *testing.T) {
cases := []struct {
label string
op string
flagVal string
compareValue string
expectedResultPattern string
testResult bool
}{
// Test Op not matching
{label: "empty - op", op: "", flagVal: "", compareValue: "", expectedResultPattern: "", testResult: false},
{label: "op=blah", op: "blah", flagVal: "foo", compareValue: "bar", expectedResultPattern: "", testResult: false},
// Test Op "eq"
{label: "op=eq, both empty", op: "eq", flagVal: "", compareValue: "", expectedResultPattern: "'' is equal to ''", testResult: true},
{label: "op=eq, true==true", op: "eq", flagVal: "true",
compareValue: "true",
expectedResultPattern: "'true' is equal to 'true'",
testResult: true},
{label: "op=eq, false==false", op: "eq", flagVal: "false",
compareValue: "false",
expectedResultPattern: "'false' is equal to 'false'",
testResult: true},
{label: "op=eq, false==true", op: "eq", flagVal: "false",
compareValue: "true",
expectedResultPattern: "'false' is equal to 'true'",
testResult: false},
{label: "op=eq, strings match", op: "eq", flagVal: "KubeletConfiguration",
compareValue: "KubeletConfiguration",
expectedResultPattern: "'KubeletConfiguration' is equal to 'KubeletConfiguration'",
testResult: true},
{label: "op=eq, flagVal=empty", op: "eq", flagVal: "",
compareValue: "KubeletConfiguration",
expectedResultPattern: "'' is equal to 'KubeletConfiguration'",
testResult: false},
{label: "op=eq, compareValue=empty", op: "eq", flagVal: "KubeletConfiguration",
compareValue: "",
expectedResultPattern: "'KubeletConfiguration' is equal to ''",
testResult: false},
// Test Op "noteq"
{label: "op=noteq, both empty", op: "noteq", flagVal: "",
compareValue: "", expectedResultPattern: "'' is not equal to ''",
testResult: false},
{label: "op=noteq, true!=true", op: "noteq", flagVal: "true",
compareValue: "true",
expectedResultPattern: "'true' is not equal to 'true'",
testResult: false},
{label: "op=noteq, false!=false", op: "noteq", flagVal: "false",
compareValue: "false",
expectedResultPattern: "'false' is not equal to 'false'",
testResult: false},
{label: "op=noteq, false!=true", op: "noteq", flagVal: "false",
compareValue: "true",
expectedResultPattern: "'false' is not equal to 'true'",
testResult: true},
{label: "op=noteq, strings match", op: "noteq", flagVal: "KubeletConfiguration",
compareValue: "KubeletConfiguration",
expectedResultPattern: "'KubeletConfiguration' is not equal to 'KubeletConfiguration'",
testResult: false},
{label: "op=noteq, flagVal=empty", op: "noteq", flagVal: "",
compareValue: "KubeletConfiguration",
expectedResultPattern: "'' is not equal to 'KubeletConfiguration'",
testResult: true},
{label: "op=noteq, compareValue=empty", op: "noteq", flagVal: "KubeletConfiguration",
compareValue: "",
expectedResultPattern: "'KubeletConfiguration' is not equal to ''",
testResult: true},
// Test Op "gt"
// TODO: test for non-numeric values.
// toNumeric function currently uses os.Exit, which stops tests.
// {label: "op=gt, both empty", op: "gt", flagVal: "",
// compareValue: "", expectedResultPattern: "'' is greater than ''",
// testResult: true},
{label: "op=gt, 0 > 0", op: "gt", flagVal: "0",
compareValue: "0", expectedResultPattern: "0 is greater than 0",
testResult: false},
{label: "op=gt, 4 > 5", op: "gt", flagVal: "4",
compareValue: "5", expectedResultPattern: "4 is greater than 5",
testResult: false},
{label: "op=gt, 5 > 4", op: "gt", flagVal: "5",
compareValue: "4", expectedResultPattern: "5 is greater than 4",
testResult: true},
{label: "op=gt, 5 > 5", op: "gt", flagVal: "5",
compareValue: "5", expectedResultPattern: "5 is greater than 5",
testResult: false},
// Test Op "lt"
// TODO: test for non-numeric values.
// toNumeric function currently uses os.Exit, which stops tests.
// {label: "op=lt, both empty", op: "lt", flagVal: "",
// compareValue: "", expectedResultPattern: "'' is lower than ''",
// testResult: true},
{label: "op=gt, 0 < 0", op: "lt", flagVal: "0",
compareValue: "0", expectedResultPattern: "0 is lower than 0",
testResult: false},
{label: "op=gt, 4 < 5", op: "lt", flagVal: "4",
compareValue: "5", expectedResultPattern: "4 is lower than 5",
testResult: true},
{label: "op=gt, 5 < 4", op: "lt", flagVal: "5",
compareValue: "4", expectedResultPattern: "5 is lower than 4",
testResult: false},
{label: "op=gt, 5 < 5", op: "lt", flagVal: "5",
compareValue: "5", expectedResultPattern: "5 is lower than 5",
testResult: false},
// Test Op "gte"
// TODO: test for non-numeric values.
// toNumeric function currently uses os.Exit, which stops tests.
// {label: "op=gt, both empty", op: "gte", flagVal: "",
// compareValue: "", expectedResultPattern: "'' is greater or equal to ''",
// testResult: true},
{label: "op=gt, 0 >= 0", op: "gte", flagVal: "0",
compareValue: "0", expectedResultPattern: "0 is greater or equal to 0",
testResult: true},
{label: "op=gt, 4 >= 5", op: "gte", flagVal: "4",
compareValue: "5", expectedResultPattern: "4 is greater or equal to 5",
testResult: false},
{label: "op=gt, 5 >= 4", op: "gte", flagVal: "5",
compareValue: "4", expectedResultPattern: "5 is greater or equal to 4",
testResult: true},
{label: "op=gt, 5 >= 5", op: "gte", flagVal: "5",
compareValue: "5", expectedResultPattern: "5 is greater or equal to 5",
testResult: true},
// Test Op "lte"
// TODO: test for non-numeric values.
// toNumeric function currently uses os.Exit, which stops tests.
// {label: "op=gt, both empty", op: "lte", flagVal: "",
// compareValue: "", expectedResultPattern: "'' is lower or equal to ''",
// testResult: true},
{label: "op=gt, 0 <= 0", op: "lte", flagVal: "0",
compareValue: "0", expectedResultPattern: "0 is lower or equal to 0",
testResult: true},
{label: "op=gt, 4 <= 5", op: "lte", flagVal: "4",
compareValue: "5", expectedResultPattern: "4 is lower or equal to 5",
testResult: true},
{label: "op=gt, 5 <= 4", op: "lte", flagVal: "5",
compareValue: "4", expectedResultPattern: "5 is lower or equal to 4",
testResult: false},
{label: "op=gt, 5 <= 5", op: "lte", flagVal: "5",
compareValue: "5", expectedResultPattern: "5 is lower or equal to 5",
testResult: true},
// Test Op "has"
{label: "op=gt, both empty", op: "has", flagVal: "",
compareValue: "", expectedResultPattern: "'' has ''",
testResult: true},
{label: "op=gt, flagVal=empty", op: "has", flagVal: "",
compareValue: "blah", expectedResultPattern: "'' has 'blah'",
testResult: false},
{label: "op=gt, compareValue=empty", op: "has", flagVal: "blah",
compareValue: "", expectedResultPattern: "'blah' has ''",
testResult: true},
{label: "op=gt, 'blah' has 'la'", op: "has", flagVal: "blah",
compareValue: "la", expectedResultPattern: "'blah' has 'la'",
testResult: true},
{label: "op=gt, 'blah' has 'LA'", op: "has", flagVal: "blah",
compareValue: "LA", expectedResultPattern: "'blah' has 'LA'",
testResult: false},
{label: "op=gt, 'blah' has 'lo'", op: "has", flagVal: "blah",
compareValue: "lo", expectedResultPattern: "'blah' has 'lo'",
testResult: false},
// Test Op "nothave"
{label: "op=gt, both empty", op: "nothave", flagVal: "",
compareValue: "", expectedResultPattern: " '' not have ''",
testResult: false},
{label: "op=gt, flagVal=empty", op: "nothave", flagVal: "",
compareValue: "blah", expectedResultPattern: " '' not have 'blah'",
testResult: true},
{label: "op=gt, compareValue=empty", op: "nothave", flagVal: "blah",
compareValue: "", expectedResultPattern: " 'blah' not have ''",
testResult: false},
{label: "op=gt, 'blah' not have 'la'", op: "nothave", flagVal: "blah",
compareValue: "la", expectedResultPattern: " 'blah' not have 'la'",
testResult: false},
{label: "op=gt, 'blah' not have 'LA'", op: "nothave", flagVal: "blah",
compareValue: "LA", expectedResultPattern: " 'blah' not have 'LA'",
testResult: true},
{label: "op=gt, 'blah' not have 'lo'", op: "nothave", flagVal: "blah",
compareValue: "lo", expectedResultPattern: " 'blah' not have 'lo'",
testResult: true},
// Test Op "regex"
{label: "op=gt, both empty", op: "regex", flagVal: "",
compareValue: "", expectedResultPattern: " '' matched by ''",
testResult: true},
{label: "op=gt, flagVal=empty", op: "regex", flagVal: "",
compareValue: "blah", expectedResultPattern: " '' matched by 'blah'",
testResult: false},
// Test Op "valid_elements"
{label: "op=valid_elements, valid_elements both empty", op: "valid_elements", flagVal: "",
compareValue: "", expectedResultPattern: "'' contains valid elements from ''",
testResult: true},
{label: "op=valid_elements, valid_elements flagVal empty", op: "valid_elements", flagVal: "",
compareValue: "a,b", expectedResultPattern: "'' contains valid elements from 'a,b'",
testResult: false},
{label: "op=valid_elements, valid_elements expectedResultPattern empty", op: "valid_elements", flagVal: "a,b",
compareValue: "", expectedResultPattern: "'a,b' contains valid elements from ''",
testResult: false},
}
for _, c := range cases {
expectedResultPattern, testResult := compareOp(c.op, c.flagVal, c.compareValue)
if expectedResultPattern != c.expectedResultPattern {
t.Errorf("'expectedResultPattern' did not match - label: %q op: %q expected 'expectedResultPattern':%q got:%q\n", c.label, c.op, c.expectedResultPattern, expectedResultPattern)
}
if testResult != c.testResult {
t.Errorf("'testResult' did not match - label: %q op: %q expected 'testResult':%t got:%t\n", c.label, c.op, c.testResult, testResult)
}
}
}
func TestToNumeric(t *testing.T) {
cases := []struct {
firstValue string
secondValue string
expectedToFail bool
}{
{
firstValue: "a",
secondValue: "b",
expectedToFail: true,
},
{
firstValue: "5",
secondValue: "b",
expectedToFail: true,
},
{
firstValue: "5",
secondValue: "6",
expectedToFail: false,
},
}
for _, c := range cases {
f, s, err := toNumeric(c.firstValue, c.secondValue)
if c.expectedToFail && err == nil {
t.Errorf("TestToNumeric - Expected error while converting %s and %s", c.firstValue, c.secondValue)
}
if !c.expectedToFail && (f != 5 || s != 6) {
t.Errorf("TestToNumeric - Expected to return %d,%d , but instead got %d,%d", 5, 6, f, s)
}
}
}

View File

@@ -15,41 +15,63 @@
package cmd
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/aquasecurity/kube-bench/check"
"github.com/golang/glog"
"github.com/spf13/viper"
)
var (
errmsgs string
)
// NewRunFilter constructs a Predicate based on FilterOpts which determines whether tested Checks should be run or not.
func NewRunFilter(opts FilterOpts) (check.Predicate, error) {
if opts.CheckList != "" && opts.GroupList != "" {
return nil, fmt.Errorf("group option and check option can't be used together")
}
var groupIDs map[string]bool
if opts.GroupList != "" {
groupIDs = cleanIDs(opts.GroupList)
}
var checkIDs map[string]bool
if opts.CheckList != "" {
checkIDs = cleanIDs(opts.CheckList)
}
return func(g *check.Group, c *check.Check) bool {
var test = true
if len(groupIDs) > 0 {
_, ok := groupIDs[g.ID]
test = test && ok
}
if len(checkIDs) > 0 {
_, ok := checkIDs[c.ID]
test = test && ok
}
test = test && (opts.Scored && c.Scored || opts.Unscored && !c.Scored)
return test
}, nil
}
func runChecks(nodetype check.NodeType) {
var summary check.Summary
var file string
var err error
var typeConf *viper.Viper
switch nodetype {
case check.MASTER:
file = masterFile
case check.NODE:
file = nodeFile
case check.FEDERATED:
file = federatedFile
// Verify config file was loaded into Viper during Cobra sub-command initialization.
if configFileError != nil {
colorPrint(check.FAIL, fmt.Sprintf("Failed to read config file: %v\n", configFileError))
os.Exit(1)
}
path, err := getConfigFilePath(kubeVersion, getKubeVersion(), file)
if err != nil {
exitWithError(fmt.Errorf("can't find %s controls file in %s: %v", nodetype, cfgDir, err))
}
def := filepath.Join(path, file)
def := loadConfig(nodetype)
in, err := ioutil.ReadFile(def)
if err != nil {
exitWithError(fmt.Errorf("error opening %s controls file: %v", nodetype, err))
@@ -57,58 +79,52 @@ func runChecks(nodetype check.NodeType) {
glog.V(1).Info(fmt.Sprintf("Using benchmark file: %s\n", def))
// Merge kubernetes version specific config if any.
viper.SetConfigFile(path + "/config.yaml")
err = viper.MergeInConfig()
// Get the set of executables and config files we care about on this type of node.
typeConf := viper.Sub(string(nodetype))
binmap, err := getBinaries(typeConf, nodetype)
// Checks that the executables we need for the node type are running.
if err != nil {
if os.IsNotExist(err) {
glog.V(2).Info(fmt.Sprintf("No version-specific config.yaml file in %s", path))
} else {
exitWithError(fmt.Errorf("couldn't read config file %s: %v", path+"/config.yaml", err))
}
} else {
glog.V(1).Info(fmt.Sprintf("Using config file: %s\n", viper.ConfigFileUsed()))
exitWithError(err)
}
// Get the set of exectuables and config files we care about on this type of node. This also
// checks that the executables we need for the node type are running.
typeConf = viper.Sub(string(nodetype))
binmap := getBinaries(typeConf)
confmap := getConfigFiles(typeConf)
confmap := getFiles(typeConf, "config")
svcmap := getFiles(typeConf, "service")
kubeconfmap := getFiles(typeConf, "kubeconfig")
cafilemap := getFiles(typeConf, "ca")
// Variable substitutions. Replace all occurrences of variables in controls files.
s := string(in)
s = makeSubstitutions(s, "bin", binmap)
s = makeSubstitutions(s, "conf", confmap)
s = makeSubstitutions(s, "svc", svcmap)
s = makeSubstitutions(s, "kubeconfig", kubeconfmap)
s = makeSubstitutions(s, "cafile", cafilemap)
controls, err := check.NewControls(nodetype, []byte(s))
if err != nil {
exitWithError(fmt.Errorf("error setting up %s controls: %v", nodetype, err))
}
if groupList != "" && checkList == "" {
ids := cleanIDs(groupList)
summary = controls.RunGroup(ids...)
} else if checkList != "" && groupList == "" {
ids := cleanIDs(checkList)
summary = controls.RunChecks(ids...)
} else if checkList != "" && groupList != "" {
exitWithError(fmt.Errorf("group option and check option can't be used together"))
} else {
summary = controls.RunGroup()
runner := check.NewRunner()
filter, err := NewRunFilter(filterOpts)
if err != nil {
exitWithError(fmt.Errorf("error setting up run filter: %v", err))
}
summary = controls.RunChecks(runner, filter)
// if we successfully ran some tests and it's json format, ignore the warnings
if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0) && jsonFmt {
if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0 || summary.Info > 0) && jsonFmt {
out, err := controls.JSON()
if err != nil {
exitWithError(fmt.Errorf("failed to output in JSON format: %v", err))
}
fmt.Println(string(out))
PrintOutput(string(out), outputFile)
} else {
// if we want to store in PostgreSQL, convert to JSON and save it
if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0) && pgSQL {
if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0 || summary.Info > 0) && pgSQL {
out, err := controls.JSON()
if err != nil {
exitWithError(fmt.Errorf("failed to output in JSON format: %v", err))
@@ -136,6 +152,10 @@ func prettyPrint(r *check.Controls, summary check.Summary) {
colorPrint(check.INFO, fmt.Sprintf("%s %s\n", g.ID, g.Text))
for _, c := range g.Checks {
colorPrint(c.State, fmt.Sprintf("%s %s\n", c.ID, c.Text))
if includeTestOutput && c.State == check.FAIL && len(c.ActualValue) > 0 {
printRawOutput(c.ActualValue)
}
}
}
@@ -148,7 +168,7 @@ func prettyPrint(r *check.Controls, summary check.Summary) {
colors[check.WARN].Printf("== Remediations ==\n")
for _, g := range r.Groups {
for _, c := range g.Checks {
if c.State != check.PASS {
if c.State == check.FAIL || c.State == check.WARN {
fmt.Printf("%s %s\n", c.ID, c.Remediation)
}
}
@@ -169,8 +189,151 @@ func prettyPrint(r *check.Controls, summary check.Summary) {
}
colors[res].Printf("== Summary ==\n")
fmt.Printf("%d checks PASS\n%d checks FAIL\n%d checks WARN\n",
summary.Pass, summary.Fail, summary.Warn,
fmt.Printf("%d checks PASS\n%d checks FAIL\n%d checks WARN\n%d checks INFO\n",
summary.Pass, summary.Fail, summary.Warn, summary.Info,
)
}
}
// loadConfig finds the correct config dir based on the kubernetes version,
// merges any specific config.yaml file found with the main config
// and returns the benchmark file to use.
func loadConfig(nodetype check.NodeType) string {
var file string
var err error
switch nodetype {
case check.MASTER:
file = masterFile
case check.NODE:
file = nodeFile
}
benchmarkVersion, err := getBenchmarkVersion(kubeVersion, benchmarkVersion, viper.GetViper())
if err != nil {
exitWithError(err)
}
path, err := getConfigFilePath(benchmarkVersion, file)
if err != nil {
exitWithError(fmt.Errorf("can't find %s controls file in %s: %v", nodetype, cfgDir, err))
}
// Merge kubernetes version specific config if any.
viper.SetConfigFile(path + "/config.yaml")
err = viper.MergeInConfig()
if err != nil {
if os.IsNotExist(err) {
glog.V(2).Info(fmt.Sprintf("No version-specific config.yaml file in %s", path))
} else {
exitWithError(fmt.Errorf("couldn't read config file %s: %v", path+"/config.yaml", err))
}
} else {
glog.V(1).Info(fmt.Sprintf("Using config file: %s\n", viper.ConfigFileUsed()))
}
return filepath.Join(path, file)
}
func mapToBenchmarkVersion(kubeToBenchmarkMap map[string]string, kv string) (string, error) {
cisVersion, found := kubeToBenchmarkMap[kv]
for !found && (kv != defaultKubeVersion && !isEmpty(kv)) {
kv = decrementVersion(kv)
cisVersion, found = kubeToBenchmarkMap[kv]
glog.V(2).Info(fmt.Sprintf("mapToBenchmarkVersion for cisVersion: %q found: %t\n", cisVersion, found))
}
if !found {
glog.V(1).Info(fmt.Sprintf("mapToBenchmarkVersion unable to find a match for: %q", kv))
glog.V(3).Info(fmt.Sprintf("mapToBenchmarkVersion kubeToBenchmarkSMap: %#v", kubeToBenchmarkMap))
return "", fmt.Errorf("Unable to find a matching Benchmark Version match for kubernetes version: %s", kubeVersion)
}
return cisVersion, nil
}
func loadVersionMapping(v *viper.Viper) (map[string]string, error) {
kubeToBenchmarkMap := v.GetStringMapString("version_mapping")
if kubeToBenchmarkMap == nil || (len(kubeToBenchmarkMap) == 0) {
return nil, fmt.Errorf("config file is missing 'version_mapping' section")
}
return kubeToBenchmarkMap, nil
}
func getBenchmarkVersion(kubeVersion, benchmarkVersion string, v *viper.Viper) (bv string, err error) {
if !isEmpty(kubeVersion) && !isEmpty(benchmarkVersion) {
return "", fmt.Errorf("It is an error to specify both --version and --benchmark flags")
}
if isEmpty(benchmarkVersion) {
if isEmpty(kubeVersion) {
kubeVersion, err = getKubeVersion()
if err != nil {
return "", fmt.Errorf("Version check failed: %s\nAlternatively, you can specify the version with --version", err)
}
}
kubeToBenchmarkMap, err := loadVersionMapping(v)
if err != nil {
return "", err
}
benchmarkVersion, err = mapToBenchmarkVersion(kubeToBenchmarkMap, kubeVersion)
if err != nil {
return "", err
}
glog.V(2).Info(fmt.Sprintf("Mapped Kubernetes version: %s to Benchmark version: %s", kubeVersion, benchmarkVersion))
}
return benchmarkVersion, nil
}
// isMaster verify if master components are running on the node.
func isMaster() bool {
glog.V(2).Info("Checking if the current node is running master components")
masterConf := viper.Sub(string(check.MASTER))
if masterConf == nil {
glog.V(2).Info("No master components found to be running")
return false
}
components, err := getBinariesFunc(masterConf, check.MASTER)
if err != nil {
glog.V(2).Info(err)
return false
}
if len(components) == 0 {
glog.V(2).Info("No master binaries specified")
return false
}
return true
}
func printRawOutput(output string) {
for _, row := range strings.Split(output, "\n") {
fmt.Println(fmt.Sprintf("\t %s", row))
}
}
func writeOutputToFile(output string, outputFile string) error {
file, err := os.Create(outputFile)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
fmt.Fprintln(w, output)
return w.Flush()
}
func PrintOutput(output string, outputFile string) {
if len(outputFile) == 0 {
fmt.Println(output)
} else {
err := writeOutputToFile(output, outputFile)
if err != nil {
exitWithError(fmt.Errorf("Failed to write to output file %s: %v", outputFile, err))
}
}
}

403
cmd/common_test.go Normal file
View File

@@ -0,0 +1,403 @@
// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/aquasecurity/kube-bench/check"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
func TestNewRunFilter(t *testing.T) {
type TestCase struct {
Name string
FilterOpts FilterOpts
Group *check.Group
Check *check.Check
Expected bool
}
testCases := []TestCase{
{
Name: "Should return true when scored flag is enabled and check is scored",
FilterOpts: FilterOpts{Scored: true, Unscored: false},
Group: &check.Group{},
Check: &check.Check{Scored: true},
Expected: true,
},
{
Name: "Should return false when scored flag is enabled and check is not scored",
FilterOpts: FilterOpts{Scored: true, Unscored: false},
Group: &check.Group{},
Check: &check.Check{Scored: false},
Expected: false,
},
{
Name: "Should return true when unscored flag is enabled and check is not scored",
FilterOpts: FilterOpts{Scored: false, Unscored: true},
Group: &check.Group{},
Check: &check.Check{Scored: false},
Expected: true,
},
{
Name: "Should return false when unscored flag is enabled and check is scored",
FilterOpts: FilterOpts{Scored: false, Unscored: true},
Group: &check.Group{},
Check: &check.Check{Scored: true},
Expected: false,
},
{
Name: "Should return true when group flag contains group's ID",
FilterOpts: FilterOpts{Scored: true, Unscored: true, GroupList: "G1,G2,G3"},
Group: &check.Group{ID: "G2"},
Check: &check.Check{},
Expected: true,
},
{
Name: "Should return false when group flag doesn't contain group's ID",
FilterOpts: FilterOpts{GroupList: "G1,G3"},
Group: &check.Group{ID: "G2"},
Check: &check.Check{},
Expected: false,
},
{
Name: "Should return true when check flag contains check's ID",
FilterOpts: FilterOpts{Scored: true, Unscored: true, CheckList: "C1,C2,C3"},
Group: &check.Group{},
Check: &check.Check{ID: "C2"},
Expected: true,
},
{
Name: "Should return false when check flag doesn't contain check's ID",
FilterOpts: FilterOpts{CheckList: "C1,C3"},
Group: &check.Group{},
Check: &check.Check{ID: "C2"},
Expected: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
filter, _ := NewRunFilter(testCase.FilterOpts)
assert.Equal(t, testCase.Expected, filter(testCase.Group, testCase.Check))
})
}
t.Run("Should return error when both group and check flags are used", func(t *testing.T) {
// given
opts := FilterOpts{GroupList: "G1", CheckList: "C1"}
// when
_, err := NewRunFilter(opts)
// then
assert.EqualError(t, err, "group option and check option can't be used together")
})
}
func TestIsMaster(t *testing.T) {
testCases := []struct {
name string
cfgFile string
getBinariesFunc func(*viper.Viper, check.NodeType) (map[string]string, error)
isMaster bool
}{
{
name: "valid config, is master and all components are running",
cfgFile: "../cfg/config.yaml",
getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) {
return map[string]string{"apiserver": "kube-apiserver"}, nil
},
isMaster: true,
},
{
name: "valid config, is master and but not all components are running",
cfgFile: "../cfg/config.yaml",
getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) {
return map[string]string{}, nil
},
isMaster: false,
},
{
name: "valid config, is master, not all components are running and fails to find all binaries",
cfgFile: "../cfg/config.yaml",
getBinariesFunc: func(viper *viper.Viper, nt check.NodeType) (strings map[string]string, i error) {
return map[string]string{}, errors.New("failed to find binaries")
},
isMaster: false,
},
{
name: "valid config, does not include master",
cfgFile: "../cfg/node_only.yaml",
isMaster: false,
},
}
for _, tc := range testCases {
cfgFile = tc.cfgFile
initConfig()
oldGetBinariesFunc := getBinariesFunc
getBinariesFunc = tc.getBinariesFunc
defer func() {
getBinariesFunc = oldGetBinariesFunc
cfgFile = ""
}()
assert.Equal(t, tc.isMaster, isMaster(), tc.name)
}
}
func TestMapToCISVersion(t *testing.T) {
viperWithData, err := loadConfigForTest()
if err != nil {
t.Fatalf("Unable to load config file %v", err)
}
kubeToBenchmarkMap, err := loadVersionMapping(viperWithData)
if err != nil {
t.Fatalf("Unable to load config file %v", err)
}
cases := []struct {
kubeVersion string
succeed bool
exp string
}{
{kubeVersion: "1.9", succeed: false, exp: ""},
{kubeVersion: "1.11", succeed: true, exp: "cis-1.3"},
{kubeVersion: "1.12", succeed: true, exp: "cis-1.3"},
{kubeVersion: "1.13", succeed: true, exp: "cis-1.4"},
{kubeVersion: "1.16", succeed: true, exp: "cis-1.4"},
{kubeVersion: "ocp-3.10", succeed: true, exp: "rh-0.7"},
{kubeVersion: "ocp-3.11", succeed: true, exp: "rh-0.7"},
{kubeVersion: "unknown", succeed: false, exp: ""},
}
for _, c := range cases {
rv, err := mapToBenchmarkVersion(kubeToBenchmarkMap, c.kubeVersion)
if c.succeed {
if err != nil {
t.Errorf("[%q]-Unexpected error: %v", c.kubeVersion, err)
}
if len(rv) == 0 {
t.Errorf("[%q]-missing return value", c.kubeVersion)
}
if c.exp != rv {
t.Errorf("[%q]- expected %q but Got %q", c.kubeVersion, c.exp, rv)
}
} else {
if c.exp != rv {
t.Errorf("mapToBenchmarkVersion kubeversion: %q Got %q expected %s", c.kubeVersion, rv, c.exp)
}
}
}
}
func TestLoadVersionMapping(t *testing.T) {
setDefault := func(v *viper.Viper, key string, value interface{}) *viper.Viper {
v.SetDefault(key, value)
return v
}
viperWithData, err := loadConfigForTest()
if err != nil {
t.Fatalf("Unable to load config file %v", err)
}
cases := []struct {
n string
v *viper.Viper
succeed bool
}{
{n: "empty", v: viper.New(), succeed: false},
{
n: "novals",
v: setDefault(viper.New(), "version_mapping", "novals"),
succeed: false,
},
{
n: "good",
v: viperWithData,
succeed: true,
},
}
for _, c := range cases {
rv, err := loadVersionMapping(c.v)
if c.succeed {
if err != nil {
t.Errorf("[%q]-Unexpected error: %v", c.n, err)
}
if len(rv) == 0 {
t.Errorf("[%q]-missing mapping value", c.n)
}
} else {
if err == nil {
t.Errorf("[%q]-Expected error but got none", c.n)
}
}
}
}
func TestGetBenchmarkVersion(t *testing.T) {
viperWithData, err := loadConfigForTest()
if err != nil {
t.Fatalf("Unable to load config file %v", err)
}
type getBenchmarkVersionFnToTest func(kubeVersion, benchmarkVersion string, v *viper.Viper) (string, error)
withFakeKubectl := func(kubeVersion, benchmarkVersion string, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) {
execCode := `#!/bin/sh
echo "Server Version: v1.13.10"
`
restore, err := fakeExecutableInPath("kubectl", execCode)
if err != nil {
t.Fatal("Failed when calling fakeExecutableInPath ", err)
}
defer restore()
return fn(kubeVersion, benchmarkVersion, v)
}
withNoPath := func(kubeVersion, benchmarkVersion string, v *viper.Viper, fn getBenchmarkVersionFnToTest) (string, error) {
restore, err := prunePath()
if err != nil {
t.Fatal("Failed when calling prunePath ", err)
}
defer restore()
return fn(kubeVersion, benchmarkVersion, v)
}
type getBenchmarkVersionFn func(string, string, *viper.Viper, getBenchmarkVersionFnToTest) (string, error)
cases := []struct {
n string
kubeVersion string
benchmarkVersion string
v *viper.Viper
callFn getBenchmarkVersionFn
exp string
succeed bool
}{
{n: "both versions", kubeVersion: "1.11", benchmarkVersion: "cis-1.3", exp: "cis-1.3", callFn: withNoPath, v: viper.New(), succeed: false},
{n: "no version-missing-kubectl", kubeVersion: "", benchmarkVersion: "", v: viperWithData, exp: "", callFn: withNoPath, succeed: false},
{n: "no version-fakeKubectl", kubeVersion: "", benchmarkVersion: "", v: viperWithData, exp: "cis-1.4", callFn: withFakeKubectl, succeed: true},
{n: "kubeVersion", kubeVersion: "1.11", benchmarkVersion: "", v: viperWithData, exp: "cis-1.3", callFn: withNoPath, succeed: true},
{n: "ocpVersion310", kubeVersion: "ocp-3.10", benchmarkVersion: "", v: viperWithData, exp: "rh-0.7", callFn: withNoPath, succeed: true},
{n: "ocpVersion311", kubeVersion: "ocp-3.11", benchmarkVersion: "", v: viperWithData, exp: "rh-0.7", callFn: withNoPath, succeed: true},
}
for _, c := range cases {
rv, err := c.callFn(c.kubeVersion, c.benchmarkVersion, c.v, getBenchmarkVersion)
if c.succeed {
if err != nil {
t.Errorf("[%q]-Unexpected error: %v", c.n, err)
}
if len(rv) == 0 {
t.Errorf("[%q]-missing return value", c.n)
}
if c.exp != rv {
t.Errorf("[%q]- expected %q but Got %q", c.n, c.exp, rv)
}
} else {
if err == nil {
t.Errorf("[%q]-Expected error but got none", c.n)
}
}
}
}
func loadConfigForTest() (*viper.Viper, error) {
viperWithData := viper.New()
viperWithData.SetConfigFile(filepath.Join("..", cfgDir, "config.yaml"))
if err := viperWithData.ReadInConfig(); err != nil {
return nil, err
}
return viperWithData, nil
}
type restoreFn func()
func fakeExecutableInPath(execFile, execCode string) (restoreFn, error) {
pathenv := os.Getenv("PATH")
tmp, err := ioutil.TempDir("", "TestfakeExecutableInPath")
if err != nil {
return nil, err
}
wd, err := os.Getwd()
if err != nil {
return nil, err
}
err = os.Chdir(tmp)
if err != nil {
return nil, err
}
if len(execCode) > 0 {
ioutil.WriteFile(filepath.Join(tmp, execFile), []byte(execCode), 0700)
} else {
f, err := os.OpenFile(execFile, os.O_CREATE|os.O_EXCL, 0700)
if err != nil {
return nil, err
}
err = f.Close()
if err != nil {
return nil, err
}
}
err = os.Setenv("PATH", fmt.Sprintf("%s:%s", tmp, pathenv))
if err != nil {
return nil, err
}
restorePath := func() {
os.RemoveAll(tmp)
os.Chdir(wd)
os.Setenv("PATH", pathenv)
}
return restorePath, nil
}
func prunePath() (restoreFn, error) {
pathenv := os.Getenv("PATH")
err := os.Setenv("PATH", "")
if err != nil {
return nil, err
}
restorePath := func() {
os.Setenv("PATH", pathenv)
}
return restorePath, nil
}

View File

@@ -49,11 +49,11 @@ func savePgsql(jsonInfo string) {
}
db, err := gorm.Open("postgres", connInfo)
defer db.Close()
if err != nil {
exitWithError(fmt.Errorf("received error connecting to database: %s", err))
}
defer db.Close()
db.Debug().AutoMigrate(&ScanResult{})
db.Save(&ScanResult{ScanHost: hostname, ScanTime: timestamp, ScanInfo: jsonInfo})
glog.V(2).Info(fmt.Sprintf("successfully stored result to: %s", envVars["PGSQL_HOST"]))

View File

@@ -1,41 +0,0 @@
// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"github.com/aquasecurity/kube-bench/check"
"github.com/spf13/cobra"
)
// nodeCmd represents the node command
var federatedCmd = &cobra.Command{
Use: "federated",
Short: "Run benchmark checks for a Kubernetes federated deployment.",
Long: `Run benchmark checks for a Kubernetes federated deployment.`,
Run: func(cmd *cobra.Command, args []string) {
runChecks(check.FEDERATED)
},
}
func init() {
federatedCmd.PersistentFlags().StringVarP(&federatedFile,
"file",
"f",
"/federated.yaml",
"Alternative YAML file for federated checks",
)
RootCmd.AddCommand(federatedCmd)
}

View File

@@ -20,33 +20,51 @@ import (
"os"
"github.com/aquasecurity/kube-bench/check"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type FilterOpts struct {
CheckList string
GroupList string
Scored bool
Unscored bool
}
var (
envVarsPrefix = "KUBE_BENCH"
defaultKubeVersion = "1.6"
defaultKubeVersion = "1.11"
kubeVersion string
benchmarkVersion string
cfgFile string
cfgDir string
jsonFmt bool
pgSQL bool
checkList string
groupList string
masterFile string
nodeFile string
federatedFile string
masterFile = "master.yaml"
nodeFile = "node.yaml"
noResults bool
noSummary bool
noRemediations bool
filterOpts FilterOpts
includeTestOutput bool
outputFile string
configFileError error
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: os.Args[0],
Short: "Run CIS Benchmarks checks against a Kubernetes deployment",
Long: `This tool runs the CIS Kubernetes Benchmark (http://www.cisecurity.org/benchmark/kubernetes/)`,
Long: `This tool runs the CIS Kubernetes Benchmark (https://www.cisecurity.org/benchmark/kubernetes/)`,
Run: func(cmd *cobra.Command, args []string) {
if isMaster() {
glog.V(1).Info("== Running master checks ==\n")
runChecks(check.MASTER)
}
glog.V(1).Info("== Running node checks ==\n")
runChecks(check.NODE)
},
}
// Execute adds all child commands to the root command sets flags appropriately.
@@ -57,8 +75,12 @@ func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
// flush before exit non-zero
glog.Flush()
os.Exit(-1)
}
// flush before exit
glog.Flush()
}
func init() {
@@ -70,16 +92,20 @@ func init() {
RootCmd.PersistentFlags().BoolVar(&noRemediations, "noremediations", false, "Disable printing of remediations section")
RootCmd.PersistentFlags().BoolVar(&jsonFmt, "json", false, "Prints the results as JSON")
RootCmd.PersistentFlags().BoolVar(&pgSQL, "pgsql", false, "Save the results to PostgreSQL")
RootCmd.PersistentFlags().BoolVar(&filterOpts.Scored, "scored", true, "Run the scored CIS checks")
RootCmd.PersistentFlags().BoolVar(&filterOpts.Unscored, "unscored", true, "Run the unscored CIS checks")
RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the actual result when test fails")
RootCmd.PersistentFlags().StringVar(&outputFile, "outputfile", "", "Writes the JSON results to output file")
RootCmd.PersistentFlags().StringVarP(
&checkList,
&filterOpts.CheckList,
"check",
"c",
"",
`A comma-delimited list of checks to run as specified in CIS document. Example --check="1.1.1,1.1.2"`,
)
RootCmd.PersistentFlags().StringVarP(
&groupList,
&filterOpts.GroupList,
"group",
"g",
"",
@@ -88,6 +114,7 @@ func init() {
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./cfg/config.yaml)")
RootCmd.PersistentFlags().StringVarP(&cfgDir, "config-dir", "D", "./cfg/", "config directory")
RootCmd.PersistentFlags().StringVar(&kubeVersion, "version", "", "Manually specify Kubernetes version, automatically detected if unset")
RootCmd.PersistentFlags().StringVar(&benchmarkVersion, "benchmark", "", "Manually specify CIS benchmark version. It would be an error to specify both --version and --benchmark flags")
goflag.CommandLine.VisitAll(func(goflag *goflag.Flag) {
RootCmd.PersistentFlags().AddGoFlag(goflag)
@@ -104,12 +131,27 @@ func initConfig() {
viper.AddConfigPath(cfgDir) // adding ./cfg as first search path
}
// Read flag values from environment variables.
// Precedence: Command line flags take precedence over environment variables.
viper.SetEnvPrefix(envVarsPrefix)
viper.AutomaticEnv() // read in environment variables that match
viper.AutomaticEnv()
if kubeVersion == "" {
if env := viper.Get("version"); env != nil {
kubeVersion = env.(string)
}
}
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err != nil {
colorPrint(check.FAIL, fmt.Sprintf("Failed to read config file: %v\n", err))
os.Exit(1)
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Config file not found; ignore error for now to prevent commands
// which don't need the config file exiting.
configFileError = err
} else {
// Config file was found but another error was produced
colorPrint(check.FAIL, fmt.Sprintf("Failed to read config file: %v\n", err))
os.Exit(1)
}
}
}

View File

@@ -27,28 +27,24 @@ var (
var psFunc func(string) string
var statFunc func(string) (os.FileInfo, error)
var getBinariesFunc func(*viper.Viper, check.NodeType) (map[string]string, error)
var TypeMap = map[string][]string{
"ca": []string{"cafile", "defaultcafile"},
"kubeconfig": []string{"kubeconfig", "defaultkubeconfig"},
"service": []string{"svc", "defaultsvc"},
"config": []string{"confs", "defaultconf"},
}
func init() {
psFunc = ps
statFunc = os.Stat
}
func printlnWarn(msg string) {
fmt.Fprintf(os.Stderr, "[%s] %s\n",
colors[check.WARN].Sprintf("%s", check.WARN),
msg,
)
}
func sprintlnWarn(msg string) string {
return fmt.Sprintf("[%s] %s",
colors[check.WARN].Sprintf("%s", check.WARN),
msg,
)
getBinariesFunc = getBinaries
}
func exitWithError(err error) {
fmt.Fprintf(os.Stderr, "\n%v\n", err)
// flush before exit non-zero
glog.Flush()
os.Exit(1)
}
@@ -64,20 +60,25 @@ func continueWithError(err error, msg string) string {
return ""
}
func cleanIDs(list string) []string {
func cleanIDs(list string) map[string]bool {
list = strings.Trim(list, ",")
ids := strings.Split(list, ",")
set := make(map[string]bool)
for _, id := range ids {
id = strings.Trim(id, " ")
set[id] = true
}
return ids
return set
}
// ps execs out to the ps command; it's separated into a function so we can write tests
func ps(proc string) string {
cmd := exec.Command("ps", "-C", proc, "-o", "cmd", "--no-headers")
// TODO: truncate proc to 15 chars
// See https://github.com/aquasecurity/kube-bench/issues/328#issuecomment-506813344
cmd := exec.Command("/bin/ps", "-C", proc, "-o", "cmd", "--no-headers")
out, err := cmd.Output()
if err != nil {
continueWithError(fmt.Errorf("%s: %s", cmd.Args, err), "")
@@ -86,8 +87,9 @@ func ps(proc string) string {
return string(out)
}
// getBinaries finds which of the set of candidate executables are running
func getBinaries(v *viper.Viper) map[string]string {
// getBinaries finds which of the set of candidate executables are running.
// It returns an error if one mandatory executable is not running.
func getBinaries(v *viper.Viper, nodetype check.NodeType) (map[string]string, error) {
binmap := make(map[string]string)
for _, component := range v.GetStringSlice("components") {
@@ -101,7 +103,8 @@ func getBinaries(v *viper.Viper) map[string]string {
if len(bins) > 0 {
bin, err := findExecutable(bins)
if err != nil && !optional {
exitWithError(fmt.Errorf("need %s executable but none of the candidates are running", component))
glog.Warning(buildComponentMissingErrorMessage(nodetype, component, bins))
return nil, fmt.Errorf("unable to detect running programs for component %q", component)
}
// Default the executable name that we'll substitute to the name of the component
@@ -115,42 +118,22 @@ func getBinaries(v *viper.Viper) map[string]string {
}
}
return binmap
return binmap, nil
}
// getConfigFilePath locates the config files we should be using based on either the specified
// version, or the running version of kubernetes if not specified
func getConfigFilePath(specifiedVersion string, runningVersion string, filename string) (path string, err error) {
var fileVersion string
// getConfigFilePath locates the config files we should be using CIS version
func getConfigFilePath(benchmarkVersion string, filename string) (path string, err error) {
glog.V(2).Info(fmt.Sprintf("Looking for config specific CIS version %q", benchmarkVersion))
if specifiedVersion != "" {
fileVersion = specifiedVersion
} else {
fileVersion = runningVersion
}
for {
path = filepath.Join(cfgDir, fileVersion)
file := filepath.Join(path, string(filename))
glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", file))
if _, err = os.Stat(file); !os.IsNotExist(err) {
if specifiedVersion == "" && fileVersion != runningVersion {
glog.V(1).Info(fmt.Sprintf("No test file found for %s - using tests for Kubernetes %s\n", runningVersion, fileVersion))
}
return path, nil
}
// If we were given an explicit version to look for, don't look for any others
if specifiedVersion != "" {
return "", err
}
fileVersion = decrementVersion(fileVersion)
if fileVersion == "" {
return "", fmt.Errorf("no test files found <= runningVersion")
}
path = filepath.Join(cfgDir, benchmarkVersion)
file := filepath.Join(path, string(filename))
glog.V(2).Info(fmt.Sprintf("Looking for config file: %s", file))
if _, err = os.Stat(file); os.IsNotExist(err) {
glog.V(2).Infof("error accessing config file: %q error: %v\n", file, err)
return "", fmt.Errorf("no test files found <= benchmark version: %s", benchmarkVersion)
}
return path, nil
}
// decrementVersion decrements the version number
@@ -158,6 +141,9 @@ func getConfigFilePath(specifiedVersion string, runningVersion string, filename
// just in case someone wants to specify their own test files for that version
func decrementVersion(version string) string {
split := strings.Split(version, ".")
if len(split) < 2 {
return ""
}
minor, err := strconv.Atoi(split[1])
if err != nil {
return ""
@@ -169,11 +155,11 @@ func decrementVersion(version string) string {
return strings.Join(split, ".")
}
// getConfigFiles finds which of the set of candidate config files exist
// accepts a string 't' which indicates the type of config file, conf,
// podspec or untifile.
func getConfigFiles(v *viper.Viper) map[string]string {
confmap := make(map[string]string)
// getFiles finds which of the set of candidate files exist
func getFiles(v *viper.Viper, fileType string) map[string]string {
filemap := make(map[string]string)
mainOpt := TypeMap[fileType][0]
defaultOpt := TypeMap[fileType][1]
for _, component := range v.GetStringSlice("components") {
s := v.Sub(component)
@@ -181,25 +167,25 @@ func getConfigFiles(v *viper.Viper) map[string]string {
continue
}
// See if any of the candidate config files exist
conf := findConfigFile(s.GetStringSlice("confs"))
if conf == "" {
if s.IsSet("defaultconf") {
conf = s.GetString("defaultconf")
glog.V(2).Info(fmt.Sprintf("Using default config file name '%s' for component %s", conf, component))
// See if any of the candidate files exist
file := findConfigFile(s.GetStringSlice(mainOpt))
if file == "" {
if s.IsSet(defaultOpt) {
file = s.GetString(defaultOpt)
glog.V(2).Info(fmt.Sprintf("Using default %s file name '%s' for component %s", fileType, file, component))
} else {
// Default the config file name that we'll substitute to the name of the component
glog.V(2).Info(fmt.Sprintf("Missing config file for %s", component))
conf = component
// Default the file name that we'll substitute to the name of the component
glog.V(2).Info(fmt.Sprintf("Missing %s file for %s", fileType, component))
file = component
}
} else {
glog.V(2).Info(fmt.Sprintf("Component %s uses config file '%s'", component, conf))
glog.V(2).Info(fmt.Sprintf("Component %s uses %s file '%s'", component, fileType, file))
}
confmap[component] = conf
filemap[component] = file
}
return confmap
return filemap
}
// verifyBin checks that the binary specified is running
@@ -265,19 +251,46 @@ func multiWordReplace(s string, subname string, sub string) string {
return strings.Replace(s, subname, sub, -1)
}
func getKubeVersion() string {
const missingKubectlKubeletMessage = `
Unable to find the programs kubectl or kubelet in the PATH.
These programs are used to determine which version of Kubernetes is running.
Make sure the /usr/bin directory is mapped to the container,
either in the job.yaml file, or Docker command.
For job.yaml:
...
- name: usr-bin
mountPath: /usr/bin
...
For docker command:
docker -v $(which kubectl):/usr/bin/kubectl ....
Alternatively, you can specify the version with --version
kube-bench --version <VERSION> ...
`
func getKubeVersion() (string, error) {
// These executables might not be on the user's path.
_, err := exec.LookPath("kubectl")
if err != nil {
_, err = exec.LookPath("kubelet")
if err != nil {
exitWithError(fmt.Errorf("Version check failed: need kubectl or kubelet binaries to get kubernetes version.\nAlternately, you can specify the version with --version"))
// Search for the kubelet binary all over the filesystem and run the first match to get the kubernetes version
cmd := exec.Command("/bin/sh", "-c", "`find / -type f -executable -name kubelet 2>/dev/null | grep -m1 .` --version")
out, err := cmd.CombinedOutput()
if err == nil {
return getVersionFromKubeletOutput(string(out)), nil
}
glog.Warning(missingKubectlKubeletMessage)
return "", fmt.Errorf("unable to find the programs kubectl or kubelet in the PATH")
}
return getKubeVersionFromKubelet()
return getKubeVersionFromKubelet(), nil
}
return getKubeVersionFromKubectl()
return getKubeVersionFromKubectl(), nil
}
func getKubeVersionFromKubectl() string {
@@ -305,7 +318,7 @@ func getVersionFromKubectlOutput(s string) string {
serverVersionRe := regexp.MustCompile(`Server Version: v(\d+.\d+)`)
subs := serverVersionRe.FindStringSubmatch(s)
if len(subs) < 2 {
printlnWarn(fmt.Sprintf("Unable to get kubectl version, using default version: %s", defaultKubeVersion))
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubectl, using default version: %s", defaultKubeVersion))
return defaultKubeVersion
}
return subs[1]
@@ -315,7 +328,7 @@ func getVersionFromKubeletOutput(s string) string {
serverVersionRe := regexp.MustCompile(`Kubernetes v(\d+.\d+)`)
subs := serverVersionRe.FindStringSubmatch(s)
if len(subs) < 2 {
printlnWarn(fmt.Sprintf("Unable to get kubelet version, using default version: %s", defaultKubeVersion))
glog.V(1).Info(fmt.Sprintf("Unable to get Kubernetes version from kubelet, using default version: %s", defaultKubeVersion))
return defaultKubeVersion
}
return subs[1]
@@ -325,7 +338,7 @@ func makeSubstitutions(s string, ext string, m map[string]string) string {
for k, v := range m {
subst := "$" + k + ext
if v == "" {
glog.V(2).Info(fmt.Sprintf("No subsitution for '%s'\n", subst))
glog.V(2).Info(fmt.Sprintf("No substitution for '%s'\n", subst))
continue
}
glog.V(2).Info(fmt.Sprintf("Substituting %s with '%s'\n", subst, v))
@@ -334,3 +347,34 @@ func makeSubstitutions(s string, ext string, m map[string]string) string {
return s
}
func isEmpty(str string) bool {
return len(strings.TrimSpace(str)) == 0
}
func buildComponentMissingErrorMessage(nodetype check.NodeType, component string, bins []string) string {
errMessageTemplate := `
Unable to detect running programs for component %q
The following %q programs have been searched, but none of them have been found:
%s
These program names are provided in the config.yaml, section '%s.%s.bins'
`
componentRoleName := "master node"
componentType := "master"
if nodetype == check.NODE {
componentRoleName = "worker node"
componentType = "node"
}
binList := ""
for _, bin := range bins {
binList = fmt.Sprintf("%s\t- %s\n", binList, bin)
}
return fmt.Sprintf(errMessageTemplate, component, componentRoleName, binList, componentType, component)
}

View File

@@ -22,6 +22,7 @@ import (
"strconv"
"testing"
"github.com/aquasecurity/kube-bench/check"
"github.com/spf13/viper"
)
@@ -109,38 +110,51 @@ func TestFindExecutable(t *testing.T) {
func TestGetBinaries(t *testing.T) {
cases := []struct {
config map[string]interface{}
psOut string
exp map[string]string
config map[string]interface{}
psOut string
exp map[string]string
expectErr bool
}{
{
config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}},
psOut: "kube-apiserver",
exp: map[string]string{"apiserver": "kube-apiserver"},
config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}},
psOut: "kube-apiserver",
exp: map[string]string{"apiserver": "kube-apiserver"},
expectErr: false,
},
{
// "thing" is not in the list of components
config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver thing",
exp: map[string]string{"apiserver": "kube-apiserver"},
config: map[string]interface{}{"components": []string{"apiserver"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver thing",
exp: map[string]string{"apiserver": "kube-apiserver"},
expectErr: false,
},
{
// "anotherthing" in list of components but doesn't have a defintion
config: map[string]interface{}{"components": []string{"apiserver", "anotherthing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver thing",
exp: map[string]string{"apiserver": "kube-apiserver"},
config: map[string]interface{}{"components": []string{"apiserver", "anotherthing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver thing",
exp: map[string]string{"apiserver": "kube-apiserver"},
expectErr: false,
},
{
// more than one component
config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver \nthing",
exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"},
config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}}},
psOut: "kube-apiserver \nthing",
exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"},
expectErr: false,
},
{
// default binary to component name
config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}, "optional": true}},
psOut: "kube-apiserver \notherthing some params",
exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"},
config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}, "optional": true}},
psOut: "kube-apiserver \notherthing some params",
exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"},
expectErr: false,
},
{
// missing mandatory component
config: map[string]interface{}{"components": []string{"apiserver", "thing"}, "apiserver": map[string]interface{}{"bins": []string{"apiserver", "kube-apiserver"}}, "thing": map[string]interface{}{"bins": []string{"something else", "thing"}, "optional": true}},
psOut: "otherthing some params",
exp: map[string]string{"apiserver": "kube-apiserver", "thing": "thing"},
expectErr: true,
},
}
@@ -153,8 +167,12 @@ func TestGetBinaries(t *testing.T) {
for k, val := range c.config {
v.Set(k, val)
}
m := getBinaries(v)
if !reflect.DeepEqual(m, c.exp) {
m, err := getBinaries(v, check.MASTER)
if c.expectErr {
if err == nil {
t.Fatal("Got nil Expected error")
}
} else if !reflect.DeepEqual(m, c.exp) {
t.Fatalf("Got %v\nExpected %v", m, c.exp)
}
})
@@ -192,8 +210,8 @@ func TestKubeVersionRegex(t *testing.T) {
}
ver = getVersionFromKubectlOutput("Something completely different")
if ver != "1.6" {
t.Fatalf("Expected 1.6 got %s", ver)
if ver != defaultKubeVersion {
t.Fatalf("Expected %s got %s", defaultKubeVersion, ver)
}
}
@@ -281,7 +299,82 @@ func TestGetConfigFiles(t *testing.T) {
e = c.statResults
eIndex = 0
m := getConfigFiles(v)
m := getFiles(v, "config")
if !reflect.DeepEqual(m, c.exp) {
t.Fatalf("Got %v\nExpected %v", m, c.exp)
}
})
}
}
func TestGetServiceFiles(t *testing.T) {
cases := []struct {
config map[string]interface{}
exp map[string]string
statResults []error
}{
{
config: map[string]interface{}{
"components": []string{"kubelet"},
"kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}},
},
statResults: []error{os.ErrNotExist, nil},
exp: map[string]string{"kubelet": "10-kubeadm.conf"},
},
{
// Component "thing" isn't included in the list of components
config: map[string]interface{}{
"components": []string{"kubelet"},
"kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}},
"thing": map[string]interface{}{"svc": []string{"/my/file/thing"}},
},
statResults: []error{os.ErrNotExist, nil},
exp: map[string]string{"kubelet": "10-kubeadm.conf"},
},
{
// More than one component
config: map[string]interface{}{
"components": []string{"kubelet", "thing"},
"kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}},
"thing": map[string]interface{}{"svc": []string{"/my/file/thing"}},
},
statResults: []error{os.ErrNotExist, nil, nil},
exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "/my/file/thing"},
},
{
// Default thing to specified default service
config: map[string]interface{}{
"components": []string{"kubelet", "thing"},
"kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}},
"thing": map[string]interface{}{"svc": []string{"/my/file/thing"}, "defaultsvc": "another/thing"},
},
statResults: []error{os.ErrNotExist, nil, os.ErrNotExist},
exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "another/thing"},
},
{
// Default thing to component name
config: map[string]interface{}{
"components": []string{"kubelet", "thing"},
"kubelet": map[string]interface{}{"svc": []string{"kubelet", "10-kubeadm.conf"}},
"thing": map[string]interface{}{"svc": []string{"/my/file/thing"}},
},
statResults: []error{os.ErrNotExist, nil, os.ErrNotExist},
exp: map[string]string{"kubelet": "10-kubeadm.conf", "thing": "thing"},
},
}
v := viper.New()
statFunc = fakestat
for id, c := range cases {
t.Run(strconv.Itoa(id), func(t *testing.T) {
for k, val := range c.config {
v.Set(k, val)
}
e = c.statResults
eIndex = 0
m := getFiles(v, "service")
if !reflect.DeepEqual(m, c.exp) {
t.Fatalf("Got %v\nExpected %v", m, c.exp)
}
@@ -316,7 +409,7 @@ func TestGetConfigFilePath(t *testing.T) {
t.Fatalf("Failed to create temp directory")
}
defer os.RemoveAll(cfgDir)
d := filepath.Join(cfgDir, "1.8")
d := filepath.Join(cfgDir, "cis-1.4")
err = os.Mkdir(d, 0666)
if err != nil {
t.Fatalf("Failed to create temp file")
@@ -324,29 +417,57 @@ func TestGetConfigFilePath(t *testing.T) {
ioutil.WriteFile(filepath.Join(d, "master.yaml"), []byte("hello world"), 0666)
cases := []struct {
specifiedVersion string
runningVersion string
benchmarkVersion string
succeed bool
exp string
}{
{runningVersion: "1.8", succeed: true, exp: d},
{runningVersion: "1.9", succeed: true, exp: d},
{runningVersion: "1.10", succeed: true, exp: d},
{runningVersion: "1.1", succeed: false},
{specifiedVersion: "1.8", succeed: true, exp: d},
{specifiedVersion: "1.9", succeed: false},
{specifiedVersion: "1.10", succeed: false},
{benchmarkVersion: "cis-1.4", succeed: true, exp: d},
{benchmarkVersion: "cis-1.5", succeed: false, exp: ""},
{benchmarkVersion: "1.1", succeed: false, exp: ""},
}
for _, c := range cases {
t.Run(c.specifiedVersion+"-"+c.runningVersion, func(t *testing.T) {
path, err := getConfigFilePath(c.specifiedVersion, c.runningVersion, "/master.yaml")
if err != nil && c.succeed {
t.Fatalf("Error %v", err)
}
if path != c.exp {
t.Fatalf("Got %s expected %s", path, c.exp)
t.Run(c.benchmarkVersion, func(t *testing.T) {
path, err := getConfigFilePath(c.benchmarkVersion, "/master.yaml")
if c.succeed {
if err != nil {
t.Fatalf("Error %v", err)
}
if path != c.exp {
t.Fatalf("Got %s expected %s", path, c.exp)
}
} else {
if err == nil {
t.Fatalf("Expected Error, but none")
}
}
})
}
}
func TestDecrementVersion(t *testing.T) {
cases := []struct {
kubeVersion string
succeed bool
exp string
}{
{kubeVersion: "1.13", succeed: true, exp: "1.12"},
{kubeVersion: "1.15", succeed: true, exp: "1.14"},
{kubeVersion: "1.11", succeed: true, exp: "1.10"},
{kubeVersion: "1.1", succeed: true, exp: ""},
{kubeVersion: "invalid", succeed: false, exp: ""},
}
for _, c := range cases {
rv := decrementVersion(c.kubeVersion)
if c.succeed {
if c.exp != rv {
t.Fatalf("decrementVersion(%q) - Got %q expected %s", c.kubeVersion, rv, c.exp)
}
} else {
if len(rv) > 0 {
t.Fatalf("decrementVersion(%q) - Expected empty string but Got %s", c.kubeVersion, rv)
}
}
}
}

23
cmd/version.go Normal file
View File

@@ -0,0 +1,23 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var KubeBenchVersion string
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Shows the version of kube-bench.",
Long: `Shows the version of kube-bench.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(KubeBenchVersion)
},
}
func init() {
RootCmd.AddCommand(versionCmd)
}

341
docs/README.md Normal file
View File

@@ -0,0 +1,341 @@
# Test and config files
`kube-bench` runs checks specified in `controls` files that are a YAML
representation of the CIS Kubernetes Benchmark checks (or other distribution-specific hardening guides).
## Controls
`controls` is a YAML document that contains checks that must be run against a
specific Kubernetes node type, master or node and version.
`controls` is the fundamental input to `kube-bench`. The following is an example
of a basic `controls`:
```yml
---
controls:
id: 1
text: "Master Node Security Configuration"
type: "master"
groups:
- id: 1.1
text: API Server
checks:
- id: 1.1.1
text: "Ensure that the --allow-privileged argument is set (Scored)"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--allow-privileged"
set: true
- flag: "--some-other-flag"
set: false
remediation: "Edit the /etc/kubernetes/config file on the master node and
set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.2
text: Scheduler
checks:
- id: 1.2.1
text: "Ensure that the --profiling argument is set to false (Scored)"
audit: "ps -ef | grep kube-scheduler | grep -v grep"
tests:
bin_op: or
test_items:
- flag: "--profiling"
set: true
- flag: "--some-other-flag"
set: false
remediation: "Edit the /etc/kubernetes/config file on the master node and
set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
```
`controls` is composed of a hierarchy of groups, sub-groups and checks. Each of
the `controls` components have an id and a text description which are displayed
in the `kube-bench` output.
`type` specifies what kubernetes node type a `controls` is for. Possible values
for `type` are `master` and `node`.
## Groups
`groups` is a list of subgroups that test the various Kubernetes components
that run on the node type specified in the `controls`.
For example, one subgroup checks parameters passed to the API server binary, while
another subgroup checks parameters passed to the controller-manager binary.
```yml
groups:
- id: 1.1
text: API Server
# ...
- id: 1.2
text: Scheduler
# ...
```
These subgroups have `id`, `text` fields which serve the same purposes described
in the previous paragraphs. The most important part of the subgroup is the
`checks` field which is the collection of actual `check`s that form the subgroup.
This is an example of a subgroup and checks in the subgroup.
```yml
id: 1.1
text: API Server
checks:
- id: 1.1.1
text: "Ensure that the --allow-privileged argument is set (Scored)"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
# ...
- id: 1.1.2
text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
# ...
```
`kube-bench` supports running a subgroup by specifying the subgroup `id` on the
command line, with the flag `--group` or `-g`.
## Check
The CIS Kubernetes Benchmark recommends configurations to harden Kubernetes components. These recommendations are usually configuration options and can be
specified by flags to Kubernetes binaries, or in configuration files.
The Benchmark also provides commands to audit a Kubernetes installation, identify
places where the cluster security can be improved, and steps to remediate these
identified problems.
In `kube-bench`, `check` objects embody these recommendations. This an example
`check` object:
```yml
id: 1.1.1
text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
remediation: |
Edit the API server pod specification file kube-apiserver
on the master node and set the below parameter.
--anonymous-auth=false
scored: false
```
A `check` object has an `id`, a `text`, an `audit`, a `tests`, `remediation`
and `scored` fields.
`kube-bench` supports running individual checks by specifying the check's `id`
as a comma-delimited list on the command line with the `--check` flag.
The `audit` field specifies the command to run for a check. The output of this
command is then evaluated for conformance with the CIS Kubernetes Benchmark
recommendation.
The audit is evaluated against criteria specified by the `tests`
object. `tests` contain `bin_op` and `test_items`.
`test_items` specify the criteria(s) the `audit` command's output should meet to
pass a check. This criteria is made up of keywords extracted from the output of
the `audit` command and operations that compare these keywords against
values expected by the CIS Kubernetes Benchmark.
There are two ways to extract keywords from the output of the `audit` command,
`flag` and `path`.
`flag` is used when the keyword is a command-line flag. The associated `audit`
command is usually a `ps` command and a `grep` for the binary whose flag we are
checking:
```sh
ps -ef | grep somebinary | grep -v grep
```
Here is an example usage of the `flag` option:
```yml
# ...
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests:
test_items:
- flag: "--anonymous-auth"
# ...
```
`path` is used when the keyword is an option set in a JSON or YAML config file.
The associated `audit` command is usually `cat /path/to/config-yaml-or-json`.
For example:
```yml
# ...
text: "Ensure that the --anonymous-auth argument is set to false (Not Scored)"
audit: "cat /path/to/some/config"
tests:
test_items:
- path: "{.someoption.value}"
# ...
```
`test_item` compares the output of the audit command and keywords using the
`set` and `compare` fields.
```yml
test_items:
- flag: "--anonymous-auth"
compare:
op: eq
value: false
set: true
```
`set` checks if a keyword is present in the output of the audit command or a config file. The possible values for `set` are true and false.
If `set` is true, the check passes only if the keyword is present in the output
of the audit command, or config file. If `set` is false, the check passes only
if the keyword is not present in the output of the audit command, or config file.
`compare` has two fields `op` and `value` to compare keywords with expected
value. `op` specifies which operation is used for the comparison, and `value`
specifies the value to compare against.
> To use `compare`, `set` must true. The comparison will be ignored if `set` is
> false
The `op` (operations) currently supported in `kube-bench` are:
- `eq`: tests if the keyword is equal to the compared value.
- `noteq`: tests if the keyword is unequal to the compared value.
- `gt`: tests if the keyword is greater than the compared value.
- `gte`: tests if the keyword is greater than or equal to the compared value.
- `lt`: tests if the keyword is less than the compared value.
- `lte`: tests if the keyword is less than or equal to the compared value.
- `has`: tests if the keyword contains the compared value.
- `nothave`: tests if the keyword does not contain the compared value.
- `regex`: tests if the flag value matches the compared value regular expression.
When defining regular expressions in YAML it is generally easier to wrap them in
single quotes, for example `'^[abc]$'`, to avoid issues with string escaping.
## Configuration and Variables
Kubernetes component configuration and binary file locations and names
vary based on cluster deployment methods and Kubernetes distribution used.
For this reason, the locations of these binaries and config files are configurable
by editing the `cfg/config.yaml` file and these binaries and files can be
referenced in a `controls` file via variables.
The `cfg/config.yaml` file is a global configuration file. Configuration files
can be created for specific Kubernetes versions (distributions). Values in the
version-specific config overwrite similar values in `cfg/config.yaml`.
For example, the kube-apiserver in Red Hat OCP distribution is run as
`hypershift openshift-kube-apiserver` instead of the default `kube-apiserver`.
This difference can be specified by editing the `master.apiserver.defaultbin`
entry `cfg/ocp-3.10/config.yaml`.
Below is the structure of `cfg/config.yaml`:
```
nodetype
|-- components
|-- component1
|-- component1
|-- bins
|-- defaultbin (optional)
|-- confs
|-- defaultconf (optional)
|-- svcs
|-- defaultsvc (optional)
|-- kubeconfig
|-- defaultkubeconfig (optional)
```
Every node type has a subsection that specifies the main configuration items.
- `components`: A list of components for the node type. For example master
will have an entry for **apiserver**, **scheduler** and **controllermanager**.
Each component has the following entries:
- `bins`: A list of candidate binaries for a component. `kube-bench` checks this
list and selects the first binary that is running on the node.
If none of the binaries in `bins` list is running, `kube-bench` checks if the
binary specified by `defaultbin` is running and terminates if none of the
binaries in both `bins` and `defaultbin` is running.
The selected binary for a component can be referenced in `controls` using a
variable in the form `$<component>bin`. In the example below, we reference
the selected API server binary with the variable `$apiserverbin` in an `audit`
command.
```yml
id: 1.1.1
text: "Ensure that the --anonymous-auth argument is set to false (Scored)"
audit: "ps -ef | grep $apiserverbin | grep -v grep"
# ...
```
- `confs`: A list of candidate configuration files for a component. `kube-bench`
checks this list and selects the first config file that is found on the node.
If none of the config files exists, `kube-bench` defaults conf to the value
of `defaultconf`.
The selected config for a component can be referenced in `controls` using a
variable in the form `$<component>conf`. In the example below, we reference the
selected API server config file with the variable `$apiserverconf` in an `audit`
command.
```yml
id: 1.4.1
text: "Ensure that the API server pod specification file permissions are
set to 644 or more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'"
```
- `svcs`: A list of candidate unitfiles for a component. `kube-bench` checks this
list and selects the first unitfile that is found on the node. If none of the
unitfiles exists, `kube-bench` defaults unitfile to the value of `defaultsvc`.
The selected unitfile for a component can be referenced in `controls` via a
variable in the form `$<component>svc`. In the example below, the selected
kubelet unitfile is referenced with `$kubeletsvc` in the `remediation` of the
`check`.
```yml
id: 2.1.1
# ...
remediation: |
Edit the kubelet service file $kubeletsvc
on each worker node and set the below parameter in KUBELET_SYSTEM_PODS_ARGS variable.
--allow-privileged=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
# ...
```
- `kubeconfig`: A list of candidate kubeconfig files for a component. `kube-bench`
checks this list and selects the first file that is found on the node. If none
of the files exists, `kube-bench` defaults kubeconfig to the value of
`defaultkubeconfig`.
The selected kubeconfig for a component can be referenced in `controls` with a variable in the form `$<component>kubeconfig`. In the example below, the
selected kubelet kubeconfig is referenced with `$kubeletkubeconfig` in the
`audit` command.
```yml
id: 2.2.1
text: "Ensure that the kubelet.conf file permissions are set to 644 or
more restrictive (Scored)"
audit: "/bin/sh -c 'if test -e $kubeletkubeconfig; then stat -c %a $kubeletkubeconfig; fi'"
# ...
```

24
go.mod Normal file
View File

@@ -0,0 +1,24 @@
module github.com/aquasecurity/kube-bench
go 1.12
require (
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
github.com/fatih/color v1.5.0
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jinzhu/gorm v0.0.0-20160404144928-5174cc5c242a
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d // indirect
github.com/jinzhu/now v1.0.1 // indirect
github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd // indirect
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597 // indirect
github.com/mattn/go-isatty v0.0.0-20170307163044-57fdcb988a5c // indirect
github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/spf13/cobra v0.0.1
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.3.0
gopkg.in/yaml.v2 v2.2.2
k8s.io/client-go v10.0.0+incompatible
)

253
go.sum Normal file
View File

@@ -0,0 +1,253 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aquasecurity/kube-bench v0.0.29 h1:jn0odIPAx+OArSfGGjA529PxZSS4xps6gq8LlX4h5wk=
github.com/aquasecurity/kube-bench v0.0.29/go.mod h1:OJtT6nbmq/4tkF3sIKHO8DIZz7PVXDwYlXJusc33R3Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.5.0 h1:vBh+kQp8lg9XPr56u1CPrWjFXtdphMoGWVHr9/1c+A0=
github.com/fatih/color v1.5.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v0.0.0-20171017181929-23c074d0eceb h1:1OvvPvZkn/yCQ3xBcM8y4020wdkMXPHLB4+NfoGWh4U=
github.com/hashicorp/hcl v0.0.0-20171017181929-23c074d0eceb/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/gorm v0.0.0-20160404144928-5174cc5c242a h1:pfPxlCVlKqBRqHpyCxOIKhhB4ERpz02iadDpRVevLm4=
github.com/jinzhu/gorm v0.0.0-20160404144928-5174cc5c242a/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd h1:2RDaVc4/izhWyAvYxNm8c9saSyCDIxefNwOcqaH7pcU=
github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v0.0.0-20171031211101-49d762b9817b h1:bR3tkU6ocnK5a0NsdgTMWc7sILt+BY0PceUYC6EpSqc=
github.com/magiconair/properties v0.0.0-20171031211101-49d762b9817b/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597 h1:hGizH4aMDFFt1iOA4HNKC13lqIBoCyxIjWcAnWIy7aU=
github.com/mattn/go-colorable v0.0.0-20170210172801-5411d3eea597/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.0-20170307163044-57fdcb988a5c h1:AHfQR/s6GNi92TOh+kfGworqDvTxj2rMsS+Hca87nck=
github.com/mattn/go-isatty v0.0.0-20170307163044-57fdcb988a5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v0.0.0-20171017171808-06020f85339e h1:PtGHLB3CX3TFPcksODQMxncoeQKWwCgTg0bJ40VLJP4=
github.com/mitchellh/mapstructure v0.0.0-20171017171808-06020f85339e/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pelletier/go-toml v0.0.0-20171222114548-0131db6d737c h1:38Gz4xhAnFXimzmHWtvA13DKjvKbXA8OoCpUwCsfmAk=
github.com/pelletier/go-toml v0.0.0-20171222114548-0131db6d737c/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v0.0.0-20171228125011-57afd63c6860 h1:Sah2mqQfQuPUyJ+MJN2JevGfVjF80KsRLR5fcaERajg=
github.com/spf13/afero v0.0.0-20171228125011-57afd63c6860/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.1.0 h1:0Rhw4d6C8J9VPu6cjZLIhZ8+aAOHcDvGeKn+cq5Aq3k=
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8=
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386 h1:zBoLErXXAvWnNsu+pWkRYl6Cx1KXmIfAVsIuYkPN6aY=
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20171106142849-4c012f6dcd95 h1:fBkxrj/ArtKnC3J1DOZhn3SYiVkVRFZC574bq2Ifa/0=
github.com/spf13/pflag v0.0.0-20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.0.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I=
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/client-go v10.0.0+incompatible h1:F1IqCqw7oMBzDkqlcBymRq1450wD0eNqLE9jzUrIi34=
k8s.io/client-go v10.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=

46
hack/debug.yaml Normal file
View File

@@ -0,0 +1,46 @@
# use this pod with: kubectl run ubuntu -it --pid=host -- /bin/bash
# this allows you to debug what is running on the host.
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
spec:
hostPID: true
containers:
- name: ubuntu
image: ubuntu
command: [ "/bin/bash", "-c", "--" ]
args: [ "while true; do sleep 30; done;" ]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
# /usr/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
# You can omit this mount if you specify --version as part of the command.
- name: usr-bin
mountPath: /usr/bin
- name: kind-bin
mountPath: /kind/bin
resources:
limits:
memory: "128Mi"
cpu: "500m"
volumes:
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"
- name: kind-bin
hostPath:
path: "/kind/bin"

50
hack/kind.yaml Normal file
View File

@@ -0,0 +1,50 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
metadata:
labels:
app: kube-bench
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:${VERSION}
command: ["kube-bench"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
# /usr/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
# You can omit this mount if you specify --version as part of the command.
- name: usr-bin
mountPath: /usr/bin
- name: kind-bin
mountPath: /kind/bin
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: "/var/lib/etcd"
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"
- name: kind-bin
hostPath:
path: "/kind/bin"

32
job-eks.yaml Normal file
View File

@@ -0,0 +1,32 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
# Push the image to your ECR and then refer to it here
image: <ID.dkr.ecr.region.amazonaws.com/aquasec/kube-bench:ref>
command: ["kube-bench", "--version", "1.11"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"

33
job-iks.yaml Normal file
View File

@@ -0,0 +1,33 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench", "--version", "1.13", "node"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/lib/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"

38
job-master.yaml Normal file
View File

@@ -0,0 +1,38 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench-master
spec:
template:
spec:
hostPID: true
nodeSelector:
node-role.kubernetes.io/master: ""
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench","master"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
- name: etc-kubernetes
mountPath: /etc/kubernetes
# /usr/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
# You can omit this mount if you specify --version as part of the command.
- name: usr-bin
mountPath: /usr/bin
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: "/var/lib/etcd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"

37
job-node.yaml Normal file
View File

@@ -0,0 +1,37 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench-node
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench","node"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
# /usr/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
# You can omit this mount if you specify --version as part of the command.
- name: usr-bin
mountPath: /usr/bin
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"

45
job.yaml Normal file
View File

@@ -0,0 +1,45 @@
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
metadata:
labels:
app: kube-bench
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
- name: etc-systemd
mountPath: /etc/systemd
- name: etc-kubernetes
mountPath: /etc/kubernetes
# /usr/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
# You can omit this mount if you specify --version as part of the command.
- name: usr-bin
mountPath: /usr/bin
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: "/var/lib/etcd"
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
- name: usr-bin
hostPath:
path: "/usr/bin"

View File

@@ -1,7 +1,71 @@
SOURCES := $(shell find . -name '*.go')
TARGET_OS := linux
BINARY := kube-bench
DOCKER_REGISTRY ?= aquasec
VERSION ?= $(shell git rev-parse --short=7 HEAD)
KUBEBENCH_VERSION ?= $(shell git describe --tags --abbrev=0)
IMAGE_NAME ?= $(DOCKER_REGISTRY)/$(BINARY):$(VERSION)
TARGET_OS ?= linux
BUILD_OS := linux
uname := $(shell uname -s)
ifneq ($(findstring Microsoft,$(shell uname -r)),)
BUILD_OS := windows
else ifeq ($(uname),Linux)
BUILD_OS := linux
else ifeq ($(uname),Darwin)
BUILD_OS := darwin
endif
# kind cluster name to use
KIND_PROFILE ?= kube-bench
KIND_CONTAINER_NAME=$(KIND_PROFILE)-control-plane
build: kube-bench
$(BINARY): $(SOURCES)
GOOS=$(TARGET_OS) go build -o $(BINARY) .
GOOS=$(TARGET_OS) go build -ldflags "-X github.com/aquasecurity/kube-bench/cmd.KubeBenchVersion=$(KUBEBENCH_VERSION)" -o $(BINARY) .
# builds the current dev docker version
build-docker:
docker build --build-arg BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ") \
--build-arg VCS_REF=$(shell git rev-parse --short HEAD) \
--build-arg KUBEBENCH_VERSION=$(KUBEBENCH_VERSION) \
-t $(IMAGE_NAME) .
tests:
GO111MODULE=on go test -v -short -race -timeout 30s -coverprofile=coverage.txt -covermode=atomic ./...
# creates a kind cluster to be used for development.
HAS_KIND := $(shell command -v kind;)
kind-test-cluster:
ifndef HAS_KIND
go get -u sigs.k8s.io/kind
endif
@if [ -z $$(kind get clusters | grep $(KIND_PROFILE)) ]; then\
echo "Could not find $(KIND_PROFILE) cluster. Creating...";\
kind create cluster --name $(KIND_PROFILE) --image kindest/node:v1.15.3 --wait 5m;\
fi
# pushses the current dev version to the kind cluster.
kind-push:
kind load docker-image $(IMAGE_NAME) --name $(KIND_PROFILE)
# runs the current version on kind using a job and follow logs
kind-run: KUBECONFIG = "$(shell kind get kubeconfig-path --name="$(KIND_PROFILE)")"
kind-run: ensure-stern
sed "s/\$${VERSION}/$(VERSION)/" ./hack/kind.yaml > ./hack/kind.test.yaml
-KUBECONFIG=$(KUBECONFIG) \
kubectl delete job kube-bench
KUBECONFIG=$(KUBECONFIG) \
kubectl apply -f ./hack/kind.test.yaml
KUBECONFIG=$(KUBECONFIG) \
stern -l app=kube-bench --container kube-bench
# ensures that stern is installed
HAS_STERN := $(shell command -v stern;)
ensure-stern:
ifndef HAS_STERN
curl -LO https://github.com/wercker/stern/releases/download/1.10.0/stern_$(BUILD_OS)_amd64 && \
chmod +rx ./stern_$(BUILD_OS)_amd64 && \
mv ./stern_$(BUILD_OS)_amd64 /usr/local/bin/stern
endif