Firewhale is a proxy for the Docker socket.
Supported Tags
Name | Description | Example |
---|---|---|
latest |
The latest stable version of Firewhale. | ghcr.io/celsiusnarhwal/firewhale:latest |
Major version number | The latest version of Firewhale with this major version number. May be optionally prefixed with a v . |
ghcr.io/celsiusnarhwal/firewhale:1 ghcr.io/celsiusnarhwal/firewhale:v1 |
Exact version number | This version of Firewhale exactly. May be optionally prefixed with a v . |
ghcr.io/celsiusnarhwal/firewhale:1.0.0 ghcr.io/celsiusnarhwal/firewhale:v1.0.0 |
edge |
The latest commit to Firewhale's main branch. Unstable. |
ghcr.io/celsiusnarhwal/firewhale:edge |
Supported Architectures
amd64
arm64
Giving a service direct access to your Docker socket is equivalent to giving it root access on your host. However, some services require access to the Docker socket for various reasons. Firewhale allows you to grant limited, per-service, access to the Docker socket, allowing you to control what individual services can and cannot see and do.
Note
Firewhale is designed to be used with other Docker containers. If you need a proxy for use with non-containerized services, see Tecnativa/docker-socket-proxy.
Warning
Do you use Watchtower? Read this before using Firewhale.
Firewhale is built on top of Caddy. It reads Docker service labels to determine which Docker API endpoints services are and aren't allowed to access and generates a Caddyfile that enforces those rules.
Firewhale is designed to be used with Docker Compose.
services:
firewhale:
image: ghcr.io/celsiusnarhwal/firewhale:latest
container_name: firewhale
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
By default, Firewhale will be accessible from services with which it shares a network at http://firewhale:2375
.
A service's access to the Docker socket can be controlled with labels. The firewhale.read
label controls
which Docker API endpoints a service can read from (i.e., send GET
and HEAD
requests to) and the firewhale.write
label controls which endpoints a service can write to
(i.e., additonally send POST
, PUT
, PATCH
, and DELETE
requests to). The value of each label should be a
space-separated list of
endpoints the service should be able to read or write to.
Take a look at this example:
services:
firewhale:
image: ghcr.io/celsiusnarhwal/firewhale:latest
container_name: firewhale
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
foobar:
image: foobar:latest
container_name: foobar
restart: unless-stopped
depends_on:
firewhale:
condition: service_healthy
environment:
DOCKER_HOST: http://firewhale:2375
labels:
firewhale.read: containers images info
firewhale.write: networks volumes
See the generated Caddyfile
:2375 {
map {path} {endpoint} {
~^(?:\/v[0-9.]+)?\/([^\/?]+) "${1}"
}
@foobar_read {
remote_host foobar
method GET HEAD
vars {endpoint} containers images info
}
handle @foobar_read {
reverse_proxy unix//var/run/docker.sock
}
@foobar_write {
remote_host foobar
vars {endpoint} networks volumes
}
handle @foobar_write {
reverse_proxy unix//var/run/docker.sock
}
@events_ping_version {
remote_host foobar
method GET HEAD
vars {endpoint} events _ping version
}
handle @events_ping_version {
reverse_proxy unix//var/run/docker.sock
}
handle {
respond 403
}
}
In this example, foobar
has read access to the containers
images
, and info
, endpoints
and write access to networks
and volumes
.
You can find an exhaustive list of endpoints in the Docker Engine API documentation.
Tip
Firewhale accepts endpoint names both with and without a leading forward slash (e.g., containers
and /containers
are both valid).
Important
Read access to the events
,
_ping
, and
version
endpoints is always granted,
whether or not you do so explicitly. The information returned by these endpoints is practically harmless, and most
services that hook into the Docker socket require these endpoints at a minimum.
Both the firewhale.read
and firewhale.write
labels accept a special value called all
. all
grants
unrestricted read or write access to the Docker socket and is equivalent to specifying each endpoint individually.
Iterating on the previous example:
services:
firewhale:
image: ghcr.io/celsiusnarhwal/firewhale:latest
container_name: firewhale
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
foobar:
image: foobar:latest
container_name: foobar
restart: unless-stopped
depends_on:
firewhale:
condition: service_healthy
environment:
DOCKER_HOST: http://firewhale:2375
labels:
firewhale.read: all
firewhale.write: containers images
See the generated Caddyfile
:2375 {
map {path} {endpoint} {
~^(?:\/v[0-9.]+)?\/([^\/?]+) "${1}"
}
@foobar_read {
remote_host foobar
method GET HEAD
}
handle @foobar_read {
reverse_proxy unix//var/run/docker.sock
}
@foobar_write {
remote_host foobar
vars {endpoint} containers images
}
handle @foobar_write {
reverse_proxy unix//var/run/docker.sock
}
@events_ping_version {
remote_host foobar
method GET HEAD
vars {endpoint} events _ping version
}
handle @events_ping_version {
reverse_proxy unix//var/run/docker.sock
}
handle {
respond 403
}
}
In this example, foobar
has read access to all endpoints and write access
to containers
and images
.
You can view the Caddyfile Firewhale is currently using with firewhale view
.
docker exec firewhale firewhale view
By default, Caddy's admin API is available inside Firewhale's container at
localhost:2019
. You can use this to view Firewhale's configuration in Caddy's canonical JSON.
docker exec firewhale curl -s localhost:2019/config/
Caution
Avoid making configuration changes via the Caddy API. They'll be lost when Firewhale reloads its configuration.
Firewhale supports all versions of the Docker API that are supported by Docker's official Python SDK.
Some aspects of Firewhale can be configured via environment variables.
Environment Variable | Description | Default |
---|---|---|
FIREWHALE_PORT |
The port on which Firewhale should listen. Firewhale will be accessible at http://firewhale:${FIREWHALE_PORT} . Must be an integer between 0 and 65535 and different than FIREWHALE_CADDY_API_PORT . |
2375 |
FIREWHALE_CADDY_API_PORT |
The port on which Caddy's admin API should listen. The Caddy API will be accessible at localhost:${FIREWHALE_CADDY_API_PORT} within Firewhale's container. Must be an integer between 0 and 65535 and different than FIREWHALE_PORT . |
2019 |
FIREWHALE_HTTP_STATUS_CODE |
The HTTP status code Firewhale should respond with when it receives a request it has not been configured to allow. Must be an integer between 100 and 599. | 403 |
FIREWHALE_RELOAD_INTERVAL |
The interval at which Firewhale will query Docker for any changes to your services' labels and update its rules accordingly. Must be in the format of a Go duration string, except you can also use d for day, w for week, mm for month, and y for year.1 |
30s |
FIREWHALE_LABEL_PREFIX |
The prefix with which Firewhale labels should begin. Endpoint access will be configurable via the ${LABEL_PREFIX}.read and ${LABEL_PREFIX}.write labels. |
firewhale |
FIREWHALE_LOG_LEVEL |
The minimum severity level for which Firewhale and Caddy should output logs. Must be one of DEBUG , INFO , WARN , or ERROR . |
INFO |
FIREWHALE_LOG_FORMAT |
The format in which Firewhale and Caddy should output logs. Must be either json or console . |
json |
- Firewhale only works on user-defined bridge networks. This shouldn't be an issue if you're using Docker Compose, where such networks are the default for services that aren't explicitly defined to use something else.
- You don't need to, and in fact shouldn't, publish any ports on Firewhale's container.
- Firewhale only supports connections over plain HTTP. TLS connections aren't supported and aren't planned to be.
- Firewhale does not honor the following environment variables and setting them will have no effect:
DOCKER_HOST
DOCKER_TLS_VERIFY
DOCKER_CERT_PATH
CADDY_ADMIN
If you use Watchtower, it's important to make sure that either:
- a) Watchtower does not use Firewhale as its Docker host, or
- b) Firewhale is exempted from Watchtower's update routine.
If Watchtower tries to update Firewhale while using it as its Docker host, it will lose access to the Docker API after stopping Firewhale and will not be able to complete its update routine. Naturally, this will break anything depending on Firewhale and any other containers Watchtower stopped during the update routine will be left as such.
See Watchtower's documentation for more info.
Footnotes
-
1 day = 24 hours, 1 week = 7 days, 1 month = 30 days, and 1 year = 365 days. ↩