syncable_cli/analyzer/kubelint/templates/
unsafeprocmount.rs

1//! Unsafe proc mount detection template.
2
3use 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
8/// Template for detecting unsafe /proc mount settings.
9pub struct UnsafeProcMountTemplate;
10
11impl Template for UnsafeProcMountTemplate {
12    fn key(&self) -> &str {
13        "unsafe-proc-mount"
14    }
15
16    fn human_name(&self) -> &str {
17        "Unsafe Proc Mount"
18    }
19
20    fn description(&self) -> &str {
21        "Detects containers with unsafe /proc mount (procMount: Unmasked)"
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(UnsafeProcMountCheck))
37    }
38}
39
40struct UnsafeProcMountCheck;
41
42impl CheckFunc for UnsafeProcMountCheck {
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 container in extract::container::all_containers(pod_spec) {
48                if let Some(sc) = &container.security_context {
49                    if let Some(proc_mount) = &sc.proc_mount {
50                        if proc_mount == "Unmasked" {
51                            diagnostics.push(Diagnostic {
52                                message: format!(
53                                    "Container '{}' has unsafe /proc mount (procMount: Unmasked)",
54                                    container.name
55                                ),
56                                remediation: Some(
57                                    "Use the Default procMount type unless Unmasked is absolutely required. \
58                                     Unmasked proc mount exposes sensitive kernel information."
59                                        .to_string(),
60                                ),
61                            });
62                        }
63                    }
64                }
65            }
66        }
67
68        diagnostics
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::analyzer::kubelint::parser::yaml::parse_yaml;
76
77    #[test]
78    fn test_unsafe_proc_mount_detected() {
79        let yaml = r#"
80apiVersion: apps/v1
81kind: Deployment
82metadata:
83  name: unsafe-procmount
84spec:
85  template:
86    spec:
87      containers:
88      - name: nginx
89        image: nginx:1.21.0
90        securityContext:
91          procMount: Unmasked
92"#;
93        let objects = parse_yaml(yaml).unwrap();
94        let check = UnsafeProcMountCheck;
95        let diagnostics = check.check(&objects[0]);
96        assert_eq!(diagnostics.len(), 1);
97        assert!(diagnostics[0].message.contains("Unmasked"));
98    }
99
100    #[test]
101    fn test_default_proc_mount_ok() {
102        let yaml = r#"
103apiVersion: apps/v1
104kind: Deployment
105metadata:
106  name: safe-procmount
107spec:
108  template:
109    spec:
110      containers:
111      - name: nginx
112        image: nginx:1.21.0
113        securityContext:
114          procMount: Default
115"#;
116        let objects = parse_yaml(yaml).unwrap();
117        let check = UnsafeProcMountCheck;
118        let diagnostics = check.check(&objects[0]);
119        assert!(diagnostics.is_empty());
120    }
121
122    #[test]
123    fn test_no_proc_mount_ok() {
124        let yaml = r#"
125apiVersion: apps/v1
126kind: Deployment
127metadata:
128  name: no-procmount
129spec:
130  template:
131    spec:
132      containers:
133      - name: nginx
134        image: nginx:1.21.0
135"#;
136        let objects = parse_yaml(yaml).unwrap();
137        let check = UnsafeProcMountCheck;
138        let diagnostics = check.check(&objects[0]);
139        assert!(diagnostics.is_empty());
140    }
141}