Skip to content
Closed
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
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ An embargo period applies between fix and disclosure. The reporter will be notif

The CashPilot worker (`drumsergio/cashpilot-worker`) creates and manages containers, so it needs the Docker API. Raw access to the Docker socket (`/var/run/docker.sock`) is root on the host. The UI never touches Docker; it commands workers over their authenticated API.

**The shipped compose files do not give the worker the socket.** A Docker socket proxy, [sockguard](https://github.com/CodesWhat/sockguard) `codeswhat/sockguard:2.4.5` pinned by digest, holds it read-only and serves the worker a filtered socket. It refuses every Docker API call the worker does not make, and it reads request bodies, so it refuses privileged containers, binds of `/` or of the socket, capabilities and devices outside the catalog's, host namespaces, a container without `no-new-privileges`, and exec of anything but the catalog's reader commands. [Security defaults: Docker socket proxy](docs/security-defaults.md#docker-socket-proxy) has the full policy, how it is tested, and what a compromised worker can still do through it. It is not yet a full root boundary: on sockguard 2.4.5 a compromised worker can still reach root in two steps, by joining another root container's process namespace or through a symlink planted in a host folder the proxy allows, so it narrows the risk rather than removing it.
**The shipped compose files do not give the worker the socket.** A Docker socket proxy, [sockguard](https://github.com/CodesWhat/sockguard) `codeswhat/sockguard:2.4.6` pinned by digest, holds it read-only and serves the worker a filtered socket. It refuses every Docker API call the worker does not make, and it reads request bodies, so it refuses privileged containers, binds of `/` or of the socket, capabilities and devices outside the catalog's, host namespaces, a container without `no-new-privileges`, and exec of anything but the catalog's reader commands. [Security defaults: Docker socket proxy](docs/security-defaults.md#docker-socket-proxy) has the full policy, how it is tested, and what a compromised worker can still do through it. It is not yet a full root boundary: on sockguard 2.4.6 a compromised worker can still reach root in two steps, through a symlink planted in a host folder the proxy allows (owner isolation closes the other one, joining another container's process namespace), so it narrows the risk rather than removing it.

Other mitigations:

Expand Down
2 changes: 1 addition & 1 deletion app/socket_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
("GET", "/networks"),
("GET", "/networks/*"),
# The LAN fence (app/lan_fence.py) creates cashpilot-isolated, moves
# earners onto it and recreates it after a prune. sockguard 2.4.5 cannot
# earners onto it and recreates it after a prune. sockguard 2.4.6 cannot
# limit these by network name, so they are allowed for any network.
("POST", "/networks/create"),
("POST", "/networks/*/connect"),
Expand Down
2 changes: 1 addition & 1 deletion contrib/app-stores/casaos/CashPilot/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ services:
# Same service, same policy as the repository's docker-compose.yml;
# scripts/socket_proxy_policy.py writes the policy into both.
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
2 changes: 1 addition & 1 deletion contrib/app-stores/runtipi/cashpilot/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ services:
# Same service, same policy as the repository's docker-compose.yml;
# scripts/socket_proxy_policy.py writes the policy into both.
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ services:
# Pinned by digest and bumped on purpose; the in-app update never recreates
# it (it runs `up --no-deps` on the UI and worker only).
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.fleet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ services:
# Pinned by digest and bumped on purpose; the in-app update never recreates
# it (it runs `up --no-deps` on the UI and worker only).
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.worker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ services:
# Pinned by digest and bumped on purpose; the in-app update never recreates
# it (it runs `up --no-deps` on the UI and worker only).
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ services:
# Pinned by digest and bumped on purpose; the in-app update never recreates
# it (it runs `up --no-deps` on the UI and worker only).
cashpilot-socket-proxy:
image: codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c
image: codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f
container_name: cashpilot-socket-proxy
# Root with no capabilities: uid 0 opens the root-owned host socket, and
# group 1000 (the worker's) lets it give the worker its socket.
Expand Down
8 changes: 4 additions & 4 deletions docs/security-defaults.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ machine. In the shipped compose files that holder is a proxy, not the worker.

The shipped compose files (`docker-compose.yml`, `docker-compose.fleet.yml`,
`docker-compose.worker.yml`, `docker-compose.build.yml`) run
[sockguard](https://github.com/CodesWhat/sockguard) 2.4.5 as
[sockguard](https://github.com/CodesWhat/sockguard) 2.4.6 as
`cashpilot-socket-proxy`, pinned by digest
(`codeswhat/sockguard:2.4.5@sha256:d15f93e856626ff21f48400034d887874a4dafbbf6bb1e31799511beea07d84c`).
(`codeswhat/sockguard:2.4.6@sha256:9a7142d9f8254e7fde7543e046eff920463f5df27b36d959915b44fb4878d06f`).
It holds the socket read-only, runs as root with every capability dropped and no
network, and serves a filtered socket in the `cashpilot-docker-api-owned` volume. The
worker mounts that volume and sets `DOCKER_HOST` to it. It has no Docker socket
Expand Down Expand Up @@ -164,7 +164,7 @@ those. Images are not scoped: a custom service can still pull any image.
privileged container, a bind of `/` or of the socket, an added capability.
Owner isolation closes joining another container's process namespace, which
read a root container's files through `/proc/1/root`. One two-step way is
left, because sockguard 2.4.5 has no setting for it:
left, because sockguard 2.4.6 has no setting for it:

- **A symlink in an allowed folder.** sockguard checks a bind's source as a
string; Docker follows symlinks when it mounts. A container that binds an
Expand Down Expand Up @@ -198,7 +198,7 @@ stands between the worker and root.
folder is not bindable at all.
- Create Docker networks, and attach, detach and delete the ones it created.
The LAN fence needs this to build `cashpilot-isolated` and move earners onto
it, and sockguard 2.4.5 cannot limit it to that one network. The proxy lets bridge driver options and
it, and sockguard 2.4.6 cannot limit it to that one network. The proxy lets bridge driver options and
a forced disconnect through, because the fence uses both; custom drivers,
swarm scope, custom address plans and endpoint settings stay refused, except
network aliases, which sockguard always lets through. It adds little reach, since the worker
Expand Down
Loading