Using Access Control Lists (ACLs) with both addresses and keys
  • 06 Sep 2018
  • 2 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Using Access Control Lists (ACLs) with both addresses and keys

  • Dark
    Light
  • PDF

Article summary

Question:

How can I configure allow-update to permit updates, only if BOTH of the following are true?

  • They hold the secret key (TSIG)
  • They're in the 'permitted' subnet

Answer:

Here's the long version:

acl address_allow { 10/8; };
acl address_reject { !address_allow; any; };
allow-update { !address_reject; key "...."; };

And now in shorthand:

allow-update { !{!10/8;any;}; key update-key; };

Doesn't this permit any clients on the 10/8 network?
Intuitively, by (mistakenly) attempting to apply Boolean logic (despite the lack of an AND operator in BIND ACLs) , you might think that it does, but it doesn't.  What it actually says is 'deny anyone who isn't in subnet 10/8', and then 'allow anyone using this key'.

ACLs aren't boolean.  The ACL elements are processed in order (from left to right) and each ACL element has three possible results instead of two :

  • match and accept
  • match and reject
  • no match (which means "continue processing")

When an ordinary ACL element matches and is negated (for example, the element is "!10/8;" and the address is 10.0.0.1) that means "match and reject."  But if the match is inside of a nested ACL, then it's treated differently.  A negative result means "the nested ACL didn't match"--and so you continue processing.

Remember that a 'match' inside a nested ACL is treated differently to a match of an ordinary ACL element.
The point explained immediately above this information box is significant and important, hence the repetition with emphasis!

Therefore if you're checking address A against an ACL of one of the following forms, these will be the results:

    {     A;    B; }   == A is allowed, accept immediately
    {  {  A; }; B; }   == A is allowed, accept immediately
    {    !A;    B; }   == A is forbidden, reject immediately
    { !{  A; }; B; }   == A is forbidden, reject immediately
    {  { !A; }; B; }   == A matched but was negated, try element B
    { !{ !A; }; B; }   == A matched but was negated, try element B

Those last two lines there are confusingly similar (and, as written, useless).  The difference is what happens if you're checking an address other than A, and something else in the nested ACL matches it.

    {  { !A; any; }; B; }  == any address other than A is accepted at once,
                              but A is only accepted if B matches too.
                              boolean translation: ((not A) or (A and B))
    { !{ !A; any; }; B; }  == any address other than A is *rejected* at once,
                              but A is accepted as long as B matches too.
                              boolean translation: (A and B)

Hope that's helpful!