Skip to content

Commit

Permalink
Merge pull request #17 from wollomatic/develop
Browse files Browse the repository at this point in the history
support multiple hostnames, comma separated
  • Loading branch information
wollomatic authored May 6, 2024
2 parents 6370f42 + 7a01644 commit e9d549f
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 28 deletions.
35 changes: 21 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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

Expand Down
25 changes: 14 additions & 11 deletions cmd/socket-proxy/handlehttprequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log/slog"
"net"
"net/http"
"strings"
)

// handleHttpRequest checks if the request is allowed and sends it to the proxy.
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions examples/docker-compose/dozzle/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit e9d549f

Please sign in to comment.