From 4cc745983c444c3de5433e809700857bc80a92f6 Mon Sep 17 00:00:00 2001 From: Dryusdan Date: Wed, 1 Jul 2020 10:44:00 +0200 Subject: [PATCH] Add playbook --- files/ds-records | 20 ++++ files/keygen | 27 +++++ files/signzone | 22 ++++ files/updatezone | 20 ++++ handlers/main.yml | 7 ++ meta/main.yml | 21 ++++ tasks/configure.yml | 63 ++++++++++++ tasks/main.yml | 54 ++++++++++ templates/nsd.conf.j2 | 231 ++++++++++++++++++++++++++++++++++++++++++ templates/zone.j2 | 8 ++ 10 files changed, 473 insertions(+) create mode 100644 files/ds-records create mode 100644 files/keygen create mode 100644 files/signzone create mode 100644 files/updatezone create mode 100644 handlers/main.yml create mode 100644 meta/main.yml create mode 100644 tasks/configure.yml create mode 100644 tasks/main.yml create mode 100644 templates/nsd.conf.j2 create mode 100644 templates/zone.j2 diff --git a/files/ds-records b/files/ds-records new file mode 100644 index 0000000..df3d41e --- /dev/null +++ b/files/ds-records @@ -0,0 +1,20 @@ +#!/bin/sh + +DOMAIN="$1" + +if [ -z "$DOMAIN" ]; then + echo "Domain name must be defined" 1>&2 + exit 1 +fi + +echo -e "\n> DS record 1 [Digest Type = SHA1] :" +ldns-key2ds -n -1 "/etc/nsd/zones/${DOMAIN}.signed" + +echo -e "\n> DS record 2 [Digest Type = SHA256] :" +ldns-key2ds -n -2 "/etc/nsd/zones/${DOMAIN}.signed" + +echo -e "\n> Public KSK Key :" +tail -n 1 "/etc/nsd/zones/K${DOMAIN}.ksk.key" +echo "" + +exit 0 diff --git a/files/keygen b/files/keygen new file mode 100644 index 0000000..0ceab3d --- /dev/null +++ b/files/keygen @@ -0,0 +1,27 @@ +#!/bin/sh + +DOMAIN="$1" + +if [ -z "$DOMAIN" ]; then + echo "Domain name must be defined" 1>&2 + exit 1 +fi + +cd /etc/nsd/zones || exit 1 +if [ ! -f "K${DOMAIN}.zsk.key" ] +then + echo "Generating ZSK & KSK keys for '${DOMAIN}'" + ZSK=$(ldns-keygen -a ECDSAP384SHA384 -b 384 "$DOMAIN") + KSK=$(ldns-keygen -k -a ECDSAP384SHA384 -b 384 "$DOMAIN") + + rm -f "$ZSK".ds "$KSK".ds + + mv "$ZSK".key "K${DOMAIN}.zsk.key" + mv "$ZSK".private "K${DOMAIN}.zsk.private" + + mv "$KSK".key "K${DOMAIN}.ksk.key" + mv "$KSK".private "K${DOMAIN}.ksk.private" + + chmod 600 -- *.private + exit 0 +fi diff --git a/files/signzone b/files/signzone new file mode 100644 index 0000000..8fa8db8 --- /dev/null +++ b/files/signzone @@ -0,0 +1,22 @@ +#!/bin/sh + +DOMAIN="$1" +EXPIRATION_DATE="$2" + +if [ -z "$DOMAIN" ]; then + echo "Domain name must be defined" 1>&2 + exit 1 +fi + +if [ -n "$EXPIRATION_DATE" ]; then + arg="-e${EXPIRATION_DATE}" +fi + +cd /etc/nsd/zones || exit 1 + +echo "Signing zone for ${DOMAIN}" +ldns-signzone -n -p ${arg} -s "$(head /dev/urandom | tr -dc A-Za-z0-9 | sha1sum | head -c 30)" \ + -f "${DOMAIN}.zone.signed" "${DOMAIN}.zone" "K${DOMAIN}.zsk" "K${DOMAIN}.ksk" + +updatezone "$DOMAIN" +exit 0 diff --git a/files/updatezone b/files/updatezone new file mode 100644 index 0000000..78d9fb3 --- /dev/null +++ b/files/updatezone @@ -0,0 +1,20 @@ +#!/bin/sh + +DOMAIN="$1" + +if [ -z "$DOMAIN" ]; then + echo "Domain name must be defined" 1>&2 + exit 1 +fi + +echo -n "NSD configuration rebuild... " +nsd-control reconfig + +echo -n "Reloading zone for ${DOMAIN}... " +nsd-control reload "$DOMAIN" + +echo -n "Notify slave servers... " +nsd-control notify "$DOMAIN" + +echo "Done." +exit 0 diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..ef0117b --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,7 @@ +--- +# handlers file for ansible-role-nsd +- name: restart nsd + service: name=nsd state=restarted + +- name: reload nsd + shell: nsd-control reconfig diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..743539f --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,21 @@ +--- +galaxy_info: + author: elnappo + description: Install and configure nsd name server + license: MIT + min_ansible_version: 2.2 + platforms: + - name: Ubuntu + versions: + - trusty + - xenial + - bionic + - name: Debian + versions: + - jessie + - stretch + galaxy_tags: + - networking + - dns + +dependencies: [] diff --git a/tasks/configure.yml b/tasks/configure.yml new file mode 100644 index 0000000..a563efb --- /dev/null +++ b/tasks/configure.yml @@ -0,0 +1,63 @@ +--- +- name: NSD already installed ? + shell: dpkg-query -W 'nsd' + ignore_errors: true + register: nsd + +- name: update apt-cache + apt: update_cache=yes + when: nsd is failed + +- name: Create zone folder + file: + path: /etc/nsd/zones + state: directory + +- name: Install nsd + apt: + pkg: "{{item}}" + state: latest + update_cache: yes + with_items: + - nsd + - dnsutils + - ldnsutils + when: nsd is failed + +- name: Create SSL keys and certificates + command: nsd-control-setup creates=/etc/nsd/nsd_control.pem + when: nsd_remote_control_enable == "yes" + notify: + - restart nsd + +- name: Add file for DNSSEC + copy: + src: ds-records + dest: /usr/local/bin/ds-records + owner: root + group: root + mode: 0755 + +- name: Add file for DNSSEC + copy: + src: keygen + dest: /usr/local/bin/keygen + owner: root + group: root + mode: 0755 + +- name: Add file for DNSSEC + copy: + src: signzone + dest: /usr/local/bin/signzone + owner: root + group: root + mode: 0755 + +- name: Add file for DNSSEC + copy: + src: updatezone + dest: /usr/local/bin/updatezone + owner: root + group: root + mode: 0755 diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..5406a83 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- include_tasks: configure.yml + +- name: "{{ role_path|basename }} | get unix time" + shell: echo $(date +%s) + register: unix_time_stamp + delegate_to: localhost + run_once: true + become: no + +- name: Create config file + template: + src: nsd.conf.j2 + dest: /etc/nsd/nsd.conf + owner: root + group: root + mode: 0644 + validate: 'nsd-checkconf %s' + notify: + - reload nsd + +- name: "{{ role_path|basename }} setting execution facts" + set_fact: + serial: "{{ unix_time_stamp.stdout }}" + run_once: true + become: no + +- name: Add zone + template: + src: zone.j2 + dest: "/etc/nsd/zones/{{ item.name }}.zone" + loop: "{{ dns_zones }}" + when: "dns_zones is defined" + + +- name: Enable NSD on boot and start it + service: name=nsd state=started enabled=yes + +- name: Generate key for dnssec + shell: "/usr/local/bin/keygen {{ item.name }}" + loop: "{{ dns_zones }}" + when: "dns_zones is defined" + +- name: Generate key for dnssec + shell: "/usr/local/bin/signzone {{ item.name }}" + loop: "{{ dns_zones }}" + when: "dns_zones is defined" + +- name: Get ds-record + shell: "/usr/local//bin/ds-records {{ item.name }}" + loop: "{{ dns_zones }}" + when: "dns_zones is defined" + register: dsrecord + diff --git a/templates/nsd.conf.j2 b/templates/nsd.conf.j2 new file mode 100644 index 0000000..a082543 --- /dev/null +++ b/templates/nsd.conf.j2 @@ -0,0 +1,231 @@ +# This file is ansible managed + +{% macro bool(value) %} +{{ 'yes' if value | bool else 'no' -}} +{% endmacro %} + +# +# nsd.conf -- the NSD(8) configuration file, nsd.conf(5). +# +# Copyright (c) 2001-2011, NLnet Labs. All rights reserved. +# +# See LICENSE for the license. +# + +# This is a comment. +# Sample configuration file +# include: "file" # include that file's text over here. + +# options for the nsd server +server: + # Number of NSD servers to fork. Put the number of CPUs to use here. + server-count: {{ nsd_server_count }} + + # uncomment to specify specific interfaces to bind (default are the + # wildcard interfaces 0.0.0.0 and ::0). + # For servers with multiple IP addresses, list them one by one, + # or the source address of replies could be wrong. + # Use ip-transparent to be able to list addresses that turn on later. + {% for ip in nsd_ip_listen %} + ip-address: {{ ip }} + {% endfor %} + # ip-address: 1.2.3.4@5678 + # ip-address: 12fe::8ef0 + + # Allow binding to non local addresses. Default no. + ip-transparent: {{ bool(nsd_ip_transparent) }} + + # enable debug mode, does not fork daemon process into the background. + # debug-mode: no + + # listen on IPv4 connections + do-ip4: {{ bool(nsd_do_ip4) }} + + # listen on IPv6 connections + do-ip6: {{ bool(nsd_do_ip6) }} + + # port to answer queries on. default is 53. + port: {{ nsd_port }} + + # Verbosity level. + # verbosity: 0 + + # After binding socket, drop user privileges. + # can be a username, id or id.gid. + # username: nsd + + # Run NSD in a chroot-jail. + # make sure to have pidfile and database reachable from there. + # by default, no chroot-jail is used. + # chroot: "/etc/nsd" + + # The directory for zonefile: files. The daemon chdirs here. + # zonesdir: "/etc/nsd" + + # the list of dynamically added zones. + # zonelistfile: "/var/lib/nsd/zone.list" + + # the database to use + # if set to "" then no disk-database is used, less memory usage. + # database: "/var/lib/nsd/nsd.db" + + # log messages to file. Default to stderr and syslog (with + # facility LOG_DAEMON). stderr disappears when daemon goes to bg. + # logfile: "/var/log/nsd.log" + + # File to store pid for nsd in. + # pidfile: "/run/nsd/nsd.pid" + + # The file where secondary zone refresh and expire timeouts are kept. + # If you delete this file, all secondary zones are forced to be + # 'refreshing' (as if nsd got a notify). Set to "" to disable. + # xfrdfile: "/var/lib/nsd/xfrd.state" + + # The directory where zone transfers are stored, in a subdir of it. + # xfrdir: "/tmp" + + # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries + hide-version: {{ bool(nsd_hide_version) }} + + # version string the server responds with for chaos queries. + # default is 'NSD x.y.z' with the server's version number. + # version: "NSD" + + # identify the server (CH TXT ID.SERVER entry). + identity: "{{ nsd_identity }}" + + # NSID identity (hex string, or "ascii_somestring"). default disabled. + # nsid: "aabbccdd" + + # Maximum number of concurrent TCP connections per server. + # tcp-count: 100 + + # Maximum number of queries served on a single TCP connection. + # By default 0, which means no maximum. + # tcp-query-count: 0 + + # Override the default (120 seconds) TCP timeout. + # tcp-timeout: 120 + + # Preferred EDNS buffer size for IPv4. + # ipv4-edns-size: 4096 + + # Preferred EDNS buffer size for IPv6. + # ipv6-edns-size: 4096 + + # statistics are produced every number of seconds. Prints to log. + # Default is 0, meaning no statistics are produced. + # statistics: 3600 + + # Number of seconds between reloads triggered by xfrd. + # xfrd-reload-timeout: 1 + + # log timestamp in ascii (y-m-d h:m:s.msec), yes is default. + # log-time-ascii: yes + + # round robin rotation of records in the answer. + # round-robin: no + + # check mtime of all zone files on start and sighup + # zonefiles-check: yes + + # write changed zonefiles to disk, every N seconds. + # default is 0(disabled) or 3600(if database is ""). + # zonefiles-write: 3600 + + # RRLconfig + # Response Rate Limiting, size of the hashtable. Default 1000000. + # rrl-size: 1000000 + + # Response Rate Limiting, maximum QPS allowed (from one query source). + # If set to 0, ratelimiting is disabled. Also set + # rrl-whitelist-ratelimit to 0 to disable ratelimit processing. + # Default is on. + # rrl-ratelimit: 200 + + # Response Rate Limiting, number of packets to discard before + # sending a SLIP response (a truncated one, allowing an honest + # resolver to retry with TCP). Default is 2 (one half of the + # queries will receive a SLIP response, 0 disables SLIP (all + # packets are discarded), 1 means every request will get a + # SLIP response. When the ratelimit is hit the traffic is + # divided by the rrl-slip value. + # rrl-slip: 2 + + # Response Rate Limiting, IPv4 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv4-prefix-length: 24 + + # Response Rate Limiting, IPv6 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv6-prefix-length: 64 + + # Response Rate Limiting, maximum QPS allowed (from one query source) + # for whitelisted types. Default is on. + # rrl-whitelist-ratelimit: 2000 + # RRLend + +# Remote control config section. +remote-control: + # Enable remote control with nsd-control(8) here. + # set up the keys and certificates with nsd-control-setup. + control-enable: {{ bool(nsd_remote_control_enable) }} + + # what interfaces are listened to for control, default is on localhost. +{% for control_interface in nsd_remote_control_interfaces %} + control-interface: {{ control_interface }} +{% endfor %} + + # port number for remote control operations (uses TLS over TCP). + control-port: {{ nsd_remote_control_port }} + + # nsd server key file for remote control. + server-key-file: "/etc/nsd/nsd_server.key" + + # nsd server certificate file for remote control. + server-cert-file: "/etc/nsd/nsd_server.pem" + + # nsd-control key file. + control-key-file: "/etc/nsd/nsd_control.key" + + # nsd-control certificate file. + control-cert-file: "/etc/nsd/nsd_control.pem" + +{% for zone in zones %} +{% if zone.secret is defined %} +key: + name: "{{ zone.name }}-key" + algorithm: {{ zone.algorithm|default('hmac-sha256') }} + secret: "{{ zone.secret }}" + +{% endif %} +{% endfor %} +{% for zone in zones %} +zone: + name: "{{ zone.name }}" + zonefile: "zones/{{ zone.name }}.zone.signed" + +{% if zone.slaves is defined %} +{% for slave in zone.slaves %} +{% if zone.secret is defined %} + notify: {{ slave }} {{ zone.name }}-key + provide-xfr: {{ slave }} {{ zone.name }}-key +{% else %} + notify: {{ slave }} NOKEY + provide-xfr: {{ slave }} NOKEY +{% endif %} +{% endfor %} +{% endif %} +{% if zone.masters is defined %} +{% for master in zone.masters %} +{% if zone.secret is defined %} + allow-notify: {{ master }} {{ zone.name }}-key + request-xfr: AXFR {{ master }}@53 {{ zone.name }}-key +{% else %} + allow-notify: {{ master }} NOKEY + request-xfr: AXFR {{ master }}@53 NOKEY +{% endif %} +{% endfor %} +{% endif %} + +{% endfor %} diff --git a/templates/zone.j2 b/templates/zone.j2 new file mode 100644 index 0000000..0ba8b4a --- /dev/null +++ b/templates/zone.j2 @@ -0,0 +1,8 @@ +$ORIGIN {{ item.name }}. +$TTL {{ item.ttl }} + +@ IN SOA {{ item.ns_master }}. {{ item.email | replace("@", ".") }}. ({{ serial }} {{ item.refresh or "86400" }} {{ item.retry or "7200" }} {{ item.expire or "3600000" }} {{ item.default_ttl or "600" }} ) + +{% for entry in item.records %} +{{ entry }} +{% endfor %}