Skip to content

fix: enforce docker:read on container start/stop/kill/restart mutations#4332

Open
iExad wants to merge 1 commit intoDokploy:canaryfrom
iExad:canary
Open

fix: enforce docker:read on container start/stop/kill/restart mutations#4332
iExad wants to merge 1 commit intoDokploy:canaryfrom
iExad:canary

Conversation

@iExad
Copy link
Copy Markdown

@iExad iExad commented Apr 30, 2026

Summary

  • Four Docker mutation endpoints (docker.startContainer,
    docker.stopContainer, docker.killContainer,
    docker.restartContainer) were gated by withPermission("service", "read"),
    which the default member role grants. Any authenticated org member
    with an API key could change the state of any container in the
    organization — regardless of canAccessToDocker or
    accessedServices scope. Read-only endpoints and removeContainer
    were already correctly gated on docker:read.
  • Switch the four mutations to withPermission("docker", "read"),
    matching removeContainer, getConfig and getContainers. Member
    callers now need canAccessToDocker=true (legacy override in
    getLegacyOverrides) or an explicit docker:read grant via a
    custom role.

Severity

Privilege escalation inside an organization — a member could DoS
production containers (DBs, Traefik, app/compose services) and cycle
them in a loop. No audit log in OSS, so recovery is via host-level
docker logs only.

Reproduction (before fix)

With a member API key where canAccessToDocker=false and the target
container is outside accessedServices/accessedProjects:

curl -X POST -H "x-api-key: $KEY_MEMBER" \
  -H 'Content-Type: application/json' \
  -d "{\"containerId\":\"$CID\"}" \
  "$HOST/api/docker.killContainer"
# before: HTTP 200, container is killed
# after:  HTTP 401, body {"message":"unauthorized to access resource \"docker\"",...}

Greptile Summary

This PR fixes a privilege escalation vulnerability where four Docker mutation endpoints (restartContainer, startContainer, stopContainer, killContainer) were gated by withPermission("service", "read") instead of withPermission("docker", "read"). Since the memberRole grants service: ["read"] but docker: [], any authenticated org member could mutate container state without needing canAccessToDocker=true or a custom role granting docker access. The fix aligns all destructive docker endpoints with removeContainer, getConfig, and getContainers, which were already correctly gated on docker:read.

Confidence Score: 5/5

Safe to merge — minimal, targeted fix with no regressions.

The change is a four-line permission string swap, each line verified against the access-control statements (docker only defines "read", and memberRole grants docker: []). The fix is consistent with the pre-existing pattern used by removeContainer, getConfig, and getContainers. No logic, schema, or API surface is changed beyond the permission gate.

No files require special attention.

Reviews (1): Last reviewed commit: "fix: gate docker container lifecycle mut..." | Re-trigger Greptile

restartContainer, startContainer, stopContainer and killContainer
were guarded by withPermission("service", "read"), which the default
member role grants. Any authenticated org member with an API key
could start/stop/kill/restart any container in the organization,
regardless of canAccessToDocker or accessedServices scope.

Switch the four mutations to withPermission("docker", "read"),
matching removeContainer, getConfig and getContainers. Members now
need canAccessToDocker=true (legacy override) or an explicit
docker:read grant via a custom role to invoke them.
@iExad iExad requested a review from Siumauricio as a code owner April 30, 2026 09:58
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working labels Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant