Media CDN uses Google Cloud Armor security policies to prevent unwelcome traffic from hitting its services. You can allow or deny requests based on the following:
- IPv4 and IPv6 addresses and ranges (CIDRs)
- Country code (geography)
- Layer 7 filtering
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.
You can decorate requests that Google Cloud Armor allows by inserting custom headers with configurable names and values.
Google Cloud Armor security policies apply to all content served from Media CDN, including both cached content and cache misses.
Google Cloud Armor security policies are configured per Media CDN service—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 to them, 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.
Media CDN doesn't consider the referer
header during
rule evaluation of Layer 7 header filtering edge security policies when it's
set to any of the following values:
- Multiple URLs
- A relative URL
- A valid absolute URLs containing user information or a fragment component
Configure security policies
Use the following instructions to configure a security policy.
Before you begin
To attach a Google Cloud Armor security policy to a Media CDN service, ensure the following:
- Be familiar with Google Cloud Armor.
- Have an existing Media CDN service that you want to apply the policy to.
- Optional, but recommended: enable logging on your Media CDN service so that you can identify blocked requests.
You also need the following Identity and Access Management permissions to authorize, create, and attach security policies to a Media CDN 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 a Media CDN 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, Google Cloud Armor continues to evaluate
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 don't 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
, use the
gcloud compute security-policies create
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
Add rules to a security policy
Google Cloud Armor security policies are sets of rules that match on Layer 7 attributes to protect externally facing applications or services. Each rule is evaluated with respect to incoming traffic.
These attributes can be used for HTTP requests in security policies:
request.headers
, request.method
, request.path
, request.scheme
, and
request.query
. For more information about writing expressions for security
policy rules, see the
Google Cloud Armor custom rules language reference.
A Google Cloud Armor security policy rule consists of a match condition and an action to take when that condition is met.
gcloud
To create a rule for a security policy, use the
gcloud compute security-policies rules create PRIORITY
command.
Replace PRIORITY
with the priority of the rule in the
policy:
gcloud compute security-policies rules create PRIORITY \ --security-policy POLICY_NAME \ --description DESCRIPTION \ --src-ip-ranges IP_RANGES | --expression EXPRESSION \ --action=[ allow | deny-403 | deny-404 | deny-502 ] \ --preview
Attach a policy to a service
gcloud
To attach an existing Google Cloud Armor policy to a
Media CDN service, use the
gcloud edge-cache services update
command:
gcloud edge-cache services update MY_SERVICE \ --edge-security-policy=SECURITY_POLICY
Update a rule in a security policy
Use these instructions to update a single rule in a Google Cloud Armor security policy. Alternatively, you can atomically update multiple rules in a security policy.
gcloud
Use the
gcloud compute security-policies rules update
command:
gcloud compute security-policies rules update PRIORITY [ \ --security-policy POLICY_NAME \ --description DESCRIPTION \ --src-ip-ranges IP_RANGES | --expression EXPRESSION \ --action=[ allow | deny-403 | deny-404 | deny-502 ] \ --preview ]
For example, the following command updates a rule with priority 1111 to allow traffic from the IP address range 192.0.2.0/24:
gcloud compute security-policies rules update 1111 \ --security-policy my-policy \ --description "allow traffic from 192.0.2.0/24" \ --src-ip-ranges "192.0.2.0/24" \ --action "allow"
To update the priority of a rule, you must use the REST API. For more
information, see the
securityPolicies.patchRule
method.
View a policy attachment
To review what policy is attached to an existing service, inspect (describe) that service.
gcloud
To view the Google Cloud Armor policy that's attached to a
Media CDN service, use the
gcloud edge-cache services describe
command:
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
Use the
gcloud edge-cache services update
command:
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.
Examples
Consider the following detailed example use cases.
Example: Identify blocked requests
You must have logging enabled for a 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"
Example: 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:
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.
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 want to only allow clients located in India, as well as
any IP addresses that are in the allowlist, including those of 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 combine multiple rules by using boolean operators.
The policy allows traffic from clients in India, allows clients from a defined IP range, and denies all other traffic.
When you view the details of the policy, the output 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 by using
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
combine 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 combined 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 combine 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
Example: Deny requests for cached content with specific headers
An edge security policy applies to all requests targeting any Media CDN service that the policy is attached to. This policy enforcement takes place before any cache lookup. Requests that are not allowed by the edge security policy are denied with the configured status code.
The following expression matches against requests from the IP address 1.2.3.4
that contain the string user1
in the user-agent
header:
inIpRange(origin.ip, '1.2.3.4/32') && request.headers['user-agent'].contains('user1')
The following command adds the filtering rule 105
to the edge security policy
my-edge-policy
, which is attached to a Media CDN service:
gcloud compute security-policies rules create 105 \ --security-policy my-edge-policy \ --expression = "inIpRange(origin.ip, '1.2.3.4/32') && request.headers['user-agent'].contains('charlie')" \ --action= deny-403 \ --description="block requests from IP addresses in which the user-agent header contains the string charlie"
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, or 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 combined together.
- Visit the logging documentation to understand how to query request logs and check which requests have been blocked.