Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Bind9 DNSSEC keys management

licencecodacy

Since version 9.7, BIND9 added support for auto-dnssec. After initial configuration, servers using auto-dnssec will automatically sign and re-sign zones at the appropriate time as determined by key metadata. However, keys generation and rotation planning is left to the DNS operator.

Since BIND9 9.9, BIND9 support inline signing. This means that the signature process in completely transparent with regard to the DNS zone file.

This program purpose is to ease this initial configuration and automate key generation and rotation planning while letting BIND9 signs zone and effectively rotate keys.

Once setup, it will automatically generate new ZSK and KSK as defined in its configuration and sets keys metadata to allow BIND9 to do the rotation.

ZSK are rotate automatically. Then a KSK needs to be rotate, this program will wait for you to publish the new KSK DS in the parent zone before planing the old KSK retirement.

This program depends of the following programs

  • python 3
  • dnspython3
  • Bind utils: dnssec-settime, dnssec-dsfromkey, dnssec-keygen and rndc

Here there is a table with the name of packages providing these dependencies on Debian 9 Centos 7.

Debian 9Centos 7
python3python34
python3-dnspythonN/A
bind9bind
bind9utilsN/A

The binaries provided by bind9utils on Debian 9 are included in bind on Centos 7. dnspython for python 3 is not packaged for Centos 7. You can either retrieve it from https://pypi.org/project/dnspython3/ or install ip with pip3 or let it uninstalled. dnssec_keys_management.py will still works without it, but some handy but optional functionalities will be unavailable.

Just download and copy dnssec_keys_management.py on the same server as your BIND9 master.

Copy dnssec_keys_management.ini.sample into dnssec_keys_management.ini and place it in one of the following locations:

  • In the same directory as dnssec_keys_management.py
  • /etc/dnssec_keys_management.ini
  • In ~/.config/dnssec_keys_management.ini where ~ is the home of the user what will launch dnssec_keys_management.py.

Edit dnssec_keys_management.ini and edit the base_directory settings. dnssec_keys_management.py will store dnssec keys in that directory. Bind9 must be able to access that directory. You can let the other parameters as is or change it. Be sur you understand the implication before changing interval.

Finally, setup a cron task to run dnssec_keys_management.py --cron at least once a day:

0 0 * * * root /usr/local/bind-dnssec/dnssec_keys_management.py -c

The cron will handle new key generation and rotation planing. It will output to stderr messages when you need to take actions, this will be sent by mail by cron. Be sur cron is configured so you can receive mail from it.

  1. We suppose here you already have an unsigned DNS zone setup in BIND9. We also suppose you let the base_directory settings to its default value /etc/bind/keys and the zone SOA expire time is one week.

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    };
    
  2. First, lets create the initials keys for the zone:

    # ./dnssec_keys_management.py --make example.com
    
  3. Then edit BIND config to enable zone signature:

    zone "example.com" in {
    type master;
    notify yes;
    file "/var/lib/bind/zones/db.example.com";
    allow-query { any; };
    key-directory "/etc/bind/keys/example.com";
    inline-signing yes;
    auto-dnssec maintain;
    // resign once a day, signatures are valid 4 weeks (4*SOA expire time)
    sig-validity-interval 28 27;
    };
    

    sig-validity-interval define how long signatures will be valid in days (first parameters). You should put 4*SOA expire time in here. The second define how long before the signatures expire the zone should be resign. I personally like my zones to have a new signatures every days, so I have time if something breaks to fix it. 28 - 1 equals 27, so I set the second parameters to 27. It is very important that the signature interval (1 day here) is lower than the interval parameter in dnssec_keys_management.ini.

  4. After that, you must publish the zone KSK DS into the parent zone. Depending of your registrar, it will ask you for a DS record or the KSK public key.

    To generate DS records just use the command:

    # ./dnssec_keys_management.py --ds all example.com
    example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
    example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
    dnssec-dsfromkey: fatal: unknown algorithm GOST
    example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE
    

    The GOST algorithm is not available on my server leading to the error here. It can just be ignored, publish the successfully generated DS in the parent zone.

    To fetch the KSK public key, use the command:

    # ./dnssec_keys_management.py --key ksk example.com
    ; This is a key-signing key, keyid 58525, for example.com.
    ; Created: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
    ; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
    example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
    
  5. Finally, enable nsec3 so your zone cannot be crawled:

    # ./dnssec_keys_management.py --nsec3 example.com
    Enabling nsec3 for zone example.com:
    request queued
    

By default, dnssec_keys_management.py will generate a new KSK once a year and publish it into the DNS zone. In order to activate it and plan the old KSK removal, you need to take actions: the new KSK DS must be publish into the parent zone. dnssec_keys_management.py --cron will output New KSK needs DS seen and/or old KSK needs inactivate/remove for zone example.com every day until you inform it you have publish DS in the parent zone.

You can display the keys actualy present with the following commands:

# ./dnssec_keys_management.py --key-table default example.com
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
| Zone name |T|KeyId| Publish | Activate | Inactive | Delete |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+
|example.com|K|58525|2017-05-06 12:39:10|2017-05-06 12:39:10| N/A | N/A |
|example.com|K|48010|2018-04-04 12:54:24| N/A | N/A | N/A |
|example.com|Z|38943|2018-05-06 12:39:10|2018-05-06 12:39:10|2018-06-05 12:39:10|2018-06-28 12:39:10|
|example.com|Z| 8409|2018-05-13 12:39:10|2018-06-05 12:39:10| N/A | N/A |
+-----------+-+-----+-------------------+-------------------+-------------------+-------------------+

You can see that the KSK 48010 needs to be activated. To do so, publish its DS into the parent zone. Do not remove previous DS yet.

# ./dnssec_keys_management.py --ds all example.com 2>/dev/null
example.com. IN DS 58525 8 1 32B5BA350B9AB7CF1B2B2E66379A7BF337C6AB09
example.com. IN DS 58525 8 2 BAFECE6ABD12DC33F8001770EA6507DCC9476E6B504FF8E3FC2FD7DD28950666
example.com. IN DS 58525 8 4 8BB9438C58CD3287A4B76FD28C766EE06566EE63BD5D5E17F4492224923A2F99A1C73F069A7E65B2D00B0442A43BDE37
example.com. IN DS 48010 8 1 15E9FBDDEF7D91D6AEE353AE3E0209187C21BEF8
example.com. IN DS 48010 8 2 C5E23F94FF50A0D09CE76622CB150ED3209F22C6699626492DAFB72515B434D9
example.com. IN DS 48010 8 4 D9B3F0D8019AFC4BFDB7338F92C3D03EF08CDB6DA596887D8987E3739F9AA90E45CAB22AFB436B419880E7802CD0CE87
# ./dnssec_keys_management.py --key ksk example.com
; This is a key-signing key, keyid 58525, for example.com.
; Created: 20180506123910 (Sun May 6 14:39:10 2018)
; Publish: 20180506123910 (Sun May 6 14:39:10 2018)
; Activate: 20180506123910 (Sun May 6 14:39:10 2018)
example.com. IN DNSKEY 257 3 8 AwEAAaMDcXZfm5S0MS4fhUcUfZCu1v2pRGi7bGZD1jHF36X2/eaARgxf xFFySSon3gM9wZdTBlYwZUpATLQTVCAj8REwvn7cJyNptxt67IgHluUW 4j7+kjB2m686+o2KIrupapVnOvDdet2oMqCVOsQv+g/Tt2N2ycxfEPm9 edySl67HtsNCIA6NnxTywL8ihwcDEqqNW2SVhMK8O49ti1XcIYPsE4ep jRwCNOFqOcbygAeMxER5pxTgVxndzpteXkM/jTMJB2SzsOcGg4yo3s43 bf8WjqFKND6tpXZQRRbkSD0/GGJBdUSmvrippF1RpBmWrvIUKHOoMytc GBIp2n8=
; This is a key-signing key, keyid 48010, for example.com.
; Created: 20180506125424 (Sun May 6 14:54:24 2018)
; Publish: 20180506125424 (Sun May 6 14:54:24 2018)
example.com. IN DNSKEY 257 3 8 AwEAAe+YUTscIDDZHdJ36lE/3rGXcDwfs3DqqIoLNfhpA5Hjne9Os7sR B2ekTf4ZfSVTDLApBcZvXeV1/w29VnssJCWztG7BraJ0khcF23vmHzNk 2TGKYePD3rKsJlGCOz/whJckbaDt2dKx5BAvSeBucWm0JhFTCy7UwFyO V1LamXm8l9m3a9Eo3KQzuOpGkHed7WikA60EYSi1lYNXkLOLseMHP7FS NBfkPrX0kWTm0V1R6txIfeSBPBoEn5rK2S756zV3TyzVWqbOgfKXdB+X 2EAiRow7Rym6B+8xrgk3uyfFzxsaQnRd8t+I9GqQi/u+N5YUbD+Zfj0p 9dm5EQc=

Keys are always sorted from the oldest to the newest, indeed, the key 48010 is last.

Once you have publish DS into the parent zone, you can check that the DS is available with the command ./dnssec_keys_management.py --ds-check 48010 example.com.

In case of errors, you will have this kind of output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS not found on the following parent servers:
* d.gtld-servers.net. (192.31.80.30)
* k.gtld-servers.net. (192.52.178.30)
* b.gtld-servers.net. (192.33.14.30)
* a.gtld-servers.net. (192.5.6.30)
* l.gtld-servers.net. (192.41.162.30)
* c.gtld-servers.net. (192.26.92.30)
* m.gtld-servers.net. (192.55.83.30)
* g.gtld-servers.net. (192.42.93.30)
* j.gtld-servers.net. (192.48.79.30)
* h.gtld-servers.net. (192.54.112.30)
* i.gtld-servers.net. (192.43.172.30)
* f.gtld-servers.net. (192.35.51.30)
* e.gtld-servers.net. (192.12.94.30)
Found keys are 31406, 31589, 43547

If every things is ok, the command will output:

# ./dnssec_keys_management.py --ds-check 48010 example.com
DS for key 48010 found on all parent servers

Only then, you can inform dnssec_keys_management.py the DS are successfully published and the key rotation can be perform with to command: ./dnssec_keys_management.py --ds-seen 48010 example.com.

# ./dnssec_keys_management.py --ds-seen 48010 example.com
DS for key 48010 found on all parent servers

This will schedule the new KSK to be activated in interval and the old one to be removed from the zone in 2 * interval. Wait for the old KSK removal form the zone to remove corresponding DS from the parent zone.

You can use the excellent web site http://dnsviz.net to analyse dnssec for your zone. For instance http://dnsviz.net/d/example.com/dnssec/.

The two commands dnssec_keys_management.py --ds-seen and dnssec_keys_management.py --ds-check check the DNS parent server for the zone DS. In order to do so, the python library pythondns3 is needed. Without it you will get the error: Python dnspython module not available, check failed.

Without pythondns3 you cannot use dnssec_keys_management.py --ds-check. dnssec_keys_management.py --ds-seen perform the same checks as --ds-check before scheduling KSK rotation, but you can skip the tests with the --no-check options.

Beware that with the --no-check keys will be rotate whether the DS are published in the parent zone or not. Use it very carefully.

About

Automatic management of BIND9 dnssec keys generation and rotation

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages