├── .dockerignore ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Dockerfile ├── Makefile ├── README.md ├── bin ├── ds-records ├── keygen ├── run.sh ├── signzone └── updatezone └── test ├── config ├── db.example.org └── nsd.conf └── tests.bats /.dockerignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .git/ 3 | .gitignore 4 | .gitmodules 5 | .travis.yml 6 | Makefile 7 | README.md 8 | test/ 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/bats"] 2 | path = test/bats 3 | url = https://github.com/sstephenson/bats 4 | [submodule "test/test_helper/bats-support"] 5 | path = test/test_helper/bats-support 6 | url = https://github.com/ztombol/bats-support 7 | [submodule "test/test_helper/bats-assert"] 8 | path = test/test_helper/bats-assert 9 | url = https://github.com/ztombol/bats-assert 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | sudo: required 3 | services: 4 | - docker 5 | script: 6 | - make all 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 2 | 3 | LABEL description "Simple DNS authoritative server with DNSSEC support" \ 4 | maintainer="Hardware " 5 | 6 | ARG NSD_VERSION=4.2.1 7 | 8 | # https://pgp.mit.edu/pks/lookup?search=0x7E045F8D&fingerprint=on&op=index 9 | # pub 4096R/7E045F8D 2011-04-21 W.C.A. Wijngaards 10 | ARG GPG_SHORTID="0x7E045F8D" 11 | ARG GPG_FINGERPRINT="EDFA A3F2 CA4E 6EB0 5681 AF8E 9F6F 1C2D 7E04 5F8D" 12 | ARG SHA256_HASH="d17c0ea3968cb0eb2be79f2f83eb299b7bfcc554b784007616eed6ece828871f" 13 | 14 | ENV UID=991 GID=991 15 | 16 | RUN apk add --no-cache --virtual build-dependencies \ 17 | gnupg \ 18 | build-base \ 19 | libevent-dev \ 20 | openssl-dev \ 21 | ca-certificates \ 22 | && apk add --no-cache \ 23 | ldns \ 24 | ldns-tools \ 25 | libevent \ 26 | openssl \ 27 | tini \ 28 | && cd /tmp \ 29 | && wget -q https://www.nlnetlabs.nl/downloads/nsd/nsd-${NSD_VERSION}.tar.gz \ 30 | && wget -q https://www.nlnetlabs.nl/downloads/nsd/nsd-${NSD_VERSION}.tar.gz.asc \ 31 | && echo "Verifying both integrity and authenticity of nsd-${NSD_VERSION}.tar.gz..." \ 32 | && CHECKSUM=$(sha256sum nsd-${NSD_VERSION}.tar.gz | awk '{print $1}') \ 33 | && if [ "${CHECKSUM}" != "${SHA256_HASH}" ]; then echo "ERROR: Checksum does not match!" && exit 1; fi \ 34 | && ( \ 35 | gpg --keyserver ha.pool.sks-keyservers.net --recv-keys ${GPG_SHORTID} || \ 36 | gpg --keyserver keyserver.pgp.com --recv-keys ${GPG_SHORTID} || \ 37 | gpg --keyserver pgp.mit.edu --recv-keys ${GPG_SHORTID} \ 38 | ) \ 39 | && FINGERPRINT="$(LANG=C gpg --verify nsd-${NSD_VERSION}.tar.gz.asc nsd-${NSD_VERSION}.tar.gz 2>&1 \ 40 | | sed -n "s#Primary key fingerprint: \(.*\)#\1#p")" \ 41 | && if [ -z "${FINGERPRINT}" ]; then echo "ERROR: Invalid GPG signature!" && exit 1; fi \ 42 | && if [ "${FINGERPRINT}" != "${GPG_FINGERPRINT}" ]; then echo "ERROR: Wrong GPG fingerprint!" && exit 1; fi \ 43 | && echo "All seems good, now unpacking nsd-${NSD_VERSION}.tar.gz..." \ 44 | && tar xzf nsd-${NSD_VERSION}.tar.gz && cd nsd-${NSD_VERSION} \ 45 | && ./configure \ 46 | CFLAGS="-O2 -flto -fPIE -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat -Werror=format-security" \ 47 | LDFLAGS="-Wl,-z,now -Wl,-z,relro" \ 48 | && make && make install \ 49 | && apk del build-dependencies \ 50 | && rm -rf /var/cache/apk/* /tmp/* /root/.gnupg 51 | 52 | COPY bin /usr/local/bin 53 | VOLUME /zones /etc/nsd /var/db/nsd 54 | EXPOSE 53 53/udp 55 | CMD ["run.sh"] 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = hardware/nsd-dnssec:testing 2 | 3 | all: build-no-cache init fixtures run clean 4 | all-fast: build init fixtures run clean 5 | no-build: init fixtures run clean 6 | 7 | build-no-cache: 8 | docker build --no-cache -t $(NAME) . 9 | 10 | build: 11 | docker build -t $(NAME) . 12 | 13 | init: 14 | -docker rm -f nsd_unsigned nsd_default 15 | 16 | sleep 2 17 | 18 | docker run \ 19 | -d \ 20 | --name nsd_unsigned \ 21 | -v "`pwd`/test/config/nsd.conf":/etc/nsd/nsd.conf \ 22 | -v "`pwd`/test/config/db.example.org":/zones/db.example.org \ 23 | -t $(NAME) 24 | 25 | docker run \ 26 | -d \ 27 | --name nsd_default \ 28 | -v "`pwd`/test/config/nsd.conf":/etc/nsd/nsd.conf \ 29 | -v "`pwd`/test/config/db.example.org":/zones/db.example.org \ 30 | -t $(NAME) 31 | 32 | fixtures: 33 | docker exec nsd_default keygen example.org 34 | docker exec nsd_default signzone example.org 35 | 36 | run: 37 | ./test/bats/bin/bats test/tests.bats 38 | 39 | clean: 40 | docker container stop nsd_unsigned nsd_default || true 41 | docker container rm --volumes nsd_unsigned nsd_default || true 42 | docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hardware/nsd-dnssec 2 | 3 | ![nsd](https://i.imgur.com/tPgkQVB.png "nsd") 4 | 5 | ### What is this? 6 | 7 | NSD is an authoritative only, high performance, simple and open source name server. 8 | 9 | ### Features 10 | 11 | - Lightweight & secure image (no root process) 12 | - Based on Alpine Linux 13 | - Latest NSD version (4.2.1 - Jul 9, 2019) 14 | - ZSK and KSK keys, DS-Records management and zone signature with ldns 15 | 16 | ### Build-time variables 17 | 18 | - **NSD_VERSION** : version of NSD 19 | - **GPG_SHORTID** : short gpg key ID 20 | - **GPG_FINGERPRINT** : fingerprint of signing key 21 | - **SHA256_HASH** : SHA256 hash of NSD archive 22 | 23 | ### Ports 24 | 25 | - **53/tcp** 26 | - **53/udp** (for AXFR zones transfer queries) 27 | 28 | ### Environment variables 29 | 30 | | Variable | Description | Type | Default value | 31 | | -------- | ----------- | ---- | ------------- | 32 | | **UID** | nsd user id | *optional* | 991 33 | | **GID** | nsd group id | *optional* | 991 34 | 35 | ### Setup 36 | 37 | Put your dns zone file in `/mnt/docker/nsd/zones/db.domain.tld`. 38 | 39 | Example: 40 | 41 | ``` 42 | $ORIGIN domain.tld. 43 | $TTL 7200 44 | 45 | ; SOA 46 | 47 | @ IN SOA ns1.domain.tld. hostmaster.domain.tld. ( 48 | 2016020202 ; Serial 49 | 7200 ; Refresh 50 | 1800 ; Retry 51 | 1209600 ; Expire 52 | 86400 ) ; Minimum 53 | 54 | ; NAMESERVERS 55 | 56 | @ IN NS ns1.domain.tld. 57 | @ IN NS ns2.domain.tld. 58 | 59 | ; A RECORDS 60 | 61 | @ IN A IPv4 62 | hostname IN A IPv4 63 | ns1 IN A IPv4 64 | ns2 IN A IPv4 65 | 66 | ; CNAME RECORDS 67 | 68 | www IN CNAME hostname 69 | 70 | ; MAIL RECORDS 71 | 72 | @ IN MX 10 hostname.domain.tld. 73 | 74 | ... 75 | ``` 76 | 77 | Put the nsd config in `/mnt/docker/nsd/conf/nsd.conf`. 78 | 79 | Primary server example: 80 | 81 | ```yaml 82 | server: 83 | server-count: 1 84 | ip4-only: yes 85 | hide-version: yes 86 | identity: "" 87 | zonesdir: "/zones" 88 | 89 | remote-control: 90 | control-enable: yes 91 | 92 | key: 93 | name: "sec_key" 94 | algorithm: hmac-sha256 95 | secret: "WU9VUl9TRUNSRVRfS0VZCg==" # echo "YOUR_SECRET_KEY" | base64 96 | 97 | zone: 98 | name: domain.tld 99 | zonefile: db.domain.tld.signed 100 | notify: ip_of_secondary_server sec_key 101 | notify: ip_of_secondary_public_server NOKEY 102 | provide-xfr: ip_of_secondary_server sec_key 103 | provide-xfr: ip_of_secondary_public_server NOKEY 104 | 105 | # "ip_of_secondary_server" is your secondary nameserver IP 106 | # "ip_of_secondary_public_server" can be your registrar's nameserver IP 107 | ``` 108 | 109 | Secondary server example (optional): 110 | 111 | ```yaml 112 | server: 113 | server-count: 1 114 | ip4-only: yes 115 | hide-version: yes 116 | identity: "" 117 | zonesdir: "/zones" 118 | 119 | remote-control: 120 | control-enable: yes 121 | 122 | key: 123 | name: "sec_key" 124 | algorithm: hmac-sha256 125 | secret: "WU9VUl9TRUNSRVRfS0VZCg==" 126 | 127 | zone: 128 | name: domain.tld 129 | zonefile: db.domain.tld.signed 130 | allow-notify: ip_of_primary_server sec_key 131 | request-xfr: AXFR ip_of_primary_server sec_key 132 | 133 | # "ip_of_primary_server" is your primary nameserver IP 134 | ``` 135 | 136 | Check your zone and nsd configuration: 137 | 138 | ``` 139 | cd /mnt/docker/nsd 140 | docker run --rm -v `pwd`/zones:/zones -ti hardware/nsd-dnssec nsd-checkzone domain.tld /zones/db.domain.tld 141 | docker run --rm -v `pwd`/conf:/etc/nsd -ti hardware/nsd-dnssec nsd-checkconf /etc/nsd/nsd.conf 142 | ``` 143 | 144 | ### Docker-compose 145 | 146 | #### Docker-compose.yml 147 | 148 | ```yaml 149 | nsd: 150 | image: hardware/nsd-dnssec 151 | container_name: nsd 152 | ports: 153 | - "PUBLIC_IP_ADDRESS:53:53" 154 | - "PUBLIC_IP_ADDRESS:53:53/udp" 155 | volumes: 156 | - /mnt/docker/nsd/conf:/etc/nsd 157 | - /mnt/docker/nsd/zones:/zones 158 | - /mnt/docker/nsd/db:/var/db/nsd 159 | ``` 160 | 161 | **Note** : replace `PUBLIC_IP_ADDRESS` with your public IP address. 162 | 163 | #### Run it 164 | 165 | ``` 166 | docker-compose up -d 167 | ``` 168 | 169 | ### Generating DNSSEC keys and signed zone 170 | 171 | Generate ZSK and KSK keys with ECDSAP384SHA384 algorithm (it may take some time; you can install `haveged` in your base system to speed it up): 172 | 173 | ``` 174 | docker-compose exec nsd keygen domain.tld 175 | 176 | Generating ZSK & KSK keys for 'domain.tld' 177 | Done. 178 | ``` 179 | 180 | Then sign your dns zone (default expiration date is 1 month): 181 | 182 | ``` 183 | docker-compose exec nsd signzone domain.tld 184 | 185 | Signing zone for domain.tld 186 | NSD configuration rebuild... reconfig start, read /etc/nsd/nsd.conf 187 | ok 188 | Reloading zone for domain.tld... ok 189 | Notify slave servers... ok 190 | Done. 191 | 192 | # or set custom RRSIG RR expiration date : 193 | 194 | docker-compose exec nsd signzone domain.tld [YYYYMMDDhhmmss] 195 | docker-compose exec nsd signzone domain.tld 20170205220210 196 | ``` 197 | 198 | :warning: **Do not forget to add a cron task to increment the serial and sign your zone periodically to avoid the expiration of RRSIG RR records!** 199 | 200 | This example shows how to update the serial and your TLSA record (if you have one) programmatically : 201 | 202 | ```bash 203 | #!/bin/bash 204 | 205 | LETS_ENCRYPT_LIVE_PATH=/path/to/your/lets/encrypt/folder 206 | fingerprint=$(openssl x509 -noout -in "${LETS_ENCRYPT_LIVE_PATH}/cert.pem" -fingerprint -sha256 | cut -c 20- | sed s/://g) 207 | 208 | domain="domain.tld" 209 | zonename="db.${domain}" 210 | zonefile="/mnt/docker/nsd/zones/${zonename}" 211 | serial=$(date -d "+1 day" +'%Y%m%d%H') 212 | tlsa_line_number=$(grep -n TLSA $zonefile | cut -d : -f 1) 213 | tlsa_dns_record="_dane IN TLSA 3 0 1 ${fingerprint}" 214 | expiration_date=$(date -d "+6 months" +'%Y%m%d%H%M%S') 215 | 216 | sed -i -e "s/20[0-9][0-9]\{7\} ; Serial/${serial} ; Serial/g" \ 217 | -e "${tlsa_line_number}s/.*/${tlsa_dns_record}/" $zonefile 218 | 219 | if docker exec nsd nsd-checkzone "$domain" /zones/"$zonename" | grep -q "zone ${domain} is ok"; then 220 | docker exec nsd signzone "$domain" "$expiration_date" 221 | fi 222 | ``` 223 | 224 | Show your DS-Records (Delegation Signer): 225 | 226 | ``` 227 | docker-compose exec nsd ds-records domain.tld 228 | 229 | > DS record 1 [Digest Type = SHA1] : 230 | domain.tld. 600 IN DS xxxx 14 1 xxxxxxxxxxxxxx 231 | 232 | > DS record 2 [Digest Type = SHA256] : 233 | domain.tld. 600 IN DS xxxx 14 2 xxxxxxxxxxxxxx 234 | 235 | > Public KSK Key : 236 | domain.tld. IN DNSKEY 257 3 14 xxxxxxxxxxxxxx ; {id = xxxx (ksk), size = 384b} 237 | ``` 238 | 239 | Restart the DNS server to take the changes into account: 240 | 241 | ``` 242 | docker-compose restart nsd 243 | ``` 244 | -------------------------------------------------------------------------------- /bin/ds-records: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOMAIN="$1" 4 | 5 | if [ -z "$DOMAIN" ]; then 6 | echo "Domain name must be defined" 1>&2 7 | exit 1 8 | fi 9 | 10 | echo -e "\n> DS record 1 [Digest Type = SHA1] :" 11 | ldns-key2ds -n -1 "/zones/db.${DOMAIN}.signed" 12 | 13 | echo -e "\n> DS record 2 [Digest Type = SHA256] :" 14 | ldns-key2ds -n -2 "/zones/db.${DOMAIN}.signed" 15 | 16 | echo -e "\n> Public KSK Key :" 17 | tail -n 1 "/zones/K${DOMAIN}.ksk.key" 18 | echo "" 19 | 20 | exit 0 -------------------------------------------------------------------------------- /bin/keygen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOMAIN="$1" 4 | 5 | if [ -z "$DOMAIN" ]; then 6 | echo "Domain name must be defined" 1>&2 7 | exit 1 8 | fi 9 | 10 | cd /zones || exit 1 11 | 12 | echo "Generating ZSK & KSK keys for '${DOMAIN}'" 13 | ZSK=$(ldns-keygen -a ECDSAP384SHA384 -b 384 "$DOMAIN") 14 | KSK=$(ldns-keygen -k -a ECDSAP384SHA384 -b 384 "$DOMAIN") 15 | 16 | rm -f "$ZSK".ds "$KSK".ds 17 | 18 | mv "$ZSK".key "K${DOMAIN}.zsk.key" 19 | mv "$ZSK".private "K${DOMAIN}.zsk.private" 20 | 21 | mv "$KSK".key "K${DOMAIN}.ksk.key" 22 | mv "$KSK".private "K${DOMAIN}.ksk.private" 23 | 24 | chmod 600 -- *.private 25 | exit 0 -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f /etc/nsd/nsd_server.pem ]; then 4 | nsd-control-setup 5 | fi 6 | 7 | chown -R $UID:$GID /var/db/nsd/ /etc/nsd /tmp 8 | 9 | exec /sbin/tini -- nsd -u $UID.$GID -P /tmp/nsd.pid -d 10 | -------------------------------------------------------------------------------- /bin/signzone: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOMAIN="$1" 4 | EXPIRATION_DATE="$2" 5 | 6 | if [ -z "$DOMAIN" ]; then 7 | echo "Domain name must be defined" 1>&2 8 | exit 1 9 | fi 10 | 11 | if [ -n "$EXPIRATION_DATE" ]; then 12 | arg="-e${EXPIRATION_DATE}" 13 | fi 14 | 15 | cd /zones || exit 1 16 | 17 | echo "Signing zone for ${DOMAIN}" 18 | ldns-signzone -n -p ${arg} -s "$(head /dev/urandom | tr -dc A-Za-z0-9 | sha1sum | head -c 30)" \ 19 | -f "db.${DOMAIN}.signed" "db.${DOMAIN}" "K${DOMAIN}.zsk" "K${DOMAIN}.ksk" 20 | 21 | updatezone "$DOMAIN" 22 | exit 0 23 | -------------------------------------------------------------------------------- /bin/updatezone: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOMAIN="$1" 4 | 5 | if [ -z "$DOMAIN" ]; then 6 | echo "Domain name must be defined" 1>&2 7 | exit 1 8 | fi 9 | 10 | echo -n "NSD configuration rebuild... " 11 | nsd-control reconfig 12 | 13 | echo -n "Reloading zone for ${DOMAIN}... " 14 | nsd-control reload "$DOMAIN" 15 | 16 | echo -n "Notify slave servers... " 17 | nsd-control notify "$DOMAIN" 18 | 19 | echo "Done." 20 | exit 0 21 | -------------------------------------------------------------------------------- /test/config/db.example.org: -------------------------------------------------------------------------------- 1 | $ORIGIN example.org. 2 | $TTL 3600 3 | 4 | 5 | @ IN SOA ( 6 | ns1.example.org. ; primary nameserver 7 | hostmaster.example.org. ; admin email 8 | 2019043001 ; Serial 9 | 3600 ; Refresh 10 | 1800 ; Retry 11 | 1209600 ; Expire 12 | 600 ; Minimum 13 | ) 14 | 15 | ; NAMESERVERS 16 | 17 | @ IN NS ns1.example.org. 18 | @ IN NS ns2.example.org. 19 | 20 | ; A RECORDS 21 | 22 | @ IN A 10.20.30.40 23 | hostname IN A 10.20.30.40 24 | ns1 IN A 10.20.30.40 25 | ns2 IN A 10.20.30.40 26 | 27 | ; CNAME RECORDS 28 | 29 | www IN CNAME hostname 30 | 31 | ; MAIL RECORDS 32 | 33 | @ IN MX 10 hostname.example.org. 34 | -------------------------------------------------------------------------------- /test/config/nsd.conf: -------------------------------------------------------------------------------- 1 | server: 2 | server-count: 1 3 | ip4-only: yes 4 | hide-version: yes 5 | identity: "" 6 | zonesdir: "/zones" 7 | 8 | remote-control: 9 | control-enable: yes 10 | 11 | key: 12 | name: "sec_key" 13 | algorithm: hmac-sha256 14 | secret: "WU9VUl9TRUNSRVRfS0VZCg==" 15 | 16 | zone: 17 | name: example.org 18 | zonefile: db.example.org.signed 19 | -------------------------------------------------------------------------------- /test/tests.bats: -------------------------------------------------------------------------------- 1 | load 'test_helper/bats-support/load' 2 | load 'test_helper/bats-assert/load' 3 | 4 | 5 | @test "check zone" { 6 | run docker exec nsd_unsigned nsd-checkzone example.org /zones/db.example.org 7 | assert_success 8 | assert_output "zone example.org is ok" 9 | } 10 | 11 | @test "check conf" { 12 | run docker exec nsd_unsigned nsd-checkconf /etc/nsd/nsd.conf 13 | assert_success 14 | assert_output "" 15 | } 16 | 17 | @test "generate key" { 18 | run docker exec nsd_unsigned [ ! -f /etc/nsd/*.key ] 19 | assert_success 20 | run docker exec nsd_unsigned [ ! -f /etc/nsd/*.pem ] 21 | assert_success 22 | run docker exec nsd_unsigned keygen example.org 23 | assert_success 24 | assert_output "Generating ZSK & KSK keys for 'example.org'" 25 | run docker exec nsd_unsigned [ -f /etc/nsd/nsd_control.key ] 26 | assert_success 27 | run docker exec nsd_unsigned [ -f /etc/nsd/nsd_control.pem ] 28 | assert_success 29 | run docker exec nsd_unsigned [ -f /etc/nsd/nsd_server.key ] 30 | assert_success 31 | run docker exec nsd_unsigned [ -f /etc/nsd/nsd_server.pem ] 32 | assert_success 33 | } 34 | 35 | @test "check DS records" { 36 | run docker exec nsd_default ds-records example.org 37 | assert_success 38 | assert_line --index 0 '> DS record 1 [Digest Type = SHA1] :' 39 | assert_line --index 1 --regexp '^example.org. 3600 IN DS [0-9]{2,5} 14 1 [0-9a-f]{40}$' 40 | assert_line --index 2 '> DS record 2 [Digest Type = SHA256] :' 41 | assert_line --index 3 --regexp '^example.org. 3600 IN DS [0-9]{3,5} 14 2 [0-9a-f]{64}$' 42 | assert_line --index 4 '> Public KSK Key :' 43 | assert_line --index 5 --regexp '^example.org. IN DNSKEY 257 3 14 [^ ]{128} ;\{id = [0-9]{4,5} \(ksk\), size = 384b\}$' 44 | } 45 | 46 | @test "dig result" { 47 | run bash -c "dig example.org @$(docker inspect --format '{{.NetworkSettings.IPAddress}}' nsd_default) | grep -v '^;'" 48 | assert_success 49 | assert_line --index 0 'example.org. 3600 IN A 10.20.30.40' 50 | assert_line --index 1 'example.org. 3600 IN NS ns1.example.org.' 51 | assert_line --index 2 'example.org. 3600 IN NS ns2.example.org.' 52 | assert_line --index 3 'ns1.example.org. 3600 IN A 10.20.30.40' 53 | assert_line --index 4 'ns2.example.org. 3600 IN A 10.20.30.40' 54 | } 55 | 56 | @test "dig rrsig result" { 57 | run bash -c "dig example.org RRSIG @$(docker inspect --format '{{.NetworkSettings.IPAddress}}' nsd_default) | grep -v '^;'" 58 | assert_success 59 | assert_line --index 0 --regexp '^example.org. 3600 IN RRSIG SOA 14 2 3600 ' 60 | assert_line --index 1 --regexp '^example.org. 3600 IN RRSIG A 14 2 3600 ' 61 | assert_line --index 2 --regexp '^example.org. 3600 IN RRSIG NS 14 2 3600 ' 62 | assert_line --index 3 --regexp '^example.org. 3600 IN RRSIG MX 14 2 3600 ' 63 | assert_line --index 4 --regexp '^example.org. 3600 IN RRSIG DNSKEY 14 2 3600 ' 64 | assert_line --index 5 --regexp '^example.org. 3600 IN RRSIG NSEC3PARAM 14 2 3600 ' 65 | assert_line --index 6 --regexp '^example.org. 3600 IN NS ns1.example.org.' 66 | assert_line --index 7 --regexp '^example.org. 3600 IN NS ns2.example.org.' 67 | assert_line --index 8 'ns1.example.org. 3600 IN A 10.20.30.40' 68 | assert_line --index 9 'ns2.example.org. 3600 IN A 10.20.30.40' 69 | } 70 | --------------------------------------------------------------------------------