bind: "nsupdate -l" failed with status "update failed: REFUSED"

21,201

Solution 1

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.

Solution 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.

Share:
21,201
Neurotransmitter
Author by

Neurotransmitter

Updated on September 18, 2022

Comments

  • Neurotransmitter
    Neurotransmitter almost 2 years

    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?

    • Håkan Lindqvist
      Håkan Lindqvist over 8 years
      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.
    • Neurotransmitter
      Neurotransmitter over 8 years
      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.
    • Håkan Lindqvist
      Håkan Lindqvist over 8 years
      Could anything ever hit the second view? (Based on what I noted about the first view)
    • Neurotransmitter
      Neurotransmitter over 8 years
      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.
    • Håkan Lindqvist
      Håkan Lindqvist over 8 years
      Are the views actually in this order? Or the other way around?
    • Neurotransmitter
      Neurotransmitter over 8 years
      Order have meaning? Okay, I'll paste my whole named.conf.local now. Wait a minute.
    • Håkan Lindqvist
      Håkan Lindqvist over 8 years
      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.)
    • Neurotransmitter
      Neurotransmitter over 8 years
      Here you go, I updated the question. Take a look.
  • Neurotransmitter
    Neurotransmitter over 8 years
    Okay, so how do I update global_view's somehost.tld zone via nsupdate? There is nothing interesting in the logs, by the way.
  • Håkan Lindqvist
    Håkan Lindqvist over 8 years
    @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
    Håkan Lindqvist over 8 years
    @TranslucentCloud Matching on key rather than client IP is generally easier to deal with.
  • Neurotransmitter
    Neurotransmitter over 8 years
    Okay, am I understood it right, that I should generate a special key for this?
  • Håkan Lindqvist
    Håkan Lindqvist over 8 years
    @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.
  • Neurotransmitter
    Neurotransmitter over 8 years
    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.
  • Håkan Lindqvist
    Håkan Lindqvist over 8 years
    @TranslucentCloud You do not need both. Use whichever best matches your requirements. update-policy is more flexible. ftp.isc.org/isc/bind9/cur/9.10/doc/arm/…