diff --git a/README.md b/README.md index 69a4fa8..8e03c2a 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,27 @@ Newt can integrate with the Docker socket to provide remote inspection of Docker **Configuration:** -You can specify the Docker socket path using the `--docker-socket` CLI argument or by setting the `DOCKER_SOCKET` environment variable. On most linux systems the socket is `/var/run/docker.sock`. When deploying newt as a container, you need to mount the host socket as a volume for the newt container to access it. If the Docker socket is not available or accessible, Newt will gracefully disable Docker integration and continue normal operation. +You can specify the Docker socket path using the `--docker-socket` CLI argument or by setting the `DOCKER_SOCKET` environment variable. If the Docker socket is not available or accessible, Newt will gracefully disable Docker integration and continue normal operation. + +Supported values include: + +- Local UNIX socket (default): + >You must mount the socket file into the container using a volume, so Newt can access it. + + `unix:///var/run/docker.sock` + +- TCP socket (e.g., via Docker Socket Proxy): + + `tcp://localhost:2375` + +- HTTP/HTTPS endpoints (e.g., remote Docker APIs): + + `http://your-host:2375` + +- SSH connections (experimental, requires SSH setup): + + `ssh://user@host` + ```yaml services: @@ -219,8 +239,9 @@ services: - PANGOLIN_ENDPOINT=https://example.com - NEWT_ID=2ix2t8xk22ubpfy - NEWT_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2 - - DOCKER_SOCKET=/var/run/docker.sock + - DOCKER_SOCKET=unix:///var/run/docker.sock ``` +>If you previously used just a path like `/var/run/docker.sock`, it still works — Newt assumes it is a UNIX socket by default. #### Hostnames vs IPs diff --git a/docker/client.go b/docker/client.go index 226e099..f8b9d0c 100644 --- a/docker/client.go +++ b/docker/client.go @@ -53,22 +53,55 @@ type Network struct { DNSNames []string `json:"dnsNames,omitempty"` } +// Strcuture parts of docker api endpoint +type dockerHost struct { + protocol string // e.g. unix, http, tcp, ssh + address string // e.g. "/var/run/docker.sock" or "host:port" +} + +// Parse the docker api endpoint into its parts +func parseDockerHost(raw string) (dockerHost, error) { + switch { + case strings.HasPrefix(raw, "unix://"): + return dockerHost{"unix", strings.TrimPrefix(raw, "unix://")}, nil + case strings.HasPrefix(raw, "ssh://"): + // SSH is treated as TCP-like transport by the docker client + return dockerHost{"ssh", strings.TrimPrefix(raw, "ssh://")}, nil + case strings.HasPrefix(raw, "tcp://"), strings.HasPrefix(raw, "http://"), strings.HasPrefix(raw, "https://"): + s := raw + s = strings.TrimPrefix(s, "tcp://") + s = strings.TrimPrefix(s, "http://") + s = strings.TrimPrefix(s, "https://") + return dockerHost{"tcp", s}, nil + default: + // default fallback to unix + return dockerHost{"unix", raw}, nil + } +} + // CheckSocket checks if Docker socket is available func CheckSocket(socketPath string) bool { // Use the provided socket path or default to standard location if socketPath == "" { - socketPath = "/var/run/docker.sock" + socketPath = "unix:///var/run/docker.sock" } + host, err := parseDockerHost(socketPath) + if err != nil { + logger.Debug("Invalid Docker socket path '%s': %v", socketPath, err) + return false + } + protocol := host.protocol + addr := host.address - // Try to create a connection to the Docker socket - conn, err := net.Dial("unix", socketPath) + // ssh might need different verification, but tcp works for basic reachability + conn, err := net.DialTimeout(protocol, addr, 2*time.Second) if err != nil { - logger.Debug("Docker socket not available at %s: %v", socketPath, err) + logger.Debug("Docker not reachable via %s at %s: %v", protocol, addr, err) return false } defer conn.Close() - logger.Debug("Docker socket is available at %s", socketPath) + logger.Debug("Docker reachable via %s at %s", protocol, addr) return true } @@ -132,7 +165,7 @@ func ListContainers(socketPath string, enforceNetworkValidation bool) ([]Contain // Create client with custom socket path cli, err := client.NewClientWithOpts( - client.WithHost("unix://"+socketPath), + client.WithHost(socketPath), client.WithAPIVersionNegotiation(), ) if err != nil { @@ -182,7 +215,6 @@ func ListContainers(socketPath string, enforceNetworkValidation bool) ([]Contain hostname = containerInfo.Config.Hostname } - // Skip host container if set if hostContainerId != "" && c.ID == hostContainerId { continue diff --git a/main.go b/main.go index dbb141f..14c74d3 100644 --- a/main.go +++ b/main.go @@ -158,7 +158,7 @@ func main() { flag.StringVar(&tlsPrivateKey, "tls-client-cert", "", "Path to client certificate used for mTLS") } if dockerSocket == "" { - flag.StringVar(&dockerSocket, "docker-socket", "", "Path to Docker socket (typically /var/run/docker.sock)") + flag.StringVar(&dockerSocket, "docker-socket", "", "Path or address to Docker socket (typically unix:///var/run/docker.sock)") } if pingIntervalStr == "" { flag.StringVar(&pingIntervalStr, "ping-interval", "3s", "Interval for pinging the server (default 3s)")