CFINSPEC-77 : Add ipnat resource

Signed-off-by: Sonu Saha <sonu.saha@progress.com>
This commit is contained in:
Sonu Saha 2022-02-25 15:51:57 +05:30 committed by Clinton Wolfe
parent 96832435ae
commit 437abc866c
5 changed files with 167 additions and 0 deletions

View file

@ -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") }

View file

@ -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

7
test/fixtures/cmd/ipnat-l vendored Normal file
View file

@ -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:

View file

@ -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"),

View file

@ -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