5

I just switched to bind 9.9.5 dynamic DNS feature with semi-automatic management of DNSSEC entries, the whole process went good and my zone files were updated well, but now I can't update or add entries via nsupdate tool.

The /etc/bind/named.conf.local:

// 1
view "localhost_view" {
    
    allow-query-on { 127.0.0.1; };
    allow-query { localhost_acl; };
    match-clients { localhost_acl; };

    zone "somehost.tld" {
            type master;
            file "/etc/bind/db.somehost.tld_10";
    };

    zone "168.192.in-addr.arpa" {
            type master;
            notify no;
            file "/etc/bind/db.192.168.10";
    };

    // formerly named.conf.default-zones

        zone "." {
                type hint;
                file "/etc/bind/db.root";
        };

        zone "localhost" {
                type master;
                file "/etc/bind/db.local";
        };

        zone "127.in-addr.arpa" {
                type master;
                file "/etc/bind/db.127";
        };

        zone "0.in-addr.arpa" {
                type master;
                file "/etc/bind/db.0";
        };

        zone "255.in-addr.arpa" {
                type master;
                file "/etc/bind/db.255";
        };

    // formerly zones.rfc1918

        zone "10.in-addr.arpa"      { type master; file "/etc/bind/db.empty"; };
        zone "16.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "17.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "18.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "19.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "20.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "21.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "22.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "23.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "24.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "25.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "26.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "27.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "28.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "29.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "30.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "31.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };

};

// 2
view "internal_10_view" {
    
    allow-query-on { 192.168.10.1; };
    allow-query { internal_10_acl; };
    match-clients { internal_10_acl; };

    zone "somehost.tld" {
            type master;
            file "/etc/bind/db.somehost.tld_10";
    };

    zone "168.192.in-addr.arpa" {
            type master;
            notify no;
            file "/etc/bind/db.192.168.10";
    };

    // formerly named.conf.default-zones

        zone "." {
                type hint;
                file "/etc/bind/db.root";
        };

        zone "localhost" {
                type master;
                file "/etc/bind/db.local";
        };

        zone "127.in-addr.arpa" {
                type master;
                file "/etc/bind/db.127";
        };

        zone "0.in-addr.arpa" {
                type master;
                file "/etc/bind/db.0";
        };

        zone "255.in-addr.arpa" {
                type master;
                file "/etc/bind/db.255";
        };

    // formerly zones.rfc1918

        zone "10.in-addr.arpa"      { type master; file "/etc/bind/db.empty"; };
        zone "16.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "17.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "18.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "19.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "20.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "21.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "22.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "23.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "24.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "25.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "26.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "27.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "28.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "29.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "30.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "31.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };

};

// 3
view "internal_150_view" {

        allow-query-on { 192.168.150.1; };
        allow-query { internal_150_acl; };
    match-clients { internal_150_acl; };

    zone "somehost.tld" {
            type master;
            file "/etc/bind/db.somehost.tld_150";
    };

    zone "168.192.in-addr.arpa" {
            type master;
            notify no;
            file "/etc/bind/db.192.168.150";
    };

    // formerly named.conf.default-zones

        zone "." {
                type hint;
                file "/etc/bind/db.root";
        };

        zone "localhost" {
                type master;
                file "/etc/bind/db.local";
        };

        zone "127.in-addr.arpa" {
                type master;
                file "/etc/bind/db.127";
        };

        zone "0.in-addr.arpa" {
                type master;
                file "/etc/bind/db.0";
        };

        zone "255.in-addr.arpa" {
                type master;
                file "/etc/bind/db.255";
        };

    // formerly zones.rfc1918

        zone "10.in-addr.arpa"      { type master; file "/etc/bind/db.empty"; };
        zone "16.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "17.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "18.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "19.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "20.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "21.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "22.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "23.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "24.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "25.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "26.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "27.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "28.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "29.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "30.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "31.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };

};

// 4
view "vpn_view" {
    
    allow-query-on { 192.168.200.1; };
    allow-query { vpn_acl; };
    match-clients { vpn_acl; };
        
    zone "somehost.tld" {
        type master;
        file "/etc/bind/db.somehost.tld_vpn";
    };

    // formerly named.conf.default-zones

        zone "." {
                type hint;
                file "/etc/bind/db.root";
        };

        zone "localhost" {
                type master;
                file "/etc/bind/db.local";
        };

        zone "127.in-addr.arpa" {
                type master;
                file "/etc/bind/db.127";
        };

        zone "0.in-addr.arpa" {
                type master;
                file "/etc/bind/db.0";
        };

        zone "255.in-addr.arpa" {
                type master;
                file "/etc/bind/db.255";
        };

    // formerly zones.rfc1918

        zone "10.in-addr.arpa"      { type master; file "/etc/bind/db.empty"; };
        zone "16.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "17.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "18.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "19.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "20.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "21.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "22.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "23.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "24.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "25.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "26.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "27.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "28.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "29.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "30.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "32.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };

};

// 5
view "global_view" {
    
    allow-query-on { 1.2.3.4; };
//  match-clients { any; !localhost_acl; !internal_10_acl; !internal_150_acl; !vpn_acl; };
    recursion no;
        
    zone "somehost.tld" {

        type master;

        update-policy local;
        auto-dnssec maintain;

        file "/etc/bind/db.somehost.tld_global";

        key-directory "/etc/bind/keys";

    };

    zone "26/4.3.2.1.in-addr.arpa" IN {
        type master;
        file "/etc/bind/db.rev";
    };

    // formerly named.conf.default-zones

        zone "." {
                type hint;
                file "/etc/bind/db.root";
        };

        zone "localhost" {
                type master;
                file "/etc/bind/db.local";
        };

        zone "127.in-addr.arpa" {
                type master;
                file "/etc/bind/db.127";
        };

        zone "0.in-addr.arpa" {
                type master;
                file "/etc/bind/db.0";
        };

        zone "255.in-addr.arpa" {
                type master;
                file "/etc/bind/db.255";
        };

    // formerly zones.rfc1918

        zone "10.in-addr.arpa"      { type master; file "/etc/bind/db.empty"; };
        zone "16.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "17.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "18.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "19.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "20.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "21.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "22.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "23.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "24.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "25.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "26.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "27.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "28.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "29.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "30.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };
        zone "32.172.in-addr.arpa"  { type master; file "/etc/bind/db.empty"; };

};

ACLs:

acl localhost_acl {
        127.0.0.0/8;
};

acl internal_10_acl {
        192.168.10.0/24;
};

acl internal_150_acl {
        192.168.150.0/24;
};

acl vpn_acl {
        192.168.200.2;
        192.168.200.5;
};

So the update-policy local; is here, /var/run/named/session.key is successfully generated and user bind readable, but when I perform the add command via nsupdate -l (as root), I always get the update failed: REFUSED error (here with debug messages):

root@somehost:/etc/bind# nsupdate -l -v -D
setup_system()
Creating key...
namefromtext
keycreate
reset_system()
user_interaction()
> ttl 46000
do_next_command()
> zone somehost.tld.
do_next_command()
> update add whatever.somehost.tld. A 1.1.1.1
do_next_command()
evaluate_update()
update_addordelete()
> send
do_next_command()
start_update()
send_update()
Sending update to 127.0.0.1#53
show_message()
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  15363
;; flags:; ZONE: 1, PREREQ: 0, UPDATE: 1, ADDITIONAL: 1
;; ZONE SECTION:
;somehost.tld.                      IN      SOA

;; UPDATE SECTION:
whatever.somehost.tld.  46000   IN      A       1.1.1.1

;; TSIG PSEUDOSECTION:
local-ddns.             0       ANY     TSIG    hmac-sha256. 1446539060 300 32 r2lt18dGihGnJepoUjvIKx8l5BPMohNJvsLoO+WQiBE                                                                         = 15363 NOERROR 0

update_completed()
tsig verification successful
show_message()

Reply from update query:
;; ->>HEADER<<- opcode: UPDATE, status: REFUSED, id:  15363
;; flags: qr ra; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;somehost.tld.                      IN      SOA

;; TSIG PSEUDOSECTION:
local-ddns.             0       ANY     TSIG    hmac-sha256. 1446539060 300 32 Cnh9Tgg5vhKngPRk2J8n0wiRzdBLlQrp0F0qmfUotN8                                                                         = 15363 NOERROR 0

done_update()
reset_system()
user_interaction()
> quit

It is some kind of permission issue? What's wrong?

Neurotransmitter
  • 468
  • 1
  • 6
  • 17
  • Does your update hit the correct view? I noted that `match-clients` starts with `any`, so I guess all elements after that are essentially pointless. If there are similar mistakes for other views that's one thing that could explain hitting the wrong view. – Håkan Lindqvist Nov 03 '15 at 09:16
  • I've commented the `match-clients` string in the `global_view` view and added one more view (`localhost_view`) to the question for your reference. The error persists. – Neurotransmitter Nov 03 '15 at 09:50
  • Could anything ever hit the second view? (Based on what I noted about the first view) – Håkan Lindqvist Nov 03 '15 at 09:52
  • The second (`localhost_view`) view is hit only by `localhost_acl` IPs which are belong to the `127.0.0.0/8` subnet. And they actually hit it. – Neurotransmitter Nov 03 '15 at 09:54
  • Are the views actually in this order? Or the other way around? – Håkan Lindqvist Nov 03 '15 at 09:54
  • Order have meaning? Okay, I'll paste my whole `named.conf.local` now. Wait a minute. – Neurotransmitter Nov 03 '15 at 09:55
  • Yes, for any incoming message (query, update, ...) the first view that matches is the view it will hit. (And for address match lists, the first element that matches decides the result, hence why `any` at the beginning is a bad idea.) – Håkan Lindqvist Nov 03 '15 at 09:57
  • Here you go, I updated the question. Take a look. – Neurotransmitter Nov 03 '15 at 09:59

2 Answers2

7

Finally figured it out. Thanks to @Håkan Lindqvist for inspiration.

These solutions are probably Debian/Ubuntu-specific and weren't tested in other distros.

1. FIRST SOLUTION

(Using update-policy local;).

You may actually use update-policy local; directive in /etc/bind/named.conf.local in each zone declaration you want, which restricts update requests from the internet or LAN for better security. In this case the key is generated automatically and nsupdate will use it if run with -l option.

Instead of server X.X.X.X command one should use local X.X.X.X. It accepts even public IP as argument if it is local to the system!

Note: the key is not world-readable, so use sudo.

Example:

me@somehost:~$ sudo nsupdate -l
> local 1.2.3.4
> zone somehost.tld
> update add something.somehost.tld. 86400 A 1.1.1.1
> send
> quit

2. SECOND SOLUTION

(Using ddns-confgen).

I have plenty of views (localhost_view, global_view etc.), some of which have common zones (somehost.tld in my example). If I want to dynamically update them, I should use server X.X.X.X command when do nsupdate. Therefore nsupdate will send an update request to the appropriate interface and the appropriate view will handle it.

update-policy local; is not suitable in this configuration because it forbids use of the server command in nsupdate. So it is needed to generate a DDNS key and specify it in all of the zones declarations, which should be dynamically updated by nsupdate. In the Debian universe there is a ddns-confgen command which simplifies this task a lot:

me@somehost:~$ ddns-confgen
# To activate this key, place the following in named.conf, and
# in a separate keyfile on the system or systems from which nsupdate
# will be run:
key "ddns-key" {
        algorithm hmac-sha256;
        secret "pXohPnPR7dyri9ADfDLtSz+lHw/QliISyiEe0wg0a14=";
};

# Then, in the "zone" statement for each zone you wish to dynamically
# update, place an "update-policy" statement granting update permission
# to this key.  For example, the following statement grants this key
# permission to update any name within the zone:
update-policy {
        grant ddns-key zonesub ANY;
};

# After the keyfile has been placed, the following command will
# execute nsupdate using this key:
nsupdate -k <keyfile>

The output of this command is quite self-descriptive. It is needed to add key... snippet to the /etc/bind/named.conf and separate file with any name, and update-policy... snippet into each zone declaration, which will be managed by nsupdate.

To properly use nsupdate tool in multi-view BIND environment, it is needed to explicitly specify a server directive before executing any other command. So, for updating localhost_view's somehost.tld zone (considering key... snippet was saved to the /etc/bind/ddns-key.key) the commands are as follows (note the server 127.0.0.1):

me@somehost:~$ nsupdate -k /etc/bind/ddns-key.key
> server 127.0.0.1
> zone somehost.tld
> update add something.somehost.tld. 86400 A 1.1.1.1
> send
> quit

whereas to manipulate global_view's somehost.tld zone the commands are essentially the same, but with different server. In this case it is needed to use public IP (1.2.3.4 in my example):

me@somehost:~$ nsupdate -k /etc/bind/ddns-key.key
> server 1.2.3.4
> zone somehost.tld
> update add something.somehost.tld. 86400 A 1.1.1.1
> send
> quit

Therefore nsupdate sends a request to a proper interface (which may or may not be the local one) and a specific view works.

Neurotransmitter
  • 468
  • 1
  • 6
  • 17
2

You're using nsupdate -l which sends the update message to localhost (the verbose output confirms that it used the loopback address, as expected, Sending update to 127.0.0.1#53).

However, the zone you are attempting to update is not in the view that this update message will hit. Your first view (localhost_view) has match-clients { localhost_acl; };.

acl localhost_acl {
        127.0.0.0/8;
};

The zone you are trying to update is in the view global_view defined later in your config.

If you check your logs, I would think that the failure is logged and the log message will likely include information about which view it hit (should be localhost_view, based on your config).

It's important to note that views are ordered and the first matching view will receive any given message.

From the section on views in the manual:

Each view statement defines a view of the DNS namespace that will be seen by a subset of clients. A client matches a view if its source IP address matches the address_match_list of the view's match-clients clause and its destination IP address matches the address_match_list of the view's match-destinations clause. If not specified, both match-clients and match-destinations default to matching all addresses. In addition to checking IP addresses match-clients and match-destinations can also take keys which provide an mechanism for the client to select the view. A view can also be specified as match-recursive-only, which means that only recursive requests from matching clients will match that view. The order of the view statements is significant — a client request will be resolved in the context of the first view that it matches.

As is mentioned in the quoted explanation, you could match by TSIG key instead of IP if that helps (by adjusting match-client).

It's worth noting that address_match_list (the type of argument for eg match-client) accepts both IP addresses and keys. Also it is, much like the views, ordered as and the first matching element decides the outcome. Putting any first renders any other elements in the list pointless.

Håkan Lindqvist
  • 33,741
  • 5
  • 65
  • 90
  • Okay, so how do I update `global_view`'s `somehost.tld` zone via `nsupdate`? There is nothing interesting in the logs, by the way. – Neurotransmitter Nov 03 '15 at 10:17
  • @TranslucentCloud By either changing `match-client` of your views or changing how the message is sent so that the update message will hit the desired view. (The latter would mean abandoning the convenience of `-l`.) – Håkan Lindqvist Nov 03 '15 at 10:34
  • @TranslucentCloud Matching on key rather than client IP is generally easier to deal with. – Håkan Lindqvist Nov 03 '15 at 10:35
  • Okay, am I understood it right, that I should generate a special key for this? – Neurotransmitter Nov 03 '15 at 10:43
  • @TranslucentCloud Either match based on the key you currently use implicitly by using the `-l` option (`local-ddns.`) if that doesn't conflict with your other requirements or generate and add additional keys to your configuration and specify those keys as necessary to `nsupdate` instead of using `-l`. – Håkan Lindqvist Nov 03 '15 at 10:48
  • As far as I understood, I need to provide the `allow-update { key "somekey"; };` to my zone definition, however this option is mutually exclusive with `update-policy local;` option I have got there. – Neurotransmitter Nov 03 '15 at 12:25
  • @TranslucentCloud You do not need both. Use whichever best matches your requirements. `update-policy` is more flexible. http://ftp.isc.org/isc/bind9/cur/9.10/doc/arm/Bv9ARM.ch06.html#dynamic_update_policies – Håkan Lindqvist Nov 03 '15 at 12:30