vault_audit_tools/commands/
k8s_auth.rs1use crate::audit::types::AuditEntry;
33use crate::utils::progress::ProgressBar;
34use anyhow::Result;
35use std::collections::HashMap;
36use std::fs::File;
37use std::io::{BufRead, BufReader};
38
39fn format_number(n: usize) -> String {
40 let s = n.to_string();
41 let mut result = String::new();
42 for (i, c) in s.chars().rev().enumerate() {
43 if i > 0 && i % 3 == 0 {
44 result.push(',');
45 }
46 result.push(c);
47 }
48 result.chars().rev().collect()
49}
50
51pub fn run(log_files: &[String], output: Option<&str>) -> Result<()> {
52 let mut k8s_logins = 0;
53 let mut entities_seen: HashMap<String, usize> = HashMap::new();
54 let mut total_lines = 0;
55
56 for (file_idx, log_file) in log_files.iter().enumerate() {
58 eprintln!(
59 "[{}/{}] Processing: {}",
60 file_idx + 1,
61 log_files.len(),
62 log_file
63 );
64
65 let file_size = std::fs::metadata(log_file).ok().map(|m| m.len() as usize);
67 let mut progress = if let Some(size) = file_size {
68 ProgressBar::new(size, "Processing")
69 } else {
70 ProgressBar::new_spinner("Processing")
71 };
72
73 let file = File::open(log_file)?;
74 let reader = BufReader::new(file);
75
76 let mut file_lines = 0;
77 let mut bytes_read = 0;
78
79 for line in reader.lines() {
80 file_lines += 1;
81 total_lines += 1;
82 let line = line?;
83 bytes_read += line.len() + 1; if file_lines % 10_000 == 0 {
87 if let Some(size) = file_size {
88 progress.update(bytes_read.min(size)); } else {
90 progress.update(file_lines);
91 }
92 }
93
94 let entry: AuditEntry = match serde_json::from_str(&line) {
95 Ok(e) => e,
96 Err(_) => continue,
97 };
98
99 if entry.entry_type != "response" || entry.error.is_some() {
101 continue;
102 }
103
104 let request = match &entry.request {
105 Some(r) => r,
106 None => continue,
107 };
108
109 let path = match &request.path {
110 Some(p) => p.as_str(),
111 None => continue,
112 };
113
114 if !path.ends_with("/login") {
115 continue;
116 }
117
118 let is_k8s_by_path = path.contains("kubernetes") || path.contains("openshift");
120 let is_k8s_by_mount = request
121 .mount_type
122 .as_deref()
123 .map(|mt| mt == "kubernetes" || mt == "openshift")
124 .unwrap_or(false);
125
126 if is_k8s_by_path || is_k8s_by_mount {
127 k8s_logins += 1;
128
129 if let Some(entity_id) = entry.auth.as_ref().and_then(|a| a.entity_id.as_deref()) {
130 *entities_seen.entry(entity_id.to_string()).or_insert(0) += 1;
131 }
132 }
133 }
134
135 if let Some(size) = file_size {
137 progress.update(size);
138 }
139
140 progress.finish_with_message(&format!(
141 "Processed {} lines from this file",
142 format_number(file_lines)
143 ));
144 }
145
146 eprintln!(
147 "\nTotal: Processed {} lines, found {} K8s logins",
148 format_number(total_lines),
149 format_number(k8s_logins)
150 );
151
152 println!("\n{}", "=".repeat(80));
153 println!("KUBERNETES/OPENSHIFT AUTHENTICATION ANALYSIS");
154 println!("{}", "=".repeat(80));
155
156 println!("\nSummary:");
157 println!(" Total lines processed: {}", format_number(total_lines));
158 println!(
159 " Total K8s/OpenShift logins: {}",
160 format_number(k8s_logins)
161 );
162 println!(" Unique entities: {}", format_number(entities_seen.len()));
163
164 if k8s_logins > 0 {
165 let ratio = k8s_logins as f64 / entities_seen.len() as f64;
166 println!(" Login-to-Entity ratio: {:.2}", ratio);
167
168 println!("\nTop 20 Entities by Login Count:");
169 println!("{}", "-".repeat(80));
170
171 let mut sorted: Vec<_> = entities_seen.iter().collect();
172 sorted.sort_by(|a, b| b.1.cmp(a.1));
173
174 for (i, (entity, count)) in sorted.iter().take(20).enumerate() {
175 println!("{}. {} - {} logins", i + 1, entity, format_number(**count));
176 }
177 }
178
179 if let Some(output_file) = output {
180 use std::fs::File;
181 use std::io::Write;
182 let mut file = File::create(output_file)?;
183 writeln!(file, "entity_id,login_count")?;
184 for (entity, count) in &entities_seen {
185 writeln!(file, "{},{}", entity, count)?;
186 }
187 println!("\nOutput written to: {}", output_file);
188 }
189
190 println!("\n{}", "=".repeat(80));
191
192 Ok(())
193}