Set up authorization policies for Application Load Balancers

In Application Load Balancers, authorization policies are called after evaluating route extensions, network security policies (evaluated by Google Cloud Armor), cross-origin resource sharing (CORS) policies, and Identity-Aware Proxy (IAP), but before running traffic management actions.

This page shows you how to set up authorization policies for Application Load Balancers.

Before you begin

Set up the load balancer

If you haven't created a load balancer, see the following pages to set up your preferred Application Load Balancer:

Create and configure secure tags or service accounts

With internal Application Load Balancers, you can optionally apply authorization policies based on the secure tags and service accounts attached to the client VMs. If you plan to apply tag-based authorization policies, use Resource Manager to create tags.

If you're using external Application Load Balancers, you don't need to create and configure secure tags.

For more information, see Use Tags for firewalls and Service accounts.

Attach service accounts to client VMs

For step-by-step information about attaching a service account to a VM instance, see the following documents:

Create the secure tag keys and values

Before associating secure tags with the instance group template, create the secure tag keys and values, and designate them to the GCE_FIREWALL purpose. This is necessary for Google Cloud networking features, including Secure Web Proxy and authorization policies.

To create secure tags, you need the Tag Administrator role (roles/resourcemanager.tagAdmin).

Console

  1. In the Google Cloud console, go to the Tags page.

    Go to Tags

  2. Click Create.

  3. In the Tag key description field, enter a description.

  4. Select the For use with network firewall checkbox.

  5. In the Project list, select the Google Cloud project where you want to create the tag.

  6. In the Network field, select LB_NETWORK.

  7. Click Add value.

  8. In the Tag value field, enter TAG_VALUE. The value must be a numeric value.

  9. In the Tag value description field, enter a description.

  10. When you have finished adding tag values, click Create tag key.

gcloud

  1. Create the secure tag key.

    gcloud resource-manager tags keys create TAG_KEY \
        --parent=organizations/ORG_ID \
        --purpose=GCE_FIREWALL \
        --purpose-data=network=LB_NETWORK
    

    Replace the following:

    • TAG_KEY: the name of your secure tag key.
    • ORG_ID: the ID of your organization.
    • LB_NETWORK: the name of your VPC network.
  2. Add the secure tag value to the numeric tag key.

    gcloud resource-manager tags values create TAG_VALUE \
        --parent=ORG_ID/TAG_KEY
    

    Replace TAG_VALUE with a numeric tag value.

Bind the secure tag to the instance group template

Tag administrators can bind secure tags to individual VM instances or the instance group template and attach the tag's value to the VMs or template's backends.

To bind secure tags, you need the Tag User role (roles/resourcemanager.tagUser).

  1. Define the full name prefix for your project and zone:

    FULL_NAME_PREFIX=//compute.googleapis.com/projects/PROJECT_ID/zones/ZONE/instances/
    

    Replace the following:

    • PROJECT_ID: the ID of your project.
    • ZONE: the zone that the managed instance group is located in.
  2. Get the instance group template ID:

    TEMPLATE_ID=$(gcloud compute instance-templates describe TEMPLATE_NAME --region=LOCATION --format='value(id)')
    

    Replace the following:

    • TEMPLATE_NAME: the name of your instance group template.
    • LOCATION: your Google Cloud region.
  3. Concatenate the values of FULL_NAME_PREFIX and TEMPLATE_ID:

    PARENT="$FULL_NAME_PREFIX$TEMPLATE_ID"
    echo $PARENT
    
  4. Create the bindings.

    gcloud resource-manager tags bindings create \
        --location LOCATION \
        --tag-value ORG_ID/TAG_KEY/TAG_VALUE \
        --parent PARENT
    

    Replace the following:

    • ORG_ID: the ID of your organization.
    • LOCATION: your Google Cloud region.
    • TAG_KEY: the name of your secure tag key.
    • TAG_VALUE: the numeric tag value.

Create the authorization policy

To create an authorization policy, you create a YAML file defining the target and the rules, and then import the file using the gcloud beta network-security authz-policies command.

Authorization policy to deny requests

Global and Cross-region

If you're using a global external Application Load Balancer or a cross-region internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to deny certain requests.

    The following example creates an authz-policy-deny.yaml file for the forwarding rule LB_FORWARDING_RULE in the global location. The policy denies clients from *.hello.com to access the /api/payments URL path.

    $ cat >authz-policy-deny.yaml <<EOF
    name: my-authz-policy-deny
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - from:
        sources:
        - principals:
          - suffix: ".hello.com"
      to:
        operations:
        - paths:
          - prefix: "/api/payments"
    action: DENY
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. For global external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. For cross-region internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies:

    gcloud beta network-security authz-policies import my-authz-policy-deny \
        --source=authz-policy-deny.yaml \
        --location=global
    

Regional

If you're using a regional external Application Load Balancer or a regional internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to allow certain requests.

    The following example creates an authz-policy-deny.yaml file for the forwarding rule LB_FORWARDING_RULE in a Google Cloud region. The policy denies clients with identities matching *.hello.com to access the /api/payments URL path. The policy also denies access to any client VMs with the service account my-sa-123@PROJECT_ID.iam.gserviceaccount.com to the /api/payments URL.

    $ cat >authz-policy-deny.yaml <<EOF
    name: my-authz-policy-deny
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/regions/LOCATION/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - from:
        sources:
        - principals:
          - suffix: ".hello.com"
      to:
        operations:
        - paths:
          - prefix: "/api/payments"
    - from:
        sources:
        - resources:
           - iamServiceAccount:
               exact: "my-sa-123@PROJECT_ID.iam.gserviceaccount.com"
      to:
        operations:
        - paths:
          - prefix: "/api/payments"
    action: DENY
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. For regional external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. For regional internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LOCATION: your Google Cloud region.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies in the LOCATION region:

    gcloud beta network-security authz-policies import my-authz-policy-deny \
        --source=authz-policy-deny.yaml \
        --location=LOCATION
    

Authorization policy to allow requests

Global and Cross-region

If you're using a global external Application Load Balancer or a cross-region internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to allow certain requests.

    The following example creates an authz-policy-allow.yaml file for the forwarding rule LB_FORWARDING_RULE in the global location. The policy allows only clients from *.example.com to access the /api/payments URL path.

    $ cat >authz-policy-allow.yaml <<EOF
    name: my-authz-policy-allow
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - from:
      sources:
      - principals:
        - suffix: ".example.com"
      to:
        operations:
        - paths:
          - exact: "/api/payments"
    action: ALLOW
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. For global external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. For cross-region internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies:

    gcloud beta network-security authz-policies import my-authz-policy-allow \
        --source=authz-policy-allow.yaml \
        --location=global
    

Regional

If you're using a regional external Application Load Balancer or a regional internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to allow certain requests.

    The following example creates an authz-policy-allow.yaml file for the forwarding rule LB_FORWARDING_RULE in a Google Cloud region of a regional internal Application Load Balancer. The policy allows only clients from *.example.com to access the /api/payments URL path when the request originates from a VM with the resource tag TAG_VALUE.

    $ cat >authz-policy-allow.yaml <<EOF
    name: my-authz-policy-allow
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/regions/LOCATION/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - from:
      sources:
      - principals:
        - suffix: ".example.com"
        resources:
        - tagValueIdSet:
          - ids: "TAG_VALUE"
      to:
        operations:
        - paths:
          - exact: "/api/payments"
    action: ALLOW
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. If you're using regional external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. If you're using regional internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LOCATION: your Google Cloud region.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies in the LOCATION region:

    gcloud beta network-security authz-policies import my-authz-policy-allow \
        --source=authz-policy-allow.yaml \
        --location=LOCATION
    

Authorization policy to delegate to a service extension

Before you begin, set up an external authorization engine. For more information about service extensions, see Cloud Load Balancing callouts overview.

Global and Cross-region

If you're using a global external Application Load Balancer or a cross-region internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to delegate certain requests to an external service.

    The following example creates an authz-policy-custom.yaml file for the forwarding rule LB_FORWARDING_RULE in the global location. The policy calls the AUTHZ_EXTENSION extension for all traffic to the /api/payments URL path when the request contains a non-empty Authorization header.

    $ cat >authz-policy-custom.yaml <<EOF
    name: my-authz-policy-custom
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - to:
        operations:
        - paths:
          - exact: "/api/payments"
      when: 'request.headers["Authorization"] != ""'
    action: CUSTOM
    customProvider:
      authzExtension:
        resources:
        - "https://networkservices.googleapis.com/v1/projects/PROJECT_ID/locations/global/authzExtensions/AUTHZ_EXTENSION"
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. For global external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. For cross-region internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
    • AUTHZ_EXTENSION: the name of the authorization extension.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies:

    gcloud beta network-security authz-policies import my-authz-policy-custom \
        --source=authz-policy-custom.yaml \
        --location=global
    

Regional

If you're using a regional external Application Load Balancer or a regional internal Application Load Balancer, follow these steps to create and import the authorization policy:

  1. Create the authorization policy file to delegate certain requests to an external service.

    The following example creates an authz-policy-custom.yaml file for the forwarding rule LB_FORWARDING_RULE in a Google Cloud region of a regional internal Application Load Balancer. The policy calls the AUTHZ_EXTENSION extension for all traffic to the /api/payments URL path when the request contains a non-empty Authorization header.

    $ cat >authz-policy-custom.yaml <<EOF
    name: my-authz-policy-custom
    target:
      loadBalancingScheme: LB_SCHEME
      resources:
      - "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/regions/LOCATION/forwardingRules/LB_FORWARDING_RULE"
    httpRules:
    - to:
        operations:
        - paths:
          - exact: "/api/payments"
      when: 'request.headers["Authorization"] != ""'
    action: CUSTOM
    customProvider:
      authzExtension:
        resources:
        - "https://networkservices.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/authzExtensions/AUTHZ_EXTENSION"
    EOF
    

    Replace the following:

    • LB_SCHEME: your load balancing scheme. For regional external Application Load Balancer, set the scheme to EXTERNAL_MANAGED. For regional internal Application Load Balancer, set the scheme to INTERNAL_MANAGED.
    • PROJECT_ID: the ID of your Google Cloud project.
    • LOCATION: your Google Cloud region.
    • LB_FORWARDING_RULE: the name of the load balancer's forwarding rule.
    • AUTHZ_EXTENSION: the name of the authorization extension.
  2. Create the authorization policy and import the YAML file.

    The following example command imports the previously created policy file and creates the authorization policies in the LOCATION region:

    gcloud beta network-security authz-policies import my-authz-policy-custom \
        --source=authz-policy-custom.yaml \
        --location=LOCATION
    

Test the authorization policies

To test the authorization policies, send some traffic to the load balancer. For more information, see the following pages:

Understand authorization policy logs in Cloud Logging

To understand how the authorization policies are logged when a request is allowed or denied, see the following:

  • When a request matches neither the ALLOW nor DENY policies, the DENY policy allows it and logs it as allowed_as_no_allow_policies_matched_request. However, the ALLOW policy denies it and logs it as denied_as_no_allow_policies_matched_request. Because one of the policies denies the request, it is denied.

    If you're using a global external Application Load Balancer, statusDetails is set to denied_by_authz_policy in the log. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "allowed_as_no_deny_policies_matched_request"
                result: "ALLOWED"
              }
              1: {
                details: "denied_as_no_allow_policies_matched_request"
                result: "DENIED"
              }
            ]
            result: "DENIED"
          }
          backendTargetProjectNumber: "projects/12345567"
          remoteIp: "00.100.11.104"
          proxyStatus: "error=\"http_request_error\"; details=\"denied_by_authz_policy\""
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
    

    If you're using a regional internal Application Load Balancer, regional external Application Load Balancer, or cross-region internal Application Load Balancer, proxyStatus is set to error=\"http_request_error\"; details=\"denied_by_authz_policy\" in the log. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "allowed_as_no_deny_policies_matched_request"
                result: "ALLOWED"
              }
              1: {
                details: "denied_as_no_allow_policies_matched_request"
                result: "DENIED"
              }
            ]
            result: "DENIED"
          }
          backendTargetProjectNumber: "projects/12345567"
          remoteIp: "00.100.11.104"
          proxyStatus: "error=\"http_request_error\"; details=\"denied_by_authz_policy\""
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
    
    • When a request matches the DENY policy, it is denied and the policy that denied the request is logged.

      If you're using a global external Application Load Balancer, statusDetails is set to denied_by_authz_policy in the log and the name of the policy that denied the request is logged in policies. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "name: "projects/12345567/locations/global/authzPolicies/deny-authz-policy-test""
                result: "DENIED"
              }
            ]
            result: "DENIED"
          }
          backendTargetProjectNumber: "projects/12345567"
          cacheDecision: [2]
          remoteIp: "00.100.11.104"
          statusDetails: "denied_by_authz_policy"
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
      

      If you're using a regional internal Application Load Balancer, regional external Application Load Balancer, or cross-region internal Application Load Balancer, proxyStatus is set to error=\"http_request_error\"; details=\"denied_by_authz_policy\" and the name of the policy is logged in policies. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "name: "projects/12345567/locations/$REGION/authzPolicies/deny-authz-policy-test""
                result: "DENIED"
              }
            ]
            result: "DENIED"
          }
          backendTargetProjectNumber: "projects/12345567"
          remoteIp: "00.100.11.104"
          proxyStatus: "error=\"http_request_error\"; details=\"denied_by_authz_policy\""
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
      
    • When a request doesn't match the DENY policy, but matches with the ALLOW policy, the request is allowed. In the log, this action is logged as allowed_as_no_deny_policies_matched_request for the DENY policy. The policy that allowed the request is also logged.

      If you're using a global external Application Load Balancer, there is no statusDetails in the log. The policy that allowed the request is also logged in policies. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "allowed_as_no_deny_policies_matched_request"
                result: "ALLOWED"
              }
              1: {
                details: "name: "projects/12345567/locations/global/authzPolicies/allow-authz-policy-test""
                result: "ALLOWED"
              }
            ]
            result: "ALLOWED"
          }
          backendTargetProjectNumber: "projects/12345567"
          cacheDecision: [2]
          remoteIp: "00.100.11.104"
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
      
    • If you're using a regional internal Application Load Balancer, regional external Application Load Balancer, or cross-region internal Application Load Balancer, there is no proxyStatus field in the log. The policy that allowed the request is also logged in policies. See the following example:

      {
        httpRequest: {8}
        insertId: "example-id"
        jsonPayload: {
          @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
          authzPolicyInfo: {
            policies: [
              0: {
                details: "allowed_as_no_deny_policies_matched_request"
                result: "ALLOWED"
              }
              1: {
                details: "name: "projects/12345567/locations/$REGION/authzPolicies/allow-authz-policy-test""
                result: "ALLOWED"
              }
            ]
            result: "ALLOWED"
          }
          backendTargetProjectNumber: "projects/12345567"
          cacheDecision: [2]
          remoteIp: "00.100.11.104"
        }
        logName: "projects/example-project/logs/requests"
        receiveTimestamp: "2024-08-28T15:33:56.046651035Z"
        resource: {2}
        severity: "WARNING"
        spanId: "3e1a09a8e5e3e14d"
        timestamp: "2024-08-28T15:33:55.355042Z"
        trace: "projects/example-project/traces/8c8b3dbf9a19c85954d0fa2d958ca509"
      }
      

What's next