feat: Discover docker ports as well
If processes are running in a container then the fwd process can't read their internal FDs without the CAP_SYS_ADMIN property which is equivalent to sudo. Even with sudo, I think you need to do a lot of work to be able to read them -- spawning a process within the cgroup, doing work there, and then communicating back. This just uses the docker api to populate some default ports, which later get overwritten if fwd can find a native process. The Docker port scan takes about 1.5ms, and the full port scan takes 40+ms, so this adds basically no overhead.
This commit is contained in:
parent
66da323481
commit
6c10d8eece
4 changed files with 707 additions and 103 deletions
747
Cargo.lock
generated
747
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@ bench = false
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bollard = "0.17.0"
|
||||
bytes = "1"
|
||||
copypasta = "0.10.1"
|
||||
crossterm = { version = "0.25", features = ["event-stream"] }
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ async fn server_loop<Reader: AsyncRead + Unpin>(
|
|||
match reader.read().await? {
|
||||
Ping => (),
|
||||
Refresh => {
|
||||
let ports = match refresh::get_entries() {
|
||||
let ports = match refresh::get_entries().await {
|
||||
Ok(ports) => ports,
|
||||
Err(e) => {
|
||||
error!("Error scanning: {:?}", e);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
use crate::message::PortDesc;
|
||||
use anyhow::Result;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn get_entries() -> Result<Vec<PortDesc>> {
|
||||
pub async fn get_entries() -> Result<Vec<PortDesc>> {
|
||||
use anyhow::bail;
|
||||
bail!("Not supported on this operating system");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_entries() -> Result<Vec<PortDesc>> {
|
||||
pub async fn get_entries() -> Result<Vec<PortDesc>> {
|
||||
let start = std::time::Instant::now();
|
||||
use procfs::process::FDTarget;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
|
@ -31,8 +34,13 @@ pub fn get_entries() -> Result<Vec<PortDesc>> {
|
|||
}
|
||||
}
|
||||
}
|
||||
log::trace!("procfs elapsed={:?}", start.elapsed());
|
||||
|
||||
let mut h: HashMap<u16, PortDesc> = HashMap::new();
|
||||
let mut h = if let Ok(ports) = find_docker_ports().await {
|
||||
ports
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
// Go through all the listening IPv4 and IPv6 sockets and take the first
|
||||
// instance of listening on each port *if* the address is loopback or
|
||||
|
|
@ -57,5 +65,49 @@ pub fn get_entries() -> Result<Vec<PortDesc>> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(h.into_values().collect())
|
||||
let vals = h.into_values().collect();
|
||||
log::trace!("total portscan elapsed={:?}", start.elapsed());
|
||||
Ok(vals)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn find_docker_ports(
|
||||
) -> Result<HashMap<u16, PortDesc>, bollard::errors::Error> {
|
||||
use bollard::container::ListContainersOptions;
|
||||
use bollard::Docker;
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let client = Docker::connect_with_defaults()?;
|
||||
log::trace!("docker connect elapsed={:?}", start.elapsed());
|
||||
|
||||
let port_start = std::time::Instant::now();
|
||||
let mut port_to_name = HashMap::new();
|
||||
let opts: ListContainersOptions<String> =
|
||||
ListContainersOptions { all: false, ..Default::default() };
|
||||
for container in client.list_containers(Some(opts)).await? {
|
||||
let name = container
|
||||
.names
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.next()
|
||||
.unwrap_or_else(|| "<unknown docker>".to_owned());
|
||||
for port in container.ports.iter().flatten() {
|
||||
if let Some(public_port) = port.public_port {
|
||||
let private_port = port.private_port;
|
||||
port_to_name.insert(
|
||||
public_port,
|
||||
PortDesc {
|
||||
port: public_port,
|
||||
desc: format!("{name} (docker->{private_port})"),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::trace!(
|
||||
"docker port elapsed={:?} total docker elapsed={:?}",
|
||||
port_start.elapsed(),
|
||||
start.elapsed()
|
||||
);
|
||||
Ok(port_to_name)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue