From 2c8813cc49defad3ccdded955cccebf369ce60bc Mon Sep 17 00:00:00 2001 From: joshvanl Date: Thu, 9 Apr 2026 19:13:53 -0300 Subject: [PATCH 1/4] [1.18] WorkflowAccessPolicy Implementation based on https://github.com/dapr/dapr/pull/9790 Signed-off-by: joshvanl --- .../content/en/concepts/security-concept.md | 8 + .../workflow/workflow-multi-app.md | 42 +++ .../workflow/workflow-overview.md | 6 + .../security/workflow-access-policy.md | 315 ++++++++++++++++++ .../support/support-preview-features.md | 1 + .../workflow-access-policy-schema.md | 85 +++++ 6 files changed, 457 insertions(+) create mode 100644 daprdocs/content/en/operations/security/workflow-access-policy.md create mode 100644 daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md diff --git a/daprdocs/content/en/concepts/security-concept.md b/daprdocs/content/en/concepts/security-concept.md index 2008c38a039..cf85a4ed414 100644 --- a/daprdocs/content/en/concepts/security-concept.md +++ b/daprdocs/content/en/concepts/security-concept.md @@ -183,6 +183,14 @@ Dapr provides application-level scoping for components by allowing you to specif Dapr components can use Dapr's built-in secret management capability to manage secrets. Read the [secret store overview]({{% ref secrets-overview %}}) and [How-To: Reference secrets in components]({{% ref component-secrets %}}) for more details. +## Workflow access control + +Dapr supports fine-grained access control for workflow and activity scheduling through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can schedule specific workflows and activities on a target application. + +Workflow access policies are enforced on the callee side. The caller’s identity is determined from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, which means mTLS must be active for cross-app enforcement. If policies are defined but mTLS is not enabled, all workflow scheduling requests are denied. + +Policies support glob pattern matching for workflow and activity names and use specificity-based rule resolution, where the most specific matching rule wins. Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for configuration details and examples. + ## Bindings security Authentication with a binding target is configured by the binding’s configuration file. Generally, you should configure the minimum required access rights. For example, if you only read from a binding target, you should configure the binding to use an account with read-only access rights. diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md index 83cf373ad27..3064e42a1a3 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md @@ -243,6 +243,48 @@ public sealed class BusinessWorkflow : Workflow {{< /tabpane >}} +## Security: Workflow access policies + +When using multi-application workflows, you may want to restrict which applications can schedule activities or child workflows on a target application. Dapr provides the `WorkflowAccessPolicy` resource for this purpose. + +{{% alert title="Important" color="warning" %}} +When workflow access policies are active, the target application must include itself in the `callers` list for its own activities. This is because activities are executed internally via actor reminders, and the self-invocation is also subject to policy enforcement. +{{% /alert %}} + +The following example policy allows `orchestrator-app` to call activities on `ml-worker`, and also allows `ml-worker` to execute its own activities: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: ml-worker-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: orchestrator-app + operations: + - type: activity + name: "TrainModel" + action: allow + - type: activity + name: "ValidateModel" + action: allow + - callers: + - appID: ml-worker + operations: + - type: activity + name: "TrainModel" + action: allow + - type: activity + name: "ValidateModel" + action: allow + scopes: + - ml-worker +``` + +Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for full configuration details, policy evaluation rules, and additional examples. + ## Related links - [Try out Dapr Workflows using the quickstart]({{% ref workflow-quickstart.md %}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md index cce02d3316b..dd0c6863381 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md @@ -156,6 +156,12 @@ See [How-To: Manage workflows]({{< ref howto-manage-workflow.md >}}) for detaile - Azure Cosmos DB has [payload and workflow complexity limitations]({{% ref "setup-azure-cosmosdb.md#workflow-limitations" %}}). - AWS DynamoDB has [workflow complexity limitations]({{% ref "setup-azure-cosmosdb.md#workflow-limitations" %}}). +## Workflow security + +Dapr provides fine-grained access control for workflow and activity scheduling through the `WorkflowAccessPolicy` resource. You can restrict which applications are permitted to start specific workflows or call specific activities on your application. + +This is especially important for multi-application workflows, where activities and child workflows execute across application boundaries. Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for full configuration details. + ## Watch the demo Watch [this video for an overview on Dapr Workflow](https://youtu.be/s1p9MNl4VGo?t=131): diff --git a/daprdocs/content/en/operations/security/workflow-access-policy.md b/daprdocs/content/en/operations/security/workflow-access-policy.md new file mode 100644 index 00000000000..ad834ff35ad --- /dev/null +++ b/daprdocs/content/en/operations/security/workflow-access-policy.md @@ -0,0 +1,315 @@ +--- +type: docs +title: "How-To: Apply workflow access policies for workflow and activity scheduling" +linkTitle: "Workflow access policy" +weight: 4000 +description: "Restrict which applications can schedule workflows and activities on a target application" +--- + +Using workflow access policies, you can control which calling applications are permitted to schedule specific workflows and activities on a target application. A `WorkflowAccessPolicy` is a standalone Kubernetes CRD (or YAML file in self-hosted mode) that is scoped to one or more target applications and evaluated on the callee side. + +Workflow access policies use glob pattern matching for workflow and activity names and a specificity-based rule resolution system. The most specific matching rule wins, and deny takes precedence over allow at the same specificity level. + +{{% alert title="Preview feature" color="warning" %}} +Workflow access policies are a preview feature. You must enable the `WorkflowAccessPolicy` feature flag in your Dapr Configuration to use this feature. See [Enable the feature flag](#enable-the-feature-flag) below. +{{% /alert %}} + +[See example scenarios.](#example-scenarios) + +## Prerequisites + +- [Dapr installed with mTLS enabled]({{% ref mtls %}}) -- mTLS is required for cross-app enforcement because the caller's identity is extracted from the SPIFFE ID in the mTLS certificate. +- The `WorkflowAccessPolicy` feature flag enabled in your Dapr Configuration. + +## Terminology + +### Caller App ID + +The Dapr application identity (App ID) of the application that is requesting to schedule a workflow or activity on the target application. The caller identity is extracted from the mTLS connection. + +### SPIFFE Identity + +Dapr uses [SPIFFE](https://spiffe.io/) identities embedded in mTLS certificates to identify callers. The SPIFFE ID has the format `spiffe:///ns//`. The App ID is extracted from this identity when evaluating workflow access policies. + +### Glob Pattern + +Workflow and activity names in policy rules support glob pattern matching: +- `*` matches any sequence of characters +- `?` matches any single character +- `[abc]` matches any character in the set + +### Specificity + +When multiple rules match a given workflow or activity name, the most specific rule wins. Specificity is determined by the longest literal (non-glob) prefix. An exact match is always more specific than a glob pattern. If two rules have the same specificity, `deny` takes precedence over `allow`. + +## CRD specification + +Below is a complete example of a `WorkflowAccessPolicy` resource: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: order-processing-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: app1 + - appID: app2 + operations: + - type: workflow + name: "OrderWorkflow" + action: allow + - type: activity + name: "ProcessPayment" + action: allow + - callers: + - appID: app3 + operations: + - type: workflow + name: "Report*" + action: allow + scopes: + - order-app +``` + +### Spec fields + +| Field | Required | Type | Description | +|-------|:--------:|------|-------------| +| `defaultAction` | N | string | Global default action when no rule matches. Accepted values: `allow` or `deny`. Defaults to `deny`. | +| `rules` | N | list | List of rules that define which callers can perform which operations. | +| `rules[].callers` | Y | list | List of caller objects that this rule applies to. | +| `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | +| `rules[].operations` | Y | list | List of operations (workflows or activities) that this rule controls. | +| `rules[].operations[].type` | Y | string | The type of operation: `workflow` or `activity`. | +| `rules[].operations[].name` | Y | string | The name of the workflow or activity. Supports glob patterns (`*`, `?`, `[abc]`). | +| `rules[].operations[].action` | Y | string | The access action: `allow` or `deny`. | +| `scopes` | N | list | List of App IDs to which this policy applies. If empty, the policy applies to all applications. | + +## Policy rules + +The following rules describe how workflow access policies are evaluated: + +1. **No policies loaded:** If no workflow access policies are loaded for an application, all workflow and activity scheduling requests are allowed. This preserves backward compatibility. +2. **mTLS not active (remote calls):** If policies exist for the target application but mTLS is not active, remote cross-app calls are denied because the caller's SPIFFE identity cannot be verified. Local calls (same-sidecar) are enforced using the app ID directly and do not require mTLS. +3. **Scheduling operations (start workflow, call activity):** The caller's App ID is matched against the `callers` list, and the requested operation type and name are matched against the `operations` list. The matching rule's `action` is applied. +4. **Most specific rule wins:** If multiple rules match, the rule with the most specific name pattern wins (longest literal prefix). An exact name match beats a glob pattern. At the same specificity level, `deny` beats `allow`. +5. **Management operations (terminate, purge, raise event):** For these operations, the system checks whether the caller is known to any rule in the policy. If the caller appears in at least one rule, the management operation is permitted. +6. **Cross-namespace calls denied:** When workflow access policies are active, cross-namespace calls are denied. + +## Enforcement paths + +Workflow access policies are enforced at two levels, covering all paths into the workflow engine: +- **Remote calls (callee-side):** The target sidecar's internal gRPC handler (`CallActor`, `CallActorStream`, `CallActorReminder`) validates the caller's SPIFFE identity from the mTLS connection. +- **Local calls (same-sidecar):** The actor router enforces policies using the app's own ID before dispatching to the local workflow/activity actor. + +Both the HTTP and gRPC public APIs (e.g., `StartWorkflow`) are covered because they dispatch through the actor system, which hits one of these enforcement points. + +## Example scenarios + +### Scenario 1: Allow specific callers for specific workflows + +Allow `frontend-app` to start the `OrderWorkflow` and `CheckoutWorkflow` on the `order-service` application, while denying all other callers. + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: order-service-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: frontend-app + operations: + - type: workflow + name: "OrderWorkflow" + action: allow + - type: workflow + name: "CheckoutWorkflow" + action: allow + scopes: + - order-service +``` + +### Scenario 2: Deny-all with exceptions (recommended for production) + +Start with a `deny` default and explicitly allow only the callers and operations that are needed. This is the recommended approach for production deployments. + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: production-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: api-gateway + - appID: scheduler-service + operations: + - type: workflow + name: "ProcessOrder" + action: allow + - type: workflow + name: "RefundOrder" + action: allow + - type: activity + name: "SendNotification" + action: allow + - callers: + - appID: admin-service + operations: + - type: workflow + name: "*" + action: allow + scopes: + - order-service +``` + +### Scenario 3: Glob patterns for workflow families + +Use glob patterns to allow access to a family of related workflows without listing each one individually. + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: reporting-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: analytics-app + operations: + - type: workflow + name: "Report*" + action: allow + - type: activity + name: "Generate*Report" + action: allow + - callers: + - appID: analytics-app + operations: + - type: workflow + name: "ReportFinancialAudit" + action: deny + scopes: + - reporting-service +``` + +In this example, `analytics-app` can start any workflow beginning with `Report` except `ReportFinancialAudit`, which is explicitly denied. Because the exact name `ReportFinancialAudit` is more specific than the glob `Report*`, the deny rule wins. + +### Scenario 4: Cross-app activity calls with self-invocation + +When using multi-application workflows, the target application must include itself in the callers list so that it can execute activities internally via reminders. This is a common requirement for any app that hosts activities called from other apps. + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: ml-training-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: orchestrator-app + operations: + - type: activity + name: "TrainModel" + action: allow + - type: activity + name: "ValidateModel" + action: allow + - callers: + - appID: ml-worker + operations: + - type: activity + name: "TrainModel" + action: allow + - type: activity + name: "ValidateModel" + action: allow + scopes: + - ml-worker +``` + +{{% alert title="Important" color="warning" %}} +Activities are executed internally via actor reminders. When a remote app schedules an activity on the target, the target app itself must be listed in the `callers` for those activities. If the target app is not included, the internal reminder-based execution of the activity will be denied. +{{% /alert %}} + +## Production best practices + +- **Always use `defaultAction: deny`.** This ensures that only explicitly allowed callers and operations are permitted, following the principle of least privilege. +- **Include the target app itself in callers for activity execution.** Activities are dispatched internally via actor reminders. The target app must be an allowed caller for its own activities. +- **Use glob patterns conservatively.** Overly broad patterns like `*` can inadvertently allow access to workflows or activities that should be restricted. Prefer exact names when possible. +- **Enable mTLS.** mTLS is required for cross-app enforcement. Without mTLS, all requests are denied when policies are active. +- **Monitor warning logs for policy violations.** Dapr logs a warning when a request is denied by a workflow access policy. Use these logs to audit access and detect misconfiguration. +- **Use `scopes` to limit which apps load the policy.** Apply policies only to the applications that need them, reducing unnecessary policy evaluation overhead. + +## Self-hosted setup + +In self-hosted mode, place the workflow access policy YAML file in the components directory (default: `$HOME/.dapr/components` or the path specified with `--resources-path`). + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: my-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: app1 + operations: + - type: workflow + name: "MyWorkflow" + action: allow + scopes: + - my-app +``` + +Ensure that mTLS is enabled by running the Sentry service locally. See [Setup & configure mTLS certificates]({{% ref mtls %}}) for details on configuring mTLS in self-hosted mode. + +## Kubernetes setup + +In Kubernetes, apply the `WorkflowAccessPolicy` CRD to your cluster using `kubectl`: + +```bash +kubectl apply -f workflow-access-policy.yaml +``` + +The Dapr operator watches for `WorkflowAccessPolicy` resources and distributes them to the appropriate sidecars based on the `scopes` field. mTLS is enabled by default in Kubernetes mode. + +## Enable the feature flag + +The `WorkflowAccessPolicy` feature must be enabled in your Dapr Configuration: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: myappconfig +spec: + features: + - name: WorkflowAccessPolicy + enabled: true +``` + +Apply this configuration to each application that needs workflow access policy enforcement. + +## Hot-reload support + +Workflow access policies support hot-reloading in both Kubernetes and self-hosted modes. When a policy is created, updated, or deleted, the changes take effect without restarting the Dapr sidecar. This allows you to adjust policies in real time without application downtime. + +## Related links + +- [Security concepts]({{% ref security-concept.md %}}) +- [Multi-application workflows]({{% ref workflow-multi-app.md %}}) +- [Workflow overview]({{% ref workflow-overview.md %}}) +- [Service invocation access control]({{% ref invoke-allowlist.md %}}) +- [Setup & configure mTLS certificates]({{% ref mtls %}}) +- [WorkflowAccessPolicy CRD reference]({{% ref workflow-access-policy-schema.md %}}) +- [Preview features]({{% ref support-preview-features.md %}}) diff --git a/daprdocs/content/en/operations/support/support-preview-features.md b/daprdocs/content/en/operations/support/support-preview-features.md index 2c0f29df8f6..8d583caeb41 100644 --- a/daprdocs/content/en/operations/support/support-preview-features.md +++ b/daprdocs/content/en/operations/support/support-preview-features.md @@ -23,3 +23,4 @@ For CLI there is no explicit opt-in, just the version that this was first made a | **Subscription Hot Reloading** | Allows for declarative subscriptions to be "hot reloaded". A subscription is reloaded either when it is created/updated/deleted in Kubernetes, or on file in self-hosted mode. In-flight messages are unaffected when reloading. | `HotReload`| [Hot Reloading]({{% ref "subscription-methods.md#declarative-subscriptions" %}}) | v1.14 | | **Workflows Clustered Deployment** | Enable Workflows to function when workflow clients communicate to multiple daprds of the same appID who are behind a loadbalancer. Only relevant when using [Dapr shared]({{% ref "kubernetes-dapr-shared" %}}) | `WorkflowsClusteredDeployment`| [Dapr Shared]({{% ref "kubernetes-dapr-shared" %}}) | v1.16 | | **Workflows Durable Activity Results** | If set, ensures that activity results are durably sent to the owning workflow in multi-application scenarios, even when the owning workflow application is unavailable. Unless running multiple Dapr versions, this feature gate should be enabled. Disabled by default for backwards compatibility. | `WorkflowsRemoteActivityReminder` | [Multi-application Workflows]({{% ref "workflow-multi-app.md#durable-activity-results" %}}) | v1.17 | +| **Workflow Access Policy** | Enables fine-grained access control for workflow and activity scheduling. When enabled, `WorkflowAccessPolicy` resources are loaded and enforced, controlling which applications can schedule specific workflows and activities on a target application. Requires mTLS for cross-app enforcement. | `WorkflowAccessPolicy` | [Workflow Access Policy]({{% ref "workflow-access-policy.md" %}}) | v1.17 | diff --git a/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md new file mode 100644 index 00000000000..86e5c41e195 --- /dev/null +++ b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md @@ -0,0 +1,85 @@ +--- +type: docs +title: "WorkflowAccessPolicy spec" +linkTitle: "WorkflowAccessPolicy" +description: "The basic spec for a Dapr WorkflowAccessPolicy resource" +weight: 6000 +--- + +The `WorkflowAccessPolicy` is a Dapr resource that controls which applications are permitted to schedule specific workflows and activities on a target application. + +## Format + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: +spec: + defaultAction: + rules: + - callers: + - appID: + operations: + - type: + name: + action: + scopes: + - +``` + +## Spec fields + +| Field | Required | Type | Description | Example | +|-------|:--------:|------|-------------|---------| +| `defaultAction` | N | string | Global default action when no rule matches. Accepted values: `allow` or `deny`. Defaults to `deny`. | `deny` | +| `rules` | N | list | List of access rules. Each rule maps callers to permitted or denied operations. | See below | +| `rules[].callers` | Y | list | List of caller objects that this rule applies to. | See below | +| `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | `frontend-app` | +| `rules[].operations` | Y | list | List of operations controlled by this rule. | See below | +| `rules[].operations[].type` | Y | string | The type of operation. Accepted values: `workflow` or `activity`. | `workflow` | +| `rules[].operations[].name` | Y | string | The name of the workflow or activity. Supports glob patterns: `*` (any sequence), `?` (single character), `[abc]` (character set). | `OrderWorkflow`, `Report*` | +| `rules[].operations[].action` | Y | string | The access action for this operation. Accepted values: `allow` or `deny`. | `allow` | +| `scopes` | N | list | List of target App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | `["order-service"]` | + +## Example + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: WorkflowAccessPolicy +metadata: + name: order-processing-policy +spec: + defaultAction: deny + rules: + - callers: + - appID: frontend-app + - appID: api-gateway + operations: + - type: workflow + name: "OrderWorkflow" + action: allow + - type: workflow + name: "CheckoutWorkflow" + action: allow + - type: activity + name: "ProcessPayment" + action: allow + - callers: + - appID: admin-app + operations: + - type: workflow + name: "*" + action: allow + - type: activity + name: "*" + action: allow + scopes: + - order-service +``` + +## Related links + +- [Learn more about how to configure workflow access policies]({{% ref workflow-access-policy.md %}}) +- [Security concepts]({{% ref security-concept.md %}}) +- [Multi-application workflows]({{% ref workflow-multi-app.md %}}) From b174a97910d263500285e8f555d7229a82fbc3e0 Mon Sep 17 00:00:00 2001 From: joshvanl Date: Mon, 13 Apr 2026 18:47:38 -0300 Subject: [PATCH 2/4] Clarify cross app without mTLS Signed-off-by: joshvanl --- daprdocs/content/en/concepts/security-concept.md | 2 +- .../content/en/operations/security/workflow-access-policy.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daprdocs/content/en/concepts/security-concept.md b/daprdocs/content/en/concepts/security-concept.md index cf85a4ed414..0f8081fb07a 100644 --- a/daprdocs/content/en/concepts/security-concept.md +++ b/daprdocs/content/en/concepts/security-concept.md @@ -187,7 +187,7 @@ Dapr components can use Dapr's built-in secret management capability to manage s Dapr supports fine-grained access control for workflow and activity scheduling through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can schedule specific workflows and activities on a target application. -Workflow access policies are enforced on the callee side. The caller’s identity is determined from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, which means mTLS must be active for cross-app enforcement. If policies are defined but mTLS is not enabled, all workflow scheduling requests are denied. +Workflow access policies are enforced on the callee side. For cross-app requests, the caller’s identity is determined from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, which means mTLS must be active for cross-app enforcement. If policies are defined but mTLS is not enabled, remote cross-app workflow scheduling requests are denied, while local calls through the same sidecar can still be evaluated using the local app ID. Policies support glob pattern matching for workflow and activity names and use specificity-based rule resolution, where the most specific matching rule wins. Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for configuration details and examples. diff --git a/daprdocs/content/en/operations/security/workflow-access-policy.md b/daprdocs/content/en/operations/security/workflow-access-policy.md index ad834ff35ad..2cbdd1ebb80 100644 --- a/daprdocs/content/en/operations/security/workflow-access-policy.md +++ b/daprdocs/content/en/operations/security/workflow-access-policy.md @@ -2,11 +2,11 @@ type: docs title: "How-To: Apply workflow access policies for workflow and activity scheduling" linkTitle: "Workflow access policy" -weight: 4000 +weight: 5000 description: "Restrict which applications can schedule workflows and activities on a target application" --- -Using workflow access policies, you can control which calling applications are permitted to schedule specific workflows and activities on a target application. A `WorkflowAccessPolicy` is a standalone Kubernetes CRD (or YAML file in self-hosted mode) that is scoped to one or more target applications and evaluated on the callee side. +Using workflow access policies, you can control which calling applications are permitted to schedule specific workflows and activities on a target application. A `WorkflowAccessPolicy` is a standalone Kubernetes CRD (or YAML file in self-hosted mode) that is evaluated on the callee side. You can scope it to one or more target applications with `scopes`, or omit `scopes` to apply the policy to all applications. Workflow access policies use glob pattern matching for workflow and activity names and a specificity-based rule resolution system. The most specific matching rule wins, and deny takes precedence over allow at the same specificity level. From c3200ab1b95a541c2d26f4cfc7125a8a7e3592d5 Mon Sep 17 00:00:00 2001 From: joshvanl Date: Tue, 19 May 2026 12:06:24 +0000 Subject: [PATCH 3/4] WorkflowAccessPolicy: rewrite for v1.18 allow-list shape Reflect the policy reshape from dapr/dapr#9850, #9870, and #9838: - Schema now uses separate workflows/activities blocks; workflows have an operations list (schedule, terminate, raise, pause, resume, purge, get, rerun). No defaultAction, no per-rule action; rules are a pure allow-list. - Self-calls (caller appID == target appID) are always allowed. The multi-app example no longer needs the target to list itself in callers. - Remove the WorkflowAccessPolicy feature flag entry from the preview features page and drop the "enable the feature flag" instructions; the gate was deleted upstream. - Update the security concept page and CRD reference doc to match. Signed-off-by: joshvanl --- .../content/en/concepts/security-concept.md | 6 +- .../workflow/workflow-multi-app.md | 32 +- .../security/workflow-access-policy.md | 299 +++++++----------- .../support/support-preview-features.md | 1 - .../workflow-access-policy-schema.md | 69 ++-- 5 files changed, 163 insertions(+), 244 deletions(-) diff --git a/daprdocs/content/en/concepts/security-concept.md b/daprdocs/content/en/concepts/security-concept.md index 0f8081fb07a..2b923e25e1d 100644 --- a/daprdocs/content/en/concepts/security-concept.md +++ b/daprdocs/content/en/concepts/security-concept.md @@ -185,11 +185,11 @@ Dapr components can use Dapr's built-in secret management capability to manage s ## Workflow access control -Dapr supports fine-grained access control for workflow and activity scheduling through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can schedule specific workflows and activities on a target application. +Dapr supports fine-grained access control for workflow and activity operations through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can invoke specific workflow operations (such as `schedule`, `terminate`, `get`) and which activities can be scheduled on a target application. -Workflow access policies are enforced on the callee side. For cross-app requests, the caller’s identity is determined from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, which means mTLS must be active for cross-app enforcement. If policies are defined but mTLS is not enabled, remote cross-app workflow scheduling requests are denied, while local calls through the same sidecar can still be evaluated using the local app ID. +Workflow access policies are enforced on the callee side as a pure allow-list. For cross-app requests, the caller's identity is taken from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, so mTLS must be active for cross-app enforcement. Self-calls (where the caller and target are the same app) are always permitted. Policies support glob pattern matching for workflow and activity names. -Policies support glob pattern matching for workflow and activity names and use specificity-based rule resolution, where the most specific matching rule wins. Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for configuration details and examples. +Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for configuration details and examples. ## Bindings security diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md index 3064e42a1a3..a6fdaee3c9f 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md @@ -247,43 +247,25 @@ public sealed class BusinessWorkflow : Workflow When using multi-application workflows, you may want to restrict which applications can schedule activities or child workflows on a target application. Dapr provides the `WorkflowAccessPolicy` resource for this purpose. -{{% alert title="Important" color="warning" %}} -When workflow access policies are active, the target application must include itself in the `callers` list for its own activities. This is because activities are executed internally via actor reminders, and the self-invocation is also subject to policy enforcement. -{{% /alert %}} - -The following example policy allows `orchestrator-app` to call activities on `ml-worker`, and also allows `ml-worker` to execute its own activities: +Policies are a pure allow-list and self-calls are always permitted, so the target application does not need to list itself in the `callers` to execute its own activities. The following example allows `orchestrator-app` to schedule the `TrainModel` and `ValidateModel` activities on `ml-worker`: ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: ml-worker-policy +scopes: + - ml-worker spec: - defaultAction: deny rules: - callers: - appID: orchestrator-app - operations: - - type: activity - name: "TrainModel" - action: allow - - type: activity - name: "ValidateModel" - action: allow - - callers: - - appID: ml-worker - operations: - - type: activity - name: "TrainModel" - action: allow - - type: activity - name: "ValidateModel" - action: allow - scopes: - - ml-worker + activities: + - name: TrainModel + - name: ValidateModel ``` -Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for full configuration details, policy evaluation rules, and additional examples. +Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for the full operation set (`schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`), more examples, and details on the cross-app enforcement model. ## Related links diff --git a/daprdocs/content/en/operations/security/workflow-access-policy.md b/daprdocs/content/en/operations/security/workflow-access-policy.md index 2cbdd1ebb80..7029221d434 100644 --- a/daprdocs/content/en/operations/security/workflow-access-policy.md +++ b/daprdocs/content/en/operations/security/workflow-access-policy.md @@ -1,281 +1,240 @@ --- type: docs -title: "How-To: Apply workflow access policies for workflow and activity scheduling" +title: "How-To: Apply workflow access policies" linkTitle: "Workflow access policy" weight: 5000 -description: "Restrict which applications can schedule workflows and activities on a target application" +description: "Restrict which applications can invoke workflow and activity operations on a target application" --- -Using workflow access policies, you can control which calling applications are permitted to schedule specific workflows and activities on a target application. A `WorkflowAccessPolicy` is a standalone Kubernetes CRD (or YAML file in self-hosted mode) that is evaluated on the callee side. You can scope it to one or more target applications with `scopes`, or omit `scopes` to apply the policy to all applications. +Using workflow access policies, you can control which calling applications are permitted to invoke specific workflow operations on a target application. A `WorkflowAccessPolicy` is a Kubernetes CRD (or YAML resource in self-hosted mode) that is evaluated on the callee side. You can scope it to one or more target applications with `scopes`, or omit `scopes` to apply the policy to all applications. -Workflow access policies use glob pattern matching for workflow and activity names and a specificity-based rule resolution system. The most specific matching rule wins, and deny takes precedence over allow at the same specificity level. - -{{% alert title="Preview feature" color="warning" %}} -Workflow access policies are a preview feature. You must enable the `WorkflowAccessPolicy` feature flag in your Dapr Configuration to use this feature. See [Enable the feature flag](#enable-the-feature-flag) below. -{{% /alert %}} - -[See example scenarios.](#example-scenarios) +Workflow access policies are a pure allow-list. A request is permitted if, and only if, some rule in some loaded policy matches the caller, the operation, and the workflow or activity name. With no policies loaded, all calls are allowed (open by default), preserving backward compatibility. Self-calls (where the caller App ID is the same as the target App ID) are always allowed, regardless of policy contents. ## Prerequisites -- [Dapr installed with mTLS enabled]({{% ref mtls %}}) -- mTLS is required for cross-app enforcement because the caller's identity is extracted from the SPIFFE ID in the mTLS certificate. -- The `WorkflowAccessPolicy` feature flag enabled in your Dapr Configuration. +- [Dapr installed with mTLS enabled]({{% ref mtls %}}). mTLS is required for cross-app enforcement because the caller's identity is extracted from the SPIFFE ID embedded in the mTLS client certificate. ## Terminology ### Caller App ID -The Dapr application identity (App ID) of the application that is requesting to schedule a workflow or activity on the target application. The caller identity is extracted from the mTLS connection. +The Dapr application identity (App ID) of the application making the request. For cross-app calls the caller identity is taken from the SPIFFE ID in the mTLS certificate. For same-sidecar (self) calls the local App ID is used directly. -### SPIFFE Identity +### SPIFFE identity -Dapr uses [SPIFFE](https://spiffe.io/) identities embedded in mTLS certificates to identify callers. The SPIFFE ID has the format `spiffe:///ns//`. The App ID is extracted from this identity when evaluating workflow access policies. +Dapr uses [SPIFFE](https://spiffe.io/) identities embedded in mTLS certificates to identify callers. The SPIFFE ID has the format `spiffe:///ns//`. The App ID is extracted from this identity when a workflow access policy is evaluated. -### Glob Pattern +### Glob pattern Workflow and activity names in policy rules support glob pattern matching: - `*` matches any sequence of characters - `?` matches any single character - `[abc]` matches any character in the set -### Specificity +### Operations -When multiple rules match a given workflow or activity name, the most specific rule wins. Specificity is determined by the longest literal (non-glob) prefix. An exact match is always more specific than a glob pattern. If two rules have the same specificity, `deny` takes precedence over `allow`. +A workflow rule grants the listed callers access to one or more of these operations: -## CRD specification +| Operation | Triggered by | +| --- | --- | +| `schedule` | `StartWorkflow` / `CreateWorkflowInstance` | +| `terminate` | `TerminateWorkflow` | +| `raise` | `RaiseEventWorkflow` | +| `pause` | `PauseWorkflow` | +| `resume` | `ResumeWorkflow` | +| `purge` | `PurgeWorkflow` | +| `get` | `GetWorkflow` / `WaitForRuntimeStatus` | +| `rerun` | `RerunWorkflowFromEvent` | -Below is a complete example of a `WorkflowAccessPolicy` resource: +Activities only support the `schedule` operation, so an activity rule has no `operations` field. + +## CRD specification ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: - name: order-processing-policy + name: orders-policy + namespace: production +scopes: + - orders-target spec: - defaultAction: deny rules: - callers: - - appID: app1 - - appID: app2 - operations: - - type: workflow - name: "OrderWorkflow" - action: allow - - type: activity - name: "ProcessPayment" - action: allow - - callers: - - appID: app3 - operations: - - type: workflow - name: "Report*" - action: allow - scopes: - - order-app + - appID: frontend + - appID: ops-console + workflows: + - name: OrderWF + operations: + - schedule + - terminate + - raise + - pause + - resume + - purge + - get + - rerun + - name: "Report*" + operations: [get] + activities: + - name: ChargePayment + - name: "RefundEvent*" ``` ### Spec fields | Field | Required | Type | Description | |-------|:--------:|------|-------------| -| `defaultAction` | N | string | Global default action when no rule matches. Accepted values: `allow` or `deny`. Defaults to `deny`. | -| `rules` | N | list | List of rules that define which callers can perform which operations. | -| `rules[].callers` | Y | list | List of caller objects that this rule applies to. | +| `rules` | N | list | Allow-list of rules. A call is permitted if any rule matches. With no rules and policies loaded, all cross-app calls are denied. | +| `rules[].callers` | Y | list | List of caller objects this rule applies to. Must contain at least one entry. | | `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | -| `rules[].operations` | Y | list | List of operations (workflows or activities) that this rule controls. | -| `rules[].operations[].type` | Y | string | The type of operation: `workflow` or `activity`. | -| `rules[].operations[].name` | Y | string | The name of the workflow or activity. Supports glob patterns (`*`, `?`, `[abc]`). | -| `rules[].operations[].action` | Y | string | The access action: `allow` or `deny`. | -| `scopes` | N | list | List of App IDs to which this policy applies. If empty, the policy applies to all applications. | +| `rules[].workflows` | N* | list | Workflow rules granted to the matched callers. | +| `rules[].workflows[].name` | Y | string | Exact name or glob pattern of the workflow. | +| `rules[].workflows[].operations` | Y | list | One or more of `schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`. | +| `rules[].activities` | N* | list | Activity rules granted to the matched callers. | +| `rules[].activities[].name` | Y | string | Exact name or glob pattern of the activity. Activities only support the `schedule` operation, so there is no `operations` field. | +| `scopes` | N | list | App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | -## Policy rules +\* At least one of `workflows` or `activities` must be present in each rule. -The following rules describe how workflow access policies are evaluated: +## Policy semantics -1. **No policies loaded:** If no workflow access policies are loaded for an application, all workflow and activity scheduling requests are allowed. This preserves backward compatibility. -2. **mTLS not active (remote calls):** If policies exist for the target application but mTLS is not active, remote cross-app calls are denied because the caller's SPIFFE identity cannot be verified. Local calls (same-sidecar) are enforced using the app ID directly and do not require mTLS. -3. **Scheduling operations (start workflow, call activity):** The caller's App ID is matched against the `callers` list, and the requested operation type and name are matched against the `operations` list. The matching rule's `action` is applied. -4. **Most specific rule wins:** If multiple rules match, the rule with the most specific name pattern wins (longest literal prefix). An exact name match beats a glob pattern. At the same specificity level, `deny` beats `allow`. -5. **Management operations (terminate, purge, raise event):** For these operations, the system checks whether the caller is known to any rule in the policy. If the caller appears in at least one rule, the management operation is permitted. -6. **Cross-namespace calls denied:** When workflow access policies are active, cross-namespace calls are denied. +1. **No policies loaded:** All workflow and activity requests are allowed. This preserves backward compatibility when no policies exist. +2. **One or more policies loaded:** The target defaults to deny. A request is permitted only if some rule matches the caller, the operation, and the workflow or activity name. +3. **Self-calls are always allowed:** If the caller App ID is the same as the target App ID, the request is permitted regardless of policy contents. This means a target app does not need to list itself in its own policy to call its own workflows or activities (including the internal reminder-based execution path). +4. **Cross-namespace calls are denied** when policies are active. +5. **mTLS is required for cross-app enforcement:** if any policy is loaded and mTLS is not active, cross-app calls are denied because the caller's SPIFFE identity cannot be verified. +6. **Glob matching:** `*`, `?`, and character classes work on both workflow and activity names. ## Enforcement paths -Workflow access policies are enforced at two levels, covering all paths into the workflow engine: -- **Remote calls (callee-side):** The target sidecar's internal gRPC handler (`CallActor`, `CallActorStream`, `CallActorReminder`) validates the caller's SPIFFE identity from the mTLS connection. -- **Local calls (same-sidecar):** The actor router enforces policies using the app's own ID before dispatching to the local workflow/activity actor. +Workflow access policies are enforced inside the orchestrator and activity actors, under the actor lock, after the workflow's internal state has been loaded. This eliminates any time-of-check-to-time-of-use race between resolving a workflow's name and dispatching the operation. -Both the HTTP and gRPC public APIs (e.g., `StartWorkflow`) are covered because they dispatch through the actor system, which hits one of these enforcement points. +The gRPC and HTTP public APIs (`StartWorkflow`, `TerminateWorkflow`, `RaiseEventWorkflow`, `PauseWorkflow`, `ResumeWorkflow`, `PurgeWorkflow`, `GetWorkflow`, `RerunWorkflowFromEvent`) all flow through this enforcement point, so coverage is the same regardless of which protocol the caller uses. Cross-app callers attempting non-subject actor methods, or attempting to inject reminders, are also denied. ## Example scenarios -### Scenario 1: Allow specific callers for specific workflows +### Scenario 1: Allow a frontend to drive a specific workflow -Allow `frontend-app` to start the `OrderWorkflow` and `CheckoutWorkflow` on the `order-service` application, while denying all other callers. +Allow `frontend-app` to schedule and observe `OrderWF` on the `order-service` application. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: order-service-policy +scopes: + - order-service spec: - defaultAction: deny rules: - callers: - appID: frontend-app - operations: - - type: workflow - name: "OrderWorkflow" - action: allow - - type: workflow - name: "CheckoutWorkflow" - action: allow - scopes: - - order-service + workflows: + - name: OrderWF + operations: + - schedule + - get + - terminate ``` -### Scenario 2: Deny-all with exceptions (recommended for production) +### Scenario 2: Read-only access for a reporting tool -Start with a `deny` default and explicitly allow only the callers and operations that are needed. This is the recommended approach for production deployments. +Grant a reporting application read-only access to any workflow whose name begins with `Report`. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: - name: production-policy + name: reporting-readonly +scopes: + - reporting-service spec: - defaultAction: deny rules: - callers: - - appID: api-gateway - - appID: scheduler-service - operations: - - type: workflow - name: "ProcessOrder" - action: allow - - type: workflow - name: "RefundOrder" - action: allow - - type: activity - name: "SendNotification" - action: allow - - callers: - - appID: admin-service - operations: - - type: workflow - name: "*" - action: allow - scopes: - - order-service + - appID: analytics-app + workflows: + - name: "Report*" + operations: [get] ``` -### Scenario 3: Glob patterns for workflow families +### Scenario 3: Cross-app activities (multi-application workflows) -Use glob patterns to allow access to a family of related workflows without listing each one individually. +When using multi-application workflows, the target application no longer needs to list itself in the `callers` to execute its own activities. Self-calls are always allowed, so the policy only describes which other apps may schedule activities on the target. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: - name: reporting-policy + name: ml-worker-policy +scopes: + - ml-worker spec: - defaultAction: deny rules: - callers: - - appID: analytics-app - operations: - - type: workflow - name: "Report*" - action: allow - - type: activity - name: "Generate*Report" - action: allow - - callers: - - appID: analytics-app - operations: - - type: workflow - name: "ReportFinancialAudit" - action: deny - scopes: - - reporting-service + - appID: orchestrator-app + activities: + - name: TrainModel + - name: ValidateModel ``` -In this example, `analytics-app` can start any workflow beginning with `Report` except `ReportFinancialAudit`, which is explicitly denied. Because the exact name `ReportFinancialAudit` is more specific than the glob `Report*`, the deny rule wins. +`ml-worker` can still schedule `TrainModel` and `ValidateModel` on itself without appearing in the rule because it is the local app. -### Scenario 4: Cross-app activity calls with self-invocation +### Scenario 4: Mixed workflow and activity access for a single caller -When using multi-application workflows, the target application must include itself in the callers list so that it can execute activities internally via reminders. This is a common requirement for any app that hosts activities called from other apps. +A single rule can grant a caller both workflow and activity access. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: - name: ml-training-policy + name: payments-policy +scopes: + - payments-service spec: - defaultAction: deny rules: - callers: - - appID: orchestrator-app - operations: - - type: activity - name: "TrainModel" - action: allow - - type: activity - name: "ValidateModel" - action: allow - - callers: - - appID: ml-worker - operations: - - type: activity - name: "TrainModel" - action: allow - - type: activity - name: "ValidateModel" - action: allow - scopes: - - ml-worker + - appID: api-gateway + workflows: + - name: ChargeCustomer + operations: [schedule, get] + activities: + - name: ChargePayment + - name: "Refund*" ``` -{{% alert title="Important" color="warning" %}} -Activities are executed internally via actor reminders. When a remote app schedules an activity on the target, the target app itself must be listed in the `callers` for those activities. If the target app is not included, the internal reminder-based execution of the activity will be denied. -{{% /alert %}} - ## Production best practices -- **Always use `defaultAction: deny`.** This ensures that only explicitly allowed callers and operations are permitted, following the principle of least privilege. -- **Include the target app itself in callers for activity execution.** Activities are dispatched internally via actor reminders. The target app must be an allowed caller for its own activities. -- **Use glob patterns conservatively.** Overly broad patterns like `*` can inadvertently allow access to workflows or activities that should be restricted. Prefer exact names when possible. -- **Enable mTLS.** mTLS is required for cross-app enforcement. Without mTLS, all requests are denied when policies are active. -- **Monitor warning logs for policy violations.** Dapr logs a warning when a request is denied by a workflow access policy. Use these logs to audit access and detect misconfiguration. -- **Use `scopes` to limit which apps load the policy.** Apply policies only to the applications that need them, reducing unnecessary policy evaluation overhead. +- **Use deny by default.** Loading any `WorkflowAccessPolicy` for a target automatically denies cross-app requests that are not explicitly listed. Keep policies minimal and review them when adding new workflows. +- **Use glob patterns conservatively.** Patterns like `*` can grant broader access than intended. Prefer exact names where possible, and use glob patterns only for stable name families. +- **Enable mTLS.** mTLS is required for cross-app enforcement. Without mTLS, cross-app requests are denied when any policy is loaded. +- **Audit denial logs.** Dapr logs a warning whenever a request is denied by a workflow access policy. Use these logs to spot misconfiguration and unauthorized callers. +- **Use `scopes` to target the policy.** Apply each policy only to the apps that should enforce it, reducing the surface area each daprd has to load. ## Self-hosted setup -In self-hosted mode, place the workflow access policy YAML file in the components directory (default: `$HOME/.dapr/components` or the path specified with `--resources-path`). +In self-hosted mode, place the workflow access policy YAML in the resources directory (default: `$HOME/.dapr/components`, or the path passed via `--resources-path`). ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: my-policy +scopes: + - my-app spec: - defaultAction: deny rules: - callers: - - appID: app1 - operations: - - type: workflow - name: "MyWorkflow" - action: allow - scopes: - - my-app + - appID: frontend + workflows: + - name: MyWorkflow + operations: [schedule, get] ``` -Ensure that mTLS is enabled by running the Sentry service locally. See [Setup & configure mTLS certificates]({{% ref mtls %}}) for details on configuring mTLS in self-hosted mode. +Ensure mTLS is enabled by running Sentry locally. See [Setup & configure mTLS certificates]({{% ref mtls %}}) for details on configuring mTLS in self-hosted mode. ## Kubernetes setup -In Kubernetes, apply the `WorkflowAccessPolicy` CRD to your cluster using `kubectl`: +In Kubernetes, apply the `WorkflowAccessPolicy` CRD with `kubectl`: ```bash kubectl apply -f workflow-access-policy.yaml @@ -283,26 +242,9 @@ kubectl apply -f workflow-access-policy.yaml The Dapr operator watches for `WorkflowAccessPolicy` resources and distributes them to the appropriate sidecars based on the `scopes` field. mTLS is enabled by default in Kubernetes mode. -## Enable the feature flag - -The `WorkflowAccessPolicy` feature must be enabled in your Dapr Configuration: - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: myappconfig -spec: - features: - - name: WorkflowAccessPolicy - enabled: true -``` - -Apply this configuration to each application that needs workflow access policy enforcement. - ## Hot-reload support -Workflow access policies support hot-reloading in both Kubernetes and self-hosted modes. When a policy is created, updated, or deleted, the changes take effect without restarting the Dapr sidecar. This allows you to adjust policies in real time without application downtime. +Workflow access policies are hot-reloaded in both Kubernetes and self-hosted modes. Creating, updating, or deleting a policy takes effect without restarting the Dapr sidecar. ## Related links @@ -312,4 +254,3 @@ Workflow access policies support hot-reloading in both Kubernetes and self-hoste - [Service invocation access control]({{% ref invoke-allowlist.md %}}) - [Setup & configure mTLS certificates]({{% ref mtls %}}) - [WorkflowAccessPolicy CRD reference]({{% ref workflow-access-policy-schema.md %}}) -- [Preview features]({{% ref support-preview-features.md %}}) diff --git a/daprdocs/content/en/operations/support/support-preview-features.md b/daprdocs/content/en/operations/support/support-preview-features.md index 8d583caeb41..2c0f29df8f6 100644 --- a/daprdocs/content/en/operations/support/support-preview-features.md +++ b/daprdocs/content/en/operations/support/support-preview-features.md @@ -23,4 +23,3 @@ For CLI there is no explicit opt-in, just the version that this was first made a | **Subscription Hot Reloading** | Allows for declarative subscriptions to be "hot reloaded". A subscription is reloaded either when it is created/updated/deleted in Kubernetes, or on file in self-hosted mode. In-flight messages are unaffected when reloading. | `HotReload`| [Hot Reloading]({{% ref "subscription-methods.md#declarative-subscriptions" %}}) | v1.14 | | **Workflows Clustered Deployment** | Enable Workflows to function when workflow clients communicate to multiple daprds of the same appID who are behind a loadbalancer. Only relevant when using [Dapr shared]({{% ref "kubernetes-dapr-shared" %}}) | `WorkflowsClusteredDeployment`| [Dapr Shared]({{% ref "kubernetes-dapr-shared" %}}) | v1.16 | | **Workflows Durable Activity Results** | If set, ensures that activity results are durably sent to the owning workflow in multi-application scenarios, even when the owning workflow application is unavailable. Unless running multiple Dapr versions, this feature gate should be enabled. Disabled by default for backwards compatibility. | `WorkflowsRemoteActivityReminder` | [Multi-application Workflows]({{% ref "workflow-multi-app.md#durable-activity-results" %}}) | v1.17 | -| **Workflow Access Policy** | Enables fine-grained access control for workflow and activity scheduling. When enabled, `WorkflowAccessPolicy` resources are loaded and enforced, controlling which applications can schedule specific workflows and activities on a target application. Requires mTLS for cross-app enforcement. | `WorkflowAccessPolicy` | [Workflow Access Policy]({{% ref "workflow-access-policy.md" %}}) | v1.17 | diff --git a/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md index 86e5c41e195..ac497b80696 100644 --- a/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md +++ b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md @@ -6,7 +6,7 @@ description: "The basic spec for a Dapr WorkflowAccessPolicy resource" weight: 6000 --- -The `WorkflowAccessPolicy` is a Dapr resource that controls which applications are permitted to schedule specific workflows and activities on a target application. +The `WorkflowAccessPolicy` is a Dapr resource that controls which applications can invoke workflow and activity operations on a target application. Policies are a pure allow-list: a call is permitted if any loaded rule matches. ## Format @@ -15,32 +15,35 @@ apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: +scopes: + - spec: - defaultAction: rules: - callers: - appID: - operations: - - type: - name: - action: - scopes: - - + workflows: + - name: + operations: + - + activities: + - name: ``` ## Spec fields | Field | Required | Type | Description | Example | |-------|:--------:|------|-------------|---------| -| `defaultAction` | N | string | Global default action when no rule matches. Accepted values: `allow` or `deny`. Defaults to `deny`. | `deny` | -| `rules` | N | list | List of access rules. Each rule maps callers to permitted or denied operations. | See below | -| `rules[].callers` | Y | list | List of caller objects that this rule applies to. | See below | +| `rules` | N | list | Allow-list of rules. A call is permitted if any rule matches. If `rules` is omitted or empty while policies are loaded, all cross-app calls are denied. | See below | +| `rules[].callers` | Y | list | List of caller objects that this rule applies to. Must contain at least one entry. | See below | | `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | `frontend-app` | -| `rules[].operations` | Y | list | List of operations controlled by this rule. | See below | -| `rules[].operations[].type` | Y | string | The type of operation. Accepted values: `workflow` or `activity`. | `workflow` | -| `rules[].operations[].name` | Y | string | The name of the workflow or activity. Supports glob patterns: `*` (any sequence), `?` (single character), `[abc]` (character set). | `OrderWorkflow`, `Report*` | -| `rules[].operations[].action` | Y | string | The access action for this operation. Accepted values: `allow` or `deny`. | `allow` | -| `scopes` | N | list | List of target App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | `["order-service"]` | +| `rules[].workflows` | N* | list | Workflow rules granted to the matched callers. | See below | +| `rules[].workflows[].name` | Y | string | Exact name or glob pattern of the workflow. Glob: `*`, `?`, `[abc]`. | `OrderWF`, `Report*` | +| `rules[].workflows[].operations` | Y | list | One or more of: `schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`. | `[schedule, get]` | +| `rules[].activities` | N* | list | Activity rules granted to the matched callers. Activities only have the `schedule` operation, so no `operations` field. | See below | +| `rules[].activities[].name` | Y | string | Exact name or glob pattern of the activity. | `ChargePayment`, `Refund*` | +| `scopes` | N | list | App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | `["order-service"]` | + +\* At least one of `workflows` or `activities` must be present in each rule. ## Example @@ -49,33 +52,27 @@ apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: order-processing-policy +scopes: + - order-service spec: - defaultAction: deny rules: - callers: - appID: frontend-app - appID: api-gateway - operations: - - type: workflow - name: "OrderWorkflow" - action: allow - - type: workflow - name: "CheckoutWorkflow" - action: allow - - type: activity - name: "ProcessPayment" - action: allow + workflows: + - name: OrderWF + operations: [schedule, get, terminate] + - name: CheckoutWF + operations: [schedule, get] + activities: + - name: ProcessPayment - callers: - appID: admin-app - operations: - - type: workflow - name: "*" - action: allow - - type: activity - name: "*" - action: allow - scopes: - - order-service + workflows: + - name: "*" + operations: [schedule, terminate, raise, pause, resume, purge, get, rerun] + activities: + - name: "*" ``` ## Related links From cf98d00233476fe1893c48786a8102bc6a40e288 Mon Sep 17 00:00:00 2001 From: joshvanl Date: Tue, 26 May 2026 07:57:37 -0300 Subject: [PATCH 4/4] Review comments and remove other operations Signed-off-by: joshvanl --- .../content/en/concepts/security-concept.md | 2 +- .../workflow/workflow-multi-app.md | 7 +- .../workflow/workflow-overview.md | 2 + .../security/workflow-access-policy.md | 117 +++++++++--------- .../workflow-access-policy-schema.md | 31 +++-- 5 files changed, 85 insertions(+), 74 deletions(-) diff --git a/daprdocs/content/en/concepts/security-concept.md b/daprdocs/content/en/concepts/security-concept.md index 0caa75fca29..dd5306d81f7 100644 --- a/daprdocs/content/en/concepts/security-concept.md +++ b/daprdocs/content/en/concepts/security-concept.md @@ -212,7 +212,7 @@ Dapr components can use Dapr's built-in secret management capability to manage s ## Workflow access control -Dapr supports fine-grained access control for workflow and activity operations through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can invoke specific workflow operations (such as `schedule`, `terminate`, `get`) and which activities can be scheduled on a target application. +Dapr supports fine-grained access control for cross-app workflow and activity scheduling through the `WorkflowAccessPolicy` resource. A workflow access policy restricts which calling applications can schedule specific workflows and activities on a target application. Workflow access policies are enforced on the callee side as a pure allow-list. For cross-app requests, the caller's identity is taken from the [SPIFFE](https://spiffe.io/) identity embedded in the mTLS certificate, so mTLS must be active for cross-app enforcement. Self-calls (where the caller and target are the same app) are always permitted. Policies support glob pattern matching for workflow and activity names. diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md index 420d8a932e4..dc51f818405 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-multi-app.md @@ -247,13 +247,16 @@ public sealed class BusinessWorkflow : Workflow When using multi-application workflows, you may want to restrict which applications can schedule activities or child workflows on a target application. Dapr provides the `WorkflowAccessPolicy` resource for this purpose. -Policies are a pure allow-list and self-calls are always permitted, so the target application does not need to list itself in the `callers` to execute its own activities. The following example allows `orchestrator-app` to schedule the `TrainModel` and `ValidateModel` activities on `ml-worker`: +Policies are a pure allow-list and self-calls are always permitted, so the target application does not need to list itself in the `callers` to execute its own activities. The following example of a workflow access policy is applied to the `ml-worker` application. All policies that target a given appID (in this case `ml-worker`) are loaded by the sidecar when the application is instantiated. + +This policy allows the `orchestrator-app` application to schedule the `TrainModel` and `ValidateModel` activities on the `ml-worker` application. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: ml-worker-policy + namespace: production scopes: - ml-worker spec: @@ -265,7 +268,7 @@ spec: - name: ValidateModel ``` -Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for the full operation set (`schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`), more examples, and details on the cross-app enforcement model. +Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for more examples and details on the cross-app enforcement model. ## Related links diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md index dd0c6863381..de6691aae95 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md @@ -160,6 +160,8 @@ See [How-To: Manage workflows]({{< ref howto-manage-workflow.md >}}) for detaile Dapr provides fine-grained access control for workflow and activity scheduling through the `WorkflowAccessPolicy` resource. You can restrict which applications are permitted to start specific workflows or call specific activities on your application. +Workflow access policies for a given application (appID) are loaded by the sidecar when the application is instantiated, and are hot-reloaded thereafter when policies are added, updated, or removed. Policies are a pure allow-list evaluated on the callee side: a cross-app schedule is permitted only if some rule in some loaded policy matches the caller and the workflow or activity name. + This is especially important for multi-application workflows, where activities and child workflows execute across application boundaries. Read [How-To: Apply workflow access policies]({{% ref workflow-access-policy %}}) for full configuration details. ## Watch the demo diff --git a/daprdocs/content/en/operations/security/workflow-access-policy.md b/daprdocs/content/en/operations/security/workflow-access-policy.md index 7029221d434..cdcd4c334e1 100644 --- a/daprdocs/content/en/operations/security/workflow-access-policy.md +++ b/daprdocs/content/en/operations/security/workflow-access-policy.md @@ -16,40 +16,38 @@ Workflow access policies are a pure allow-list. A request is permitted if, and o ## Terminology -### Caller App ID +### Caller and target applications -The Dapr application identity (App ID) of the application making the request. For cross-app calls the caller identity is taken from the SPIFFE ID in the mTLS certificate. For same-sidecar (self) calls the local App ID is used directly. +Workflow access policies describe what *caller* applications are allowed to do against a *target* application: -### SPIFFE identity +- The **target** application is the application that hosts the workflow or activity being scheduled. The policy is enforced inside the target's Dapr sidecar (callee-side enforcement). A policy applies to a target through the `spec.scopes` field, which lists the target App IDs the policy applies to. +- The **caller** application is the application that is invoking the workflow or activity. For cross-app calls, the caller's App ID is taken from the [SPIFFE](https://spiffe.io/) identity in the mTLS client certificate. For same-sidecar (self) calls, the local App ID is used directly. -Dapr uses [SPIFFE](https://spiffe.io/) identities embedded in mTLS certificates to identify callers. The SPIFFE ID has the format `spiffe:///ns//`. The App ID is extracted from this identity when a workflow access policy is evaluated. +The SPIFFE ID embedded in the mTLS certificate has the format `spiffe:///ns//`. The App ID and namespace are extracted from this identity when a workflow access policy is evaluated. -### Glob pattern +### Workflow and activity name matching -Workflow and activity names in policy rules support glob pattern matching: -- `*` matches any sequence of characters -- `?` matches any single character -- `[abc]` matches any character in the set +Workflow and activity names in policy rules are matched as either exact names or glob patterns. Glob matching follows Go's [`path.Match`](https://pkg.go.dev/path#Match) semantics: + +- `*` matches any sequence of non-separator characters +- `?` matches any single non-separator character +- `[abc]` matches any character in the set (a character class) ### Operations -A workflow rule grants the listed callers access to one or more of these operations: +Workflow and activity rules grant the listed callers permission to `schedule` the named workflow or activity. A parent workflow on one app can schedule a child workflow or activity on a target app; the target's policy decides whether the call is permitted. -| Operation | Triggered by | -| --- | --- | -| `schedule` | `StartWorkflow` / `CreateWorkflowInstance` | -| `terminate` | `TerminateWorkflow` | -| `raise` | `RaiseEventWorkflow` | -| `pause` | `PauseWorkflow` | -| `resume` | `ResumeWorkflow` | -| `purge` | `PurgeWorkflow` | -| `get` | `GetWorkflow` / `WaitForRuntimeStatus` | -| `rerun` | `RerunWorkflowFromEvent` | +- Workflow rules require an `operations` field. Set it to `[schedule]`. +- Activity rules don't have an `operations` field. Activities only support scheduling. -Activities only support the `schedule` operation, so an activity rule has no `operations` field. +{{% alert title="Operations are scheduling-only today" color="warning" %}} +`schedule` is the only operation that takes effect through the standard Dapr workflow APIs. The CRD enum accepts additional values (`terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`) for forward compatibility with future cross-app workflow APIs, but those operations currently target the local sidecar, resolve to self-calls, and so always succeed regardless of policy. Use `[schedule]` in your rules until cross-app variants of the other APIs are available. +{{% /alert %}} ## CRD specification +The example below shows every field in a workflow access policy. The policy is applied to `orders-target` in the `production` namespace (via `scopes`). It grants the `frontend` and `ops-console` applications (the `callers`) permission to schedule `OrderWF`, schedule any workflow whose name starts with `Report`, and schedule the `ChargePayment` activity and any activity whose name starts with `RefundEvent`. + ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy @@ -65,17 +63,9 @@ spec: - appID: ops-console workflows: - name: OrderWF - operations: - - schedule - - terminate - - raise - - pause - - resume - - purge - - get - - rerun + operations: [schedule] - name: "Report*" - operations: [get] + operations: [schedule] activities: - name: ChargePayment - name: "RefundEvent*" @@ -83,26 +73,28 @@ spec: ### Spec fields +Fields are listed in the order they appear in the YAML document. + | Field | Required | Type | Description | |-------|:--------:|------|-------------| -| `rules` | N | list | Allow-list of rules. A call is permitted if any rule matches. With no rules and policies loaded, all cross-app calls are denied. | -| `rules[].callers` | Y | list | List of caller objects this rule applies to. Must contain at least one entry. | -| `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | +| `scopes` | N | list | Target App IDs this policy applies to. If omitted or empty, the policy applies to all applications. The policy is always enforced on the callee (target) side. | +| `rules` | N | list | Allow-list of rules. A call is permitted if any rule matches. If `rules` is omitted or empty while a policy is loaded for the target, all cross-app calls are denied. | +| `rules[].callers` | Y | list | List of caller objects this rule applies to. Must contain at least one entry. Every caller must be listed explicitly (with the exception of self-calls, which are always allowed). | +| `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. The caller must be in the same namespace as the target; cross-namespace calls are denied when policies are active. | | `rules[].workflows` | N* | list | Workflow rules granted to the matched callers. | -| `rules[].workflows[].name` | Y | string | Exact name or glob pattern of the workflow. | -| `rules[].workflows[].operations` | Y | list | One or more of `schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`. | +| `rules[].workflows[].name` | Y | string | Exact name or [glob pattern](https://pkg.go.dev/path#Match) of the workflow. | +| `rules[].workflows[].operations` | Y | list | Set to `[schedule]`. The CRD also accepts `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun` for forward compatibility; these have no effect today because the matching public workflow APIs do not route cross-app. | | `rules[].activities` | N* | list | Activity rules granted to the matched callers. | -| `rules[].activities[].name` | Y | string | Exact name or glob pattern of the activity. Activities only support the `schedule` operation, so there is no `operations` field. | -| `scopes` | N | list | App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | +| `rules[].activities[].name` | Y | string | Exact name or [glob pattern](https://pkg.go.dev/path#Match) of the activity. Activities only support the `schedule` operation, so there is no `operations` field. | \* At least one of `workflows` or `activities` must be present in each rule. ## Policy semantics 1. **No policies loaded:** All workflow and activity requests are allowed. This preserves backward compatibility when no policies exist. -2. **One or more policies loaded:** The target defaults to deny. A request is permitted only if some rule matches the caller, the operation, and the workflow or activity name. -3. **Self-calls are always allowed:** If the caller App ID is the same as the target App ID, the request is permitted regardless of policy contents. This means a target app does not need to list itself in its own policy to call its own workflows or activities (including the internal reminder-based execution path). -4. **Cross-namespace calls are denied** when policies are active. +2. **One or more policies loaded:** The target defaults to deny. A cross-app schedule is permitted only if some rule matches the caller and the workflow or activity name. +3. **Self-calls are always allowed:** If the caller App ID is the same as the target App ID, the request is permitted regardless of policy contents. This means a target app does not need to list itself in its own policy to schedule its own workflows or activities (including the internal reminder-based execution path). +4. **Cross-namespace calls are denied** when policies are active. A policy is namespaced and applies to target apps in its own namespace via `scopes`. The caller must also be in the same namespace as the target; calls from any other namespace are rejected even if the caller App ID appears in a rule. 5. **mTLS is required for cross-app enforcement:** if any policy is loaded and mTLS is not active, cross-app calls are denied because the caller's SPIFFE identity cannot be verified. 6. **Glob matching:** `*`, `?`, and character classes work on both workflow and activity names. @@ -110,42 +102,41 @@ spec: Workflow access policies are enforced inside the orchestrator and activity actors, under the actor lock, after the workflow's internal state has been loaded. This eliminates any time-of-check-to-time-of-use race between resolving a workflow's name and dispatching the operation. -The gRPC and HTTP public APIs (`StartWorkflow`, `TerminateWorkflow`, `RaiseEventWorkflow`, `PauseWorkflow`, `ResumeWorkflow`, `PurgeWorkflow`, `GetWorkflow`, `RerunWorkflowFromEvent`) all flow through this enforcement point, so coverage is the same regardless of which protocol the caller uses. Cross-app callers attempting non-subject actor methods, or attempting to inject reminders, are also denied. +The cross-app paths covered today are scheduling a child workflow or activity on another app: a parent workflow on the calling app reaches the target app's workflow/activity actor, which evaluates the policy before dispatching. The same enforcement point also blocks cross-app callers attempting non-subject actor methods or trying to inject reminders into a target actor. -## Example scenarios +## Example policies -### Scenario 1: Allow a frontend to drive a specific workflow +### Scenario 1: Restrict who can schedule a cross-app workflow -Allow `frontend-app` to schedule and observe `OrderWF` on the `order-service` application. +Allow `orchestrator-app` to schedule `OrderWF` on the `order-service` application in the `default` namespace. No other applications can schedule this workflow, with the exception of `order-service` itself (self-calls are always allowed). ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: order-service-policy + namespace: default scopes: - order-service spec: rules: - callers: - - appID: frontend-app + - appID: orchestrator-app workflows: - name: OrderWF - operations: - - schedule - - get - - terminate + operations: [schedule] ``` -### Scenario 2: Read-only access for a reporting tool +### Scenario 2: Glob-matched workflow scheduling -Grant a reporting application read-only access to any workflow whose name begins with `Report`. +Allow `analytics-app` to schedule any workflow whose name begins with `Report` on the `reporting-service` application. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: - name: reporting-readonly + name: reporting-glob + namespace: default scopes: - reporting-service spec: @@ -154,18 +145,19 @@ spec: - appID: analytics-app workflows: - name: "Report*" - operations: [get] + operations: [schedule] ``` ### Scenario 3: Cross-app activities (multi-application workflows) -When using multi-application workflows, the target application no longer needs to list itself in the `callers` to execute its own activities. Self-calls are always allowed, so the policy only describes which other apps may schedule activities on the target. +When using multi-application workflows, the target application does not need to list itself in the `callers` to execute its own activities. Self-calls are always allowed, so the policy only describes which *other* apps may schedule activities on the target. In the policy below, `orchestrator-app` can schedule the `TrainModel` and `ValidateModel` activities on the `ml-worker` application. No other applications can. The `orchestrator-app` must be in the same namespace as `ml-worker`, because cross-namespace calls are denied when policies are active. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: ml-worker-policy + namespace: production scopes: - ml-worker spec: @@ -181,13 +173,14 @@ spec: ### Scenario 4: Mixed workflow and activity access for a single caller -A single rule can grant a caller both workflow and activity access. +A single rule can grant a caller scheduling access to both workflows and activities. Here the `api-gateway` application can schedule the `ChargeCustomer` workflow, the `ChargePayment` activity, and any activity whose name starts with `Refund` on the `payments-service` application. ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: payments-policy + namespace: default scopes: - payments-service spec: @@ -196,7 +189,7 @@ spec: - appID: api-gateway workflows: - name: ChargeCustomer - operations: [schedule, get] + operations: [schedule] activities: - name: ChargePayment - name: "Refund*" @@ -212,7 +205,7 @@ spec: ## Self-hosted setup -In self-hosted mode, place the workflow access policy YAML in the resources directory (default: `$HOME/.dapr/components`, or the path passed via `--resources-path`). +In self-hosted mode, place the workflow access policy YAML in the resources directory (`$HOME/.dapr/components` by default, or the path passed via `--resources-path`). ```yaml apiVersion: dapr.io/v1alpha1 @@ -227,10 +220,14 @@ spec: - appID: frontend workflows: - name: MyWorkflow - operations: [schedule, get] + operations: [schedule] ``` -Ensure mTLS is enabled by running Sentry locally. See [Setup & configure mTLS certificates]({{% ref mtls %}}) for details on configuring mTLS in self-hosted mode. +For cross-app enforcement, mTLS must be enabled by running Sentry locally. See [Setup & configure mTLS certificates]({{% ref mtls %}}) for details on configuring mTLS in self-hosted mode. + +{{% alert title="Local development without mTLS" color="primary" %}} +mTLS is required only for *cross-app* enforcement, because the caller identity is taken from the SPIFFE ID in the mTLS certificate. Same-sidecar (self) calls do not depend on mTLS and are always permitted, so you can develop and test a single-app workflow locally without running Sentry. As soon as you need to validate cross-app policy enforcement, run with mTLS enabled. +{{% /alert %}} ## Kubernetes setup diff --git a/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md index ac497b80696..62bd9d3c9e2 100644 --- a/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md +++ b/daprdocs/content/en/reference/resource-specs/workflow-access-policy-schema.md @@ -6,7 +6,11 @@ description: "The basic spec for a Dapr WorkflowAccessPolicy resource" weight: 6000 --- -The `WorkflowAccessPolicy` is a Dapr resource that controls which applications can invoke workflow and activity operations on a target application. Policies are a pure allow-list: a call is permitted if any loaded rule matches. +The `WorkflowAccessPolicy` is a Dapr resource that controls which applications can schedule workflows and activities cross-app on a target application. Policies are a pure allow-list: a call is permitted if any loaded rule matches. + +{{% alert title="Scheduling is the only operation today" color="warning" %}} +Use `operations: [schedule]` in workflow rules. The CRD enum reserves additional values (`terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`) for forward compatibility with future cross-app workflow APIs, but those operations currently target the local sidecar and resolve to self-calls, so they always succeed regardless of policy. +{{% /alert %}} ## Format @@ -15,6 +19,7 @@ apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: + namespace: scopes: - spec: @@ -23,35 +28,39 @@ spec: - appID: workflows: - name: - operations: - - + operations: [schedule] activities: - name: ``` ## Spec fields +Fields are listed in the order they appear in the YAML document. + | Field | Required | Type | Description | Example | |-------|:--------:|------|-------------|---------| +| `scopes` | N | list | Target App IDs that this policy applies to. If omitted or empty, the policy applies to all applications. The policy is enforced on the callee (target) side. | `["order-service"]` | | `rules` | N | list | Allow-list of rules. A call is permitted if any rule matches. If `rules` is omitted or empty while policies are loaded, all cross-app calls are denied. | See below | | `rules[].callers` | Y | list | List of caller objects that this rule applies to. Must contain at least one entry. | See below | | `rules[].callers[].appID` | Y | string | The Dapr App ID of the calling application. | `frontend-app` | | `rules[].workflows` | N* | list | Workflow rules granted to the matched callers. | See below | -| `rules[].workflows[].name` | Y | string | Exact name or glob pattern of the workflow. Glob: `*`, `?`, `[abc]`. | `OrderWF`, `Report*` | -| `rules[].workflows[].operations` | Y | list | One or more of: `schedule`, `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun`. | `[schedule, get]` | -| `rules[].activities` | N* | list | Activity rules granted to the matched callers. Activities only have the `schedule` operation, so no `operations` field. | See below | -| `rules[].activities[].name` | Y | string | Exact name or glob pattern of the activity. | `ChargePayment`, `Refund*` | -| `scopes` | N | list | App IDs to which this policy applies. If omitted or empty, the policy applies to all applications. | `["order-service"]` | +| `rules[].workflows[].name` | Y | string | Exact name or [glob pattern](https://pkg.go.dev/path#Match) of the workflow. Supports `*`, `?`, and `[abc]` character classes. | `OrderWF`, `Report*` | +| `rules[].workflows[].operations` | Y | list | Set to `[schedule]`. The CRD also accepts `terminate`, `raise`, `pause`, `resume`, `purge`, `get`, `rerun` for forward compatibility; these have no effect today because the matching public workflow APIs do not route cross-app. | `[schedule]` | +| `rules[].activities` | N* | list | Activity rules granted to the matched callers. Activities only support scheduling, so there is no `operations` field. | See below | +| `rules[].activities[].name` | Y | string | Exact name or [glob pattern](https://pkg.go.dev/path#Match) of the activity. | `ChargePayment`, `Refund*` | \* At least one of `workflows` or `activities` must be present in each rule. ## Example +The policy below applies to the `order-service` application. It grants `frontend-app` and `api-gateway` permission to schedule `OrderWF`, `CheckoutWF`, and the `ProcessPayment` activity. A second rule grants `admin-app` permission to schedule any workflow or activity on `order-service`. + ```yaml apiVersion: dapr.io/v1alpha1 kind: WorkflowAccessPolicy metadata: name: order-processing-policy + namespace: production scopes: - order-service spec: @@ -61,16 +70,16 @@ spec: - appID: api-gateway workflows: - name: OrderWF - operations: [schedule, get, terminate] + operations: [schedule] - name: CheckoutWF - operations: [schedule, get] + operations: [schedule] activities: - name: ProcessPayment - callers: - appID: admin-app workflows: - name: "*" - operations: [schedule, terminate, raise, pause, resume, purge, get, rerun] + operations: [schedule] activities: - name: "*" ```