Skip to content

Commit 0551078

Browse files
authored
Merge pull request #563 from tzneal/update-k8s-cis-guidance
feat: k8s: update to the latest CIS K8s guidance v1.11.1
2 parents d8bdb3c + 2a413b5 commit 0551078

File tree

4 files changed

+175
-24
lines changed

4 files changed

+175
-24
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"name": "CIS Kubernetes Benchmark (Worker Node)",
3-
"version": "v1.8.0",
3+
"version": "v1.11.1",
44
"url": "https://www.cisecurity.org/benchmark/kubernetes"
55
}

packages/os/os.spec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,8 @@ for p in \
667667
k8s04010100 k8s04010200 k8s04010500 k8s04010600 k8s04010700 \
668668
k8s04010800 k8s04010900 k8s04011000 k8s04020100 k8s04020200 \
669669
k8s04020300 k8s04020400 k8s04020500 k8s04020600 k8s04020900 \
670-
k8s04021000 k8s04021100 k8s04021200 k8s04021300 \
670+
k8s04021000 k8s04021100 k8s04021200 k8s04021300 k8s04021400 \
671+
k8s04021500 k8s04030100 \
671672
; do
672673
ln -rs %{buildroot}%{_cross_bindir}/kubernetes-cis-checks \
673674
%{buildroot}%{_cross_libexecdir}/cis-checks/kubernetes/${p}

sources/bloodhound/src/bin/kubernetes-cis-checks/checks.rs

Lines changed: 137 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ const KUBELET_SERVICE_FILE: &str = "/etc/systemd/system/kubelet.service.d/exec-s
1212
const KUBELET_KUBECONFIG_FILE: &str = "/etc/kubernetes/kubelet/kubeconfig";
1313
const KUBELET_CLIENT_CA_FILE: &str = "/etc/kubernetes/pki/ca.crt";
1414
const KUBELET_CONF_FILE: &str = "/etc/kubernetes/kubelet/config";
15+
pub const KUBEPROXY_CONF_FILE: &str = "/etc/kubernetes/kube-proxy/kube-proxy.conf";
1516

1617
// =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<=
1718

1819
pub struct K8S04010100Checker {}
1920

2021
impl Checker for K8S04010100Checker {
2122
fn execute(&self) -> CheckerResult {
22-
let no_x_xw_xw = S_IXUSR | S_IXGRP | S_IWGRP | S_IXOTH | S_IWOTH;
23-
check_file_not_mode(KUBELET_SERVICE_FILE, no_x_xw_xw)
23+
let no_x_xwr_xwr = S_IXUSR | S_IRWXG | S_IRWXO;
24+
check_file_not_mode(KUBELET_SERVICE_FILE, no_x_xwr_xwr)
2425
}
2526

2627
fn metadata(&self) -> CheckerMetadata {
2728
CheckerMetadata {
28-
title: "Ensure that the kubelet service file permissions are set to 644 or more restrictive".to_string(),
29+
title: "Ensure that the kubelet service file permissions are set to 600 or more restrictive".to_string(),
2930
id: "4.1.1".to_string(),
3031
level: 1,
3132
name: "k8s04010100".to_string(),
@@ -60,13 +61,13 @@ pub struct K8S04010500Checker {}
6061

6162
impl Checker for K8S04010500Checker {
6263
fn execute(&self) -> CheckerResult {
63-
let no_x_xw_xw = S_IXUSR | S_IXGRP | S_IWGRP | S_IXOTH | S_IWOTH;
64-
check_file_not_mode(KUBELET_KUBECONFIG_FILE, no_x_xw_xw)
64+
let no_x_xwr_xwr = S_IXUSR | S_IRWXG | S_IRWXO;
65+
check_file_not_mode(KUBELET_KUBECONFIG_FILE, no_x_xwr_xwr)
6566
}
6667

6768
fn metadata(&self) -> CheckerMetadata {
6869
CheckerMetadata {
69-
title: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 644 or more restrictive".to_string(),
70+
title: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive".to_string(),
7071
id: "4.1.5".to_string(),
7172
level: 1,
7273
name: "k8s04010500".to_string(),
@@ -102,13 +103,13 @@ pub struct K8S04010700Checker {}
102103

103104
impl Checker for K8S04010700Checker {
104105
fn execute(&self) -> CheckerResult {
105-
let no_x_xwr_xwr = S_IXUSR | S_IRWXG | S_IRWXO;
106-
check_file_not_mode(KUBELET_CLIENT_CA_FILE, no_x_xwr_xwr)
106+
let no_x_xw_xw = S_IXUSR | S_IXGRP | S_IWGRP | S_IXOTH | S_IWOTH;
107+
check_file_not_mode(KUBELET_CLIENT_CA_FILE, no_x_xw_xw)
107108
}
108109

109110
fn metadata(&self) -> CheckerMetadata {
110111
CheckerMetadata {
111-
title: "Ensure that the certificate authorities file permissions are set to 600 or more restrictive".to_string(),
112+
title: "Ensure that the certificate authorities file permissions are set to 644 or more restrictive".to_string(),
112113
id: "4.1.7".to_string(),
113114
level: 1,
114115
name: "k8s04010700".to_string(),
@@ -370,7 +371,7 @@ impl Checker for K8S04020400Checker {
370371

371372
fn metadata(&self) -> CheckerMetadata {
372373
CheckerMetadata {
373-
title: "Verify that the --read-only-port argument is set to 0".to_string(),
374+
title: "Verify that if defined, readOnlyPort is set to 0".to_string(),
374375
id: "4.2.4".to_string(),
375376
level: 1,
376377
name: "k8s04020400".to_string(),
@@ -722,3 +723,129 @@ impl Checker for K8S04021300Checker {
722723
}
723724
}
724725
}
726+
727+
// =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<=
728+
729+
pub struct K8S04021400Checker {}
730+
731+
impl Checker for K8S04021400Checker {
732+
fn execute(&self) -> CheckerResult {
733+
#[derive(Deserialize)]
734+
struct KubeletConfig {
735+
#[serde(rename = "seccompDefault")]
736+
seccomp_default: bool,
737+
}
738+
739+
let mut result = CheckerResult::default();
740+
741+
if let Ok(kubelet_file) = File::open(KUBELET_CONF_FILE) {
742+
if let Ok(config) = serde_yaml::from_reader::<_, KubeletConfig>(kubelet_file) {
743+
if !config.seccomp_default {
744+
result.error = "Kubelet seccompDefault is not set to true".to_string();
745+
result.status = CheckStatus::FAIL;
746+
} else {
747+
result.status = CheckStatus::PASS;
748+
}
749+
} else {
750+
// If the setting is not present then seccompDefault is false by default
751+
result.error =
752+
"Kubelet seccompDefault is not configured or set to true".to_string();
753+
result.status = CheckStatus::FAIL;
754+
}
755+
} else {
756+
result.error = format!("unable to read '{}'", KUBELET_CONF_FILE);
757+
}
758+
759+
result
760+
}
761+
762+
fn metadata(&self) -> CheckerMetadata {
763+
CheckerMetadata {
764+
title: "Ensure that the --seccomp-default parameter is set to true".to_string(),
765+
id: "4.2.14".to_string(),
766+
level: 1,
767+
name: "k8s04021400".to_string(),
768+
mode: Mode::Automatic,
769+
}
770+
}
771+
}
772+
// =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<=
773+
774+
pub struct K8S04030100Checker {}
775+
impl Checker for K8S04030100Checker {
776+
fn execute(&self) -> CheckerResult {
777+
#[derive(Deserialize)]
778+
struct KubeProxyConfig {
779+
#[serde(rename = "metricsBindAddress")]
780+
metrics_bind_address: String,
781+
}
782+
let mut result = CheckerResult::default();
783+
784+
if let Ok(kubelet_file) = File::open(KUBEPROXY_CONF_FILE) {
785+
if let Ok(config) = serde_yaml::from_reader::<_, KubeProxyConfig>(kubelet_file) {
786+
if config.metrics_bind_address.contains("0.0.0.0")
787+
|| config.metrics_bind_address.contains("[::]")
788+
{
789+
result.error =
790+
"Kubelet metricsBindAddress binds to more than localhost".to_string();
791+
result.status = CheckStatus::FAIL;
792+
} else {
793+
result.status = CheckStatus::PASS;
794+
}
795+
} else {
796+
// If the setting is not present it defaults to 127.0.0.1:10249 which is localhost only
797+
result.status = CheckStatus::PASS;
798+
}
799+
} else {
800+
result.error = format!("unable to read '{}'", KUBEPROXY_CONF_FILE);
801+
}
802+
result
803+
}
804+
fn metadata(&self) -> CheckerMetadata {
805+
CheckerMetadata {
806+
title: "Ensure that the kube-proxy metrics service is bound to localhost".to_string(),
807+
id: "4.3.1".to_string(),
808+
level: 1,
809+
name: "k8s04030100".to_string(),
810+
mode: Mode::Automatic,
811+
}
812+
}
813+
}
814+
815+
// =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<=
816+
817+
pub struct K8S04010300Checker {}
818+
impl Checker for K8S04010300Checker {
819+
fn execute(&self) -> CheckerResult {
820+
let no_x_xwr_xwr = S_IXUSR | S_IRWXG | S_IRWXO;
821+
check_file_not_mode(KUBEPROXY_CONF_FILE, no_x_xwr_xwr)
822+
}
823+
fn metadata(&self) -> CheckerMetadata {
824+
CheckerMetadata {
825+
title: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive".to_string(),
826+
id: "4.1.3".to_string(),
827+
level: 1,
828+
name: "k8s04010300".to_string(),
829+
mode: Mode::Automatic,
830+
}
831+
}
832+
}
833+
834+
// =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<= =>o.o<=
835+
836+
pub struct K8S04010400Checker {}
837+
impl Checker for K8S04010400Checker {
838+
fn execute(&self) -> CheckerResult {
839+
ensure_file_owner_and_group_root(KUBEPROXY_CONF_FILE)
840+
}
841+
fn metadata(&self) -> CheckerMetadata {
842+
CheckerMetadata {
843+
title: "If proxy kubeconfig file exists ensure ownership is set to root:root"
844+
.to_string(),
845+
id: "4.1.4".to_string(),
846+
level: 1,
847+
name: "k8s04010400".to_string(),
848+
mode: Mode::Automatic,
849+
}
850+
}
851+
}

sources/bloodhound/src/bin/kubernetes-cis-checks/main.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,24 @@ fn main() {
1616
let checker: Box<dyn Checker> = match cmd_name {
1717
"k8s04010100" => Box::new(K8S04010100Checker {}),
1818
"k8s04010200" => Box::new(K8S04010200Checker {}),
19-
"k8s04010300" => Box::new(ManualChecker {
20-
name: cmd_name.to_string(),
21-
title: "If proxy kubeconfig file exists ensure permissions are set to 644 or more restrictive".to_string(),
22-
id: "4.1.3".to_string(),
23-
level: 1,
24-
}),
25-
"k8s04010400" => Box::new(ManualChecker {
26-
name: cmd_name.to_string(),
27-
title: "If proxy kubeconfig file exists ensure ownership is set to root:root".to_string(),
28-
id: "4.1.4".to_string(),
29-
level: 1,
30-
}),
19+
"k8s04010300" => match Path::new(KUBEPROXY_CONF_FILE).exists() {
20+
true => Box::new(K8S04010300Checker {}),
21+
false => Box::new(ManualChecker {
22+
name: cmd_name.to_string(),
23+
title: "If proxy kubeconfig file exists ensure permissions are set to 600 or more restrictive".to_string(),
24+
id: "4.1.3".to_string(),
25+
level: 1,
26+
})
27+
},
28+
"k8s04010400" => match Path::new(KUBEPROXY_CONF_FILE).exists() {
29+
true => Box::new(K8S04010400Checker {}),
30+
false => Box::new(ManualChecker {
31+
name: cmd_name.to_string(),
32+
title: "If proxy kubeconfig file exists ensure ownership is set to root:root".to_string(),
33+
id: "4.1.4".to_string(),
34+
level: 1,
35+
})
36+
},
3137
"k8s04010500" => Box::new(K8S04010500Checker {}),
3238
"k8s04010600" => Box::new(K8S04010600Checker {}),
3339
"k8s04010700" => Box::new(K8S04010700Checker {}),
@@ -63,6 +69,23 @@ fn main() {
6369
"k8s04021100" => Box::new(K8S04021100Checker {}),
6470
"k8s04021200" => Box::new(K8S04021200Checker {}),
6571
"k8s04021300" => Box::new(K8S04021300Checker {}),
72+
"k8s04021400" => Box::new(K8S04021400Checker {}),
73+
"k8s04021500" => Box::new(ManualChecker {
74+
name: cmd_name.to_string(),
75+
title: "Ensure that the --IPAddressDeny is set to any".to_string(),
76+
id: "4.2.15".to_string(),
77+
level: 2,
78+
}),
79+
"k8s04030100" =>
80+
match Path::new(KUBEPROXY_CONF_FILE).exists() {
81+
true => Box::new(K8S04030100Checker {}),
82+
false => Box::new(ManualChecker {
83+
name: cmd_name.to_string(),
84+
title: "Ensure that the kube-proxy metrics service is bound to localhost".to_string(),
85+
id: "4.3.1".to_string(),
86+
level: 1,
87+
})
88+
},
6689
&_ => {
6790
eprintln!("Command {cmd_name} is not supported.");
6891
return;

0 commit comments

Comments
 (0)