From 4bb36b77356f953d69210978cc896b7674311be5 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Sun, 12 Oct 2025 17:21:05 +0200 Subject: [PATCH] Testing --- agent/src/collectors/service.rs | 105 ++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/agent/src/collectors/service.rs b/agent/src/collectors/service.rs index d933938..5752f30 100644 --- a/agent/src/collectors/service.rs +++ b/agent/src/collectors/service.rs @@ -660,7 +660,7 @@ impl ServiceCollector { return None; } let config = String::from_utf8_lossy(&output.stdout); - return self.parse_nginx_config(&config); + return self.parse_nginx_config(&config).await; } Err(e) => { tracing::warn!("Failed to execute sudo nginx -T: {}", e); @@ -694,7 +694,7 @@ impl ServiceCollector { let config = String::from_utf8_lossy(&output.stdout); tracing::debug!("Got nginx config, {} bytes", config.len()); - self.parse_nginx_config(&config) + self.parse_nginx_config(&config).await } async fn get_nginx_config_from_systemd(&self) -> Option { @@ -730,7 +730,7 @@ impl ServiceCollector { None } - fn parse_nginx_config(&self, config: &str) -> Option> { + async fn parse_nginx_config(&self, config: &str) -> Option> { let mut sites = Vec::new(); let lines: Vec<&str> = config.lines().collect(); let mut i = 0; @@ -740,31 +740,39 @@ impl ServiceCollector { // Look for server blocks if trimmed == "server {" { - if let Some(site_info) = self.parse_server_block(&lines, &mut i) { - sites.push(site_info); + if let Some(hostname) = self.parse_server_block(&lines, &mut i) { + sites.push(hostname); } } i += 1; } - tracing::info!("Extracted {} sites with routing info", sites.len()); + tracing::info!("Found {} potential sites, checking accessibility", sites.len()); + + // Check which sites are actually accessible + let mut accessible_sites = Vec::new(); + for site in sites { + if self.check_site_accessibility(&site).await { + accessible_sites.push(format!("{} ✓", site)); + } + } // Limit to reasonable number - sites.truncate(15); + accessible_sites.truncate(15); - tracing::info!("Final nginx sites list: {:?}", sites); + tracing::info!("Final accessible nginx sites: {:?}", accessible_sites); - if sites.is_empty() { - tracing::warn!("No nginx sites found"); + if accessible_sites.is_empty() { + tracing::warn!("No accessible nginx sites found"); None } else { - Some(sites) + Some(accessible_sites) } } fn parse_server_block(&self, lines: &[&str], start_index: &mut usize) -> Option { let mut server_names = Vec::new(); - let mut destinations = Vec::new(); + let mut has_redirect = false; let mut i = *start_index + 1; let mut brace_count = 1; @@ -788,37 +796,9 @@ impl ServiceCollector { } } - // Extract routing information + // Check if this server block is just a redirect if trimmed.starts_with("return") && trimmed.contains("301") { - // Skip simple HTTPS redirects (return 301 https://$host$request_uri) - if !trimmed.contains("$host") { - // Meaningful redirect: return 301 https://pages.cmtec.se$request_uri; - if let Some(url_start) = trimmed.find("https://") { - let url_part = &trimmed[url_start + 8..]; // Skip "https://" - if let Some(url_end) = url_part.find('$') { - let domain = &url_part[..url_end]; - destinations.push(format!("→ {}", domain)); - } - } - } - } else if trimmed.starts_with("proxy_pass") { - // Proxy: proxy_pass http://localhost:8080; - if let Some(url_start) = trimmed.find("http") { - let url_part = &trimmed[url_start..]; - if let Some(url_end) = url_part.find(';') { - let proxy_url = &url_part[..url_end]; - destinations.push(format!("← {}", proxy_url)); - } - } - } else if trimmed.starts_with("root") { - // Static files: root /var/www/example; - if let Some(path_start) = trimmed.find('/') { - let path_part = &trimmed[path_start..]; - if let Some(path_end) = path_part.find(';') { - let root_path = &path_part[..path_end]; - destinations.push(format!("static {}", root_path)); - } - } + has_redirect = true; } i += 1; @@ -826,14 +806,47 @@ impl ServiceCollector { *start_index = i - 1; - // Build site info string - only show sites with meaningful routing - if !server_names.is_empty() && !destinations.is_empty() { - let primary_name = &server_names[0]; - Some(format!("{} {}", primary_name, destinations[0])) + // Only return hostnames that are not redirects and have actual content + if !server_names.is_empty() && !has_redirect { + Some(server_names[0].clone()) } else { None } } + + async fn check_site_accessibility(&self, hostname: &str) -> bool { + // Try HTTPS first, then HTTP + for scheme in ["https", "http"] { + let url = format!("{}://{}", scheme, hostname); + + match tokio::time::timeout( + Duration::from_secs(5), + Command::new("curl") + .args(["-s", "-I", "--max-time", "3", &url]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + ).await { + Ok(Ok(output)) if output.status.success() => { + let response = String::from_utf8_lossy(&output.stdout); + // Check for successful HTTP status codes + if response.contains("HTTP/") && ( + response.contains(" 200 ") || + response.contains(" 301 ") || + response.contains(" 302 ") || + response.contains(" 403 ") // Some sites return 403 but are still "accessible" + ) { + tracing::debug!("Site {} accessible via {}", hostname, scheme); + return true; + } + } + _ => continue, + } + } + + tracing::debug!("Site {} not accessible", hostname); + false + } } #[async_trait]