Oxidized/Ruby prefers IPv4 address over IPv6 address resolved from DNS
I'm running Oxidized on Ubuntu 22.04 with dual stack IPv4/IPv6, and trying to back up the configuration of some devices that are not reachable from this server via IPv4, only IPv6. There are A and AAAA records for the device.
The problem I'm running into is that Oxidized (Ruby?) is using A record instead of the AAAA record to chose the IP address to try and connect to for the backup. I can see this in packet captures and from the Oxidized web it lists the IPv4 address as well. AAAA records should be preferred and tried first from my understanding. If I remove the A record from DNS, Oxidized works just fine as it uses the AAAA record and successfully connects.
I'd like to leave the A records in DNS and not have to add /etc/hosts entries on the server for every device.
oxidized-web v.0.14.0 oxidized v.0.30.1 ruby 3.3.4-p94
Thanks for any assistance!
A better way would be to have oxidized try all available IP address until it gets a connection.
This issue is stale because it has been open 90 days with no activity.
Same issue here.
This issue is stale because it has been open 90 days with no activity.
A possible work around would be to specify the IP-address of the node in the source.
This issue is stale because it has been open 90 days with no activity.
bump
What does this print in shell:
ruby -rresolv -e 'p Resolv.new.getaddress("ytti.fi")'
"2001:67c:17a0::1"
And resolve_dns is set to true (default)?
Then node.ip should be set to that, and SSH should be using that. Otherwise node.ip should be the name, which then would be resolved by Nett::SSH and I'd need to look what it does.
Is the node in question using SSH?
I can confirm that resolve_dns is set to true.
The nodes in question are all using SSH, yes.
I would expect IPv6 as well, and don’t have good answer, I’d need to dig deeper but I don’t have appetite to try to reproduce on my end. If you can create setup where I can see this and create remote access to the node, I’ll take a look. I’d ssh in from ‘ytti.fi’.
I don't have a non-prod box for you to connect to, but don't think it's necessary since you don't even need to actually connect to a node to reproduce the issue. An install with an entry in router.db using the nodes fqdn will cause it to use the IPv4 address (assuming the fqdn has an A and AAAA record) to try to connect. For example:
host ord38s31-in-f14.1e100.net
ord38s31-in-f14.1e100.net has address 142.250.191.206
ord38s31-in-f14.1e100.net has IPv6 address 2607:f8b0:4009:81a::e
I can probably stand up a test box if still needed.
I think you're running different ruby in shell and different ruby for Oxidized. If you change your Oxidized to run under the ruby you're using in shell, it should start using IPv6.
❯ /usr/bin/ruby --version
ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin24]
~
❯ ruby --version
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
~
❯ ruby -rresolv -e 'p Resolv.new.getaddresses("ytti.fi")'
["2001:67c:17a0::1", "91.198.120.1"]
~
❯ /usr/bin/ruby -rresolv -e 'p Resolv.new.getaddresses("ytti.fi")'
["91.198.120.1", "2001:67C:17A0::1"]
3.4.1 prefers IPv6, older prefers IPv4.
We could have config for resolver preference, like:
resolver:
record_preference: ["AAAA", "A"]
And then instead of calling Resolv.new.getaddresses we'd call our own method which does something like this:
def resolve_dns(name)
(Oxidized.config.resolver.record_preference || %w(AAAA A)).each do |record|
resource = Resolv::DNS::Resource::IN.const_get(record)
response = Resolv::DNS.open{it.getresource(name, resource)} rescue nil
return response.address.to_s if response
end
name
end
You're right, had installed rbenv to get Ruby to 3.3.4 for Oxidized. Interestingly the default ruby install for Ubuntu 22.04 is 3.0.2p107 and it seems to resolve IPv6 address first:
ewysong@vm:~$ which ruby
/usr/bin/ruby
ewysong@vm:~$ /usr/bin/ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
ewysong@vm:~$ ruby -rresolv -e 'p Resolv.new.getaddresses("ytti.fi")'
["2001:67c:17a0::1", "91.198.120.1"]
ewysong@vm:~$ sudo su - oxidized
oxidized@vm:~$ which ruby
/home/oxidized/.rbenv/shims/ruby
oxidized@vm:~$ rbenv version
3.3.4 (set by /home/oxidized/.rbenv/version)
oxidized@vm:~$ ruby -rresolv -e 'p Resolv.new.getaddresses("ytti.fi")'
["91.198.120.1", "2001:67c:17a0::1"]
I then upgraded to Ruby 3.4.4 using rbenv and reinstalled the gems. Now it's resolving the IPv6 address first:
oxidized@vm:~$ ruby -v
ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-linux]
oxidized@vm:~$ ruby -rresolv -e 'p Resolv.new.getaddresses("ytti.fi")'
["2001:67c:17a0::1", "91.198.120.1"]
I restarted Oxidized and it's using the IPv6 addresses now. So it definitely seems like it was a Ruby 3.3 issue. Thank you very much for the help troubleshooting! I am fine closing this issue, or if the config for resolver preference would be useful we can leave it open.
It is probably good idea to be able to force this anyhow, but let's punt that to later date when we are sure someone actually wants it.