diff --git a/.github/workflows/nameservers.yml b/.github/workflows/nameservers.yml index d8e3cac..1081230 100644 --- a/.github/workflows/nameservers.yml +++ b/.github/workflows/nameservers.yml @@ -18,4 +18,4 @@ jobs: - name: Run DNS check run: rspec --format documentation --color spec/ env: - DOMAIN: sslip.io # You can set your domain here if needed + DOMAINS: nip.io,sslip.io # You can set your domain here if needed diff --git a/README.md b/README.md index c58783a..cdf55b7 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ as ARM64 (AWS Graviton, Apple M1/M2). - `spec/` contains the tests for the production nameservers. To run the tests locally: ```bash - DOMAIN=sslip.io rspec --format documentation --color spec/ + DOMAINS=nip.io,sslip.io rspec --format documentation --color spec ``` - `k8s/document_root_sslip.io/` contains the HTML content of the sslip.io website. diff --git a/spec/check-dns_spec.rb b/spec/check-dns_spec.rb index dfef091..4520527 100644 --- a/spec/check-dns_spec.rb +++ b/spec/check-dns_spec.rb @@ -1,6 +1,7 @@ #!/usr/bin/env ruby +# rubocop:disable Metrics/BlockLength # -# DOMAIN=sslip.io rspec --format documentation --color spec +# DOMAINS=nip.io,sslip.io rspec --format documentation --color spec # # Admittedly it's overkill to use rspec to run a set of assertions # against a DNS server -- a simple shell script would have been @@ -9,115 +10,119 @@ # def get_whois_nameservers(domain) whois_output = `whois #{domain}` - soa = nil whois_lines = whois_output.split(/\n+/) nameserver_lines = whois_lines.select { |line| line =~ /^Name Server:/ } nameservers = nameserver_lines.map { |line| line.split.last.downcase }.uniq - # whois records don't have trail '.'; NS records do; add trailing '.' + # whois records don't have trailing '.'; NS records do; add trailing '.' nameservers.map { |ns| ns << '.' } nameservers end -domain = ENV['DOMAIN'] || 'example.com' +domains_env = ENV['DOMAINS'] || 'example.com' +domains = domains_env.split(',').map(&:strip) sslip_version = '4.1.1' -whois_nameservers = get_whois_nameservers(domain) -describe domain do - # I don't want a spurious failure, esp. ns-do-sg.sslip.io - # default is 3 tries, 5 seconds timeout - let(:dig_args) { ['+tries=15', '+timeout=10'] } - let(:dig_cmd) { "dig #{dig_args.join(' ')}" } - soa = nil +domains.each do |domain| + whois_nameservers = get_whois_nameservers(domain) - context "when evaluating $DOMAIN (\"#{domain}\") environment variable" do - let (:domain) { ENV['DOMAIN'] } - it 'is set' do - expect(domain).not_to be_nil - end - it 'is not an empty string' do - expect(domain).not_to eq('') - end - end + describe domain do + # I don't want a spurious failure, esp. ns-do-sg.sslip.io + # default is 3 tries, 5 seconds timeout + let(:dig_args) { ['+tries=15', '+timeout=10'] } + let(:dig_cmd) { "dig #{dig_args.join(' ')}" } + soa = nil - it "should have at least 2 nameservers" do - expect(whois_nameservers.size).to be > 1 - end - - whois_nameservers.each do |whois_nameserver| - it "nameserver #{whois_nameserver}'s NS records include all whois nameservers #{whois_nameservers}, " + - "`dig ... @#{whois_nameserver} ns #{domain} +short`" do - dig_nameservers = `#{dig_cmd} @#{whois_nameserver} ns #{domain} +short`.split(/\n+/) - expect(whois_nameservers - dig_nameservers).to be_empty + context "when evaluating $DOMAINS (\"#{domains_env}\") environment variable" do + let(:domains_env) { ENV['DOMAINS'] } + it 'is set' do + expect(domains_env).not_to be_nil + end + it 'is not an empty string' do + expect(domains_env).not_to eq('') + end end - it "nameserver #{whois_nameserver}'s SOA record match" do - dig_soa = `#{dig_cmd} @#{whois_nameserver} soa #{domain} +short` - soa = soa || dig_soa - expect(dig_soa).to eq(soa) + it 'should have at least 2 nameservers' do + expect(whois_nameservers.size).to be > 1 end - it "nameserver #{whois_nameserver}'s has an A record" do - expect(`#{dig_cmd} @#{whois_nameserver} a #{domain} +short`.chomp).not_to eq('') - expect($?.success?).to be true - end + whois_nameservers.each do |whois_nameserver| + it "nameserver #{whois_nameserver}'s NS records include all whois nameservers #{whois_nameservers}, " + + "`dig ... @#{whois_nameserver} ns #{domain} +short`" do + dig_nameservers = `#{dig_cmd} @#{whois_nameserver} ns #{domain} +short`.split(/\n+/) + expect(whois_nameservers - dig_nameservers).to be_empty + end - it "nameserver #{whois_nameserver}'s has an AAAA record" do - expect(`#{dig_cmd} @#{whois_nameserver} a #{domain} +short`.chomp).not_to eq('') - expect($?.success?).to be true - end + it "nameserver #{whois_nameserver}'s SOA record match" do + dig_soa = `#{dig_cmd} @#{whois_nameserver} soa #{domain} +short` + soa ||= dig_soa + expect(dig_soa).to eq(soa) + end - a = [ rand(256), rand(256), rand(256), rand(256) ] - it "resolves #{a.join(".")}.#{domain} to #{a.join(".")}" do - expect(`#{dig_cmd} @#{whois_nameserver} #{a.join(".") + "." + domain} +short`.chomp).to eq(a.join(".")) - end + it "nameserver #{whois_nameserver}'s has an A record" do + expect(`#{dig_cmd} @#{whois_nameserver} a #{domain} +short`.chomp).not_to eq('') + expect($?.success?).to be true + end - a = [ rand(256), rand(256), rand(256), rand(256) ] - it "resolves #{a.join("-")}.#{domain} to #{a.join(".")}" do - expect(`#{dig_cmd} @#{whois_nameserver} #{a.join("-") + "." + domain} +short`.chomp).to eq(a.join(".")) - end + it "nameserver #{whois_nameserver}'s has an AAAA record" do + expect(`#{dig_cmd} @#{whois_nameserver} a #{domain} +short`.chomp).not_to eq('') + expect($?.success?).to be true + end - a = [ rand(256), rand(256), rand(256), rand(256) ] - b = [ ('a'..'z').to_a, ('0'..'9').to_a ].flatten.shuffle[0,8].join - it "resolves #{b}.#{a.join("-")}.#{domain} to #{a.join(".")}" do - expect(`#{dig_cmd} @#{whois_nameserver} #{b}.#{a.join("-") + "." + domain} +short`.chomp).to eq(a.join(".")) - end + a = [rand(256), rand(256), rand(256), rand(256)] + it "resolves #{a.join('.')}.#{domain} to #{a.join('.')}" do + expect(`#{dig_cmd} @#{whois_nameserver} #{a.join('.') + '.' + domain} +short`.chomp).to eq(a.join('.')) + end - a = [ rand(256), rand(256), rand(256), rand(256) ] - b = [ ('a'..'z').to_a, ('0'..'9').to_a ].flatten.shuffle[0,8].join - it "resolves #{a.join("-")}.#{b} to #{a.join(".")}" do - expect(`#{dig_cmd} @#{whois_nameserver} #{a.join("-") + "." + b} +short`.chomp).to eq(a.join(".")) - end + a = [rand(256), rand(256), rand(256), rand(256)] + it "resolves #{a.join('-')}.#{domain} to #{a.join('.')}" do + expect(`#{dig_cmd} @#{whois_nameserver} #{a.join('-') + '.' + domain} +short`.chomp).to eq(a.join('.')) + end - # don't begin the hostname with a double-dash -- `dig` mistakes it for an argument - it "resolves api.--.#{domain}' to eq ::)}" do - expect(`#{dig_cmd} @#{whois_nameserver} AAAA api.--.#{domain} +short`.chomp).to eq("::") - end + a = [rand(256), rand(256), rand(256), rand(256)] + b = [('a'..'z').to_a, ('0'..'9').to_a].flatten.sample(8).join + it "resolves #{b}.#{a.join('-')}.#{domain} to #{a.join('.')}" do + expect(`#{dig_cmd} @#{whois_nameserver} #{b}.#{a.join('-') + '.' + domain} +short`.chomp).to eq(a.join('.')) + end - it "resolves localhost.--1.#{domain}' to eq ::1)}" do - expect(`#{dig_cmd} @#{whois_nameserver} AAAA localhost.api.--1.#{domain} +short`.chomp).to eq("::1") - end + a = [rand(256), rand(256), rand(256), rand(256)] + b = [('a'..'z').to_a, ('0'..'9').to_a].flatten.sample(8).join + it "resolves #{a.join('-')}.#{b} to #{a.join('.')}" do + expect(`#{dig_cmd} @#{whois_nameserver} #{a.join('-') + '.' + b} +short`.chomp).to eq(a.join('.')) + end - it "resolves 2001-4860-4860--8888.#{domain}' to eq 2001:4860:4860::8888)}" do - expect(`#{dig_cmd} @#{whois_nameserver} AAAA 2001-4860-4860--8888.#{domain} +short`.chomp).to eq("2001:4860:4860::8888") - end + # don't begin the hostname with a double-dash -- `dig` mistakes it for an argument + it "resolves api.--.#{domain}' to eq ::)}" do + expect(`#{dig_cmd} @#{whois_nameserver} AAAA api.--.#{domain} +short`.chomp).to eq('::') + end - it "resolves 2601-646-100-69f0--24.#{domain}' to eq 2601:646:100:69f0::24)}" do - expect(`#{dig_cmd} @#{whois_nameserver} AAAA 2601-646-100-69f0--24.#{domain} +short`.chomp).to eq("2601:646:100:69f0::24") - end + it "resolves localhost.--1.#{domain}' to eq ::1)}" do + expect(`#{dig_cmd} @#{whois_nameserver} AAAA localhost.api.--1.#{domain} +short`.chomp).to eq('::1') + end - it "gets the expected version number, #{sslip_version}" do - expect(`#{dig_cmd} @#{whois_nameserver} TXT version.status.#{domain} +short`).to include(sslip_version) - end + it "resolves 2001-4860-4860--8888.#{domain}' to eq 2001:4860:4860::8888)}" do + expect(`#{dig_cmd} @#{whois_nameserver} AAAA 2001-4860-4860--8888.#{domain} +short`.chomp).to eq('2001:4860:4860::8888') + end - it "gets the source (querier's) IP address" do - # Look on my Regular Expressions, ye mighty, and despair! - expect(`#{dig_cmd} @#{whois_nameserver} TXT ip.#{domain} +short`).to match(/^"(\d+\.\d+\.\d+\.\d+)|(([[:xdigit:]]*:){2,7}[[:xdigit:]]*)"$/) - end + it "resolves 2601-646-100-69f0--24.#{domain}' to eq 2601:646:100:69f0::24)}" do + expect(`#{dig_cmd} @#{whois_nameserver} AAAA 2601-646-100-69f0--24.#{domain} +short`.chomp).to eq('2601:646:100:69f0::24') + end - # check the website - it "is able to reach https://#{domain} and get a valid response (2xx)" do - `curl -If https://#{domain} 2> /dev/null` - expect($?.success?).to be true + it "gets the expected version number, #{sslip_version}" do + expect(`#{dig_cmd} @#{whois_nameserver} TXT version.status.#{domain} +short`).to include(sslip_version) + end + + it "gets the source (querier's) IP address" do + # Look on my Regular Expressions, ye mighty, and despair! + expect(`#{dig_cmd} @#{whois_nameserver} TXT ip.#{domain} +short`).to match(/^"(\d+\.\d+\.\d+\.\d+)|(([[:xdigit:]]*:){2,7}[[:xdigit:]]*)"$/) + end + + # check the website + it "is able to reach https://#{domain} and get a valid response (2xx)" do + `curl -If https://#{domain} 2> /dev/null` + expect($?.success?).to be true + end end end end +# rubocop:enable Metrics/BlockLength \ No newline at end of file