Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operator grant policy

This page is for operators of function-wasm. It shows how to write the Cedar policy that --sandbox-policy-file loads, mount it into the function’s pod and check it offline with function validate. It covers the operator layer only: the schema and how each layer decides are in the Cedar policy reference.

The policy file is the only thing that enables a sandbox capability. A module’s manifest requests a capability and the Input’s compositionPolicy can narrow it, but neither can grant more than the operator’s policy does.

Evaluation is default-deny: a capability is refused unless a permit matches it, and a forbid overrides a permit. Without a --sandbox-policy-file, no capability is grantable and every module runs in the default sandbox, which has nothing but the request. A file that permits one capability still refuses every other one, so permit each capability you mean to allow.

Actions and principal

The capability actions are:

  • usePrivateTmp: a private /tmp.
  • setEnv: environment variables bound to step credentials.
  • spendCredential: the step credentials a module reads.
  • grantEgress: HTTP egress, which is also the host allowlist.

The operator layer also decides dialAddress, on the egress page, and requireSignature and trustSigner on signatures. composeResource, below, fences a module’s output rather than granting a capability.

The principal’s attributes, and the resources and context of each action, are in the Cedar policy reference.

Write the policy

This policy lets only team-a use a private /tmp, and lets any namespace reach *.example.com with GET:

permit (principal, action == Action::"usePrivateTmp", resource)
when { principal.namespace == "team-a" };

permit (principal, action == Action::"grantEgress", resource)
when { resource in HostPattern::"example.com" && context.method == "GET" };

Mount it and set the flag

Put the policy in a ConfigMap in the namespace the Function runs in (here crossplane-system):

kubectl create configmap function-wasm-policy -n crossplane-system \
  --from-file=policy.cedar

Then mount it through the Function’s DeploymentRuntimeConfig:

spec:
  deploymentTemplate:
    spec:
      template:
        spec:
          containers:
          - name: package-runtime
            args:
            - --sandbox-policy-file=/etc/function-wasm/policy.cedar
            volumeMounts:
            - {name: policy, mountPath: /etc/function-wasm, readOnly: true}
          volumes:
          - name: policy
            configMap: {name: function-wasm-policy}

The runtime compiles the policy at startup and doesn’t reread it. After changing the ConfigMap, restart the function’s pod.

Check it offline

function validate reports the same verdicts as the runtime, without a cluster:

function validate composition.yaml --resolve \
  --sandbox-policy-file policy.cedar --xr xr.yaml

The principal comes from --xr. Without it, the zero principal matches no per-tenant condition. The command and its other flags are in Check a composition.

Fence what modules compose

composeResource isn’t a capability. It’s a ceiling on what any module may compose, judged over the response after the run. It applies only where a rule scopes it. Otherwise every module’s output is applied unchanged.

A forbid alone scopes the action and permits nothing. A ceiling needs a permit beside it. This policy refuses RBAC resources and Secrets and permits everything else:

forbid (principal, action == Action::"composeResource", resource)
when { resource in Group::"rbac.authorization.k8s.io" || resource == Kind::"core/Secret" };

permit (principal, action == Action::"composeResource", resource);