This commit is contained in:
Christoffer Martinsson 2025-10-12 17:21:05 +02:00
parent cd593e32d2
commit 4bb36b7735

View File

@ -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<String> {
@ -730,7 +730,7 @@ impl ServiceCollector {
None
}
fn parse_nginx_config(&self, config: &str) -> Option<Vec<String>> {
async fn parse_nginx_config(&self, config: &str) -> Option<Vec<String>> {
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<String> {
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]