mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
Merge pull request #6045 from inspec/ss/enhance-host-resource
CFINSPEC-90: Enhance `host` resource
This commit is contained in:
commit
0978c8c274
5 changed files with 144 additions and 44 deletions
|
@ -11,21 +11,21 @@ platform = "os"
|
|||
parent = "inspec/resources/os"
|
||||
+++
|
||||
|
||||
Use the `host` Chef InSpec audit resource to test the name used to refer to a specific host and its availability, including the Internet protocols and ports over which that host name should be available.
|
||||
Use the `host` Chef InSpec audit resource to test the specific host name and its availability. This test includes the internet protocols and ports on which the respective host name must be available.
|
||||
|
||||
## Availability
|
||||
|
||||
### Installation
|
||||
|
||||
This resource is distributed along with Chef InSpec itself. You can use it automatically.
|
||||
The Chef InSpec distributes this resource.
|
||||
|
||||
### Version
|
||||
|
||||
This resource first became available in v1.0.0 of InSpec.
|
||||
This resource is available from InSpec version 1.0.
|
||||
|
||||
## Syntax
|
||||
|
||||
A `host` resource block declares a host name, and then (depending on what is to be tested) a port and/or a protocol:
|
||||
A `host` resource block declares a host name, a port, and a protocol.
|
||||
|
||||
describe host('example.com', port: 80, protocol: 'tcp') do
|
||||
it { should be_reachable }
|
||||
|
@ -33,58 +33,98 @@ A `host` resource block declares a host name, and then (depending on what is to
|
|||
its('ipaddress') { should include '12.34.56.78' }
|
||||
end
|
||||
|
||||
where
|
||||
|
||||
- `host()` must specify a host name and may specify a port number and/or a protocol
|
||||
- `'example.com'` is the host name
|
||||
- `port:` is the port number
|
||||
- `protocol:` is the Internet protocol: TCP (`protocol: 'tcp'`), UDP (`protocol: 'udp'` or ICMP (`protocol: 'icmp'`))
|
||||
> where
|
||||
>
|
||||
> - `host()` must specify a host name. The port number and protocol are optional values.
|
||||
> - `example.com` is the host name.
|
||||
> - `port` is the port number.
|
||||
> - `protocol` is the internet protocol, TCP (`protocol: 'tcp'`), UDP (`protocol: 'udp'`), and ICMP (`protocol: 'icmp'`)
|
||||
|
||||
## Properties
|
||||
|
||||
### ipaddress
|
||||
|
||||
The `ipaddress` property returns the ipaddress of the host.
|
||||
The `ipaddress` property returns the IP addresses of the host.
|
||||
|
||||
its('ipaddress') { should include '93.184.216.34' }
|
||||
|
||||
### ipv4_address
|
||||
|
||||
The `ipv4_address` property returns the IPv4 address of the host.
|
||||
|
||||
its('ipv4_address') { should include '93.184.216.34' }
|
||||
|
||||
### ipv6_address
|
||||
|
||||
The `ipv6_address` property returns the IPv6 addresses of the host.
|
||||
|
||||
its('ipv6_address') { should include '2404:6800:4009:82a::200e' }
|
||||
|
||||
### connection
|
||||
|
||||
The `connection` property returns the connection string.
|
||||
|
||||
its('connection') { should match /connection refused/ }
|
||||
|
||||
### `protocol`
|
||||
### protocol
|
||||
|
||||
The `protocol` property returns the protocol that given host is using.
|
||||
The `protocol` property returns the protocol the specified host uses.
|
||||
|
||||
its('protocol') { should eq 'tcp' }
|
||||
its('protocol') { should eq 'TCP' }
|
||||
|
||||
### `socket`property returns the socket for the given host.
|
||||
### socket property returns the socket value of the specified host
|
||||
|
||||
its('socket') { should match /STATUS_OK/ }
|
||||
|
||||
## Matchers
|
||||
|
||||
This Chef InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
|
||||
|
||||
### be_reachable
|
||||
|
||||
The `be_reachable` matcher tests if the host name is available.
|
||||
|
||||
it { should be_reachable }
|
||||
|
||||
### be_resolvable
|
||||
|
||||
The `be_resolvable` matcher tests for host name resolution. For example, "resolvable to an IP address".
|
||||
|
||||
it { should be_resolvable }
|
||||
|
||||
## Examples
|
||||
|
||||
### Verify that host name is resolvable to specific IP address.
|
||||
### Verify host name is resolvable to a specific IP address
|
||||
|
||||
describe host('example.com') do
|
||||
its('ipaddress') { should include '93.184.216.34' }
|
||||
end
|
||||
|
||||
### Verify host name is resolvable to a specific IPv4 address
|
||||
|
||||
describe host('example.com') do
|
||||
its('ipv4_address') { should include '93.184.216.34' }
|
||||
end
|
||||
|
||||
### Verify host name is resolvable to a specific IPv6 address
|
||||
|
||||
describe host('example.com') do
|
||||
its('ipv6_address') { should include '2404:6800:4009:82a::200e' }
|
||||
end
|
||||
|
||||
### Verify a specific IP address can be resolved
|
||||
|
||||
describe host('example.com') do
|
||||
it { should be_resolvable }
|
||||
its('ipaddress') { should include '93.184.216.34' }
|
||||
end
|
||||
|
||||
### Verify host name is reachable over a specific protocol and port number
|
||||
|
||||
describe host('example.com', port: 80, protocol: 'tcp') do
|
||||
it { should be_reachable }
|
||||
end
|
||||
|
||||
### Verify that a specific IP address can be resolved
|
||||
|
||||
describe host('example.com') do
|
||||
it { should be_resolvable }
|
||||
its('ipaddress') { should include '93.184.216.34' }
|
||||
end
|
||||
|
||||
### Review the connection setup and socket contents when checking reachability
|
||||
|
||||
describe host('example.com', port: 12345, protocol: 'tcp') do
|
||||
|
@ -92,19 +132,3 @@ The `protocol` property returns the protocol that given host is using.
|
|||
its('connection') { should_not match /connection refused/ }
|
||||
its('socket') { should match /STATUS_OK/ }
|
||||
end
|
||||
|
||||
## Matchers
|
||||
|
||||
This Chef InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
|
||||
|
||||
### be_reachable
|
||||
|
||||
The `be_reachable` matcher tests if the host name is available:
|
||||
|
||||
it { should be_reachable }
|
||||
|
||||
### be_resolvable
|
||||
|
||||
The `be_resolvable` matcher tests for host name resolution, i.e. "resolvable to an IP address":
|
||||
|
||||
it { should be_resolvable }
|
||||
|
|
|
@ -113,6 +113,16 @@ module Inspec::Resources
|
|||
resolve.nil? || resolve.empty? ? nil : resolve
|
||||
end
|
||||
|
||||
# returns an array of the ipv4 addresses
|
||||
def ipv4_address
|
||||
ipaddress.select { |ip| ip.match(Resolv::IPv4::Regex) }
|
||||
end
|
||||
|
||||
# returns an array of the ipv6 addresses
|
||||
def ipv6_address
|
||||
ipaddress.select { |ip| ip.match(Resolv::IPv6::Regex) }
|
||||
end
|
||||
|
||||
def to_s
|
||||
resource_name = "Host #{hostname}"
|
||||
resource_name += " port #{port} proto #{protocol}" if port
|
||||
|
@ -296,15 +306,44 @@ module Inspec::Resources
|
|||
end
|
||||
|
||||
def resolve(hostname)
|
||||
addresses = []
|
||||
# -Type A is the DNS query for IPv4 server Address.
|
||||
cmd = inspec.command("Resolve-DnsName –Type A #{hostname} | ConvertTo-Json")
|
||||
begin
|
||||
resolv = JSON.parse(cmd.stdout)
|
||||
resolve_ipv4 = JSON.parse(cmd.stdout)
|
||||
rescue JSON::ParserError => _e
|
||||
return nil
|
||||
end
|
||||
|
||||
resolv = [resolv] unless resolv.is_a?(Array)
|
||||
resolv.map { |entry| entry["IPAddress"] }
|
||||
resolve_ipv4 = resolve_ipv4.inject(:merge) if resolve_ipv4.is_a?(Array)
|
||||
|
||||
# Append the ipv4 addresses
|
||||
resolve_ipv4.each_value do |ip|
|
||||
matched = ip.to_s.chomp.match(Resolv::IPv4::Regex)
|
||||
next if matched.nil? || addresses.include?(matched.to_s)
|
||||
|
||||
addresses << matched.to_s
|
||||
end
|
||||
|
||||
# -Type AAAA is the DNS query for IPv6 server Address.
|
||||
cmd = inspec.command("Resolve-DnsName –Type AAAA #{hostname} | ConvertTo-Json")
|
||||
begin
|
||||
resolve_ipv6 = JSON.parse(cmd.stdout)
|
||||
rescue JSON::ParserError => _e
|
||||
return nil
|
||||
end
|
||||
|
||||
resolve_ipv6 = resolve_ipv6.inject(:merge) if resolve_ipv6.is_a?(Array)
|
||||
|
||||
# Append the ipv6 addresses
|
||||
resolve_ipv6.each_value do |ip|
|
||||
matched = ip.to_s.chomp.match(Resolv::IPv6::Regex)
|
||||
next if matched.nil? || addresses.include?(matched.to_s)
|
||||
|
||||
addresses << matched.to_s
|
||||
end
|
||||
|
||||
addresses
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
12
test/fixtures/cmd/Resolve-DnsName-ipv6
vendored
Normal file
12
test/fixtures/cmd/Resolve-DnsName-ipv6
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"IP6Address": "2404:6800:4009:827::200e",
|
||||
"Name": "microsoft.com",
|
||||
"Type": 28,
|
||||
"CharacterSet": 1,
|
||||
"Section": 1,
|
||||
"DataLength": 16,
|
||||
"TTL": 176,
|
||||
"Address": "2404:6800:4009:827::200e",
|
||||
"IPAddress": "2404:6800:4009:827::200e",
|
||||
"QueryType": 28
|
||||
}
|
|
@ -369,6 +369,7 @@ class MockLoader
|
|||
"Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json" => cmd.call("get-netadapter-binding-bridge"),
|
||||
# host for Windows
|
||||
"Resolve-DnsName –Type A microsoft.com | ConvertTo-Json" => cmd.call("Resolve-DnsName"),
|
||||
"Resolve-DnsName –Type AAAA microsoft.com | ConvertTo-Json" => cmd.call("Resolve-DnsName-ipv6"),
|
||||
"Test-NetConnection -ComputerName microsoft.com -WarningAction SilentlyContinue| Select-Object -Property ComputerName, TcpTestSucceeded, PingSucceeded | ConvertTo-Json" => cmd.call("Test-NetConnection"),
|
||||
# host for Linux
|
||||
"getent ahosts example.com" => cmd.call("getent-ahosts-example.com"),
|
||||
|
|
|
@ -32,7 +32,7 @@ describe "Inspec::Resources::Host" do
|
|||
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com")
|
||||
_(resource.resolvable?).must_equal true
|
||||
_(resource.reachable?).must_equal false
|
||||
_(resource.ipaddress).must_equal ["134.170.185.46", "134.170.188.221"]
|
||||
_(resource.ipaddress).must_equal ["134.170.188.221", "2404:6800:4009:827::200e"]
|
||||
_(resource.to_s).must_equal "Host microsoft.com"
|
||||
end
|
||||
|
||||
|
@ -96,7 +96,7 @@ describe "Inspec::Resources::Host" do
|
|||
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com", port: 1234, protocol: "tcp")
|
||||
_(resource.resolvable?).must_equal true
|
||||
_(resource.reachable?).must_equal true
|
||||
_(resource.ipaddress).must_equal ["134.170.185.46", "134.170.188.221"]
|
||||
_(resource.ipaddress).must_equal ["134.170.188.221", "2404:6800:4009:827::200e"]
|
||||
_(resource.to_s).must_equal "Host microsoft.com port 1234 proto tcp"
|
||||
end
|
||||
|
||||
|
@ -355,5 +355,29 @@ describe Inspec::Resources::UnixHostProvider do
|
|||
ncat_command.expects(:exist?).returns(false)
|
||||
_(provider.netcat_check_command("foo", 1234, "tcp")).must_be_nil
|
||||
end
|
||||
|
||||
it "checks ipv4_address and ipv6_address properties on ubuntu" do
|
||||
resource = MockLoader.new(:ubuntu).load_resource("host", "example.com")
|
||||
_(resource.ipv4_address).must_equal ["12.34.56.78"]
|
||||
_(resource.ipv4_address).must_include "12.34.56.78"
|
||||
_(resource.ipv6_address).must_equal ["2606:2800:220:1:248:1893:25c8:1946"]
|
||||
_(resource.ipv6_address).must_include "2606:2800:220:1:248:1893:25c8:1946"
|
||||
end
|
||||
|
||||
it "checks ipv4_address and ipv6_address properties on windows" do
|
||||
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com")
|
||||
_(resource.ipv4_address).must_equal ["134.170.188.221"]
|
||||
_(resource.ipv4_address).must_include "134.170.188.221"
|
||||
_(resource.ipv6_address).must_equal ["2404:6800:4009:827::200e"]
|
||||
_(resource.ipv6_address).must_include "2404:6800:4009:827::200e"
|
||||
end
|
||||
|
||||
it "checks ipv4_address and ipv6_address properties on darwin" do
|
||||
resource = MockLoader.new(:macos10_10).load_resource("host", "example.com")
|
||||
_(resource.ipv4_address).must_equal ["12.34.56.78"]
|
||||
_(resource.ipv4_address).must_include "12.34.56.78"
|
||||
_(resource.ipv6_address).must_equal ["2606:2800:220:1:248:1893:25c8:1946"]
|
||||
_(resource.ipv6_address).must_include "2606:2800:220:1:248:1893:25c8:1946"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue