[READ-ONLY] Mirror of https://github.com/FoxxMD/docker-proxy-filter. Filter the contents of Docker API responses
docker docker-socket-proxy filter
0

Configure Feed

Select the types of activity you want to include in your feed.

send cloned headers

Andreas Brett (Apr 4, 2026, 9:36 PM UTC) d51533cd 57e18abb

+69 -39
+69 -39
src/proxy.rs
··· 1 - use tracing::*; 2 - use std::sync::{Arc, Mutex}; 3 - use std::collections::HashMap; 4 - use ntex::{http::{self, HttpMessage}, web::{self}}; 1 + use ::http::StatusCode; 5 2 use futures_util::TryStreamExt; 6 - use ::http::StatusCode; 3 + use ntex::{ 4 + http::{self, HttpMessage}, 5 + web::{self}, 6 + }; 7 + use std::collections::HashMap; 8 + use std::sync::{Arc, Mutex}; 9 + use tracing::*; 7 10 8 - use crate::config::{AppConfig}; 11 + use crate::config::AppConfig; 9 12 use crate::docker::{self, types::*}; 10 13 use crate::utils; 11 14 ··· 26 29 client: web::types::State<http::Client>, 27 30 forward_url: web::types::State<url::Url>, 28 31 app_config: web::types::State<AppConfig>, 29 - data: web::types::State<AppStateWithContainerMap> 32 + data: web::types::State<AppStateWithContainerMap>, 30 33 ) -> Result<web::HttpResponse, web::Error> { 31 34 let mut new_url = forward_url.get_ref().clone(); 32 35 new_url.set_path(req.uri().path()); ··· 37 40 .await 38 41 .map_err(web::Error::from)?; 39 42 43 + let mut client_resp = web::HttpResponse::build(res.status()); 44 + 40 45 // respond without body for /_ping HEAD request 41 46 if req.uri().path() == "/_ping" && req.method() == http::Method::HEAD { 42 - return Ok(web::HttpResponse::build(res.status()).finish()); 47 + for (key, value) in res.headers().iter() { 48 + client_resp.header(key.clone(), value.clone()); 49 + } 50 + return Ok(client_resp.finish()); 43 51 } 44 52 45 - let mut client_resp = web::HttpResponse::build(res.status()); 46 53 client_resp.content_type(res.content_type()); 47 54 48 55 if res.status() == 200 { 49 - 50 56 // if route is to list containers we want to return a filtered list 51 57 if new_url.path().contains("containers/json") { 52 58 let _list_span = span!(Level::DEBUG, "Container List").entered(); 53 59 54 - let container_res = &res.json::<Vec<ContainerSummary>>() 55 - // 2mb in bytes 56 - .limit(2097152).await; 60 + let container_res = &res 61 + .json::<Vec<ContainerSummary>>() 62 + // 2mb in bytes 63 + .limit(2097152) 64 + .await; 57 65 58 66 let containers = match container_res { 59 - Ok(list_res) => { 60 - list_res 61 - } 67 + Ok(list_res) => list_res, 62 68 Err(e) => { 63 69 panic!("{e}"); 64 70 } 65 71 }; 66 - 72 + 67 73 // filter all containers to only those that have values from CONTAINER_NAMES includes in their names 68 - let filtered_containers = containers.into_iter() 69 - .filter(|&con| { 70 - let _list_span = span!(Level::DEBUG, "Container", id = utils::short_id(con.id.as_ref().unwrap())).entered(); 71 - docker::container_summary_match(con, &app_config.get_ref().container_names, &app_config.get_ref().container_labels) 74 + let filtered_containers = containers 75 + .into_iter() 76 + .filter(|&con| { 77 + let _list_span = span!( 78 + Level::DEBUG, 79 + "Container", 80 + id = utils::short_id(con.id.as_ref().unwrap()) 81 + ) 82 + .entered(); 83 + docker::container_summary_match( 84 + con, 85 + &app_config.get_ref().container_names, 86 + &app_config.get_ref().container_labels, 87 + ) 72 88 }) 73 89 .collect::<Vec<&ContainerSummary>>(); 74 90 75 91 let fresp = web::HttpResponse::build(res.status()).json(&filtered_containers); 76 - debug!("{} of {} containers valid", filtered_containers.len(), containers.len()); 92 + debug!( 93 + "{} of {} containers valid", 94 + filtered_containers.len(), 95 + containers.len() 96 + ); 77 97 Ok(fresp) 78 98 } else { 79 - 80 99 // only deal with routes that are for containers like /containers/1234/{some_resource} 81 100 match docker::match_container_get(new_url.path()) { 82 101 // the regex pulls the container id from the route with a named capture group, avaiable as m.id 83 - 84 - Some (m) => { 102 + Some(m) => { 85 103 let short_cid = utils::short_id(&m.id); //format!("{start}...{end}", start = &m.id[..6], end = &m.id[&m.id.len() - 6..]); 86 104 let _con_span = span!(Level::DEBUG, "Container", id = short_cid).entered(); 87 105 debug!("Matched container route with Id {}", &m.id); ··· 93 111 // or if we encountered an error last time then try again 94 112 if !cm.contains_key(&m.id) || cm.get(&m.id).unwrap().is_none() { 95 113 debug!("Requested Id not in map, trying to inspect..."); 96 - let info_res = docker::get_container_info(&client, &forward_url, &m.id).await; 114 + let info_res = 115 + docker::get_container_info(&client, &forward_url, &m.id).await; 97 116 match info_res { 98 117 Ok((name, labels)) => { 99 - let is_container_match = docker::match_labels_or_names(&app_config.get_ref().container_names, &app_config.get_ref().container_labels, &Vec::from([name.clone()]), &labels); 100 - debug!("Recording container '{}' {} valid", name, is!(is_container_match; "as";"as not")); 118 + let is_container_match = docker::match_labels_or_names( 119 + &app_config.get_ref().container_names, 120 + &app_config.get_ref().container_labels, 121 + &Vec::from([name.clone()]), 122 + &labels, 123 + ); 124 + debug!( 125 + "Recording container '{}' {} valid", 126 + name, 127 + is!(is_container_match; "as";"as not") 128 + ); 101 129 cm.insert(m.id.clone(), Some(is_container_match)); 102 130 } 103 131 Err(e) => { ··· 114 142 115 143 // then we try to get the name from the stateful hashmap 116 144 let allowed = cm.get(&m.id).unwrap(); 117 - 145 + 118 146 match allowed { 119 147 Some(n) => { 120 148 // only return if a response if requested container has a name that includes values from CONTAINER_NAMES ··· 123 151 // if the route resource is specifically the Container Inspect API 124 152 // then we may need to scrub Envs if SCRUB_ENVS=true 125 153 if m.resource == "json" { 126 - 127 154 client_resp.content_type("application/json"); 128 155 129 156 if app_config.get_ref().scrub_envs { 130 - let mut container = res.json::<ContainerInspect>().await.unwrap(); 157 + let mut container = 158 + res.json::<ContainerInspect>().await.unwrap(); 131 159 container.config.as_mut().unwrap().env = Some(Vec::new()); 132 160 Ok(client_resp.json(&container)) 133 161 } else { 134 162 Ok(client_resp.streaming(res.into_stream())) 135 163 } 136 - 137 164 } else { 138 165 client_resp.content_type(res.content_type()); 139 166 Ok(client_resp.streaming(res.into_stream())) ··· 141 168 } else { 142 169 debug!("Does not match container filters, 404ing..."); 143 170 client_resp.status(StatusCode::NOT_FOUND); 144 - Ok(client_resp.json(&DockerErrorMessage { message: format!("No such container: {}", &m.id)})) 171 + Ok(client_resp.json(&DockerErrorMessage { 172 + message: format!("No such container: {}", &m.id), 173 + })) 145 174 } 146 175 } 147 176 None => { 148 - debug!("Does not exist or Docker API previously returned an error, 404ing..."); 177 + debug!( 178 + "Does not exist or Docker API previously returned an error, 404ing..." 179 + ); 149 180 client_resp.status(StatusCode::NOT_FOUND); 150 - Ok(client_resp.json(&DockerErrorMessage { message: format!("No such container: {}", &m.id)})) 181 + Ok(client_resp.json(&DockerErrorMessage { 182 + message: format!("No such container: {}", &m.id), 183 + })) 151 184 } 152 185 } 153 186 } ··· 158 191 } 159 192 } 160 193 } 161 - 162 194 } else { 163 195 let stream = res.into_stream(); 164 196 Ok(client_resp.streaming(stream)) 165 197 } 166 - 167 - 168 - } 198 + }