Adds support for interface on BSD and MacOS

Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
Clinton Wolfe 2020-05-14 17:28:58 -04:00 committed by Mary Jinglewski
parent 9109864fe2
commit a4000d6912
5 changed files with 72 additions and 2 deletions

View file

@ -9,7 +9,7 @@ Use the `interface` Chef InSpec audit resource to test basic network adapter pro
* On Linux platforms, `/sys/class/net/#{iface}` is used as source
* On the Windows platform, the `Get-NetAdapter` cmdlet is used as source
* The MacOS platform is not currently supported.
* On BSD and MacOS platforms, the `ifconfig` command is used as source. Link speed may not be available.
<br>
@ -81,7 +81,7 @@ The `name` property returns the name of the interface:
### speed
The `speed` property tests the speed of the network interface, in MB/sec:
The `speed` property tests the speed of the network interface, in MB/sec. Note: On BSD and MacOS platforms, this value may be nil, because it difficult to obtain reliably.
its('speed') { should eq 1000 }

View file

@ -85,6 +85,7 @@ module Inspec::Resources
@cache ||= begin
provider = LinuxInterface.new(inspec) if inspec.os.linux?
provider = WindowsInterface.new(inspec) if inspec.os.windows?
provider = BsdInterface.new(inspec) if inspec.os.bsd? # includes macOS
Hash(provider && provider.interface_info(@iface))
end
end
@ -98,6 +99,52 @@ module Inspec::Resources
end
end
class BsdInterface < InterfaceInfo
def interface_info(iface)
cmd = inspec.command("ifconfig #{iface}")
return nil if cmd.exit_status.to_i != 0
lines = cmd.stdout.split("\n")
iface_info = {
name: iface,
ipv4_addresses: [], # Actually CIDRs
ipv6_addresses: [], # are expected to go here
}
iface_info[:up] = lines[0].include?("UP")
lines.each do |line|
# IPv4 case
m = line.match(/^\s+inet\s+((?:\d{1,3}\.){3}\d{1,3})\s+netmask\s+(0x[a-f0-9]{8})/)
if m
ip = m[1]
hex_mask = m[2]
cidr = hex_mask.to_i(16).to_s(2).count("1")
iface_info[:ipv4_addresses] << "#{ip}/#{cidr}"
next
end
# IPv6 case
m = line.match(/^\s+inet6\s+([a-f0-9:]+)%#{iface}\s+prefixlen\s+(\d+)/)
if m
ip = m[1]
cidr = m[2]
iface_info[:ipv6_addresses] << "#{ip}/#{cidr}"
next
end
# Speed detect, crummy - can't detect wifi, finds any number in the string
# Ethernet autoselect (1000baseT <full-duplex>)
m = line.match(/^\s+media:\D+(\d+)/)
if m
iface_info[:speed] = m[1].to_i
next
end
end
iface_info
end
end
class LinuxInterface < InterfaceInfo
def interface_info(iface)
# will return "[mtu]\n1500\n[type]\n1"

7
test/fixtures/cmd/ifconfig-en0 vendored Normal file
View file

@ -0,0 +1,7 @@
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether ac:bc:32:c7:30:e7
inet6 fe80::8b6:c2cc:2928:3b61%en0 prefixlen 64 secured scopeid 0x5
inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255
nd6 options=201<PERFORMNUD,DAD>
media: Ethernet autoselect (1000baseT <full-duplex>)
status: active

View file

@ -314,6 +314,7 @@ class MockLoader
"/sbin/ip -br -6 address show dev eth0" => cmd.call("interface-addresses-6"),
"Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json" => cmd.call("Get-NetAdapter"),
"Get-NetIPAddress | Select-Object -Property IPv6Address, IPv4Address, InterfaceAlias, PrefixLength | ConvertTo-Json" => cmd.call("Get-NetIPAddress"),
"ifconfig en0" => cmd.call("ifconfig-en0"),
# bridge on linux
"ls -1 /sys/class/net/br0/brif/" => cmd.call("ls-sys-class-net-br"),
# bridge on Windows

View file

@ -81,6 +81,21 @@ describe "Inspec::Resources::Interface" do
_(resource.ipv6_cidrs).must_be_empty
end
it "verify interface on macos" do
resource = MockLoader.new(:osx104).load_resource("interface", "en0")
_(resource.exists?).must_equal true
_(resource.up?).must_equal true
_(resource.speed).must_equal 1000
_(resource.name).must_equal "en0"
_(resource.ipv4_cidrs).must_include "192.168.1.2/24"
_(resource.ipv4_addresses).must_include "192.168.1.2"
_(resource.ipv4_addresses_netmask).must_include "192.168.1.2/255.255.255.0"
_(resource.ipv6_cidrs).must_include "fe80::8b6:c2cc:2928:3b61/64"
_(resource.ipv6_addresses).must_include "fe80::8b6:c2cc:2928:3b61"
_(resource.ipv4_address?).must_equal true
_(resource.ipv6_address?).must_equal true
end
# undefined
it "verify interface on unsupported os" do
resource = MockLoader.new(:undefined).load_resource("interface", "eth0")