Address Match Lists and ACLs

Address match lists provide useful shorthand for an IP address subnet set of networks TSIG keys and even other address match lists. Address match lists may be applied to view definitions as we just reviewed or to certain DNS options particularly for those applying access control list (ACL) functionality and to control channel ACLs.

An address match list defines one or more address match list elements each of which may be defined as one of the following types:

  • IP address - match this individual IP address
  • IP address/length - match all addresses within this CIDR formatted network address
  • key key_id - match/require this TSIG key identified by key_id
  • acl_name - match this acl_name which itself defines an address match list
  • address_match_list - match the address list elements of this address match list (nested address match lists)

Pre-defined address_match_lists include any none localhost (IP address(es) on this server) and localnets (IP addresses on networks connected to this server). Negation of each address list element may also be defined using the bang (!) prefix.

The format of the address match list declaration follows.

address_match_list_name = address_match_list_element; [ address_ match_list_element; ]

An ACL (access control list) statement can be defined within named.conf to name an address match list for application to views, options or controls statements. The acl statement provides an explicit declaration of a address_match_list. The syntax of the acl statement is simply:

acl acl_name { address_match_list };

Thus when using address match lists they may be used explicitly within option view or controls statements or referenced by acl per the following examples.

acl 'internal' { 10.0.0.0/8; 172.16.0.0/12; };
allow-query { 10.0.0.0/8; 172.16.0.0/12; };
allow query { 'internal'; };

The last two statements above are equivalent. As you can see, the address match list syntax enables enumeration of multiple address match list elements. And this is where things get interesting. Order is important when constructing and address match list with multiple address match list elements. BIND processes an address match list by evaluating each address match list element in the order specified not as a boolean expression. Each evaluation may result in one of the following results:

  • Match accept and stop processing of subsequent elements
  • Match reject and stop processing of subsequent elements
  • No match evaluate the next element

If all elements have been evaluated with no match the evaluation is rejected. Consider the following ACL statement:

acl 'two-subnets' { 10.1.0.0/16; 10.2.0.0/16; };

ACL Evaluation

Now let's evaluate this ACL against IP address 10.2.29.56. Evaluating the first address match list element 10.1.0.0/16 we can see there is no match. So we move to the next element 10.2.0.0/16 which does match and the address is accepted. In evaluating address 10.3.0.4 the first element evaluation would result in 'no match ' as would evaluation of the second element. Therefore the 10.3.0.4 address does not match and is rejected.

OK so far not too interesting right? But now let'™s consider negation and how this impacts the outcome. Let'™s say we want to define an address match list to include all addresses on subnet 10.10.30.0/24 except IP address 10.10.30.101. You might think the following would fit the bill:

acl 'net-less-one' { ! 10.10.30.101; 10.10.30.0/24; };

However, let's consider what happens when evaluating various IP address. First, for the IP address 10.10.30.101, the first element negation of this address indeed matches and the address is rejected. The second element is not considered. For address 10.10.30.5 which we desire to pass the first element is evaluated first: address 10.10.30.5 actually matches the first element because it is not address 10.10.30.101. In this case this first element match is accepted and the second element is not evaluated. In fact the problem is that any IP address will match the first element and all addresses will be accepted except 10.10.30.101.

If we reversed the order of the elements our net-less-one acl:

acl 'net-less-one' { 10.10.30.0/24; ! 10.10.30.101; };

We can see that all addresses within the 10.10.30.0/24 would match the first element unfortunately including 10.10.30.101! And all IP addresses outside the 10.10.30.0/24 subnet would not pass this first element evaluation but would pass the second! So how do we construct a useful ACL with negation?

The answer is to use nested ACLs. A match within a nested ACL is treated differently than a match of an individual element. Within a nested ACL, a 'match and reject' becomes a 'no match' and the next element is processed in the list if any. A 'match and accept' has the same meaning whether nested or not. A nested ACL is denoted within an interior pair of curly brackets.

Consider the following nested ACL:

acl 'net-less-one' {{!10.10.30.0/24; any;}; !10.10.30.101; };

In evaluating IP address 10.10.30.4 this address does not match the nested ACL {!10.10.30.0/24; any;}. But instead of rejecting the evaluation at this point it continues with the next element !10.10.30.101 which matches. In fact this is another example of matching any address except 10.10.30.101; as we've seen there are easier ways to define this! But consider negating the nested ACL as:

acl 'net-less-one' {!{!10.10.30.0/24; any;}; !10.10.30.101; };

The negation of the nested ACL means that any address outside the 10.10.30.0/24 subnet will be rejected. The nested ACL is evaluated as pass for such addresses then negated and hence rejected. Addresses within the 10.10.30.0/24 subnet do not match the nested ACL hence calling for evaluation of the next element. But first this result is negated resulting in acceptance only if the IP address is within the 10.10.30.0/24 subnet AND is not (due to negation) 10.10.30.101. We have our desired result!

A similar strategy may be used when you desire to to negate more than one address match list element. Again the address match list processing ceases once a match is found so address 10.10.20.102 will actually pass the following address match list:

acl 'negates-only-one' { ! 10.10.30.101; ! 10.10.30.102; };

This is due to the fact that it passes the first element check, that is it is not address 10.10.30.101. Upon passing this first element, the second is not checked. To perform negation of multiple elements, define an address match with the set of addresses without negation, then create an additional address match list which negates the first. The following configuration provides the desired behavior of negating both 10.10.30.101 and 10.10.30.102.

acl 'addresses-to-negate' { 10.10.30.101; 10.10.30.102; };

acl 'negation-match-list' { !{10.10.30.101; 10.10.30.102;} };

OR

acl 'negation-match-list' { ! 'addresses-to-negate'; };

Since both 10.10.30.101 and 10.10.30.102 match the address-to-negate acl or nested ACL they are both negated by the negation-match-list.

An address match list defined with a key is useful when securing updates or zone transfers requiring TSIG signatures. Here's an example with key type address match list elements.

dns-servers = key dns1-dns2.ipamworldwide.com; key dns1-dns3.ipamworldwide.com; key dns2-dns3.ipamworldwide.com ;

Address match lists can also contain a mix of different element types as well:

acl 'mix-match' {

'two-subnets';

! 10.10.30.101;

10.10.30.0/24;

key dns1-dns2.ipamworldwide.com;

};

ACL Truth Table

The following 'truth table' summarizes the impact of evaluating example ACLs nested or unnested for a set of IP addresses.

Example ACL

ACL Evaluation Result For:

X

Y

Z

acl '1' { X; Y; };

Accept

Accept

Reject

acl '2' { !X; Y; };

Reject

Accept

Accept

acl '3' { X; !Y; };

Accept

Reject

Accept

acl '4' { !X; !Y; };

Reject

Accept

Accept

acl '5' { !{ X; Y; }; };

Reject

Reject

Accept

acl '6' { { X; any;}; Y; };

Accept

Accept

Accept

acl '7' { { X; !any;}; Y; };

Accept

Reject

Reject

acl '8' { { !X; any;}; Y; };

Reject unless Y matches

Accept

Accept

acl '9' { ! { !X; any;}; Y; };

Reject unless Y matches

Reject

Reject