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

    Access control enables the configuration of policies that restrict what operations calling applications can perform, via service invocation, on the called application. To limit access to a called applications from specific operations and HTTP verbs from the calling applications, you can define an access control policy specification in configuration.

    An access control policy is specified in configuration and be applied to Dapr sidecar for the called application. Example access policies are shown below and access to the called app is based on the matched policy action. You can provide a default global action for all calling applications and if no access control policy is specified, the default behavior is to allow all calling applications to access to the called app.

    TrustDomain - A “trust domain” is a logical group to manage 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 and this id is attached in the TLS cert. The SPIFFE id is of the format: . 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:

    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 specified and 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

    Below are some example scenarios for using access control list for service invocation. See 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 and all other invocation requests from other applications are denied

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

    With this configuration, only method op1 from appId = app1 is allowed and 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

    With this configuration, the only scenarios below are allowed access and and 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, http verb = POST/PUT
    • trustDomain = “myDomain”, namespace = “ns1”, appID = app2, operation = op2 and application protocol is GRPC , only HTTP verbs POST/PUT on method op1 from appId = app1 are allowed and 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. trustDomain: 'myDomain'
    20. namespace: "ns1"
    21. operations:
    22. - name: /op2
    23. action: allow

    Scenario 4: Allow access to all methods except trustDomain = public, namespace = default, appId = app1, operation = /op1/*, all http verbs

    1. apiVersion: dapr.io/v1alpha1
    2. kind: Configuration
    3. metadata:
    4. name: appconfig
    5. spec:
    6. accessControl:
    7. defaultAction: allow
    8. policies:
    9. - appId: app1
    10. defaultAction: allow
    11. trustDomain: 'public'
    12. namespace: "default"
    13. operations:
    14. - name: /op1/*
    15. httpVerb: ['*']
    16. 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 but belonging to different namespaces can be specified

    Hello world examples

    These examples show how to apply access control to the quickstart samples where a python app invokes a node.js app. Access control lists rely on the Dapr Sentry service to generate the TLS certificates with a SPIFFE id for authentication, which 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 nodeapp 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"

    This example uses the quickstart.

    The following steps run the Sentry service locally with mTLS enabled, set up necessary environment variables to access certificates, and then launch both the node app and python app each referencing the Sentry service to apply the ACLs.

    1. Follow these steps to run the Sentry service in self-hosted mode with mTLS enabled

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

    2. Run the node app in a separate command prompt:

      1. node app.js
    3. In another command prompt, set these environment variables:

    1. ```
    2. export DAPR_TRUST_ANCHORS=`cat $HOME/.dapr/certs/ca.crt`
    3. export DAPR_CERT_CHAIN=`cat $HOME/.dapr/certs/issuer.crt`
    4. export DAPR_CERT_KEY=`cat $HOME/.dapr/certs/issuer.key`
    5. export NAMESPACE=default
    6. ```
    7. ```
    8. $env:DAPR_TRUST_ANCHORS=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\ca.crt)
    9. $env:DAPR_CERT_CHAIN=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.crt)
    10. $env:DAPR_CERT_KEY=$(Get-Content -raw $env:USERPROFILE\.dapr\certs\issuer.key)
    11. $env:NAMESPACE="default"
    12. ```
    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:

    3. You should see the calls to the node app fail in the python app command prompt based due to the deny operation action in the nodeappconfig file. Change this action to allow and re-run the apps and you should then see this call succeed.

    This example uses the quickstart.

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

    For example, below is how the pythonapp is deployed to Kubernetes in the default namespace with this pythonappconfig configuration file. Do the same for the nodeapp deployment and then look at the logs for the pythonapp 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 and you should then see this call succeed.

    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. image: dapriosamples/hello-k8s-python:edge

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