Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/docker-image-release.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
name: Build and Publish Release
permissions:
contents: read
packages: write

on:
push:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/docker-image-testing.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
name: Build and Publish Testing
permissions:
contents: read
packages: write

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:1.24.5-alpine3.22 AS build
FROM --platform=$BUILDPLATFORM golang:1.25.0-alpine3.22 AS build
WORKDIR /application
COPY . ./
ARG TARGETOS
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# socket-proxy

## Latest image
- `wollomatic/socket-proxy:1.8.1` / `ghcr.io/wollomatic/socket-proxy:1.8.1`
- `wollomatic/socket-proxy:1.9.0` / `ghcr.io/wollomatic/socket-proxy:1.9.0`
- `wollomatic/socket-proxy:1` / `ghcr.io/wollomatic/socket-proxy:1`

## About
Expand Down Expand Up @@ -33,7 +33,7 @@ You should know what you are doing. Never expose socket-proxy to a public networ
The container image is available on [Docker Hub (wollomatic/socket-proxy)](https://hub.docker.com/r/wollomatic/socket-proxy)
and on the [GitHub Container Registry (ghcr.io/wollomatic/socket-proxy)](https://github.com/wollomatic/socket-proxy/pkgs/container/socket-proxy).

To pin one specific version, use the version tag (for example, `wollomatic/socket-proxy:1.6.0` or `ghcr.io/wollomatic/socket-proxy:1.6.0`).
To pin one specific version, use the version tag (for example, `wollomatic/socket-proxy:1.9.0` or `ghcr.io/wollomatic/socket-proxy:1.9.0`).
To always use the most recent version, use the `1` tag (`wollomatic/socket-proxy:1` or `ghcr.io/wollomatic/socket-proxy:1`). This tag will be valid as long as there is no breaking change in the deployment.

There may be an additional docker image with the `testing`-tag. This image is only for testing. Likely, documentation for the `testing` image could only be found in the GitHub commit messages. It is not recommended to use the `testing` image in production.
Expand Down Expand Up @@ -75,7 +75,7 @@ The name of a parameter should be "-allow", followed by the HTTP method name (fo

It is also possible to configure the allowlist via environment variables. The variables are called "SP_ALLOW_", followed by the HTTP method (for example, `SP_ALLLOW_GET`).

If both commandline parameter and environment variable is configured for a particular HTTP method, the environment variable is ignored.
If both commandline parameter and environment variable are configured for a particular HTTP method, the environment variable is ignored.

Use Go's regexp syntax to create the patterns for these parameters. To avoid insecure configurations, the characters ^ at the beginning and $ at the end of the string are automatically added. Note: invalid regexp results in program termination.

Expand Down Expand Up @@ -159,7 +159,7 @@ services:
# it is not the same as the traefik-servicenet

traefik:
# [...] see github.com/wollomatic/traefik2-hardened for a full example
# [...] see github.com/wollomatic/traefik-hardened for a full example
depends_on:
- dockerproxy
networks:
Expand Down Expand Up @@ -197,7 +197,7 @@ socket-proxy can be configured via command line parameters or via environment va
| Parameter | Environment Variable | Default Value | Description |
|--------------------------------|----------------------------------|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-allowfrom` | `SP_ALLOWFROM` | `127.0.0.1/32` | Specifies the IP addresses or hostnames (comma-separated) 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 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. |
| `-allowbindmountfrom` | `SP_ALLOWBINDMOUNTFROM` | (not set) | Specifies the directories (comma-separated) that are allowed as bind mount sources. If not set, no bind mount restrictions are applied. When set, only bind mounts from the specified directories or their subdirectories are allowed. Each directory must start with `/`. For example, `-allowbindmountfrom=/home,/var/log` allows bind mounts from `/home`, `/var/log`, and any subdirectories. |
| `-allowbindmountfrom` | `SP_ALLOWBINDMOUNTFROM` | (not set) | Specifies the directories (comma-separated) that are allowed as bind mount sources. If not set, no bind mount restrictions are applied. When set, only bind mounts from the specified directories or their subdirectories are allowed. Each directory must start with `/`. For example, `-allowbindmountfrom=/home,/var/log` allows bind mounts from `/home`, `/var/log`, and any subdirectories. |
| `-allowhealthcheck` | `SP_ALLOWHEALTHCHECK` | (not set/false) | 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` | `SP_LISTENIP` | `127.0.0.1` | Specifies the IP address the server will bind on. Default is only the internal network. |
| `-logjson` | `SP_LOGJSON` | (not set/false) | If set, it enables logging in JSON format. If unset, docker-proxy logs in plain text format. |
Expand Down Expand Up @@ -228,7 +228,9 @@ socket-proxy can be configured via command line parameters or via environment va

1.7 - also allow comma-separated CIDRs in `-allowfrom` (not only hostnames as in versions > 1.3)

1.8 - add optional bind mount restrictions (thanks [@powerman](https://github.com/powerman))
1.8 - add optional bind mount restrictions (thanks [@powerman](https://github.com/powerman), [@C4tWithShell](https://github.com/C4tWithShell))

1.9 - add IPv6 support to `-listenip` (thanks [@op3](https://github.com/op3))

## License
This project is licensed under the MIT License – see the [LICENSE](LICENSE) file for details.
Expand All @@ -241,3 +243,8 @@ See the comments in this file and the LICENSE file for more information.
+ [Chris Wiegman: Protecting Your Docker Socket With Traefik 2](https://chriswiegman.com/2019/11/protecting-your-docker-socket-with-traefik-2/) [@ChrisWiegman](https://github.com/ChrisWiegman)
+ [tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
+ [@justsomescripts](https://github.com/justsomescripts) fix parsing environment variable to configure unix socket

## Alternatives

+ [hectorm/cetusguard](https://github.com/hectorm/cetusguard)
+ [11notes/docker-socket-proxy](https://github.com/11notes/docker-socket-proxy)
15 changes: 12 additions & 3 deletions cmd/socket-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -58,18 +59,26 @@ func main() {
// print configuration
slog.Info("starting socket-proxy", "version", version, "os", runtime.GOOS, "arch", runtime.GOARCH, "runtime", runtime.Version(), "URL", programURL)
if cfg.ProxySocketEndpoint == "" {
slog.Info("configuration info", "socketpath", cfg.SocketPath, "listenaddress", cfg.ListenAddress, "loglevel", cfg.LogLevel, "logjson", cfg.LogJSON, "allowfrom", cfg.AllowFrom, "shutdowngracetime", cfg.ShutdownGraceTime, "allowbindmountfrom", cfg.AllowBindMountFrom)
// join the cfg.AllowFrom slice to a string to avoid the brackets in the logging (avoid confusion with IPv6 addresses)
allowFromString := strings.Join(cfg.AllowFrom, ",")
slog.Info("configuration info", "socketpath", cfg.SocketPath, "listenaddress", cfg.ListenAddress, "loglevel", cfg.LogLevel, "logjson", cfg.LogJSON, "allowfrom", allowFromString, "shutdowngracetime", cfg.ShutdownGraceTime)
} else {
slog.Info("configuration info", "socketpath", cfg.SocketPath, "proxysocketendpoint", cfg.ProxySocketEndpoint, "proxysocketendpointfilemode", cfg.ProxySocketEndpointFileMode, "loglevel", cfg.LogLevel, "logjson", cfg.LogJSON, "allowfrom", cfg.AllowFrom, "shutdowngracetime", cfg.ShutdownGraceTime, "allowbindmountfrom", cfg.AllowBindMountFrom)
slog.Info("configuration info", "socketpath", cfg.SocketPath, "proxysocketendpoint", cfg.ProxySocketEndpoint, "proxysocketendpointfilemode", cfg.ProxySocketEndpointFileMode, "loglevel", cfg.LogLevel, "logjson", cfg.LogJSON, "shutdowngracetime", cfg.ShutdownGraceTime)
slog.Info("proxysocketendpoint is set, so the TCP listener is deactivated")
}
if cfg.WatchdogInterval > 0 {
slog.Info("watchdog enabled", "interval", cfg.WatchdogInterval, "stoponwatchdog", cfg.StopOnWatchdog)
} else {
slog.Info("watchdog disabled")
}
if len(cfg.AllowBindMountFrom) > 0 {
slog.Info("Docker bind mount restrictions enabled", "allowbindmountfrom", cfg.AllowBindMountFrom)
} else {
// we only log this on DEBUG level because bind mount restrictions are a very special use case
slog.Debug("no Docker bind mount restrictions")
}

// print request allow list
// print request allowlist
if cfg.LogJSON {
for method, regex := range cfg.AllowedRequests {
slog.Info("configured allowed request", "method", method, "regex", regex)
Expand Down
17 changes: 12 additions & 5 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,20 @@ func InitConfig() (*Config, error) {
}

// check listenIP and proxyPort
if net.ParseIP(listenIP) == nil {
return nil, fmt.Errorf("invalid IP \"%s\" for listenip", listenIP)
}
if proxyPort < 1 || proxyPort > 65535 {
return nil, errors.New("port number has to be between 1 and 65535")
return nil, errors.New("port number has to be between 1 and 65535")
}
ip := net.ParseIP(listenIP)
if ip == nil {
return nil, fmt.Errorf("invalid IP \"%s\" for listenip", listenIP)
}

// Properly format address for both IPv4 and IPv6
if ip.To4() == nil {
cfg.ListenAddress = fmt.Sprintf("[%s]:%d", listenIP, proxyPort)
} else {
cfg.ListenAddress = fmt.Sprintf("%s:%d", listenIP, proxyPort)
}
cfg.ListenAddress = fmt.Sprintf("%s:%d", listenIP, proxyPort)

// parse defaultLogLevel and setup logging handler depending on defaultLogJSON
switch strings.ToUpper(logLevel) {
Expand Down
Loading