From a4000d6912a3f3b2eb920b381577d840664d7fb6 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 14 May 2020 17:28:58 -0400 Subject: [PATCH] Adds support for interface on BSD and MacOS Signed-off-by: Clinton Wolfe --- docs/resources/interface.md.erb | 4 +-- lib/inspec/resources/interface.rb | 47 +++++++++++++++++++++++++++ test/fixtures/cmd/ifconfig-en0 | 7 ++++ test/helpers/mock_loader.rb | 1 + test/unit/resources/interface_test.rb | 15 +++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/cmd/ifconfig-en0 diff --git a/docs/resources/interface.md.erb b/docs/resources/interface.md.erb index 9e0a7b9bf..fff74d8cb 100644 --- a/docs/resources/interface.md.erb +++ b/docs/resources/interface.md.erb @@ -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.
@@ -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 } diff --git a/lib/inspec/resources/interface.rb b/lib/inspec/resources/interface.rb index 0a56568ed..487e173d5 100644 --- a/lib/inspec/resources/interface.rb +++ b/lib/inspec/resources/interface.rb @@ -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 ) + 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" diff --git a/test/fixtures/cmd/ifconfig-en0 b/test/fixtures/cmd/ifconfig-en0 new file mode 100644 index 000000000..e09701377 --- /dev/null +++ b/test/fixtures/cmd/ifconfig-en0 @@ -0,0 +1,7 @@ +en0: flags=8863 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 + media: Ethernet autoselect (1000baseT ) + status: active diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index 578de874b..03e0a50fd 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -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 diff --git a/test/unit/resources/interface_test.rb b/test/unit/resources/interface_test.rb index 20c675e8a..3e7e787b7 100644 --- a/test/unit/resources/interface_test.rb +++ b/test/unit/resources/interface_test.rb @@ -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")