Files
canine/app/actions/networks/check_dns.rb
2025-12-10 14:00:34 -08:00

77 lines
2.6 KiB
Ruby

class Networks::CheckDns
extend LightService::Action
expects :ingress, :connection
class << self
def infer_expected_dns(ingress, connection)
ingress.connect(connection)
dns_record = ingress.hostname
if dns_record[:type] == :ip_address && is_private_ip?(dns_record[:value])
cluster = ingress.service.project.cluster
# This only works if it is a single node cluster like k3s
public_ip = infer_public_ip_from_cluster(connection)
dns_record = {
value: public_ip,
type: :ip_address
}
end
dns_record
end
def is_private_ip?(ip)
ip.starts_with?("10.") || ip.starts_with?("172.") || ip.starts_with?("192.")
end
def infer_public_ip_from_cluster(connection)
# The ingress is reporting a private IP address, so we need to guess the public IP address
# based on the cluster's domain name
server_name = K8::Client.new(connection).server
# Parse the hostname from the server, with ruby's URI.parse
hostname = URI.parse(server_name).hostname
# If hostname is just an ip address, then we can return it
if ip?(hostname)
hostname
else
# Otherwise, we need to use Resolv to get the public IP address
Resolv.getaddress(hostname)
end
end
def ip?(ip)
ip.match?(/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/)
end
end
executed do |context|
# TODO
expected_dns = infer_expected_dns(context.ingress, context.connection)
context.ingress.service.domains.each do |domain|
if expected_dns[:type] == :ip_address
ip_addresses = Resolv::DNS.open do |dns|
dns.getresources(domain.domain_name, Resolv::DNS::Resource::IN::A).map do |resource|
resource.address
end
end
if ip_addresses.any? && ip_addresses.first.to_s == expected_dns[:value]
domain.update(status: :dns_verified)
else
domain.update(status: :dns_incorrect, status_reason: "DNS record (#{ip_addresses.first || "empty"}) does not match expected IP address (#{expected_dns[:value]})")
end
else
hostnames = Resolv::DNS.open do |dns|
dns.getresources(domain.domain_name, Resolv::DNS::Resource::IN::CNAME).map do |resource|
resource.name
end
end
if hostnames.any? && hostnames.first.to_s == expected_dns[:value]
domain.update(status: :dns_verified)
else
domain.update(status: :dns_incorrect, status_reason: "DNS record (#{hostnames.first || "empty"}) does not match expected hostname (#{expected_dns[:value]})")
end
end
end
end
end