Skip to content

authorize-docker-requests: PathPlain carries the API version, so every equality-keyed rule is dead on a live daemon #35

Description

@phoenix-server

The hardened policy matches paths with equality on input.PathPlain (R-17). That field carries the API version prefix in production, so every equality-keyed rule is dead on a live host. A legitimate project create is denied.

Evidence

The plugin builds the input from the raw request URL — main.go:257 of open-policy-agent/opa-docker-authz:

"PathPlain":  u.Path,
"PathArr":    strings.Split(u.Path, "/"),

Nothing strips a version prefix (-skip-ping only bypasses HEAD /_ping). The plugin's own decision log on a live host, taken while running this policy under the plugin image (v0.10, embedded OPA v1.3.0) on Docker 29.6.1:

"Path":"/v1.56/containers/json","PathPlain":"/v1.56/containers/json",
"PathArr":["","v1.56","containers","json"],"Method":"GET","User":""

What it does

The documented input shape in SCHEMATIC.md reads "PathPlain": "/containers/create" for "Path": "/v1.47/containers/create?name=...". The 71-row probe table in skeleton/agent.rego.schema was run with that version-less value. With the value a real plugin sends, the rules decide differently:

$ opa eval -f raw --data agent.rego --input documented-shape.json  data.docker.authz.allow
true
$ opa eval -f raw --data agent.rego --input plugin-real-shape.json data.docker.authz.allow
false

Both inputs are the same request — POST /v1.56/containers/create, the project label, an empty HostConfig — and differ only in PathPlain (/containers/create against /v1.56/containers/create).

On a live daemon the result is a denial of work the policy intends to allow: docker run --rm --label com.docker.compose.project=<project> alpine echo hello returns authorization denied by plugin opa-docker-authz, while the same request is allowed under the documented shape. Reads and lifecycle calls keep working, because the pre-existing rules match them by prefix, which is version-agnostic.

So the hardening closed the host-access holes but also removed the grants: the sandbox can no longer create a container, volume, or network for its own project. The failure direction is safe (deny), which is why no test caught it — a failing-closed sandbox looks secure.

What must change

  1. The rules must match a version-less path. Either derive it in the policy (regex.replace(input.PathPlain, "^/v[0-9]+\\.[0-9]+", ""), or PathArr with the first two elements dropped), or the plugin must be given one — it has no option for that today.
  2. The documented input shape in SCHEMATIC.md and skeleton/agent.rego.schema must be corrected: PathPlain is the raw request path, not the path without the version.
  3. The probe table must be re-run with the real shape. The version-less table cannot catch this class again.

Found while building this schematic on a snap-installed daemon (phoenix), by running the deployed policy under the plugin image rather than under opa eval alone.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions