This commit is contained in:
@@ -473,3 +473,10 @@ without the application's external Go dependencies:
|
||||
```bash
|
||||
go test ./internal/customer/docker.go ./internal/customer/docker_test.go
|
||||
```
|
||||
|
||||
## V4.2.5 worker identity-volume migration check
|
||||
|
||||
After publishing the new worker image, start a hosted worker that already owns
|
||||
an identity volume from an older release. It must start without `permission
|
||||
denied`, keep the same client identity, and remain writable at
|
||||
`/identity/identity.json`. Do not delete the named volume for this test.
|
||||
|
||||
24
V4.2.5_WORKER_IDENTITY_PERMISSION_FIX.md
Normal file
24
V4.2.5_WORKER_IDENTITY_PERMISSION_FIX.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# V4.2.5 – Worker identity volume permission fix
|
||||
|
||||
Managed workers persist `/identity/identity.json` in a Docker named volume. A
|
||||
new named volume is normally root-owned, while identities from older releases
|
||||
may already be mode `0600` and owned by a different UID. With the hardened
|
||||
worker capability set this could make the client fail immediately with:
|
||||
|
||||
```
|
||||
open /identity/identity.json: permission denied
|
||||
```
|
||||
|
||||
V4.2.5 makes the worker image normalize only `/identity` and the fixed
|
||||
`/identity/identity.json` path before startup. The entrypoint starts as root,
|
||||
fixes ownership, and immediately executes `/app/neuralhunt-client` as the
|
||||
unprivileged `app` user via `su-exec`.
|
||||
|
||||
Customer Service still drops all Linux capabilities and adds back only the four
|
||||
bootstrap capabilities needed by that entrypoint: `CHOWN`, `DAC_OVERRIDE`,
|
||||
`SETUID`, and `SETGID`. Workers still receive no Docker socket and retain a
|
||||
read-only root filesystem.
|
||||
|
||||
Existing worker identity volumes are intentionally kept. Rebuilding/pulling the
|
||||
worker image and restarting an affected worker is sufficient; deleting the
|
||||
worker/volume would destroy its identity and is not required.
|
||||
22
docker/worker-entrypoint.sh
Normal file
22
docker/worker-entrypoint.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
# Docker named volumes are normally created as root:root. Older Neural Hunt
|
||||
# worker versions could also leave identity.json owned by a different UID.
|
||||
# Normalize only the dedicated identity mount and the fixed identity file, then
|
||||
# permanently drop to the unprivileged app user before the Go client starts.
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
if [ -L /identity/identity.json ]; then
|
||||
echo "refusing symlink at /identity/identity.json" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chown app:app /identity
|
||||
if [ -e /identity/identity.json ]; then
|
||||
chown app:app /identity/identity.json
|
||||
fi
|
||||
|
||||
exec su-exec app:app /app/neuralhunt-client "$@"
|
||||
fi
|
||||
|
||||
exec /app/neuralhunt-client "$@"
|
||||
@@ -210,10 +210,10 @@ func (d *DockerClient) CreateWorker(ctx context.Context, c WorkerContainerConfig
|
||||
name := url.QueryEscape(c.Name)
|
||||
body := map[string]any{
|
||||
"Image": c.Image,
|
||||
// Named Docker volumes are root-owned when first mounted. Managed workers
|
||||
// therefore run uid 0 only inside their own locked-down container so they
|
||||
// can create the 0600 identity file. They receive no Docker socket, all
|
||||
// Linux capabilities are dropped and the image root filesystem is read-only.
|
||||
// Named Docker volumes are root-owned when first mounted, and identities
|
||||
// created by older releases may be owned by a different UID. The dedicated
|
||||
// worker image starts as uid 0 only for its tiny ownership-normalization
|
||||
// entrypoint and immediately drops to the unprivileged app user.
|
||||
"User": "0:0",
|
||||
"Cmd": []string{"-url", c.GameURL, "-identity", "/identity/identity.json", "-non-interactive", "-quiet", "-task", c.TaskID, "-beacon-path", c.BeaconPath},
|
||||
"Env": []string{
|
||||
@@ -228,10 +228,13 @@ func (d *DockerClient) CreateWorker(ctx context.Context, c WorkerContainerConfig
|
||||
"NetworkMode": c.Network,
|
||||
"ReadonlyRootfs": true,
|
||||
"CapDrop": []string{"ALL"},
|
||||
"SecurityOpt": []string{"no-new-privileges"},
|
||||
"PidsLimit": 128,
|
||||
"Memory": 256 * 1024 * 1024,
|
||||
"NanoCpus": int64(1_000_000_000),
|
||||
// Bootstrap-only capabilities: the image entrypoint fixes ownership of
|
||||
// /identity and then su-exec permanently switches to uid/gid app.
|
||||
"CapAdd": []string{"CHOWN", "DAC_OVERRIDE", "SETUID", "SETGID"},
|
||||
"SecurityOpt": []string{"no-new-privileges"},
|
||||
"PidsLimit": 128,
|
||||
"Memory": 256 * 1024 * 1024,
|
||||
"NanoCpus": int64(1_000_000_000),
|
||||
},
|
||||
}
|
||||
// A dedicated worker image already declares /app/neuralhunt-client as its
|
||||
|
||||
Reference in New Issue
Block a user