Media CDN uses Google Cloud Armor to provide support for IP address allowlists and denylists, as well as filtering controls based on country and region codes (geographic location). You can:
- Deny requests based on IPv4 and IPv6 addresses and ranges (CIDRs).
- Allow or deny requests based on country code (geography).
- Allow requests based on IPv4 and IPv6 addresses and ranges (CIDRs).
These capabilities let you restrict content downloads to users in specific locations where you have content licensing restrictions, only allow corporate IP addresses to access testing or staging endpoints, and deny a list of known bad client IP addresses.
Google Cloud Armor policies apply to all content served from Media CDN, including both cached content and cache misses.
IP address and geographic policies are configured per-Edge Cache service
(EdgeCacheService
)—all requests destined for that service's IP address
(or hostnames) have the security policy enforced consistently. Different
services can have different security policies applied, and you can create
multiple services for different geographies, as needed.
For more fine-grained protection of content at a per-user level, we recommend using signed URLs and signed cookies in conjunction with a Google Cloud Armor policy.
Configure security policies
Use the following instructions to configure a security policy.
Before you begin
To attach a Google Cloud Armor security policy to an Edge Cache service, you should:
- Be familiar with Google Cloud Armor.
- Have an existing Edge Cache service that you wish to apply the policy to.
- Optional, but recommended: enable logging on your Edge Cache service so you can identify blocked requests.
You also need the following Identity and Access Management permissions to authorize, create, and attach security policies to an Edge Cache service:
compute.securityPolicies.addAssociation
compute.securityPolicies.create
compute.securityPolicies.delete
compute.securityPolicies.get
compute.securityPolicies.list
compute.securityPolicies.update
compute.securityPolicies.use
Users that need to attach an existing certificate to an Edge Cache service only require these IAM permissions:
compute.securityPolicies.get
compute.securityPolicies.list
compute.securityPolicies.use
The roles/networkservices.edgeCacheUser
role includes all of these
permissions.
Create a security policy
Google Cloud Armor security policies are composed of several rules, with each rule defining a set of matching criteria (an expression) for a request, and an action. For example, an expression can contain matching logic for clients that are located in India, with the associated action being allow. If a request doesn't match the rule, the evaluation continues to the next rule, until all rules have been attempted.
Security policies have a default rule with an allow action. The default
rule allows requests that do not match preceding rules. This can be changed to a
deny
rule when you want to allow
only requests that match preceding rules
and reject all others.
The following example shows how to create a rule that blocks all clients geolocated to Australia with a HTTP 403, and allows all other requests.
gcloud
To create a new policy of type CLOUD_ARMOR_EDGE
, run the following
command:
gcloud compute security-policies create block-australia \ --type="CLOUD_ARMOR_EDGE" --project="PROJECT_ID"
This creates a policy with a default allow rule at the lowest
priority (priority: 2147483647
):
Created [https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/securityPolicies/block-australia].
Then you can add a rule with a higher priority:
gcloud compute security-policies rules create 1000 \ --security-policy=block-australia --description "block AU" \ --expression="origin.region_code == 'AU'" --action="deny-403"
The output is the following:
Updated [https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/securityPolicies/block-australia].
Terraform
If you inspect the policy, you see the two rules: the first rule blocks
requests originating from Australia (origin.region_code == 'AU'
) and the
second, lowest priority rule, allows all traffic not matching the higher
priority rule (or rules).
kind: compute#securityPolicy name: block-australia rules: - action: deny(403) description: block AU kind: compute#securityPolicyRule match: expr: expression: origin.region_code == 'AU' preview: false priority: 1000 - action: allow description: default rule kind: compute#securityPolicyRule match: config: srcIpRanges: - '*' versionedExpr: SRC_IPS_V1 preview: false priority: 2147483647 ruleNumber: '1' type: CLOUD_ARMOR_EDGE
To attach this policy to your Media CDN, see the next section.
Attach a policy to a service
To attach an existing Google Cloud Armor policy named
us-only-delivery-policy
to an Edge Cache service called
prod-media-service
:
gcloud
gcloud edge-cache services update prod-media-service \ --edge-security-policy=us-only-delivery-policy
View a policy attachment
To review what policy is attached to an existing service, inspect (describe) that service:
gcloud
gcloud edge-cache services describe MY_SERVICE
The edgeSecurityPolicy
field of the service describes the
attached policy:
name: "MY_SERVICE" edgeSecurityPolicy: "SECURITY_POLICY
Remove a policy
To remove an existing policy, update the associated service and pass an empty string as the policy:
gcloud
gcloud edge-cache services update MY_SERVICE \ --edge-security-policy=""
The edgeSecurityPolicy
field is now omitted from the output of the
gcloud edge-cache services describe MY_SERVICE
command.
Identify blocked requests
You must have logging enabled for the given Edge Cache service for blocked requests to be logged.
Requests allowed or denied by a filtering policy are logged to
Logging. To filter for rejected requests, the following
Logging query
for the prod-video-service
configuration would look like:
resource.type="edge_cache_service" jsonPayload.statusDetails="denied_by_security_policy"
Customize response codes
Google Cloud Armor rules can be configured to return a specific status code
as the action associated with a given rule. In most cases, it's best to
return an HTTP 403 (deny-403
) code to clearly signal that the client was
blocked by the rule.
The supported status codes are:
- HTTP 403 (Forbidden)
- HTTP 404 (Not Found)
- HTTP 502 (Bad Gateway)
The following example demonstrates how to configure the returned status code:
gcloud
To specify one of [allow | deny-403 | deny-404 | deny-502]
as the action
associated with the rule, run the following command. This example configures
the rule to return an HTTP 502.
gcloud compute security-policies rules create 1000 \ --security-policy=block-australia --description "block AU" \ --expression="origin.region_code == 'AU'" --action="deny-502"
Each rule in a security policy can define a different status code response.
Examples
Consider the following detailed example use cases.
Example: Deny clients outside of a country, except for allowed IP addresses
A common case in media serving is denying connections from clients that are outside of the region for which you have content licenses or payment mechanisms.
For example, you might wish to only allow clients located in India, as well as
any allowlisted IP addresses (content partners and your own employees) within
the range 192.0.2.0/24
, and reject all others.
Using the Google Cloud Armor custom rules language, the following expression achieves this:
origin.region_code == "IN" || inIpRange(origin.ip, '192.0.2.0/24')
This expression is configured as an allow
rule, with a default deny
rule configured to match all other clients. Security policies
always have a default rule.
You typically configure this to default deny
traffic that you don't
explicitly allow. In other cases, you might choose to block some traffic and
default allow
all other traffic.
In the security policy output, note the following:
- The highest priority (
priority: 0
) rule allows traffic from India OR from the defined list of IP addresses. - The lowest priority rule represents a
default deny
. The rules engine denies all clients that higher priority rules don't evaluate to true. - You can chain multiple rules by using boolean operators.
The following policy allows traffic from clients in India, allows clients from a defined IP range, and denies all other traffic:
gcloud
Run the following security-policies describe
command:
gcloud compute security-policies describe allow-india-only
This outputs a Google Cloud Armor policy that resembles the following:
kind: compute#securityPolicy name: allow-india-only type: "CLOUD_ARMOR_EDGE" rules: - action: allow description: '' kind: compute#securityPolicyRule match: expr: expression: origin.region_code == "IN" || inIpRange(origin.ip, '192.0.2.0/24') preview: false priority: 0 - action: deny(403) description: Default rule, higher priority overrides it kind: compute#securityPolicyRule match: config: srcIpRanges: - '*' versionedExpr: SRC_IPS_V1 preview: false priority: 2147483647
You can also set a custom response header
with the {region_code}
header variable. This header can be inspected with
JavaScript and reflected to the client.
Example: Block malicious clients by IP address and IP ranges
Using the Google Cloud Armor custom rules language, the following expression achieves this:
inIpRange(origin.ip, '192.0.2.2/32') || inIpRange(origin.ip, '192.0.2.170/32')
You can block IP ranges up to a /8
mask in IPv4 and a /32
in IPv6. A
common case for streaming platforms is blocking the egress IP ranges of proxies
or VPN providers to minimize content licensing circumvention:
inIpRange(origin.ip, '192.0.2.0/24') || inIpRange(origin.ip, '198.51.100.0/24') || inIpRange(origin.ip, '203.0.113.0/24') || inIpRange(origin.ip, '2001:DB8::B33F:2002/64')
Both IPv4 and IPv6 address ranges are supported.
Example: Only allow a fixed list of geographies
If you have a list of country codes, you can use the boolean OR operator ||
to
chain match conditions.
Using the Google Cloud Armor custom rules language, the following expression allows users identified as coming from Australia or New Zealand:
origin.region_code == "AU" || origin.region_code == "NZ"
This can additionally be chained with origin.ip
or inIpRange(origin.ip,
'...')
expressions to allow testers, partners, and your corporate IP ranges,
even if they are not from one of the specified geographies.
There is the documented number of subexpressions for each rule with a custom expression. If you need to chain together more subexpressions, define multiple rules within a single policy.
Example: Block clients from a specific set of countries
A less common example might be to block clients from a certain set of countries, but otherwise allow requests from all other countries.
To do this, you create a policy that blocks both the country and any clients where their region cannot be determined, and then fall through to a default allow rule for all other requests.
The following example describes a policy that blocks clients from Canada, as well as any clients where the location is unknown, but allows all other traffic:
kind: compute#securityPolicy name: block-canada type: "CLOUD_ARMOR_EDGE" rules: - action: deny(403) description: '' kind: compute#securityPolicyRule match: expr: expression: origin.region_code == "CA" || origin.region_code == "ZZ" preview: false priority: 0 - action: allow description: Default rule, higher priority overrides it kind: compute#securityPolicyRule match: config: srcIpRanges: - '*' versionedExpr: SRC_IPS_V1 preview: false priority: 2147483647
Logging enforcement actions
Each request log provides details about which security policy
was applied and whether the request was allowed (ALLOW
) or rejected (DENY
).
To enable logging, ensure that logConfig.enable
is set to true
on your
service. Services without logs enabled don't log security policy events.
When a client is located outside the United States and a security policy called
deny-non-us-clients
is in force that denies requests that originate outside
the US, this is the log entry for a denied request:
enforcedSecurityPolicy: name: deny-non-us-clients outcome: DENY
Services with no Google Cloud Armor policy attached contain no_policy
as
the value of enforcedSecurityPolicy.name
and an outcome
of ALLOW
. For
example, a request log entry for a service without a
policy attached has the following values:
enforcedSecurityPolicy: name: no_policy outcome: ALLOW
Understand GeoIP classifications
Media CDN relies on Google's internal IP classification data-sources to derive a location (region, state/province, city) from an IP address. If you are migrating from, or splitting traffic between, multiple providers, a small number of IP addresses might sometimes be associated with different locations.
- Google Cloud Armor uses ISO 3166-1 alpha 2 region codes to associate a client to a geographic location.
- For example,
US
for the United States, orAU
for Australia. - In some cases, a region corresponds to a country, but this is not always the
case. For example, the
US
code includes all states of the United States, one district, and six outlying areas. - For more information, see unicode_region_subtag in the Unicode Technical Standard.
- For clients where the location cannot be derived, the
origin.region_code
is set toZZ
.
You can add
geographic data to response headers
to an Media CDN endpoint (with
routing.routeRules[].headerActions[].responseHeadersToAdd[]
) or reflect the
geographic data provided to a Cloud
Function to validate any differences
between geoIP data sources during initial integration and testing.
Additionally, Media CDN request logs include the clientRegion
and other client-specific data that you can validate against your existing data
sources.
What's next
- Learn how to use signed requests to authorize content on a per-user basis.
- Review the Google Cloud Armor rules reference to understand how IP and geographic matching rules can be expressed and chained together.
- Visit the logging documentation to understand how to query request logs and check which requests have been blocked.