Plugin Policy

drydock plugin policy is the trusted, drydock-specific contract for Argo CD config management plugin (CMP) compatibility beyond drydock’s built-in native adapters. It is not Argo CD repo-server sidecar discovery, and it does not make arbitrary discovered CMP commands trusted for execution.

Operators usually do not need a policy for Kustomize wrapper plugins. When drydock discovers a CMP command that safely normalizes to kustomize build, it uses the native Kustomize renderer automatically.

Use plugin policy for these cases:

  • Deterministic argocd-vault-plugin (AVP) placeholder redaction with engine: avp-compat.
  • Explicit native Kustomize overrides with engine: native-kustomize.
  • Trusted shellout compatibility with engine: exec and --enable-plugins.

Runtime Gate

The CLI and default Go client do not run plugin commands unless all of these are true:

  • The Application source names a plugin that matches a drydock plugin policy entry.
  • The matched policy entry uses engine: exec.
  • The caller passes --enable-plugins.
  • The exec policy came from trusted policy provenance.

No plugin command execution occurs unless --enable-plugins is passed. Native rendering paths do not execute plugin commands.

Discovered Argo CD CMP definitions that normalize to a safe kustomize build command are interpreted by drydock’s native Kustomize renderer by default. native-kustomize policy entries remain available as explicit overrides. For native argocd-vault-plugin (AVP) compatibility, avp-compat performs deterministic placeholder redaction with drydock native renderers.

When a discovered sidecar CMP has static discovery rules and an Application does not name a plugin, drydock may warn that Argo CD sidecar auto-discovery would be required. This check is intentionally bounded: drydock only evaluates discover.fileName and discover.find.glob against the local Application source directory. It never executes or emulates discover.find.command.

Trusted Provenance

The default local policy path is .drydock/plugins.yaml. Use --plugin-policy-path to select a different policy path relative to the selected policy root. Missing default policy is ignored; an explicitly selected policy path must exist. --disable-plugin-policy disables drydock policy loading only; it does not disable built-in native Kustomize interpretation for safe discovered CMP definitions.

Default local policy is trusted for native policy only. For single-tree commands such as build, test, and diag, a policy loaded from the current working tree may authorize avp-compat and native-kustomize. It is not trusted to execute engine: exec entries. Even with --enable-plugins, a matching exec entry from the current tree fails closed unless the caller also selects trusted policy provenance with --plugin-policy-ref.

Diff commands load default policy from the left/baseline side, such as --path-orig or the --ref-orig snapshot, and use that one policy for both sides of the diff. Exec policy from that baseline provenance may run only when --enable-plugins is passed. Policy command definitions and post-renderers are never sourced from the proposed/current side of a pull request.

--plugin-policy-ref means “load the policy from this explicit trusted Git ref or source.” When --plugin-policy-repo is set, drydock resolves the ref in that local Git repository; otherwise it uses the selected repository. The ref is a trust assertion by the operator or CI job, not an arbitrary escape hatch for untrusted working-tree policy. Policy paths remain relative to the policy root snapshot and may not escape it.

Schema

Policy files are strict single-document YAML. Unknown fields, duplicate mapping keys, YAML aliases, merge keys, custom tags, invalid scalar types, and multiple documents are rejected.

An editor JSON Schema is available at schemas/plugin-policy.schema.json . Use a YAML language-server comment rather than a top-level $schema field, because drydock rejects unknown policy fields:

# yaml-language-server: $schema=https://raw.githubusercontent.com/sholdee/drydock/main/schemas/plugin-policy.schema.json
apiVersion: drydock.sholdee.dev/v1alpha1
kind: PluginPolicy

The schema is an authoring aid. The Go parser remains the authoritative security boundary and may enforce checks that JSON Schema cannot fully express.

Top-level fields:

Field Required Default Notes
apiVersion Yes None Must be drydock.sholdee.dev/v1alpha1.
kind Yes None Must be PluginPolicy.
plugins No {} Mapping from Argo CD plugin name to policy entry.

Each plugins key is the spec.source.plugin.name value that drydock should match. Names are trimmed and must be non-empty and unique after trimming.

Plugin entry fields:

Field Required Default Notes
engine Yes None One of avp-compat, native-kustomize, or exec.

avp-compat and native-kustomize entries accept only engine.

exec entries support:

Field Required Default Notes
workdir No source Only source is supported. Commands run from a temporary copy of the source path.
init No None Optional command run before generate.
generate Yes None Command that writes Kubernetes manifests to stdout.
postRenderers No None Non-empty list when present. Chains stdout through stdin.
env.allow No [] Up to 64 environment variable names copied from the caller environment.
output.maxStdoutBytes No 10485760 Per-command stdout limit.
output.maxStderrBytes No 65536 Per-command stderr limit. Stderr is not printed in failure messages.

Command fields:

Field Required Default
command Yes None
timeout No 10s for init, 60s for generate, 30s for each post-renderer

timeout uses Go duration syntax such as 2s, 500ms, or 1m30s.

Exec Security Model

Exec policy is argv-only. command must be a YAML sequence of strings; shell strings such as ytt -f . are rejected. Empty argv tokens are rejected.

The command executable may be either:

  • A basename resolved on drydock’s controlled PATH: /usr/local/bin:/usr/bin:/bin.
  • An absolute path to an executable outside protected roots.

Relative executable paths such as ./render.sh are rejected. Shells and common interpreters are rejected as argv[0], including sh, bash, zsh, dash, ksh, fish, env, python, python3, node, ruby, perl, pwsh, and powershell. Use a trusted executable directly instead of a shell wrapper.

Exec commands run from a temporary copy of the resolved Application source path. Symlinks inside that source copy are rejected. The original source tree, selected repository roots, chart cache, Git cache, remote-resource cache, remote-resource forbidden roots, and the temporary workdir are protected. Exec binaries must not resolve inside protected roots, and command arguments must not point back into protected roots except for files inside the temporary workdir. Credential-bearing URLs in arguments are rejected.

The environment starts with only drydock’s controlled PATH. env.allow names additional caller environment variables that may be copied in. Names must be valid environment identifiers, cannot be duplicated, and cannot be reserved loader/interpreter variables such as PATH, LD_*, DYLD_*, PYTHONPATH, NODE_OPTIONS, or similar runtime injection names. Each copied value is capped at 16 KiB. Application-authored plugin env and parameters are rejected for policy-backed plugin sources.

Exec runs keep structured execution metadata per phase: phase name, sanitized executable basename, and elapsed duration. Metadata does not include plugin stdout, stderr, argv beyond the executable basename, environment values, or rendered manifests. If a basename executable is missing from drydock’s controlled PATH, install it there or configure an absolute trusted executable path outside protected roots.

Engines

avp-compat renders the source with drydock’s native renderer and replaces supported AVP placeholders with deterministic redacted values. It does not contact a secret backend and does not execute the AVP binary.

native-kustomize explicitly permits a named plugin to use drydock’s native Kustomize adapter. The same adapter also runs by default when drydock discovers a compatible Kustomize build CMP definition for that plugin from Argo CD settings such as Helm values or rendered argocd-cmp-cm ConfigMaps. The configured CMP command is not executed; drydock validates the command shape and uses its Go-native Kustomize renderer.

exec runs the policy-defined init, generate, and optional postRenderers commands under the gates and process controls above. It supports path-based plugin sources only; chart plugin sources fail closed.

Selective Native Engines

Additional native engines should be added only when they clearly improve speed, determinism, security, or setup burden compared with trusted engine: exec. CUE or Jsonnet are the most plausible next candidates if stable Go APIs and real repository demand line up. ytt and Tanka need separate design review because their import, environment, and convention surfaces are broader.

Native engines must remain narrow compatibility paths. drydock may interpret discovered CMP definitions only when they map to a known in-process renderer with a fail-closed validator. Discovered CMP definitions are never ambient permission to execute commands or emulate arbitrary plugin behavior.

Examples

Native argocd-vault-plugin (AVP) placeholder compatibility:

apiVersion: drydock.sholdee.dev/v1alpha1
kind: PluginPolicy
plugins:
  avp-directory-include:
    engine: avp-compat

Exec policy for a trusted non-native renderer with a post-renderer:

apiVersion: drydock.sholdee.dev/v1alpha1
kind: PluginPolicy
plugins:
  ytt-render:
    engine: exec
    generate:
      command: ["/usr/local/bin/ytt", "-f", "."]
      timeout: 45s
    postRenderers:
      - command: ["/usr/local/bin/kbld", "-f", "-"]
        timeout: 15s
    env:
      allow: ["CLUSTER_NAME", "ENVIRONMENT"]
    output:
      maxStdoutBytes: 10485760
      maxStderrBytes: 65536

The checked fixtures in testdata/plugin-policy/ are parsed and fingerprinted by unit tests so these examples do not silently drift.

For a single-tree command, run exec plugins from an explicit trusted ref:

drydock test apps --path . --plugin-policy-ref main --enable-plugins

For a pull request diff, the baseline policy is the trusted source:

drydock diff apps --path-orig ../baseline --path . --enable-plugins