AccessControl policy

What

The Access Control policy lets you allow or deny access to your APIs by specific IP addresses.

Video: Watch a short video to learn more about how the to allow or deny access to your APIs by specific IP addresses.

While you can attach this policy anywhere in the API proxy flow, you'll most likely want to check IP addresses at the beginning of the flow ( Request / ProxyEndpoint / PreFlow), even before authentication or quota checking.

Samples

The mask values in the following IPv4 samples identify which of the four octets (8, 16, 24, 32 bits) the match rule considers when allowing or denying access. The default value is 32. See the mask attribute in the Element reference for more information.

Deny 198.51.100.1

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "DENY">
      <SourceAddress mask="32">198.51.100.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Deny all requests from client address: 198.51.100.1

Allow requests from any other client address.

Deny using variables

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "DENY">
      <SourceAddress mask="{kvm.mask.value}">{kvm.ip.value}</SourceAddress>
    </MatchRule>
    </IPRules>
</AccessControl>

Suppose you're using a key value map (KVM) to store values for masking and IPs. This is a handy approach to changing IPs and masking during runtime without having to update and redeploy your API proxy. You can use the KeyValueMapOperations policy to retrieve the variables containing the values for kvm.mask.value and kvm.ip.value (assuming that's what you named the variables in your KVM policy that contain the values of the mask and IP values from your KVM). If the values you retrieved were 24 for the mask and 198.51.100.1 for the IP address, the AccessControl policy would deny all requests from: 198.51.100.*

All other client addresses would be allowed.

Deny 198.51.100.*

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "DENY">
      <SourceAddress mask="24">198.51.100.1</SourceAddress>
    </MatchRule>
    </IPRules>
</AccessControl>

Deny all requests from client address: 198.51.100.*

Allow requests from any other client address.

198.51.*.*

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "DENY">
       <SourceAddress mask="16">198.51.100.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Deny all requests from client address: 198.51.*.*

Allow requests from any other client address.

Deny 198.51.100.*, allow 192.0.2.1

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "ALLOW">
      <SourceAddress mask="32">192.0.2.1</SourceAddress>
    </MatchRule>
    <MatchRule action = "DENY">
      <SourceAddress mask="24">198.51.100.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Deny all requests from client address: 198.51.100.*, but allow 192.0.2.1.

Allow requests from any other client address.

Allow 198.51.*.*

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "DENY">
    <MatchRule action = "ALLOW">
      <SourceAddress mask="16">198.51.100.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Allow all requests from address: 198.51.*.*

Deny requests from any other client address.

Allow multiple IPs

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "DENY">
    <MatchRule action = "ALLOW">
      <SourceAddress mask="24">198.51.100.1</SourceAddress>
      <SourceAddress mask="24">192.0.2.1</SourceAddress>
      <SourceAddress mask="24">203.0.113.1</SourceAddress>
     </MatchRule>
  </IPRules>
</AccessControl>

Allow requests from client addresses: 198.51.100.* 192.0.2.* 203.0.113.*

Deny all other addresses.

Deny multiple IPs

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "DENY">
      <SourceAddress mask="24">198.51.100.1</SourceAddress>
      <SourceAddress mask="24">192.0.2.1</SourceAddress>
      <SourceAddress mask="24">203.0.113.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Deny requests from client addresses: 198.51.100.* 192.0.2.* 203.0.113.*

Allow all other addresses.

Allow multiple IPs, deny multiple IPs

<AccessControl name="ACL">
  <IPRules noRuleMatchAction = "DENY">
    <MatchRule action = "DENY">
      <SourceAddress mask="24">198.51.100.1</SourceAddress>
      <SourceAddress mask="24">192.0.2.1</SourceAddress>
      <SourceAddress mask="24">203.0.113.1</SourceAddress>
    </MatchRule>
    <MatchRule action = "ALLOW">
      <SourceAddress mask="16">198.51.100.1</SourceAddress>
      <SourceAddress mask="16">192.0.2.1</SourceAddress>
      <SourceAddress mask="16">203.0.113.1</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

Allow: 198.51.*.* 192.0.*.* 203.0.*.*

Deny a subset of the allow list: 198.51.100.* 192.0.2.* 203.0.113.*


Usage notes

In addition to protecting your APIs against malicious IPs, the Access Control policy also gives you control over legitimate IP access. For example, if you only want computers under the control of your enterprise to access the APIs exposed in your test environment, you can allow the IP address range for your internal network. Developers working from home can access these APIs using VPN.

The configuration and execution of an Access Control policy involves the following:

  • Define a set of match rules with one of two actions (ALLOW or DENY) associated with each.
  • For each match rule, specify the IP address (SourceAddress element).
    • Configure a mask for each IP address. You allow or deny access based on a mask value on the IP address. See About IP masking with CIDR notation.
    • The X-FORWARDED-FOR header is considered the source address. If there are multiple addresses in the header, use the <ValidateBasedOn> element to control which are evaluated.
  • Specify the order in which the rules are tested.
  • All the match rules are executed in the given order. When a rules matches, the corresponding action is executed and following match rules are skipped.
    • If the same rule is configured with both ALLOW and DENY actions, the rule that is defined first in the order is triggered and the subsequent rule (with the other action) is skipped.

About the X-Forwarded-For HTTP header

The Access Control policy evaulates the IP addresses in the X-Forwarded-For HTTP header. Edge for the Cloud automatically populates that header with the IP addresses it received from the last external TCP handshake (such as the client IP or router). If there are multiple IP addresses in the header, the header contains a list of IP addresses of the chain of servers that processed a request.

The leftmost IP address in the header belongs to the client and the rightmost is the server which forwarded the request to the current service. For example, the X-Forwarded-For header in the request to an API proxy (viewed in the Trace tool) contains the following:

192.0.2.0,192.0.2.128,192.0.2.244

The X-Forwarded-For header has the potential for being spoofed by an IP address you want to deny, except for the last address in the header, which is the IP address Edge received from the last external TCP handshake.

Edge Analytics writes the value of the X-Forwarded-For header to the x_forwarded_for_ip dimension. To determine the client IP that made the request to Edge, use the values in the ax_true_client_ip or ax_resolved_client_ip dimenstions. See Analytics metrics, dimensions, and filters reference for more.

For any version of Edge for the Private Cloud, you can configure it to consider all IPs in the X-Forwarded-For header. You'd want to do this in an architecture where you trust the IP addresses in X-Forwarded-For. Configuring Edge to allow X-Forwarded-For IP addresses involves setting the feature.enableMultipleXForwardCheckForACL property in your organization.

Private Cloud customers set the feature.enableMultipleXForwardCheckForACL property with the following API call. If there are other properties set on your organization, be sure to include those as well. Otherwise, they'll be removed.
curl -u email:password -X POST -H "Content-type:application/xml" http://host:8080/v1/o/myorg -d \
"<Organization type="trial" name="MyOrganization">
    <DisplayName>MyOrganization</DisplayName>
    <Properties>
        <Property name="feature.enableMultipleXForwardCheckForACL">true</Property>
        <!-- Include other existing properties as well. -->
    </Properties>
</Organization>"

After changing the value of the feature.enableMultipleXForwardCheckForACL property, you must restart your Message Processors, as described in Start/stop/restart individual components.

About IP masking with CIDR notation

CIDR notation (Classless Inter-Domain Routing) is a way of indicating a range of IP addresses through masking. It applies to both IPv4 and IPv6. Here's how it works. We'll use IPv4 in our examples for simplicity.

IP addresses are groups of numbers separated by periods. In binary terms, each group is a specific number of bits (8 for IPv4 and 16 for IPv6). The IPv4 address 198.51.100.1 looks like this in binary:

11000110.00110011.01100100.00000001

That's 4 groups of 8 bits, or 32 total bits. With CIDR, you can indicate a range by adding a /number (1-32) to the IP address, like this:

198.51.100.1/24

In this case, the 24 is the number you would use for the mask attribute value in this policy.

This notation means, "Keep the first 24 bits exactly as is, the remaining bits can be any value 0 through 255." For example:

Keep these exactly as is Possible values for the last group
198.51.100. 0 - 255

Notice that the mask happens at the end of group three. This makes things nice and tidy, in essence creating a mask like this: 198.51.100.*. In most cases, using multiples of 8 (IPv4) and 16 (IPv6) will give you the masking level you want:

IPv4: 8, 16, 24, 32

IPv6: 16, 32, 48, 64, 80, 96, 112, 128

However, you can user other numbers for finer-grained control, which involves a little binary calculation. Here's an example using a mask of 30:

Keep these exactly as is Possible values
11000110.00110011.01100100.00000 (first 30 bits) 0000000, 0000001, 0000010, or 0000011
198.51.100. 0, 1, 2, or 3

In this example, with the configuration set to <SourceAddress mask="30">198.51.100.1</SourceAddress>, the following IPs would be allowed (or denied, depending on your rules):

  • 198.51.100.0
  • 198.51.100.1
  • 198.51.100.2
  • 198.51.100.3

Element reference

The element reference describes the elements and attributes of the Access Control policy.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AccessControl async="false" continueOnError="false" enabled="true" name="Access-Control-1">
    <DisplayName>Access Control 1</DisplayName>
    <IPRules noRuleMatchAction = "ALLOW">
        <MatchRule action = "ALLOW">
            <SourceAddress mask="32">198.51.100.1</SourceAddress>
        </MatchRule>
        <MatchRule action = "DENY">
            <SourceAddress mask="24">198.51.100.1</SourceAddress>
        </MatchRule>
    </IPRules>
    <ValidateBasedOn>X_FORWARDED_FOR_ALL_IP</ValidateBasedOn>
</AccessControl>

<AccessControl> attributes

<AccessControl async="false" continueOnError="false" enabled="true" name="Access-Control-1"> 

The following table describes attributes that are common to all policy parent elements:

Attribute Description Default Presence
name

The internal name of the policy. The value of the name attribute can contain letters, numbers, spaces, hyphens, underscores, and periods. This value cannot exceed 255 characters.

Optionally, use the <DisplayName> element to label the policy in the management UI proxy editor with a different, natural-language name.

N/A Required
continueOnError

Set to false to return an error when a policy fails. This is expected behavior for most policies.

Set to true to have flow execution continue even after a policy fails.

false Optional
enabled

Set to true to enforce the policy.

Set to false to "turn off" the policy. The policy will not be enforced even if it remains attached to a flow.

true Optional
async

This attribute is deprecated.

false Deprecated

<DisplayName> element

Use in addition to the name attribute to label the policy in the management UI proxy editor with a different, natural-language name.

<DisplayName>Policy Display Name</DisplayName>
Default

N/A

If you omit this element, the value of the policy's name attribute is used.

Presence Optional
Type String

<IPRules> element

The parent element containing the rules that allow or deny IP addresses. The noRuleMatchAction attribute lets you define how to handle any IP addresses that aren't covered by your matching rules.

<IPRules noRuleMatchAction = "ALLOW">
Default N/A
Presence Optional
Type N/A

Attributes

Attribute Description Type Default Presence
noRuleMatchAction
The action to take (allow or deny access) if the match rule specified is not resolved (unmatched).
Valid value: ALLOW or DENY
String ALLOW Required

<IPRules>/<MatchRule> element

The action to take (allow or deny access) if the IP address matches the SourceAddress(es) you define.

<IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "ALLOW">
        <SourceAddress mask="32">198.51.100.1</SourceAddress>
    </MatchRule>
    <MatchRule action = "DENY">
        <SourceAddress mask="24">198.51.100.1</SourceAddress>
    </MatchRule>
</IPRules>
Default N/A
Presence Optional
Type N/A

Attributes

Attribute Description Type Default Presence
action

The action to take (allow or deny access) if the match rule specified is not resolved (unmatched).

Valid value: ALLOW or DENY

String ALLOW Required

<IPRules>/<MatchRule>/<SourceAddress> element

The IP address range of a client.

Valid value: Valid IP address (dotted decimal notation). For wildcard behavior, use the mask attribute.

<IPRules noRuleMatchAction = "ALLOW">
    <MatchRule action = "ALLOW">
        <SourceAddress mask="{variable}">198.51.100.1</SourceAddress>
    </MatchRule>
    <MatchRule action = "DENY">
        <SourceAddress mask="24">{variable}</SourceAddress>
    </MatchRule>
</IPRules>

As shown in the previous example, the SourceAddress element also supports Message templates for the mask attribute or IP address, which means you can set the values using variables that are currently available in the API proxy flow. For example, you can store an IP address in a key value map (KVM) and use the KeyValueMapOperations policy to retrieve the IP address and assign it to a variable (such as kvm.ip.value). You can then use that variable for the IP address:

<SourceAddress mask="24">{kvm.ip.value}</SourceAddress>

Setting mask and/or IP address with a variable gives you the flexibility to change values at runtime without having to modify and re-deploy your API proxy.

Default N/A
Presence Optional
Type String (single IP address only)

Attributes

Attribute Description Type Default Presence
mask

The mask attribute is a way to indicate the range of IP addresses to allow or deny. Mask is the equivalent of using CIDR notation (Classless Inter-Domain Routing). For example:

<SourceAddress mask="24">198.51.100.1</SourceAddress>

is equivalent to the following CIDR notation:

198.51.100.1/24

Valid values:

IPv4: 1-32

IPv6: 1-128

A value of zero (0) is valid only for IP 0.0.0.0, hence impractical.

Set the mask with a variable

The mask attribute also supports Message templates, which means you can set the value with a variable that's currently available in the API proxy flow. For example, you can store a mask value in a KVM and use the KeyValueMapOperations policy to retrieve the mask and assign it to a variable. To set the IP mask with the variable, use the following format, assuming the variable is named kvm.mask.value:

mask="{kvm.mask.value}"

Integer N/A Required

<ValidateBasedOn> element

When the X-Forwarded-For HTTP header contains multiple IP addresses, use this ValidateBasedOn element to control which IP addresses are checked.

The value you enter in this element lets you determine whether to check all IP addresses in the header (default), only the first IP address, or only the last IP address.

<AccessControl async="false" continueOnError="false" enabled="true" name="Access-Control-1">
    <DisplayName>Access Control 1</DisplayName>
    <IPRules noRuleMatchAction = "ALLOW">
        <MatchRule action = "DENY">
            <SourceAddress mask="32">198.51.100.1</SourceAddress>
        </MatchRule>
    </IPRules>
    <ValidateBasedOn>X_FORWARDED_FOR_ALL_IP</ValidateBasedOn>
</AccessControl>
Default X_FORWARDED_FOR_ALL_IP
Presence Optional
Type String
Valid values
  • X_FORWARDED_FOR_ALL_IP (default)
  • X_FORWARDED_FOR_FIRST_IP
  • X_FORWARDED_FOR_LAST_IP

Error reference

This section describes the fault codes and error messages that are returned and fault variables that are set by Edge when this policy triggers an error. This information is important to know if you are developing fault rules to handle faults. To learn more, see What you need to know about policy errors and Handling faults.

Runtime errors

These errors can occur when the policy executes.

Fault code HTTP status Cause Fix
steps.accesscontrol.IPDeniedAccess 403 The client IP address, or an IP address passed as part of the X-Forwarded-For HTTP header in the API request, matches an IP address specified in the <SourceAddress> element within the <MatchRule> element of the Access Control Policy, and the action attribute of the <MatchRule> element is set to DENY. build

Fault variables

These variables are set when a runtime error occurs. For more information, see Variables specific to policy errors.

Variables Where Example
fault.name="fault_name" fault_name is the name of the fault, as listed in the Runtime errors table above. The fault name is the last part of the fault code. fault.name Matches "IPDeniedAccess"
acl.policy_name.failed policy_name is the user-specified name of the policy that threw the fault. acl.AC-AllowAccess.failed = true

Example fault response

{
   "fault":{
      "detail":{
         "errorcode":"steps.accesscontrol.IPDeniedAccess"
      },
      "faultstring":"Access Denied for client ip : 52.211.243.3"
   }
}

Example fault rule

<FaultRule name="IPDeniedAccess">
    <Step>
        <Name>AM-IPDeniedAccess</Name>
        <Condition>(fault.name Matches "IPDeniedAccess") </Condition>
    </Step>
    <Condition>(acl.failed = true) </Condition>
</FaultRule>