syncable_cli/analyzer/kubelint/templates/
hostnetwork.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 HostNetworkTemplate;
10
11impl Template for HostNetworkTemplate {
12 fn key(&self) -> &str {
13 "host-network"
14 }
15
16 fn human_name(&self) -> &str {
17 "Host Network"
18 }
19
20 fn description(&self) -> &str {
21 "Detects pods using host network namespace"
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(HostNetworkCheck))
37 }
38}
39
40struct HostNetworkCheck;
41
42impl CheckFunc for HostNetworkCheck {
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 if pod_spec.host_network == Some(true) {
48 diagnostics.push(Diagnostic {
49 message: "Pod is configured to use the host's network namespace".to_string(),
50 remediation: Some(
51 "Remove hostNetwork: true unless absolutely necessary. \
52 Using host network grants access to all network interfaces on the host."
53 .to_string(),
54 ),
55 });
56 }
57 }
58
59 diagnostics
60 }
61}
62
63pub struct HostPIDTemplate;
65
66impl Template for HostPIDTemplate {
67 fn key(&self) -> &str {
68 "host-pid"
69 }
70
71 fn human_name(&self) -> &str {
72 "Host PID"
73 }
74
75 fn description(&self) -> &str {
76 "Detects pods using host PID namespace"
77 }
78
79 fn supported_object_kinds(&self) -> ObjectKindsDesc {
80 ObjectKindsDesc::default()
81 }
82
83 fn parameters(&self) -> Vec<ParameterDesc> {
84 Vec::new()
85 }
86
87 fn instantiate(
88 &self,
89 _params: &serde_yaml::Value,
90 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
91 Ok(Box::new(HostPIDCheck))
92 }
93}
94
95struct HostPIDCheck;
96
97impl CheckFunc for HostPIDCheck {
98 fn check(&self, object: &Object) -> Vec<Diagnostic> {
99 let mut diagnostics = Vec::new();
100
101 if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
102 if pod_spec.host_pid == Some(true) {
103 diagnostics.push(Diagnostic {
104 message: "Pod is configured to use the host's PID namespace".to_string(),
105 remediation: Some(
106 "Remove hostPID: true unless absolutely necessary. \
107 Using host PID allows processes in the container to see and signal all \
108 processes on the host."
109 .to_string(),
110 ),
111 });
112 }
113 }
114
115 diagnostics
116 }
117}
118
119pub struct HostIPCTemplate;
121
122impl Template for HostIPCTemplate {
123 fn key(&self) -> &str {
124 "host-ipc"
125 }
126
127 fn human_name(&self) -> &str {
128 "Host IPC"
129 }
130
131 fn description(&self) -> &str {
132 "Detects pods using host IPC namespace"
133 }
134
135 fn supported_object_kinds(&self) -> ObjectKindsDesc {
136 ObjectKindsDesc::default()
137 }
138
139 fn parameters(&self) -> Vec<ParameterDesc> {
140 Vec::new()
141 }
142
143 fn instantiate(
144 &self,
145 _params: &serde_yaml::Value,
146 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
147 Ok(Box::new(HostIPCCheck))
148 }
149}
150
151struct HostIPCCheck;
152
153impl CheckFunc for HostIPCCheck {
154 fn check(&self, object: &Object) -> Vec<Diagnostic> {
155 let mut diagnostics = Vec::new();
156
157 if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
158 if pod_spec.host_ipc == Some(true) {
159 diagnostics.push(Diagnostic {
160 message: "Pod is configured to use the host's IPC namespace".to_string(),
161 remediation: Some(
162 "Remove hostIPC: true unless absolutely necessary. \
163 Using host IPC allows processes to communicate with all processes on the host."
164 .to_string(),
165 ),
166 });
167 }
168 }
169
170 diagnostics
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::analyzer::kubelint::parser::yaml::parse_yaml;
178
179 #[test]
180 fn test_host_network_detected() {
181 let yaml = r#"
182apiVersion: apps/v1
183kind: Deployment
184metadata:
185 name: host-net-deploy
186spec:
187 template:
188 spec:
189 hostNetwork: true
190 containers:
191 - name: nginx
192 image: nginx:1.21.0
193"#;
194 let objects = parse_yaml(yaml).unwrap();
195 let check = HostNetworkCheck;
196 let diagnostics = check.check(&objects[0]);
197 assert_eq!(diagnostics.len(), 1);
198 assert!(diagnostics[0].message.contains("host's network"));
199 }
200
201 #[test]
202 fn test_no_host_network_ok() {
203 let yaml = r#"
204apiVersion: apps/v1
205kind: Deployment
206metadata:
207 name: safe-deploy
208spec:
209 template:
210 spec:
211 containers:
212 - name: nginx
213 image: nginx:1.21.0
214"#;
215 let objects = parse_yaml(yaml).unwrap();
216 let check = HostNetworkCheck;
217 let diagnostics = check.check(&objects[0]);
218 assert!(diagnostics.is_empty());
219 }
220
221 #[test]
222 fn test_host_pid_detected() {
223 let yaml = r#"
224apiVersion: apps/v1
225kind: Deployment
226metadata:
227 name: host-pid-deploy
228spec:
229 template:
230 spec:
231 hostPID: true
232 containers:
233 - name: nginx
234 image: nginx:1.21.0
235"#;
236 let objects = parse_yaml(yaml).unwrap();
237 let check = HostPIDCheck;
238 let diagnostics = check.check(&objects[0]);
239 assert_eq!(diagnostics.len(), 1);
240 assert!(diagnostics[0].message.contains("PID namespace"));
241 }
242
243 #[test]
244 fn test_host_ipc_detected() {
245 let yaml = r#"
246apiVersion: apps/v1
247kind: Deployment
248metadata:
249 name: host-ipc-deploy
250spec:
251 template:
252 spec:
253 hostIPC: true
254 containers:
255 - name: nginx
256 image: nginx:1.21.0
257"#;
258 let objects = parse_yaml(yaml).unwrap();
259 let check = HostIPCCheck;
260 let diagnostics = check.check(&objects[0]);
261 assert_eq!(diagnostics.len(), 1);
262 assert!(diagnostics[0].message.contains("IPC namespace"));
263 }
264}