How-To: Apply access control list configuration for service invocation

Restrict what operations calling applications can perform

Using access control, you can configure policies that restrict what the operations calling applications can perform, via service invocation, on the called application. You can define an access control policy specification in the Configuration schema to limit access:

  • To a called application from specific operations, and
  • To HTTP verbs from the calling applications.

An access control policy is specified in Configuration and applied to the Dapr sidecar for the called application. Access to the called app is based on the matched policy action.

You can provide a default global action for all calling applications. If no access control policy is specified, the default behavior is to allow all calling applications to access to the called app.

See examples of access policies.

Terminology

trustDomain

A “trust domain” is a logical group that manages trust relationships. Every application is assigned a trust domain, which can be specified in the access control list policy spec. If no policy spec is defined or an empty trust domain is specified, then a default value “public” is used. This trust domain is used to generate the identity of the application in the TLS cert.

App Identity

Dapr requests the sentry service to generate a SPIFFE ID for all applications. This ID is attached in the TLS cert.

The SPIFFE ID is of the format: **spiffe://\<trustdomain>/ns/\<namespace\>/\<appid\>**.

For matching policies, the trust domain, namespace, and app ID values of the calling app are extracted from the SPIFFE ID in the TLS cert of the calling app. These values are matched against the trust domain, namespace, and app ID values specified in the policy spec. If all three of these match, then more specific policies are further matched.

Configuration properties

The following tables lists the different properties for access control, policies, and operations:

Access Control

PropertyTypeDescription
defaultActionstringGlobal default action when no other policy is matched
trustDomainstringTrust domain assigned to the application. Default is “public”.
policiesstringPolicies to determine what operations the calling app can do on the called app

Policies

PropertyTypeDescription
appstringAppId of the calling app to allow/deny service invocation from
namespacestringNamespace value that needs to be matched with the namespace of the calling app
trustDomainstringTrust domain that needs to be matched with the trust domain of the calling app. Default is “public”
defaultActionstringApp level default action in case the app is found but no specific operation is matched
operationsstringoperations that are allowed from the calling app

Operations

PropertyTypeDescription
namestringPath name of the operations allowed on the called app. Wildcard “” can be used in a path to match. Wildcard “**” can be used to match under multiple paths.
httpVerblistList specific http verbs that can be used by the calling app. Wildcard “” can be used to match any http verb. Unused for grpc invocation.
actionstringAccess modifier. Accepted values “allow” (default) or “deny”

Policy rules

  1. If no access policy is specified, the default behavior is to allow all apps to access to all methods on the called app.
  2. If no global default action is specified and no app specific policies defined, the empty access policy is treated like no access policy is specified. The default behavior is to allow all apps to access to all methods on the called app.
  3. If no global default action is specified but some app specific policies have been defined, then we resort to a more secure option of assuming the global default action to deny access to all methods on the called app.
  4. If an access policy is defined and if the incoming app credentials cannot be verified, then the global default action takes effect.
  5. If either the trust domain or namespace of the incoming app do not match the values specified in the app policy, the app policy is ignored and the global default action takes effect.

Policy priority

The action corresponding to the most specific policy matched takes effect as ordered below:

  1. Specific HTTP verbs in the case of HTTP or the operation level action in the case of GRPC.
  2. The default action at the app level
  3. The default action at the global level

Example scenarios

Below are some example scenarios for using access control list for service invocation. See configuration guidance to understand the available configuration settings for an application sidecar.

Scenario 1:

Deny access to all apps except where trustDomain = public, namespace = default, appId = app1

With this configuration, all calling methods with appId = app1 are allowed. All other invocation requests from other applications are denied.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: deny
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: allow
  12. trustDomain: 'public'
  13. namespace: "default"

Scenario 2:

Deny access to all apps except trustDomain = public, namespace = default, appId = app1, operation = op1

With this configuration, only the method op1 from appId = app1 is allowed. All other method requests from all other apps, including other methods on app1, are denied.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: deny
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: deny
  12. trustDomain: 'public'
  13. namespace: "default"
  14. operations:
  15. - name: /op1
  16. httpVerb: ['*']
  17. action: allow

Scenario 3:

Deny access to all apps except when a specific verb for HTTP and operation for GRPC is matched

With this configuration, only the scenarios below are allowed access. All other method requests from all other apps, including other methods on app1 or app2, are denied.

  • trustDomain = public, namespace = default, appID = app1, operation = op1, httpVerb = POST/PUT
  • trustDomain = "myDomain", namespace = "ns1", appID = app2, operation = op2 and application protocol is GRPC

Only the httpVerb POST/PUT on method op1 from appId = app1 are allowe. All other method requests from all other apps, including other methods on app1, are denied.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: deny
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: deny
  12. trustDomain: 'public'
  13. namespace: "default"
  14. operations:
  15. - name: /op1
  16. httpVerb: ['POST', 'PUT']
  17. action: allow
  18. - appId: app2
  19. defaultAction: deny
  20. trustDomain: 'myDomain'
  21. namespace: "ns1"
  22. operations:
  23. - name: /op2
  24. action: allow

Scenario 4:

Allow access to all methods except trustDomain = public, namespace = default, appId = app1, operation = /op1/*, all httpVerb

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: allow
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: allow
  12. trustDomain: 'public'
  13. namespace: "default"
  14. operations:
  15. - name: /op1/*
  16. httpVerb: ['*']
  17. action: deny

Scenario 5:

Allow access to all methods for trustDomain = public, namespace = ns1, appId = app1 and deny access to all methods for trustDomain = public, namespace = ns2, appId = app1

This scenario shows how applications with the same app ID while belonging to different namespaces can be specified.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: allow
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: allow
  12. trustDomain: 'public'
  13. namespace: "ns1"
  14. - appId: app1
  15. defaultAction: deny
  16. trustDomain: 'public'
  17. namespace: "ns2"

Scenario 6:

Allow access to all methods except trustDomain = public, namespace = default, appId = app1, operation = /op1/**/a, all httpVerb

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. accessControl:
  7. defaultAction: allow
  8. trustDomain: "public"
  9. policies:
  10. - appId: app1
  11. defaultAction: allow
  12. trustDomain: 'public'
  13. namespace: "default"
  14. operations:
  15. - name: /op1/**/a
  16. httpVerb: ['*']
  17. action: deny

“hello world” examples

In these examples, you learn how to apply access control to the hello world tutorials.

Access control lists rely on the Dapr Sentry service to generate the TLS certificates with a SPIFFE ID for authentication. This means the Sentry service either has to be running locally or deployed to your hosting environment, such as a Kubernetes cluster.

The nodeappconfig example below shows how to deny access to the neworder method from the pythonapp, where the Python app is in the myDomain trust domain and default namespace. The Node.js app is in the public trust domain.

nodeappconfig.yaml

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: nodeappconfig
  5. spec:
  6. tracing:
  7. samplingRate: "1"
  8. accessControl:
  9. defaultAction: allow
  10. trustDomain: "public"
  11. policies:
  12. - appId: pythonapp
  13. defaultAction: allow
  14. trustDomain: 'myDomain'
  15. namespace: "default"
  16. operations:
  17. - name: /neworder
  18. httpVerb: ['POST']
  19. action: deny

pythonappconfig.yaml

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: pythonappconfig
  5. spec:
  6. tracing:
  7. samplingRate: "1"
  8. accessControl:
  9. defaultAction: allow
  10. trustDomain: "myDomain"

Self-hosted mode

When walking through this tutorial, you:

  • Run the Sentry service locally with mTLS enabled
  • Set up necessary environment variables to access certificates
  • Launch both the Node app and Python app each referencing the Sentry service to apply the ACLs

Prerequisites

Run the Node.js app

  1. In a command prompt, set these environment variables:

  1. ```
  2. ```bash
  3. export DAPR_TRUST_ANCHORS=`cat $HOME/.dapr/certs/ca.crt`
  4. export DAPR_CERT_CHAIN=`cat $HOME/.dapr/certs/issuer.crt`
  5. export DAPR_CERT_KEY=`cat $HOME/.dapr/certs/issuer.key`
  6. export NAMESPACE=default
  7. ```
  8. ```
  9. ```
  10. ```powershell
  11. $env:DAPR_TRUST_ANCHORS=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\ca.crt)
  12. $env:DAPR_CERT_CHAIN=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.crt)
  13. $env:DAPR_CERT_KEY=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.key)
  14. $env:NAMESPACE="default"
  15. ```
  16. ```
  1. Run daprd to launch a Dapr sidecar for the Node.js app with mTLS enabled, referencing the local Sentry service:

    1. daprd --app-id nodeapp --dapr-grpc-port 50002 -dapr-http-port 3501 --log-level debug --app-port 3000 --enable-mtls --sentry-address localhost:50001 --config nodeappconfig.yaml
  2. Run the Node.js app in a separate command prompt:

    1. node app.js

Run the Python app

  1. In another command prompt, set these environment variables:

  1. ````
  2. ```bash
  3. export DAPR_TRUST_ANCHORS=`cat $HOME/.dapr/certs/ca.crt`
  4. export DAPR_CERT_CHAIN=`cat $HOME/.dapr/certs/issuer.crt`
  5. export DAPR_CERT_KEY=`cat $HOME/.dapr/certs/issuer.key`
  6. export NAMESPACE=default
  7. ````
  8. ```
  9. $env:DAPR_TRUST_ANCHORS=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\ca.crt)
  10. $env:DAPR_CERT_CHAIN=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.crt)
  11. $env:DAPR_CERT_KEY=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.key)
  12. $env:NAMESPACE="default"
  13. ```
  1. Run daprd to launch a Dapr sidecar for the Python app with mTLS enabled, referencing the local Sentry service:

    1. daprd --app-id pythonapp --dapr-grpc-port 50003 --metrics-port 9092 --log-level debug --enable-mtls --sentry-address localhost:50001 --config pythonappconfig.yaml
  2. Run the Python app in a separate command prompt:

    1. python app.py

You should see the calls to the Node.js app fail in the Python app command prompt, due to the deny operation action in the nodeappconfig file. Change this action to allow and re-run the apps to see this call succeed.

Kubernetes mode

Prerequisites

Configure the Node.js and Python apps

You can create and apply the above nodeappconfig.yaml and pythonappconfig.yaml configuration files, as described in the configuration.

For example, the Kubernetes Deployment below is how the Python app is deployed to Kubernetes in the default namespace with this pythonappconfig configuration file.

Do the same for the Node.js deployment and look at the logs for the Python app to see the calls fail due to the deny operation action set in the nodeappconfig file.

Change this action to allow and re-deploy the apps to see this call succeed.

Deployment YAML example
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: pythonapp
  5. namespace: default
  6. labels:
  7. app: python
  8. spec:
  9. replicas: 1
  10. selector:
  11. matchLabels:
  12. app: python
  13. template:
  14. metadata:
  15. labels:
  16. app: python
  17. annotations:
  18. dapr.io/enabled: "true"
  19. dapr.io/app-id: "pythonapp"
  20. dapr.io/config: "pythonappconfig"
  21. spec:
  22. containers:
  23. - name: python
  24. image: dapriosamples/hello-k8s-python:edge

Demo

Watch this video on how to apply access control list for service invocation.

Next steps

Dapr APIs allow list

Last modified October 11, 2024: Fixed typo (#4389) (fe17926)