diff --git a/README.md b/README.md index b5e7b45..7f84045 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ This is meant to be an additional layer of security. It does not replace other s Socket-proxy listens per default only on `127.0.0.1`. Depending on what you need, you may want to set another listener address with the `-listenip` parameter. In almost every use case, `-listenip=0.0.0.0` will be the correct configuration when using socket-proxy in a docker image. -#### Setting up the IP address allowlist +#### Setting up the IP address or hostname allowlist Per default, only `127.0.0.1/32` is allowed to connect to socket-proxy. You may want to set another allowlist with the `-allowfrom` parameter, depending on your needs. -Since version 1.1.0, not only IP networks but also hostnames can be configured. So it is now possible to explicitly allow only one specific client to connect to the proxy, for example, `-allowfrom=traefik` +Alternatively, not only IP networks but also hostnames can be configured. So it is now possible to explicitly allow one or more specific hostnames to connect to the proxy, for example, `-allowfrom=traefik`, or `-allowfrom=traefik,dozzle`. Using the hostname is an easy-to-configure way to have more security. Access to the socket proxy will not even be permitted from the host system. @@ -152,18 +152,25 @@ To log the API calls of the client application, set the log level to `DEBUG` and ### Parameters -| Parameter | Default Value | Description | -|----------------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `-allowfrom` | `127.0.0.1/32` | Specifies the IP addresses of the clients or the hostname of one specific client allowed to connect to the proxy. The default value is `127.0.0.1/32`, which means only localhost is allowed. This default configuration may not be useful in most cases, but it is because of a secure-by-default design. To allow all IPv4 addresses, set `-allowfrom=0.0.0.0/0`. Please remember that socket-proxy should never be exposed to a public network, regardless of this extra security layer. | -| `-allowhealthcheck` | (not set) | If set, it allows the included health check binary to check the socket connection via TCP port 55555 (socket-proxy then listens on `127.0.0.1:55555/health`) | -| `-listenip` | `127.0.0.1` | Specifies the IP address the server will bind on. Default is only the internal network. | -| `-logjson` | (not set) | If set, it enables logging in JSON format. If unset, docker-proxy logs in plain text format. | -| `-loglevel` | `INFO` | Sets the log level. Accepted values are: `DEBUG`, `INFO`, `WARN`, `ERROR`. | -| `-proxyport` | `2375` | Defines the TCP port the proxy listens to. | -| `-shutdowngracetime` | `10` | Defines the time in seconds to wait before forcing the shutdown after sigtern or sigint (socket-proxy first tries to graceful shut down the TCP server) | -| `-socketpath` | `/var/run/docker.sock` | Specifies the UNIX socket path to connect to. By default, it connects to the Docker daemon socket. | -| `-stoponwatchdog` | (not set) | If set, socket-proxy will be stopped if the watchdog detects that the unix socket is not available. | -| `-watchdoginterval` | `0` | Check for socket availabibity every x seconds (disable checks, if not set or value is 0) | +| Parameter | Default Value | Description | +|----------------------|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `-allowfrom` | `127.0.0.1/32` | Specifies the IP addresses of the clients or the hostname of one specific client allowed to connect to the proxy. The default value is `127.0.0.1/32`, which means only localhost is allowed. This default configuration may not be useful in most cases, but it is because of a secure-by-default design. To allow all IPv4 addresses, set `-allowfrom=0.0.0.0/0`. Alternatively, hostnames (comma-separated) can be set, for example `-allowfrom=traefik`, or `-allowfrom=traefik,dozzle`. Please remember that socket-proxy should never be exposed to a public network, regardless of this extra security layer. | +| `-allowhealthcheck` | (not set) | If set, it allows the included health check binary to check the socket connection via TCP port 55555 (socket-proxy then listens on `127.0.0.1:55555/health`) | +| `-listenip` | `127.0.0.1` | Specifies the IP address the server will bind on. Default is only the internal network. | +| `-logjson` | (not set) | If set, it enables logging in JSON format. If unset, docker-proxy logs in plain text format. | +| `-loglevel` | `INFO` | Sets the log level. Accepted values are: `DEBUG`, `INFO`, `WARN`, `ERROR`. | +| `-proxyport` | `2375` | Defines the TCP port the proxy listens to. | +| `-shutdowngracetime` | `10` | Defines the time in seconds to wait before forcing the shutdown after sigtern or sigint (socket-proxy first tries to graceful shut down the TCP server) | +| `-socketpath` | `/var/run/docker.sock` | Specifies the UNIX socket path to connect to. By default, it connects to the Docker daemon socket. | +| `-stoponwatchdog` | (not set) | If set, socket-proxy will be stopped if the watchdog detects that the unix socket is not available. | +| `-watchdoginterval` | `0` | Check for socket availabibity every x seconds (disable checks, if not set or value is 0) | + +### Changelog + +1.0 - initial release +1.1 - add hostname support for `-allowfrom` parameter +1.2 - reformat logging of allowlist on program start +1.3 - allow multiple, comma-separated hostnames in `-allowfrom` parameter ## License diff --git a/cmd/socket-proxy/handlehttprequest.go b/cmd/socket-proxy/handlehttprequest.go index f2441e9..6876858 100644 --- a/cmd/socket-proxy/handlehttprequest.go +++ b/cmd/socket-proxy/handlehttprequest.go @@ -6,6 +6,7 @@ import ( "log/slog" "net" "net/http" + "strings" ) // handleHttpRequest checks if the request is allowed and sends it to the proxy. @@ -16,9 +17,7 @@ func handleHttpRequest(w http.ResponseWriter, r *http.Request) { // check if the client's IP is allowed to access allowedIP, err := isAllowedClient(r.RemoteAddr) if err != nil { - slog.Error("invalid RemoteAddr format", "reason", err, "method", r.Method, "URL", r.URL, "client", r.RemoteAddr) - sendHTTPError(w, http.StatusInternalServerError) - return + slog.Warn("cannot get valid IP address for client allowlist check", "reason", err, "method", r.Method, "URL", r.URL, "client", r.RemoteAddr) } if !allowedIP { communicateBlockedRequest(w, r, "forbidden IP", http.StatusForbidden) @@ -61,14 +60,18 @@ func isAllowedClient(remoteAddr string) (bool, error) { return allowedIPNet.Contains(clientIP), nil } else { // AllowFrom is not a valid CIDR, so try to resolve it via DNS - ips, err := net.LookupIP(cfg.AllowFrom) - if err != nil { - return false, errors.New("error looking up allowed client hostname: " + err.Error()) - } - for _, ip := range ips { - // Check if IP address is one of the resolved IPs - if ip.Equal(clientIP) { - return true, nil + // split over comma to support multiple hostnames + allowFroms := strings.Split(cfg.AllowFrom, ",") + for _, allowFrom := range allowFroms { + ips, err := net.LookupIP(allowFrom) + if err != nil { + slog.Warn("error looking up allowed client hostname", "hostname", allowFrom, "error", err.Error()) + } + for _, ip := range ips { + // Check if IP address is one of the resolved IPs + if ip.Equal(clientIP) { + return true, nil + } } } return false, nil diff --git a/examples/docker-compose/dozzle/compose.yaml b/examples/docker-compose/dozzle/compose.yaml index 04e4dc3..e2f9f82 100644 --- a/examples/docker-compose/dozzle/compose.yaml +++ b/examples/docker-compose/dozzle/compose.yaml @@ -5,9 +5,9 @@ services: - '-loglevel=info' - '-allowfrom=dozzle' # allow only the small subnet "docker-proxynet" - '-listenip=0.0.0.0' - - '-allowGET=/v1\..{2}/(containers/.*|events)' + - '-allowGET=/v1\..{2}/(containers/.*|events)|/_ping' - '-allowHEAD=/_ping' - - '-watchdoginterval=3600' + - '-watchdoginterval=300' - '-stoponwatchdog' - '-shutdowngracetime=10' restart: unless-stopped @@ -24,7 +24,7 @@ services: - docker-proxynet dozzle: - image: amir20/dozzle:v5.4.2 # make sure you use the most recent version + image: amir20/dozzle:v6.4.2 # make sure you use the most recent version user: 65534:65534 read_only: true mem_limit: 256M