diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index a1787e9d7..7dd2211a4 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -12,7 +12,7 @@ class NameserverRecordValidationJob < ApplicationJob domains.each do |domain| domain.nameservers.each do |nameserver| - next if nameserver.nameserver_failed_validation? || nameserver.validated? + next if nameserver.failed_validation? || nameserver.validated? result = NameserverValidator.run(domain_name: domain.name, nameserver: nameserver) @@ -38,7 +38,7 @@ class NameserverRecordValidationJob < ApplicationJob return logger.info 'Domain not has nameservers' if domain.nameservers.empty? domain.nameservers.each do |nameserver| - next if nameserver.nameserver_failed_validation? + next if nameserver.failed_validation? result = NameserverValidator.run(domain_name: domain.name, nameserver: nameserver) @@ -73,6 +73,8 @@ class NameserverRecordValidationJob < ApplicationJob nameserver.failed_validation_reason = reason nameserver.save + + failed_log(text: reason, nameserver: nameserver, domain: nameserver.domain) if nameserver.failed_validation? end def parse_result(result, nameserver) @@ -95,27 +97,26 @@ class NameserverRecordValidationJob < ApplicationJob end logger.info text - failed_log(text: text, nameserver: nameserver) add_nameserver_to_failed(nameserver: nameserver, reason: text) - false end - def failed_log(text:, nameserver:) - inform_to_tech_contact(text) + def failed_log(text:, nameserver:, domain:) + inform_to_tech_contact(domain: domain, nameserver: nameserver, text: text) inform_to_registrar(text: text, nameserver: nameserver) false end - def inform_to_tech_contact(text) - "NEED TO DO!" - text + def inform_to_tech_contact(domain:, nameserver:, text: nil) + # ContactNotification.notify_tech_contact(domain: domain, nameserver: nameserver, reason: 'nameserver') end - def inform_to_registrar(text:, nameserver:) - # nameserver.domain.registrar.notifications.create!(text: text) - "NEED TO DO!" + def inform_to_registrar(nameserver:, text: nil) + text = "Host record #{nameserver.hostname} of a domain #{nameserver.domain} is invalid. + Please fix or contact the registrant. Problem with nameserver #{nameserver} - #{nameserver.failed_validation_reason}" + logger.info text + # ContactNotification.notify_registrar(domain: nameserver.domain, text: text) end def logger diff --git a/app/jobs/validate_dnssec_job.rb b/app/jobs/validate_dnssec_job.rb new file mode 100644 index 000000000..623513768 --- /dev/null +++ b/app/jobs/validate_dnssec_job.rb @@ -0,0 +1,145 @@ +class ValidateDnssecJob < ApplicationJob + discard_on StandardError + + def perform(domain_name: nil) + unless domain_name.nil? + domain = Domain.find_by(name: domain_name) + + return logger.info "This domain not contain any dnskeys" if domain.dnskeys.empty? + + return logger.info "No domain found" if domain.nil? + + return logger.info "No related nameservers for this domain" if domain.nameservers.empty? + + iterate_nameservers(domain) + else + domain_list = Domain.all.reject { |d| d.dnskeys.empty? } + + domain_list.each do |d| + if d.nameservers.empty? + logger.info "#{d.name} has no nameserver" + + next + end + + iterate_nameservers(d) + end + end + rescue StandardError => e + logger.error e.message + raise e + end + + private + + def iterate_nameservers(domain) + domain.nameservers.each do |n| + next unless n.validated? + + validate(hostname: n.hostname, domain: domain) + + notify_contacts(domain) + logger.info "----------------------------" + end + end + + def notify_contacts(domain) + flag = domain.dnskeys.any? { |k| k.validation_datetime.present? } + + return if flag + + text = "DNSKEY record of a domain #{domain.name} is invalid. Please fix or contact the registrant." + logger.info text + # ContactNotification.notify_registrar(domain: domain, text: text) + # ContactNotification.notify_tech_contact(domain: domain, reason: 'dnssec') + end + + def validate(hostname:, domain:, type: 'DNSKEY', klass: 'IN') + resolver = prepare_validator(hostname) + answer = resolver.query(domain.name, type, klass) + + return logger.info "no any data for #{domain.name} | hostname - #{hostname}" if answer.nil? + + logger.info "-----------" + logger.info "data for domain name - #{domain.name} | hostname - #{hostname}" + logger.info "-----------" + + response_container = parse_response(answer) + + compare_dnssec_data(response_container: response_container, domain: domain) + rescue Exception => e + logger.error "#{e.message} - domain name: #{domain.name} - hostname: #{hostname}" + nil + end + + def compare_dnssec_data(response_container:, domain:) + domain.dnskeys.each do |key| + next unless key.flags.to_s == '257' + next if key.validation_datetime.present? + + flag = make_magic(response_container: response_container, dnskey: key) + text = "#{key.flags} - #{key.protocol} - #{key.alg} - #{key.public_key}" + + if flag + key.validation_datetime = Time.zone.now + key.save + + logger.info text + " ------->> succesfully!" + else + logger.info text + " ------->> not found in zone!" + end + end + end + + def make_magic(response_container:, dnskey:) + response_container.any? do |r| + r[:flags].to_s == dnskey.flags.to_s && + r[:protocol].to_s == dnskey.protocol.to_s && + r[:alg].to_s == dnskey.alg.to_s && + r[:public_key] == dnskey.public_key + end + end + + def parse_response(answer) + response_container = [] + + answer.each_answer do |a| + + a_string = a.to_s + a_string = a_string.gsub /\t/, ' ' + a_string = a_string.split(' ') + + next unless a_string[4] == '257' + + protocol = a.protocol + alg = a.algorithm.code + + response_container << { + flags: a_string[4], + protocol: protocol, + alg: alg, + public_key: a_string[8] + } + end + + response_container + end + + def prepare_validator(nameserver) + inner_resolver = Dnsruby::Resolver.new + timeouts = ENV['nameserver_validation_timeout'] || 4 + inner_resolver.do_validation = true + inner_resolver.dnssec = true + inner_resolver.nameserver = nameserver + inner_resolver.packet_timeout = timeouts.to_i + inner_resolver.query_timeout = timeouts.to_i + resolver = Dnsruby::Recursor.new(inner_resolver) + resolver.dnssec = true + + resolver + end + + def logger + @logger ||= Rails.logger + end +end diff --git a/app/mailers/contact_inform_mailer.rb b/app/mailers/contact_inform_mailer.rb new file mode 100644 index 000000000..bf5037cbf --- /dev/null +++ b/app/mailers/contact_inform_mailer.rb @@ -0,0 +1,27 @@ +class ContactInformMailer < ApplicationMailer + helper_method :address_processing + + def notify_dnssec(contact:, domain:) + @contact = contact + @domain = domain + + subject = "Domeeni #{@domain.name} DNSSEC kirjed ei ole korrektsed / The DNSKEY records of the domain #{@domain.name} are invalid" + + mail(to: contact.email, subject: subject) + end + + def notify_nameserver(contact:, domain:, nameserver:) + @contact = contact + @domain = domain + @nameserver = nameserver + + subject = "Domeeni #{@domain.name} nimeserveri kirjed ei ole korrektsed / The host records of the domain #{@domain.name} are invalid" + mail(to: contact.email, subject: subject) + end + + private + + def address_processing + Contact.address_processing? + end +end diff --git a/app/models/nameserver.rb b/app/models/nameserver.rb index 20d4656c6..ababd84cf 100644 --- a/app/models/nameserver.rb +++ b/app/models/nameserver.rb @@ -55,7 +55,7 @@ class Nameserver < ApplicationRecord } end - def nameserver_failed_validation? + def failed_validation? return false if validation_counter.nil? validation_counter >= NameserverValidator::VALID_NAMESERVER_COUNT_THRESHOLD diff --git a/app/services/contact_notification.rb b/app/services/contact_notification.rb new file mode 100644 index 000000000..e9b00c575 --- /dev/null +++ b/app/services/contact_notification.rb @@ -0,0 +1,24 @@ +module ContactNotification + extend self + + def notify_registrar(domain:, text:) + domain.registrar.notifications.create(text: text) + end + + def notify_tech_contact(domain:, nameserver: nil, reason: nil) + case reason + when 'dnssec' + domain.tech_contacts.each do |tech| + contact = Contact.find(tech.id) + + ContactInformMailer.notify_dnssec(contact: contact, domain: domain).deliver_now + end + when 'nameserver' + domain.tech_contacts.each do |tech| + contact = Contact.find(tech.id) + + ContactInformMailer.notify_nameserver(contact: contact, domain: domain, nameserver: nameserver).deliver_now + end + end + end +end diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index 410448472..c45003cd3 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -18,7 +18,7 @@ module NameserverValidator # result_response = validate(domain_name: domain_name, hostname: nameserver.ipv6) end - return { result: false, reason: 'glup record' } if result.answer.empty? if result_response[:result] + return { result: false, reason: 'glup record' } unless result_response[:result] return result_response end diff --git a/app/views/mailers/contact_inform_mailer/notify_dnssec.html.erb b/app/views/mailers/contact_inform_mailer/notify_dnssec.html.erb new file mode 100644 index 000000000..4b506b9e8 --- /dev/null +++ b/app/views/mailers/contact_inform_mailer/notify_dnssec.html.erb @@ -0,0 +1,40 @@ +
+Eesti Interneti Sihtasutusele (EIS) juhib tähelepanu, et domeeni <%= @domain.name %> DNSKEY kirjed on puudulikud ning domeeniga seotud teenuse ei pruugi korrektselt toimida. +
++Andmete parandamiseks vaadake palun üle domeeni nimeserverite seaditused või pöörduge palun oma registripidaja <%= @domain.registrar.name %> või nimeserveri teenuse pakkuja poole.
+ ++Lisaküsimuste korral võtke palun ühendust oma registripidajaga: +
+<%= @domain.registrar.name %>+Estonian Internet Foundation points out that the DNSKEY record(s) for the domain <%= @domain.name %> are invalid and the service related to the domain may not work correctly. +
++Please check the DNSKEY records of the domain or contact your registrar <%= @domain.registrar.name%> or your name server service provider to correct this information. +
++Should you have additional questions, please contact your registrar: +
+<%= @domain.registrar.name %>+Eesti Interneti Sihtasutusele (EIS) juhib tähelepanu, et domeeni <%= @domain.name %> nimeserverite seaded on puudulikud ning domeeniga seotud teenuse ei pruugi korrektselt toimida.
++Andmete parandamiseks vaadake palun üle domeeni nimeserverite seaditused või pöörduge palun oma registripidaja <%= @domain.registrar.name%> või nimeserveri teenuse pakkuja poole. +
+ +Viga nimesereriga <%= @nameserver %> - <%= @nameserver.failed_validation_reason %> + ++Lisaküsimuste korral võtke palun ühendust oma registripidajaga: +
+ +<%= @domain.registrar.name %>+ Estonian Internet Foundation points out that the settings for the name servers of the domain <%= @domain.name %> are incomplete and the service related to the domain may not work correctly. +
++Please check the name server settings of the domain or contact your registrar <%= @domain.registrar.name%> or your name server service provider to correct this information. +
+ +Problem with nameserver <%= @nameserver %> - <%= @nameserver.failed_validation_reason %>" + ++Should you have additional questions, please contact your registrar: +
+ +<%= @domain.registrar.name%>