syncable_cli/analyzer/kubelint/templates/
hostmounts.rs1use crate::analyzer::kubelint::context::Object;
4use crate::analyzer::kubelint::extract;
5use crate::analyzer::kubelint::templates::{CheckFunc, ParameterDesc, Template, TemplateError};
6use crate::analyzer::kubelint::types::{Diagnostic, ObjectKindsDesc};
7
8pub struct HostMountsTemplate;
10
11impl Template for HostMountsTemplate {
12 fn key(&self) -> &str {
13 "host-mounts"
14 }
15
16 fn human_name(&self) -> &str {
17 "Host Mounts"
18 }
19
20 fn description(&self) -> &str {
21 "Detects containers with host path volume mounts"
22 }
23
24 fn supported_object_kinds(&self) -> ObjectKindsDesc {
25 ObjectKindsDesc::default()
26 }
27
28 fn parameters(&self) -> Vec<ParameterDesc> {
29 Vec::new()
30 }
31
32 fn instantiate(
33 &self,
34 _params: &serde_yaml::Value,
35 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
36 Ok(Box::new(HostMountsCheck))
37 }
38}
39
40struct HostMountsCheck;
41
42impl CheckFunc for HostMountsCheck {
43 fn check(&self, object: &Object) -> Vec<Diagnostic> {
44 let mut diagnostics = Vec::new();
45
46 if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
47 for volume in &pod_spec.volumes {
48 if let Some(host_path) = &volume.host_path {
49 diagnostics.push(Diagnostic {
50 message: format!(
51 "Volume '{}' mounts host path '{}'",
52 volume.name, host_path.path
53 ),
54 remediation: Some(
55 "Avoid using hostPath volumes as they provide access to the host filesystem. \
56 Use PersistentVolumeClaims or ConfigMaps instead.".to_string()
57 ),
58 });
59 }
60 }
61 }
62
63 diagnostics
64 }
65}
66
67pub struct WritableHostMountTemplate;
69
70impl Template for WritableHostMountTemplate {
71 fn key(&self) -> &str {
72 "writable-host-mount"
73 }
74
75 fn human_name(&self) -> &str {
76 "Writable Host Mount"
77 }
78
79 fn description(&self) -> &str {
80 "Detects containers with writable host path volume mounts"
81 }
82
83 fn supported_object_kinds(&self) -> ObjectKindsDesc {
84 ObjectKindsDesc::default()
85 }
86
87 fn parameters(&self) -> Vec<ParameterDesc> {
88 Vec::new()
89 }
90
91 fn instantiate(
92 &self,
93 _params: &serde_yaml::Value,
94 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
95 Ok(Box::new(WritableHostMountCheck))
96 }
97}
98
99struct WritableHostMountCheck;
100
101impl CheckFunc for WritableHostMountCheck {
102 fn check(&self, object: &Object) -> Vec<Diagnostic> {
103 let mut diagnostics = Vec::new();
104
105 if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
106 let host_volumes: std::collections::HashSet<_> = pod_spec
108 .volumes
109 .iter()
110 .filter(|v| v.host_path.is_some())
111 .map(|v| v.name.as_str())
112 .collect();
113
114 for container in extract::container::all_containers(pod_spec) {
116 for mount in &container.volume_mounts {
117 if host_volumes.contains(mount.name.as_str()) {
118 let is_writable = mount.read_only != Some(true);
120
121 if is_writable {
122 diagnostics.push(Diagnostic {
123 message: format!(
124 "Container '{}' has writable host mount at '{}'",
125 container.name, mount.mount_path
126 ),
127 remediation: Some(
128 "Set volumeMounts.readOnly to true for host path mounts, \
129 or avoid using hostPath volumes entirely."
130 .to_string(),
131 ),
132 });
133 }
134 }
135 }
136 }
137 }
138
139 diagnostics
140 }
141}