mirror of
https://github.com/aquasecurity/kube-bench.git
synced 2024-12-19 13:18:07 +00:00
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.
This commit is contained in:
parent
573136a700
commit
4d3144ca21
11
Gopkg.lock
generated
11
Gopkg.lock
generated
@ -189,6 +189,17 @@
|
|||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "c95af922eae69f190717a0b7148960af8c55a072"
|
revision = "c95af922eae69f190717a0b7148960af8c55a072"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:e8e3acc03397f71fad44385631e665c639a8d55bd187bcfa6e70b695e3705edd"
|
||||||
|
name = "k8s.io/client-go"
|
||||||
|
packages = [
|
||||||
|
"third_party/forked/golang/template",
|
||||||
|
"util/jsonpath",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "e64494209f554a6723674bd494d69445fb76a1d4"
|
||||||
|
version = "v10.0.0"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
|
25
README.md
25
README.md
@ -149,7 +149,7 @@ These groups are further organized under `controls` which can be of the type `ma
|
|||||||
## Tests
|
## 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.
|
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.
|
||||||
|
|
||||||
The syntax for tests:
|
The syntax for tests operating on a flag:
|
||||||
```
|
```
|
||||||
tests:
|
tests:
|
||||||
- flag:
|
- flag:
|
||||||
@ -159,6 +159,29 @@ tests:
|
|||||||
value:
|
value:
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If using a JSON config file, the syntax is:
|
||||||
|
```
|
||||||
|
tests:
|
||||||
|
- jsonpath:
|
||||||
|
set:
|
||||||
|
compare:
|
||||||
|
op:
|
||||||
|
value:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
And for a YAML config file:
|
||||||
|
```
|
||||||
|
tests:
|
||||||
|
- yamlpath:
|
||||||
|
set:
|
||||||
|
compare:
|
||||||
|
op:
|
||||||
|
value:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Tests have various `operations` which are used to compare the output of audit commands for success.
|
Tests have various `operations` which are used to compare the output of audit commands for success.
|
||||||
These operations are:
|
These operations are:
|
||||||
|
|
||||||
|
30
cfg/1.11-json/config.yaml
Normal file
30
cfg/1.11-json/config.yaml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
## 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 nodes are controlled by EKS and not user-accessible
|
||||||
|
master:
|
||||||
|
components: []
|
||||||
|
|
||||||
|
node:
|
||||||
|
kubernetes:
|
||||||
|
confs:
|
||||||
|
- "/var/lib/kubelet/kubeconfig"
|
||||||
|
kubeconfig:
|
||||||
|
- "/var/lib/kubelet/kubeconfig"
|
||||||
|
|
||||||
|
kubelet:
|
||||||
|
bins:
|
||||||
|
- "hyperkube kubelet"
|
||||||
|
- "kubelet"
|
||||||
|
defaultconf: "/etc/kubernetes/kubelet/kubelet-config.json"
|
||||||
|
defaultsvc: "/etc/systemd/system/kubelet.service"
|
||||||
|
defaultkubeconfig: "/var/lib/kubelet/kubeconfig"
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
defaultkubeconfig: "/var/lib/kubelet/kubeconfig"
|
1446
cfg/1.11-json/master.yaml
Normal file
1446
cfg/1.11-json/master.yaml
Normal file
File diff suppressed because it is too large
Load Diff
508
cfg/1.11-json/node.yaml
Normal file
508
cfg/1.11-json/node.yaml
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
---
|
||||||
|
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: "ps -fC $kubeletbin"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- flag: "--allow-privileged"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: false
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.authentication.anonymous.enabled}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: false
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.authorization.mode}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: "AlwaysAllow"
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: or
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.readOnlyPort}"
|
||||||
|
set: false
|
||||||
|
- jsonpath: "{.readOnlyPort}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "0"
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: or
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.streamingConnectionIdleTimeout}"
|
||||||
|
set: false
|
||||||
|
- jsonpath: "{.streamingConnectionIdleTimeout}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: 0
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.protectKernelDefaults}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: true
|
||||||
|
set: 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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: or
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.makeIPTablesUtilChains}"
|
||||||
|
set: false
|
||||||
|
- jsonpath: "{.makeIPTablesUtilChains}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: true
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.eventRecordQPS}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: 0
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: and
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.tlsCertFile}"
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.tlsPrivateKeyFile}"
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: or
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.cadvisorPort}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: 0
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.cadvisorPort}"
|
||||||
|
set: false
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
bin_op: or
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.rotateCertificates}"
|
||||||
|
set: false
|
||||||
|
- jsonpath: "{.rotateCertificates}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: "false"
|
||||||
|
set: true
|
||||||
|
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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.featureGates.RotateKubeletServerCertificate}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: true
|
||||||
|
set: 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: "cat $kubeletconf"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.tlsCipherSuites}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
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"
|
||||||
|
set: true
|
||||||
|
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 $kubeletconf 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:
|
||||||
|
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 $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"
|
||||||
|
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 $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:
|
||||||
|
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 $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:
|
||||||
|
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 $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 $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
|
||||||
|
|
||||||
|
- 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:
|
||||||
|
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 (using the config file location identied in the Audit step)
|
||||||
|
chmod 644 $kubeletconf
|
||||||
|
scored: true
|
@ -2,6 +2,8 @@ package check
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
@ -11,31 +13,28 @@ const cfgDir = "../cfg/"
|
|||||||
|
|
||||||
// validate that the files we're shipping are valid YAML
|
// validate that the files we're shipping are valid YAML
|
||||||
func TestYamlFiles(t *testing.T) {
|
func TestYamlFiles(t *testing.T) {
|
||||||
// TODO: make this list dynamic
|
err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error {
|
||||||
dirs := []string{"1.6/", "1.7/"}
|
|
||||||
|
|
||||||
for _, dir := range dirs {
|
|
||||||
dir = cfgDir + dir
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error reading %s directory: %v", dir, err)
|
t.Fatalf("failure accessing path %q: %v\n", path, err)
|
||||||
}
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
for _, file := range files {
|
t.Logf("reading file: %s", path)
|
||||||
|
in, err := ioutil.ReadFile(path)
|
||||||
fileName := file.Name()
|
|
||||||
in, err := ioutil.ReadFile(dir + fileName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error opening file %s: %v", fileName, err)
|
t.Fatalf("error opening file %s: %v", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := new(Controls)
|
c := new(Controls)
|
||||||
|
|
||||||
err = yaml.Unmarshal(in, c)
|
err = yaml.Unmarshal(in, c)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
t.Fatalf("failed to load YAML from %s: %v", fileName, err)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
check/data
129
check/data
@ -157,4 +157,133 @@ groups:
|
|||||||
value: Something
|
value: Something
|
||||||
set: true
|
set: true
|
||||||
|
|
||||||
|
- id: 14
|
||||||
|
text: "jsonpath correct value on field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.readOnlyPort}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: 15000
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.readOnlyPort}"
|
||||||
|
compare:
|
||||||
|
op: gte
|
||||||
|
value: 15000
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.readOnlyPort}"
|
||||||
|
compare:
|
||||||
|
op: lte
|
||||||
|
value: 15000
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 15
|
||||||
|
text: "jsonpath correct case-sensitive value on string field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.stringValue}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: "None"
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.stringValue}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: "webhook,Something,RBAC"
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.stringValue}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "WebHook,Something,RBAC"
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 16
|
||||||
|
text: "jsonpath correct value on boolean field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.trueValue}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: somethingElse
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.trueValue}"
|
||||||
|
compare:
|
||||||
|
op: noteq
|
||||||
|
value: false
|
||||||
|
set: true
|
||||||
|
- jsonpath: "{.trueValue}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: true
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 17
|
||||||
|
text: "jsonpath field absent"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.notARealField}"
|
||||||
|
set: false
|
||||||
|
|
||||||
|
- id: 18
|
||||||
|
text: "jsonpath correct value on nested field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.authentication.anonymous.enabled}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "false"
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 19
|
||||||
|
text: "yamlpath correct value on field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- yamlpath: "{.readOnlyPort}"
|
||||||
|
compare:
|
||||||
|
op: gt
|
||||||
|
value: 14999
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 20
|
||||||
|
text: "yamlpath field absent"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- yamlpath: "{.fieldThatIsUnset}"
|
||||||
|
set: false
|
||||||
|
|
||||||
|
- id: 21
|
||||||
|
text: "yamlpath correct value on nested field"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- yamlpath: "{.authentication.anonymous.enabled}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "false"
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 22
|
||||||
|
text: "jsonpath on invalid json"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.authentication.anonymous.enabled}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "false"
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 23
|
||||||
|
text: "jsonpath with broken expression"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- jsonpath: "{.missingClosingBrace"
|
||||||
|
set: true
|
||||||
|
|
||||||
|
- id: 24
|
||||||
|
text: "yamlpath on invalid yaml"
|
||||||
|
tests:
|
||||||
|
test_items:
|
||||||
|
- yamlpath: "{.authentication.anonymous.enabled}"
|
||||||
|
compare:
|
||||||
|
op: eq
|
||||||
|
value: "false"
|
||||||
|
set: true
|
||||||
|
103
check/test.go
103
check/test.go
@ -15,11 +15,16 @@
|
|||||||
package check
|
package check
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
"k8s.io/client-go/util/jsonpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// test:
|
// test:
|
||||||
@ -37,11 +42,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type testItem struct {
|
type testItem struct {
|
||||||
Flag string
|
Flag string
|
||||||
Output string
|
Jsonpath string
|
||||||
Value string
|
Yamlpath string
|
||||||
Set bool
|
Output string
|
||||||
Compare compare
|
Value string
|
||||||
|
Set bool
|
||||||
|
Compare compare
|
||||||
}
|
}
|
||||||
|
|
||||||
type compare struct {
|
type compare struct {
|
||||||
@ -54,33 +61,85 @@ type testOutput struct {
|
|||||||
actualResult string
|
actualResult string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func failTestItem(s string) *testOutput {
|
||||||
|
return &testOutput{testResult: false, actualResult: s}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *testItem) execute(s string) *testOutput {
|
func (t *testItem) execute(s string) *testOutput {
|
||||||
result := &testOutput{}
|
result := &testOutput{}
|
||||||
match := strings.Contains(s, t.Flag)
|
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 {
|
||||||
|
// Means either t.Jsonpath != "" or t.Yamlpath != ""
|
||||||
|
// Find out and convert the input as needed
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
var jsonInterface interface{}
|
||||||
|
var pathExpression string
|
||||||
|
|
||||||
|
if t.Yamlpath != "" {
|
||||||
|
pathExpression = t.Yamlpath
|
||||||
|
err := yaml.Unmarshal([]byte(s), &jsonInterface)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to load YAML from provided input \"%s\": %v\n", s, err)
|
||||||
|
return failTestItem("failed to load YAML")
|
||||||
|
}
|
||||||
|
} else if t.Jsonpath != "" {
|
||||||
|
pathExpression = t.Jsonpath
|
||||||
|
err := json.Unmarshal([]byte(s), &jsonInterface)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to load JSON from provided input: \"%s\": %v\n", s, err)
|
||||||
|
return failTestItem("failed to load JSON")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the jsonpath/yamlpath expression...
|
||||||
|
j := jsonpath.New("jsonpath")
|
||||||
|
j.AllowMissingKeys(true)
|
||||||
|
err := j.Parse(pathExpression)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "unable to parse path expression \"%s\": %v\n", pathExpression, err)
|
||||||
|
return failTestItem("unable to parse path expression")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = j.Execute(buf, jsonInterface)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error executing path expression \"%s\": %v\n", pathExpression, err)
|
||||||
|
return failTestItem("error executing path expression")
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonpathResult := fmt.Sprintf("%s", buf)
|
||||||
|
match = (jsonpathResult != "")
|
||||||
|
flagVal = jsonpathResult
|
||||||
|
}
|
||||||
|
|
||||||
if t.Set {
|
if t.Set {
|
||||||
var flagVal string
|
|
||||||
isset := match
|
isset := match
|
||||||
|
|
||||||
if isset && t.Compare.Op != "" {
|
if isset && t.Compare.Op != "" {
|
||||||
// Expects flags in the form;
|
if t.Flag != "" {
|
||||||
// --flag=somevalue
|
// Expects flags in the form;
|
||||||
// --flag
|
// --flag=somevalue
|
||||||
// somevalue
|
// --flag
|
||||||
//pttn := `(` + t.Flag + `)(=)*([^\s,]*) *`
|
// somevalue
|
||||||
pttn := `(` + t.Flag + `)(=)*([^\s]*) *`
|
//pttn := `(` + t.Flag + `)(=)*([^\s,]*) *`
|
||||||
flagRe := regexp.MustCompile(pttn)
|
pttn := `(` + t.Flag + `)(=)*([^\s]*) *`
|
||||||
vals := flagRe.FindStringSubmatch(s)
|
flagRe := regexp.MustCompile(pttn)
|
||||||
|
vals := flagRe.FindStringSubmatch(s)
|
||||||
|
|
||||||
if len(vals) > 0 {
|
if len(vals) > 0 {
|
||||||
if vals[3] != "" {
|
if vals[3] != "" {
|
||||||
flagVal = vals[3]
|
flagVal = vals[3]
|
||||||
|
} else {
|
||||||
|
flagVal = vals[1]
|
||||||
|
}
|
||||||
} else {
|
} 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.actualResult = strings.ToLower(flagVal)
|
result.actualResult = strings.ToLower(flagVal)
|
||||||
|
@ -110,6 +110,38 @@ func TestTestExecute(t *testing.T) {
|
|||||||
controls.Groups[0].Checks[13],
|
controls.Groups[0].Checks[13],
|
||||||
"2:45 ../kubernetes/kube-apiserver --option --admission-control=Something ---audit-log-maxage=40",
|
"2:45 ../kubernetes/kube-apiserver --option --admission-control=Something ---audit-log-maxage=40",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[14],
|
||||||
|
"{\"readOnlyPort\": 15000}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[15],
|
||||||
|
"{\"stringValue\": \"WebHook,Something,RBAC\"}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[16],
|
||||||
|
"{\"trueValue\": true}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[17],
|
||||||
|
"{\"readOnlyPort\": 15000}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[18],
|
||||||
|
"{\"authentication\": { \"anonymous\": {\"enabled\": false}}}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[19],
|
||||||
|
"readOnlyPort: 15000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[20],
|
||||||
|
"readOnlyPort: 15000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[21],
|
||||||
|
"authentication:\n anonymous:\n enabled: false",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -119,3 +151,31 @@ func TestTestExecute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTestExecuteExceptions(t *testing.T) {
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
*Check
|
||||||
|
str string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[22],
|
||||||
|
"this is not valid json {} at all",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[23],
|
||||||
|
"{\"key\": \"value\"}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
controls.Groups[0].Checks[24],
|
||||||
|
"broken } yaml\nenabled: true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user