diff --git a/dnsworker/bin/dns-worker b/dnsworker/bin/dns-worker index 125ab3c..1ed82e1 100755 --- a/dnsworker/bin/dns-worker +++ b/dnsworker/bin/dns-worker @@ -1,50 +1,50 @@ #!/usr/bin/env ruby $:.unshift File.expand_path('../../lib', File.realpath(__FILE__)) require 'yaml' require 'optparse' require 'ostruct' require 'dnsworker/worker' options = OpenStruct.new options.once = false options.dry_run = false options.extra = nil OptionParser.new do |opts| - opts.banner = 'Usage: webdns-worker [options]' + opts.banner = 'Usage: dns-worker [options]' opts.on('-c', '--config CONFIG', 'Config file') do |c| options[:config] = c end opts.on('-e', '--extra CONFIG', 'Extra config file') do |e| options[:extra] = e end opts.on('-n', '--dry-run', 'Run but not execute or mark anything') do |n| options[:dry_run] = n end opts.on('-o', '--once', 'Run once') do |o| options[:once] = o end opts.on('-d', '--cmdline-dispatch TYPE:JSON') do |d| options[:cmdline_type], options[:cmdline_body] = d.split(':', 2) options.cmdline_body = JSON.parse(options.cmdline_body, symbolize_names: true) if options.cmdline_body end end.parse! cfg = YAML.load_file(options.config) cfg.merge!(YAML.load_file(options.extra)) if options.extra w = DNSWorker::Worker.new(cfg) if options.cmdline_type w.send(:cmdline, options.cmdline_type, options.cmdline_body) else w.work(once: options.once, dry_run: options.dry_run) end diff --git a/dnsworker/bin/ds-schedule b/dnsworker/bin/ds-schedule index 59053f8..6b1bfd2 100755 --- a/dnsworker/bin/ds-schedule +++ b/dnsworker/bin/ds-schedule @@ -1,105 +1,105 @@ #!/usr/bin/env ruby require 'uri' require 'rack/utils' require 'net/http' require 'open3' class DSPusher attr_reader :cfg def initialize(cfg) @cfg = cfg end def push_ds(zone=nil, dry_run=false) fetch_dss(zone).each { |d, dss| post_dss(d, dss, dry_run) } end def fetch_dss(zone) dss = Hash.new { |h, k| h[k] = [] } cmd = cfg['get_ds'] cmd += " -z #{zone}" if zone out, err = command(cmd) out.each_line { |line| line.strip! next if line.nil? || line == '' next if line.start_with?(';') domain, _ttl, _f, rtype, rdata = line.split(/\s+/, 5) next unless rtype == 'DS' domain = domain.gsub(/\.+$/, '') # Remove trailing . dss[domain] << rdata } dss end def post_dss(domain, dss, dry_run) query = Rack::Utils.build_nested_query( domain: domain, event: 'push_ds', args: dss, ) uri = URI("#{cfg['webdns_base']}#{cfg['update_state']}" % { query: query }) if dry_run p [:uri, uri] return end Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http| resp = http.request Net::HTTP::Put.new(uri.request_uri) fail JobFailed if resp.code != '200' end end private def command(cmd) out, err, status = Open3.capture3(cmd) raise "Command error err='#{err.strip}' out='#{out.strip}'" if !status.success? [out, err] end end require 'yaml' require 'optparse' require 'ostruct' options = OpenStruct.new options.dry_run = false options.extra = nil options.zone = nil OptionParser.new do |opts| - opts.banner = 'Usage: push-ds [options]' + opts.banner = 'Usage: ds-schedule [options]' opts.on('-c', '--config CONFIG', 'Config file') do |c| options[:config] = c end opts.on('-e', '--extra CONFIG', 'Extra config file') do |e| options[:extra] = e end opts.on('-n', '--dry-run', 'Run but not execute anything') do |n| options[:dry_run] = n end opts.on('-z', '--zone ZONE', 'Zone') do |z| options[:zone] = z end end.parse! cfg = YAML.load_file(options.config) cfg.merge!(YAML.load_file(options.extra)) if options.extra w = DSPusher.new(cfg) w.push_ds(options.zone, options.dry_run) diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 2e6255b..1a96a28 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -1,305 +1,305 @@ require 'test_helper' class DomainTest < ActiveSupport::TestCase def setup @domain = build(:domain) end test 'automatic SOA creation' do @domain.save! @domain.reload assert_not_nil @domain.soa end test 'increment serial on new record' do @domain.save! soa = @domain.soa assert_serial_update soa do www = A.new(name: 'www', domain: @domain, content: '1.2.3.4') www.save! end end test 'increment serial on record update' do @domain.save! www = A.new(name: 'www', domain: @domain, content: '1.2.3.4') www.save! soa = @domain.soa.reload assert_serial_update soa do www.content = '1.2.3.5' www.save! end end test 'automatic NS creation' do @domain.save! @domain.reload assert_equal WebDNS.settings[:default_ns].sort, @domain.records.where(type: 'NS').pluck(:content).sort end test 'increment serial on record destroy' do @domain.save! www = A.new(name: 'www', domain: @domain, content: '1.2.3.4') www.save! soa = @domain.soa.reload assert_serial_update soa do www.destroy! end end class SlaveDomainTest < ActiveSupport::TestCase def setup @domain = build(:slave) end test 'saves' do @domain.save assert_empty @domain.errors end test 'automatic SOA creation' do @domain.save! @domain.reload assert_not_nil @domain.soa assert_equal 1, @domain.soa.serial end test 'validates master' do @domain.master = 'not-an-ip' @domain.save assert_not_empty @domain.errors['master'] end test 'no records are allowed for users' do @domain.save! rec = build(:a, domain_id: @domain.id) assert_not rec.valid? assert_not_empty rec.errors[:type] end end class StatesDomainTest < ActiveSupport::TestCase def setup @domain = build(:domain) @policy = create(:dnssec_policy) end test 'domain lifetime' do assert_equal 'initial', @domain.state # Create assert_jobs do @domain.save! # user triggered assert_equal 'pending_install', @domain.state end @domain.installed # job triggered assert_equal 'operational', @domain.state # Convert to dnssec (sign) assert_jobs do @domain.dnssec = true @domain.dnssec_policy = @policy @domain.dnssec_parent = @domain.name.split('.', 2).last @domain.dnssec_parent_authority = 'test_authority' @domain.save! # After commit is not triggered in tests, # so we have to trigger it manually @domain.send(:after_commit_event) assert_equal 'pending_signing', @domain.state end assert_jobs do assert @domain.signed # job triggered assert_equal 'wait_for_ready', @domain.state end # Convert to dnssec (publish ds) assert_jobs do - assert @domain.push_ds(['dss1', 'dss2']) # triggered by schedule-ds script + assert @domain.push_ds(['dss1', 'dss2']) # triggered by ds-schedule script assert_equal 'pending_ds', @domain.state end assert @domain.converted # job triggered assert_equal 'operational', @domain.state # KSK rollover assert_jobs do - assert @domain.push_ds(['dss3', 'dss4']) # triggered by schedule-ds script + assert @domain.push_ds(['dss3', 'dss4']) # triggered by ds-schedule script assert_equal 'pending_ds_rollover', @domain.state end assert @domain.complete_rollover # job triggered assert_equal 'operational', @domain.state # Convert to plain assert_jobs do assert @domain.plain_convert # user triggered assert_equal 'pending_plain', @domain.state end assert @domain.converted # job triggered assert_equal 'operational', @domain.state # Remove assert_jobs do assert @domain.remove # user triggered assert_equal 'pending_remove', @domain.state end assert @domain.cleaned_up # job triggered assert_equal 'destroy', @domain.state end test 'domain lifetime #full-destroy' do assert_equal 'initial', @domain.state # Create assert_jobs do @domain.save! # user triggered assert_equal 'pending_install', @domain.state end @domain.installed # job triggered assert_equal 'operational', @domain.state # Convert to dnssec (sign) assert_jobs do @domain.dnssec = true @domain.dnssec_policy = @policy @domain.dnssec_parent = @domain.name.split('.', 2).last @domain.dnssec_parent_authority = 'test_authority' @domain.save! # After commit is not triggered in tests, # so we have to trigger it manually @domain.send(:after_commit_event) assert_equal 'pending_signing', @domain.state end assert_jobs do assert @domain.signed # job triggered assert_equal 'wait_for_ready', @domain.state end # Convert to dnssec (publish ds) assert_jobs do - assert @domain.push_ds(['dss1', 'dss2']) # triggered by schedule-ds script + assert @domain.push_ds(['dss1', 'dss2']) # triggered by ds-schedule script assert_equal 'pending_ds', @domain.state end assert @domain.converted # job triggered assert_equal 'operational', @domain.state # KSK rollover assert_jobs do - assert @domain.push_ds(['dss3', 'dss4']) # triggered by schedule-ds script + assert @domain.push_ds(['dss3', 'dss4']) # triggered by ds-schedule script assert_equal 'pending_ds_rollover', @domain.state end assert @domain.complete_rollover # job triggered assert_equal 'operational', @domain.state # Full Remove (Drops DS records) assert_jobs do assert @domain.full_remove # user triggered assert_equal 'pending_ds_removal', @domain.state end assert_jobs do assert @domain.remove # job triggered assert_equal 'pending_remove', @domain.state end assert @domain.cleaned_up # job triggered assert_equal 'destroy', @domain.state end end class DsDomainTest < ActiveSupport::TestCase def setup @domain = create(:domain) @ds = [ '31406 8 1 189968811e6eba862dd6c209f75623d8d9ed9142', '31406 8 2 f78cf3344f72137235098ecbbd08947c2c9001c7f6a085a17f518b5d8f6b916d', ] @child = "dnssec.#{@domain.name}" @extra = DS.create(domain: @domain, name: @child, content: 'other') end test 'add ds records' do Domain.replace_ds(@domain.name, @child, @ds) @extra.save! # Should be deleted assert_equal @ds.size, DS.where(name: "dnssec.#{@domain.name}").count @ds.each { |ds| assert_equal 1, DS.where(name: "dnssec.#{@domain.name}", content: ds).count } end test 'remove ds records' do Domain.replace_ds(@domain.name, @child, []) assert_equal 0, DS.where(name: "dnssec.#{@domain.name}").count end test 'check if child is a valid subdomain' do assert_raise Domain::NotAChild do Domain.replace_ds(@domain.name, 'dnssec.example.net', @ds) end end end class BulkTest < ActiveSupport::TestCase def setup @domain = create(:domain) @a = create(:a, domain: @domain) @aaaa = create(:aaaa, domain: @domain) @new = build(:mx, domain: @domain) end def valid_changes @valid_changes ||= begin {}.tap { |c| c[:deletes] = [@a.id] c[:changes] = { @aaaa.id => { content: '::42' }} c[:additions] = { 1 => @new.as_bulky_json } } end end def invalid_changes @invalid_changes ||= begin {}.tap { |c| c[:deletes] = [Record.maximum(:id) + 1] c[:changes] = { @aaaa.id => { content: '1.2.3.4' }} c[:additions] = { 1 => @new.as_bulky_json.update(prio: -1) } } end end test 'apply changes not' do err = @domain.bulk invalid_changes assert_not_empty err assert_includes err[:deletes][Record.maximum(:id) + 1], 'record not found' assert_includes err[:changes][@aaaa.id], 'not a valid IPv6' assert_includes err[:additions][1], 'not a valid DNS priority' end test 'apply changes' do err = @domain.bulk valid_changes @domain.reload @aaaa.reload assert_empty err assert_empty @domain.records.where(id: @a.id) assert_equal '::42', @aaaa.content assert_equal 1, @domain.records.where(type: :mx).count end end end