From 437abc866c59682768f6d9a502f442ce4b3e7503 Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Fri, 25 Feb 2022 15:51:57 +0530 Subject: [PATCH 1/9] CFINSPEC-77 : Add ipnat resource Signed-off-by: Sonu Saha --- .../content/inspec/resources/ipnat.md | 65 +++++++++++++++++++ lib/inspec/resources/ipnat.rb | 60 +++++++++++++++++ test/fixtures/cmd/ipnat-l | 7 ++ test/helpers/mock_loader.rb | 3 + test/unit/resources/ipnat_test.rb | 32 +++++++++ 5 files changed, 167 insertions(+) create mode 100644 docs-chef-io/content/inspec/resources/ipnat.md create mode 100644 lib/inspec/resources/ipnat.rb create mode 100644 test/fixtures/cmd/ipnat-l create mode 100644 test/unit/resources/ipnat_test.rb diff --git a/docs-chef-io/content/inspec/resources/ipnat.md b/docs-chef-io/content/inspec/resources/ipnat.md new file mode 100644 index 000000000..b23053b10 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/ipnat.md @@ -0,0 +1,65 @@ ++++ +title = "ipnat resource" +draft = false +gh_repo = "inspec" +platform = "bsd" +platform = "solaris" + +[menu] + [menu.inspec] + title = "ipnat" + identifier = "inspec/resources/os/ipnat.md ipnat resource" + parent = "inspec/resources/os" ++++ + +Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `IP NAT`. The ipnat utility is responsible for adding or removing set of rules to and from the IP NAT. Rules are added to the end of the internal lists, matching the order in which they appear when given to ipnat. `ipnat -l` helps to view the list of current NAT table entry mappings. The rule match is done against the output rules of `ipnat -l`. + +## Availability + +### Installation + +This resource is distributed along with Chef InSpec itself. You can use it automatically. + +### Version + +This resource first became available in v4.52.x of InSpec. + +## Syntax + +An `ipnat` resource block declares tests for rules set for IP NAT: + + describe ipnat do + it { should have_rule("RULE") } + end + +where + +- `have_rule('RULE')` tests the active rule for IP NAT. This must match the entire line taken from `ipnat -l`. + +## Examples + +The following examples show how to use this Chef InSpec audit resource. + +### Test if there is a rule for mapping of the internally used IP address to an ISP provided 8-bit subnet at 10.9.0.1 on the network interface en0 + + describe ipnat do + it { should have_rule("map en0 192.0.0.0/8 -> 10.9.0.1/24") } + end + +### Test if there is a rule for NAT that specifies to use the builtin ftp-proxy + + describe ipnat do + it { should have_rule("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp") } + end + +Note that the rule specification must exactly match what's in the output of `ipnat -l`, which will depend on how you've built your rules. + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). + +### have_rule + +The `have_rule` matcher tests the named rule against the information in the output rule of `ipnat -l`: + + it { should have_rule("RULE") } diff --git a/lib/inspec/resources/ipnat.rb b/lib/inspec/resources/ipnat.rb new file mode 100644 index 000000000..41d84e29a --- /dev/null +++ b/lib/inspec/resources/ipnat.rb @@ -0,0 +1,60 @@ +require "inspec/resources/command" +#require "pry-byebug" +module Inspec::Resources + class IpNat < Inspec.resource(1) + name "ipnat" + supports platform: "freebsd" + supports platform: "solaris" + desc "Use the ipnat InSpec audit resource to test rules that are defined for NAT" + example <<~EXAMPLE + describe ipnat do + it { should have_rule("map net1 192.168.0.0/24 -> 0/32") } + end + EXAMPLE + + def initialize + # checks if the instance is either bsd or solaris + return if inspec.os.bsd? || inspec.os.solaris? + + # ensures, all calls are aborted for non-supported os + @ipnat_cache = [] + skip_resource "The `ipnat` resource is not supported on your OS yet." + end + + def has_rule?(rule = nil) + # checks if the rule is part of the ruleset + retrieve_rules.any? { |line| line.casecmp(rule) == 0 } + end + + def retrieve_rules + # this would be true if the OS family was not bsd/solaris when checked in initliaze + return @ipnat_cache if defined?(@ipnat_cache) + + # construct ipnat command to show the list of current NAT table entry mappings + bin = find_ipnat_or_error + ipnat_cmd = "#{bin} -l" + cmd = inspec.command(ipnat_cmd) + #binding.pry + # Return empty array when command is not executed successfully + # or there is no output since no rules are active + return [] if cmd.exit_status.to_i != 0 || cmd.stdout == "" + + # split rules, returns array or rules + @ipnat_cache = cmd.stdout.split("\n").map(&:strip) + end + + def to_s + format("Ipnat").strip + end + + private + + def find_ipnat_or_error + %w{/usr/sbin/ipnat /sbin/ipnat ipnat}.each do |cmd| + return cmd if inspec.command(cmd).exist? + end + + raise Inspec::Exceptions::ResourceFailed, "Could not find `ipnat`" + end + end +end diff --git a/test/fixtures/cmd/ipnat-l b/test/fixtures/cmd/ipnat-l new file mode 100644 index 000000000..10e4e8786 --- /dev/null +++ b/test/fixtures/cmd/ipnat-l @@ -0,0 +1,7 @@ +List of active MAP/Redirect filters: +map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp +map en0 192.0.0.0/8 -> 10.9.0.1/32 portmap tcp/udp 20000:40000 mssclamp 1452 +map en0 192.0.0.0/8 -> 10.9.0.1/32 +map net1 192.168.0.0/24 -> 0/32 + +List of active sessions: \ No newline at end of file diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index 9d42964a8..9604c1db0 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -369,6 +369,9 @@ class MockLoader # ip6tables "/usr/sbin/ip6tables -S" => cmd.call("ip6tables-s"), %{sh -c 'type "/usr/sbin/ip6tables"'} => empty.call, + # ipnat + "/usr/sbin/ipnat -l" => cmd.call("ipnat-l"), + %{type "/usr/sbin/ipnat"} => empty.call, # apache_conf "sh -c 'find /etc/apache2/ports.conf -type f -maxdepth 1'" => cmd.call("find-apache2-ports-conf"), "sh -c 'find /etc/httpd/conf.d/*.conf -type f -maxdepth 1'" => cmd.call("find-httpd-ssl-conf"), diff --git a/test/unit/resources/ipnat_test.rb b/test/unit/resources/ipnat_test.rb new file mode 100644 index 000000000..c73d147e1 --- /dev/null +++ b/test/unit/resources/ipnat_test.rb @@ -0,0 +1,32 @@ +require "helper" +require "inspec/resource" +require "inspec/resources/ipnat" + +describe "Inspec::Resources::Ipnat" do + # freebsd11 + it "verify ipfilter on freebsd" do + resource = MockLoader.new(:freebsd11).load_resource("ipnat") + _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal true + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal true + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal true + _(resource.has_rule?(nil)).must_equal false + end + + # solaris11 + it "verify ipfilter on freebsd" do + resource = MockLoader.new(:solaris11).load_resource("ipnat") + _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal true + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal true + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal true + _(resource.has_rule?(nil)).must_equal false + end + + # undefined + it "verify ipfilter on unsupported os" do + resource = MockLoader.new(:undefined).load_resource("ipnat") + _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal false + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal false + _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal false + end + +end From dbfb5418fc9aaf6a937f23b84269c2beeb4bf936 Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Fri, 25 Feb 2022 16:02:20 +0530 Subject: [PATCH 2/9] CFINSPEC-77 : Fix RuboCop lint for ipnat.rb Signed-off-by: Sonu Saha --- lib/inspec/resources/ipnat.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/inspec/resources/ipnat.rb b/lib/inspec/resources/ipnat.rb index 41d84e29a..3df56e6a7 100644 --- a/lib/inspec/resources/ipnat.rb +++ b/lib/inspec/resources/ipnat.rb @@ -1,5 +1,4 @@ require "inspec/resources/command" -#require "pry-byebug" module Inspec::Resources class IpNat < Inspec.resource(1) name "ipnat" @@ -34,7 +33,7 @@ module Inspec::Resources bin = find_ipnat_or_error ipnat_cmd = "#{bin} -l" cmd = inspec.command(ipnat_cmd) - #binding.pry + # Return empty array when command is not executed successfully # or there is no output since no rules are active return [] if cmd.exit_status.to_i != 0 || cmd.stdout == "" From 7b2d0768991df1b02bac0df36e200828a71c3716 Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Fri, 25 Feb 2022 21:32:22 +0530 Subject: [PATCH 3/9] CFINSPEC-77 : Add Ipnat resource-cleanup Signed-off-by: Sonu Saha --- .../content/inspec/resources/ipnat.md | 6 +---- lib/inspec/resources/ipnat.rb | 9 ++++---- test/unit/resources/ipnat_test.rb | 22 ++++++++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs-chef-io/content/inspec/resources/ipnat.md b/docs-chef-io/content/inspec/resources/ipnat.md index b23053b10..3723f05e0 100644 --- a/docs-chef-io/content/inspec/resources/ipnat.md +++ b/docs-chef-io/content/inspec/resources/ipnat.md @@ -12,7 +12,7 @@ platform = "solaris" parent = "inspec/resources/os" +++ -Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `IP NAT`. The ipnat utility is responsible for adding or removing set of rules to and from the IP NAT. Rules are added to the end of the internal lists, matching the order in which they appear when given to ipnat. `ipnat -l` helps to view the list of current NAT table entry mappings. The rule match is done against the output rules of `ipnat -l`. +Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `IP NAT`. The ipnat utility is responsible for adding or removing set of rules for IP NAT. Rules are added to the end of the internal lists, matching the order in which they appear when given to ipnat. `ipnat -l` helps to view the list of current NAT table entry mappings. The rule match is done against the output rules of `ipnat -l`. ## Availability @@ -20,10 +20,6 @@ Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `I This resource is distributed along with Chef InSpec itself. You can use it automatically. -### Version - -This resource first became available in v4.52.x of InSpec. - ## Syntax An `ipnat` resource block declares tests for rules set for IP NAT: diff --git a/lib/inspec/resources/ipnat.rb b/lib/inspec/resources/ipnat.rb index 3df56e6a7..f76c7dd13 100644 --- a/lib/inspec/resources/ipnat.rb +++ b/lib/inspec/resources/ipnat.rb @@ -4,7 +4,7 @@ module Inspec::Resources name "ipnat" supports platform: "freebsd" supports platform: "solaris" - desc "Use the ipnat InSpec audit resource to test rules that are defined for NAT" + desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT" example <<~EXAMPLE describe ipnat do it { should have_rule("map net1 192.168.0.0/24 -> 0/32") } @@ -29,21 +29,20 @@ module Inspec::Resources # this would be true if the OS family was not bsd/solaris when checked in initliaze return @ipnat_cache if defined?(@ipnat_cache) - # construct ipnat command to show the list of current NAT table entry mappings + # construct ipnat command to show the list of current IP NAT table entry mappings bin = find_ipnat_or_error ipnat_cmd = "#{bin} -l" cmd = inspec.command(ipnat_cmd) # Return empty array when command is not executed successfully - # or there is no output since no rules are active - return [] if cmd.exit_status.to_i != 0 || cmd.stdout == "" + return [] if cmd.exit_status.to_i != 0 # split rules, returns array or rules @ipnat_cache = cmd.stdout.split("\n").map(&:strip) end def to_s - format("Ipnat").strip + "Ipnat" end private diff --git a/test/unit/resources/ipnat_test.rb b/test/unit/resources/ipnat_test.rb index c73d147e1..3c2c650a1 100644 --- a/test/unit/resources/ipnat_test.rb +++ b/test/unit/resources/ipnat_test.rb @@ -7,26 +7,32 @@ describe "Inspec::Resources::Ipnat" do it "verify ipfilter on freebsd" do resource = MockLoader.new(:freebsd11).load_resource("ipnat") _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal true - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal true - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal true _(resource.has_rule?(nil)).must_equal false end # solaris11 - it "verify ipfilter on freebsd" do + it "verify ipfilter on solaris11" do resource = MockLoader.new(:solaris11).load_resource("ipnat") _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal true - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal true - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal true - _(resource.has_rule?(nil)).must_equal false + _(resource.has_rule?("rule which does not exist")).must_equal false + end + + # ubuntu + it "verify ipfilter on ubuntu" do + resource = MockLoader.new(:ubuntu).load_resource("ipnat") + _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal false + end + + # windows + it "verify ipfilter on windows" do + resource = MockLoader.new(:windows).load_resource("ipnat") + _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal false end # undefined it "verify ipfilter on unsupported os" do resource = MockLoader.new(:undefined).load_resource("ipnat") _(resource.has_rule?("map net1 192.168.0.0/24 -> 0/32")).must_equal false - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32")).must_equal false - _(resource.has_rule?("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp")).must_equal false end end From 36f62064240f0a362624f1d574de6e70918f491c Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Mon, 28 Feb 2022 10:00:20 +0530 Subject: [PATCH 4/9] CFINSPEC-77 : Correct supports platform for ipnat Signed-off-by: Sonu Saha --- lib/inspec/resources/ipnat.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inspec/resources/ipnat.rb b/lib/inspec/resources/ipnat.rb index f76c7dd13..6d9a313ce 100644 --- a/lib/inspec/resources/ipnat.rb +++ b/lib/inspec/resources/ipnat.rb @@ -2,7 +2,7 @@ require "inspec/resources/command" module Inspec::Resources class IpNat < Inspec.resource(1) name "ipnat" - supports platform: "freebsd" + supports platform: "bsd" supports platform: "solaris" desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT" example <<~EXAMPLE From 5b4b313dd7c69bb297ed1cf8cb8ba7cb2d2aae41 Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Mon, 28 Feb 2022 19:20:28 +0530 Subject: [PATCH 5/9] review edits Signed-off-by: Deepa Kumaraswamy --- docs-chef-io/content/inspec/resources/ipnat.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs-chef-io/content/inspec/resources/ipnat.md b/docs-chef-io/content/inspec/resources/ipnat.md index 3723f05e0..57eb0a5b2 100644 --- a/docs-chef-io/content/inspec/resources/ipnat.md +++ b/docs-chef-io/content/inspec/resources/ipnat.md @@ -12,7 +12,7 @@ platform = "solaris" parent = "inspec/resources/os" +++ -Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `IP NAT`. The ipnat utility is responsible for adding or removing set of rules for IP NAT. Rules are added to the end of the internal lists, matching the order in which they appear when given to ipnat. `ipnat -l` helps to view the list of current NAT table entry mappings. The rule match is done against the output rules of `ipnat -l`. +Use the `ipnat` Chef InSpec audit resource to test rules that are defined for `IP NAT`. The purpose of the ipnat utility is to add or remove set of IP NAT rules. Rules are added to the end of the internal lists, matching the order in which they appear when given to ipnat. `ipnat -l` helps to view the current NAT table entry mappings. The rule match is done against the output rules of `ipnat -l`. ## Availability @@ -36,19 +36,23 @@ where The following examples show how to use this Chef InSpec audit resource. -### Test if there is a rule for mapping of the internally used IP address to an ISP provided 8-bit subnet at 10.9.0.1 on the network interface en0 +### Test to ensure mapping rule of the internally used IP address with ISP provided 8-bit subnet at 10.9.0.1 describe ipnat do it { should have_rule("map en0 192.0.0.0/8 -> 10.9.0.1/24") } end -### Test if there is a rule for NAT that specifies to use the builtin ftp-proxy +### Test to ensure if there is a NAT rule to use the builtin ftp-proxy describe ipnat do it { should have_rule("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp") } end -Note that the rule specification must exactly match what's in the output of `ipnat -l`, which will depend on how you've built your rules. +{{< note >}} + +The rule specification must match the output of `ipnat -l` that depends on how you have built your rules. + +{{< /note >}} ## Matchers From 832cb3582bf4671a8a495f76e27514919323dbfb Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Mon, 28 Feb 2022 20:11:24 +0530 Subject: [PATCH 6/9] CFINSPEC-77 : Add ipnat to resources.rb Signed-off-by: Sonu Saha --- lib/inspec/resources.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/inspec/resources.rb b/lib/inspec/resources.rb index 8dc25b56e..5e3cd43ed 100644 --- a/lib/inspec/resources.rb +++ b/lib/inspec/resources.rb @@ -138,6 +138,7 @@ require "inspec/resources/xinetd_conf" require "inspec/resources/yum" require "inspec/resources/zfs_dataset" require "inspec/resources/zfs_pool" +require "inspec/resources/ipnat" # file formats, depend on json implementation require "inspec/resources/json" From f7bff9b1b544b8ed137d47f00e269d1ae3bc6b63 Mon Sep 17 00:00:00 2001 From: Sonu Saha Date: Thu, 3 Mar 2022 11:36:58 +0530 Subject: [PATCH 7/9] CFINSPEC-77 : Add condition to skip ipnat resource for darwin instead of failure Signed-off-by: Sonu Saha --- lib/inspec/resources/ipnat.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inspec/resources/ipnat.rb b/lib/inspec/resources/ipnat.rb index 6d9a313ce..b6bdac99f 100644 --- a/lib/inspec/resources/ipnat.rb +++ b/lib/inspec/resources/ipnat.rb @@ -13,7 +13,7 @@ module Inspec::Resources def initialize # checks if the instance is either bsd or solaris - return if inspec.os.bsd? || inspec.os.solaris? + return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris? # ensures, all calls are aborted for non-supported os @ipnat_cache = [] From deecdf728732050c5717ceaccdc3945ed6077cfe Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 3 Mar 2022 13:09:54 -0500 Subject: [PATCH 8/9] Add bsd to resource index Signed-off-by: Clinton Wolfe --- docs-chef-io/content/inspec/resources/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs-chef-io/content/inspec/resources/_index.md b/docs-chef-io/content/inspec/resources/_index.md index c08921a6b..a3fe1b4a6 100644 --- a/docs-chef-io/content/inspec/resources/_index.md +++ b/docs-chef-io/content/inspec/resources/_index.md @@ -25,6 +25,12 @@ The following resources work on Linux operating systems. {{< inspec_resources platform="linux" >}} +### BSD + +The following resources work on BSD operating systems. + +{{< inspec_resources platform="bsd" >}} + ### Windows The following resources work on Windows operating systems. From ef6ecaf17be25089a521a444b2dbfb39def87771 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 3 Mar 2022 13:20:29 -0500 Subject: [PATCH 9/9] Make ipnat doc indicate bsd section only Signed-off-by: Clinton Wolfe --- docs-chef-io/content/inspec/resources/ipnat.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs-chef-io/content/inspec/resources/ipnat.md b/docs-chef-io/content/inspec/resources/ipnat.md index 57eb0a5b2..5502485bd 100644 --- a/docs-chef-io/content/inspec/resources/ipnat.md +++ b/docs-chef-io/content/inspec/resources/ipnat.md @@ -3,7 +3,6 @@ title = "ipnat resource" draft = false gh_repo = "inspec" platform = "bsd" -platform = "solaris" [menu] [menu.inspec] @@ -48,7 +47,7 @@ The following examples show how to use this Chef InSpec audit resource. it { should have_rule("map en0 192.0.0.0/8 -> 10.9.0.1/32 proxy port ftp ftp/tcp") } end -{{< note >}} +{{< note >}} The rule specification must match the output of `ipnat -l` that depends on how you have built your rules.