mirror of
https://github.com/inspec/inspec
synced 2024-11-10 23:24:18 +00:00
Change host resource to use getent ahosts on Linux (#2002)
* Change host resource to use getent ahosts on Linux In InSpec 1.31, we changed the `host` resource to use `dig` instead of `getent hosts` for name resolution because `getent hosts` does not return all entries (only the first v6 entry if it exists, then the first v4 entry) and we wanted to keep the Darwin and Linux implementation as close as possible. Unfortunately, this affected users' ability to do resolution checks for entried stored in their /etc/hosts file. This change goes back to using `getent` for Linux and changes to `getent ahosts` which returns both v4 and v6 records. Additionally, the Darwin provider's dig implementation was reordered to return v4 addresses before v6 addresses to be consistent with how `getent ahosts` returns records. Signed-off-by: Adam Leff <adam@leff.co> * Update unit tests for resolve_with_getent with proper output Signed-off-by: Adam Leff <adam@leff.co>
This commit is contained in:
parent
fb5e5c54e3
commit
1ea06ac3ea
5 changed files with 45 additions and 27 deletions
|
@ -148,13 +148,6 @@ module Inspec::Resources
|
||||||
def resolve_with_dig(hostname)
|
def resolve_with_dig(hostname)
|
||||||
addresses = []
|
addresses = []
|
||||||
|
|
||||||
# look for IPv6 addresses
|
|
||||||
cmd = inspec.command("dig +short AAAA #{hostname}")
|
|
||||||
cmd.stdout.lines.each do |line|
|
|
||||||
matched = line.chomp.match(Resolv::IPv6::Regex)
|
|
||||||
addresses << matched.to_s unless matched.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
# look for IPv4 addresses
|
# look for IPv4 addresses
|
||||||
cmd = inspec.command("dig +short A #{hostname}")
|
cmd = inspec.command("dig +short A #{hostname}")
|
||||||
cmd.stdout.lines.each do |line|
|
cmd.stdout.lines.each do |line|
|
||||||
|
@ -162,17 +155,36 @@ module Inspec::Resources
|
||||||
addresses << matched.to_s unless matched.nil?
|
addresses << matched.to_s unless matched.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# look for IPv6 addresses
|
||||||
|
cmd = inspec.command("dig +short AAAA #{hostname}")
|
||||||
|
cmd.stdout.lines.each do |line|
|
||||||
|
matched = line.chomp.match(Resolv::IPv6::Regex)
|
||||||
|
addresses << matched.to_s unless matched.nil?
|
||||||
|
end
|
||||||
|
|
||||||
addresses.empty? ? nil : addresses
|
addresses.empty? ? nil : addresses
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve_with_getent(hostname)
|
def resolve_with_getent(hostname)
|
||||||
# TODO: we rely on getent hosts for now, but it prefers to return IPv6, only then IPv4
|
cmd = inspec.command("getent ahosts #{hostname}")
|
||||||
cmd = inspec.command("getent hosts #{hostname}")
|
return nil unless cmd.exit_status.to_i.zero?
|
||||||
return nil if cmd.exit_status.to_i != 0
|
|
||||||
|
|
||||||
# extract ip adress
|
# getent ahosts output is formatted like so:
|
||||||
resolve = /^\s*(?<ip>\S+)\s+(.*)\s*$/.match(cmd.stdout.chomp)
|
# $ getent ahosts www.google.com
|
||||||
[resolve[1]] if resolve
|
# 172.217.8.4 STREAM www.google.com
|
||||||
|
# 172.217.8.4 DGRAM
|
||||||
|
# 172.217.8.4 RAW
|
||||||
|
# 2607:f8b0:4004:803::2004 STREAM
|
||||||
|
# 2607:f8b0:4004:803::2004 DGRAM
|
||||||
|
# 2607:f8b0:4004:803::2004 RAW
|
||||||
|
addresses = []
|
||||||
|
cmd.stdout.lines.each do |line|
|
||||||
|
ip, = line.split(/\s+/, 2)
|
||||||
|
next unless ip.match(Resolv::IPv4::Regex) || ip.match(Resolv::IPv6::Regex)
|
||||||
|
addresses << ip unless addresses.include?(ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
addresses
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,7 +257,7 @@ module Inspec::Resources
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve(hostname)
|
def resolve(hostname)
|
||||||
inspec.command('dig').exist? ? resolve_with_dig(hostname) : resolve_with_getent(hostname)
|
resolve_with_getent(hostname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -268,7 +268,7 @@ class MockLoader
|
||||||
'Resolve-DnsName –Type A microsoft.com | ConvertTo-Json' => cmd.call('Resolve-DnsName'),
|
'Resolve-DnsName –Type A microsoft.com | ConvertTo-Json' => cmd.call('Resolve-DnsName'),
|
||||||
'Test-NetConnection -ComputerName microsoft.com -WarningAction SilentlyContinue| Select-Object -Property ComputerName, TcpTestSucceeded, PingSucceeded | ConvertTo-Json' => cmd.call('Test-NetConnection'),
|
'Test-NetConnection -ComputerName microsoft.com -WarningAction SilentlyContinue| Select-Object -Property ComputerName, TcpTestSucceeded, PingSucceeded | ConvertTo-Json' => cmd.call('Test-NetConnection'),
|
||||||
# host for Linux
|
# host for Linux
|
||||||
'getent hosts example.com' => cmd.call('getent-hosts-example.com'),
|
'getent ahosts example.com' => cmd.call('getent-ahosts-example.com'),
|
||||||
'ping -w 1 -c 1 example.com' => cmd.call('ping-example.com'),
|
'ping -w 1 -c 1 example.com' => cmd.call('ping-example.com'),
|
||||||
# host for Darwin
|
# host for Darwin
|
||||||
'host -t AAAA example.com' => cmd.call('host-AAAA-example.com'),
|
'host -t AAAA example.com' => cmd.call('host-AAAA-example.com'),
|
||||||
|
|
6
test/unit/mock/cmd/getent-ahosts-example.com
Normal file
6
test/unit/mock/cmd/getent-ahosts-example.com
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
12.34.56.78 STREAM example.com
|
||||||
|
12.34.56.78 DGRAM
|
||||||
|
12.34.56.78 RAW
|
||||||
|
2606:2800:220:1:248:1893:25c8:1946 STREAM
|
||||||
|
2606:2800:220:1:248:1893:25c8:1946 DGRAM
|
||||||
|
2606:2800:220:1:248:1893:25c8:1946 RAW
|
|
@ -1 +0,0 @@
|
||||||
2606:2800:220:1:248:1893:25c8:1946 example.com
|
|
|
@ -11,21 +11,21 @@ describe 'Inspec::Resources::Host' do
|
||||||
resource = MockLoader.new(:ubuntu1404).load_resource('host', 'example.com')
|
resource = MockLoader.new(:ubuntu1404).load_resource('host', 'example.com')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host ping on centos 7' do
|
it 'check host ping on centos 7' do
|
||||||
resource = MockLoader.new(:centos7).load_resource('host', 'example.com')
|
resource = MockLoader.new(:centos7).load_resource('host', 'example.com')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host ping on darwin' do
|
it 'check host ping on darwin' do
|
||||||
resource = MockLoader.new(:osx104).load_resource('host', 'example.com')
|
resource = MockLoader.new(:osx104).load_resource('host', 'example.com')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host ping on windows' do
|
it 'check host ping on windows' do
|
||||||
|
@ -46,21 +46,21 @@ describe 'Inspec::Resources::Host' do
|
||||||
resource = MockLoader.new(:ubuntu1404).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
resource = MockLoader.new(:ubuntu1404).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host tcp on centos 7' do
|
it 'check host tcp on centos 7' do
|
||||||
resource = MockLoader.new(:centos7).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
resource = MockLoader.new(:centos7).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host tcp on darwin' do
|
it 'check host tcp on darwin' do
|
||||||
resource = MockLoader.new(:osx104).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
resource = MockLoader.new(:osx104).load_resource('host', 'example.com', port: 1234, protocol: 'tcp')
|
||||||
_(resource.resolvable?).must_equal true
|
_(resource.resolvable?).must_equal true
|
||||||
_(resource.reachable?).must_equal true
|
_(resource.reachable?).must_equal true
|
||||||
_(resource.ipaddress).must_equal ["2606:2800:220:1:248:1893:25c8:1946", "12.34.56.78"]
|
_(resource.ipaddress).must_equal ["12.34.56.78", "2606:2800:220:1:248:1893:25c8:1946"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'check host tcp on windows' do
|
it 'check host tcp on windows' do
|
||||||
|
@ -101,7 +101,7 @@ EOL
|
||||||
v6_command.stubs(:stdout).returns(ipv6_command_output)
|
v6_command.stubs(:stdout).returns(ipv6_command_output)
|
||||||
inspec.stubs(:command).with('dig +short AAAA testdomain.com').returns(v6_command)
|
inspec.stubs(:command).with('dig +short AAAA testdomain.com').returns(v6_command)
|
||||||
inspec.stubs(:command).with('dig +short A testdomain.com').returns(v4_command)
|
inspec.stubs(:command).with('dig +short A testdomain.com').returns(v4_command)
|
||||||
provider.resolve_with_dig('testdomain.com').must_equal(['2A03:2880:F112:83:FACE:B00C::25DE', '12.34.56.78'])
|
provider.resolve_with_dig('testdomain.com').must_equal(['12.34.56.78', '2A03:2880:F112:83:FACE:B00C::25DE'])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns only v4 addresses if no v6 addresses are available' do
|
it 'returns only v4 addresses if no v6 addresses are available' do
|
||||||
|
@ -160,16 +160,16 @@ EOL
|
||||||
|
|
||||||
describe '#resolve_with_getent' do
|
describe '#resolve_with_getent' do
|
||||||
it 'returns an array of IP addresses when successful' do
|
it 'returns an array of IP addresses when successful' do
|
||||||
command_output = "2607:f8b0:4004:805::200e testdomain.com\n"
|
command_output = "123.123.123.123 STREAM testdomain.com\n2607:f8b0:4004:805::200e STREAM\n"
|
||||||
command = mock('getent_command')
|
command = mock('getent_command')
|
||||||
command.stubs(:stdout).returns(command_output)
|
command.stubs(:stdout).returns(command_output)
|
||||||
command.stubs(:exit_status).returns(0)
|
command.stubs(:exit_status).returns(0)
|
||||||
|
|
||||||
inspec = mock('inspec')
|
inspec = mock('inspec')
|
||||||
inspec.stubs(:command).with('getent hosts testdomain.com').returns(command)
|
inspec.stubs(:command).with('getent ahosts testdomain.com').returns(command)
|
||||||
|
|
||||||
provider = Inspec::Resources::UnixHostProvider.new(inspec)
|
provider = Inspec::Resources::UnixHostProvider.new(inspec)
|
||||||
provider.resolve_with_getent('testdomain.com').must_equal(['2607:f8b0:4004:805::200e'])
|
provider.resolve_with_getent('testdomain.com').must_equal(['123.123.123.123', '2607:f8b0:4004:805::200e'])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil if command is not successful' do
|
it 'returns nil if command is not successful' do
|
||||||
|
@ -177,7 +177,7 @@ EOL
|
||||||
command.stubs(:exit_status).returns(1)
|
command.stubs(:exit_status).returns(1)
|
||||||
|
|
||||||
inspec = mock('inspec')
|
inspec = mock('inspec')
|
||||||
inspec.stubs(:command).with('getent hosts testdomain.com').returns(command)
|
inspec.stubs(:command).with('getent ahosts testdomain.com').returns(command)
|
||||||
|
|
||||||
provider = Inspec::Resources::UnixHostProvider.new(inspec)
|
provider = Inspec::Resources::UnixHostProvider.new(inspec)
|
||||||
provider.resolve_with_getent('testdomain.com').must_be_nil
|
provider.resolve_with_getent('testdomain.com').must_be_nil
|
||||||
|
@ -232,6 +232,7 @@ describe Inspec::Resources::LinuxHostProvider do
|
||||||
provider.missing_requirements('tcp').must_equal(['netcat must be installed'])
|
provider.missing_requirements('tcp').must_equal(['netcat must be installed'])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tcp_check_command' do
|
describe '#tcp_check_command' do
|
||||||
it 'returns an nc command when nc exists' do
|
it 'returns an nc command when nc exists' do
|
||||||
inspec.expects(:command).with('nc').returns(nc_command)
|
inspec.expects(:command).with('nc').returns(nc_command)
|
||||||
|
|
Loading…
Reference in a new issue