Added puppetlabs-firewall (required by puppetlabs-postgresql), updated the other modules.
This commit is contained in:
parent
5f4b7a3b72
commit
dee66abcdd
137 changed files with 11118 additions and 419 deletions
437
modules/firewall/CHANGELOG.md
Normal file
437
modules/firewall/CHANGELOG.md
Normal file
|
@ -0,0 +1,437 @@
|
|||
##2014-06-04 - Release 1.1.2
|
||||
###Summary
|
||||
|
||||
This is a release of the code previously released as 1.1.1, with updated metadata.
|
||||
|
||||
## 2014-05-16 Release 1.1.1
|
||||
###Summary
|
||||
|
||||
This release reverts the alphabetical ordering of 1.1.0. We found this caused
|
||||
a regression in the Openstack modules so in the interest of safety we have
|
||||
removed this for now.
|
||||
|
||||
## 2014-05-13 Release 1.1.0
|
||||
###Summary
|
||||
|
||||
This release has a significant change from previous releases; we now apply the
|
||||
firewall resources alphabetically by default, removing the need to create pre
|
||||
and post classes just to enforce ordering. It only effects default ordering
|
||||
and further information can be found in the README about this. Please test
|
||||
this in development before rolling into production out of an abundance of
|
||||
caution.
|
||||
|
||||
We've also added `mask` which is required for --recent in recent (no pun
|
||||
intended) versions of iptables, as well as connlimit and connmark. This
|
||||
release has been validated against Ubuntu 14.04 and RHEL7 and should be fully
|
||||
working on those platforms.
|
||||
|
||||
####Features
|
||||
|
||||
- Apply firewall resources alphabetically.
|
||||
- Add support for connlimit and connmark.
|
||||
- Add `mask` as a parameter. (Used exclusively with the recent parameter).
|
||||
|
||||
####Bugfixes
|
||||
|
||||
- Add systemd support for RHEL7.
|
||||
- Replace &&'s with the correct and in manifests.
|
||||
- Fix tests on Trusty and RHEL7
|
||||
- Fix for Fedora Rawhide.
|
||||
- Fix boolean flag tests.
|
||||
- Fix DNAT->SNAT typo in an error message.
|
||||
|
||||
####Known Bugs
|
||||
|
||||
* For Oracle, the `owner` and `socket` parameters require a workaround to function. Please see the Limitations section of the README.
|
||||
|
||||
|
||||
## 2014-03-04 Supported Release 1.0.2
|
||||
###Summary
|
||||
|
||||
This is a supported release. This release removes a testing symlink that can
|
||||
cause trouble on systems where /var is on a seperate filesystem from the
|
||||
modulepath.
|
||||
|
||||
####Features
|
||||
####Bugfixes
|
||||
####Known Bugs
|
||||
|
||||
* For Oracle, the `owner` and `socket` parameters require a workaround to function. Please see the Limitations section of the README.
|
||||
|
||||
### Supported release - 2014-03-04 1.0.1
|
||||
|
||||
####Summary
|
||||
|
||||
An important bugfix was made to the offset calculation for unmanaged rules
|
||||
to handle rules with 9000+ in the name.
|
||||
|
||||
####Features
|
||||
|
||||
####Bugfixes
|
||||
- Offset calculations assumed unmanaged rules were numbered 9000+.
|
||||
- Gracefully fail to manage ip6tables on iptables 1.3.x
|
||||
|
||||
####Known Bugs
|
||||
|
||||
* For Oracle, the `owner` and `socket` parameters require a workaround to function. Please see the Limitations section of the README.
|
||||
|
||||
---
|
||||
### 1.0.0 - 2014-02-11
|
||||
|
||||
No changes, just renumbering to 1.0.0.
|
||||
|
||||
---
|
||||
### 0.5.0 - 2014-02-10
|
||||
|
||||
##### Summary:
|
||||
This is a bigger release that brings in "recent" connection limiting (think
|
||||
"port knocking"), firewall chain purging on a per-chain/per-table basis, and
|
||||
support for a few other use cases. This release also fixes a major bug which
|
||||
could cause modifications to the wrong rules when unmanaged rules are present.
|
||||
|
||||
##### New Features:
|
||||
* Add "recent" limiting via parameters `rdest`, `reap`, `recent`, `rhitcount`,
|
||||
`rname`, `rseconds`, `rsource`, and `rttl`
|
||||
* Add negation support for source and destination
|
||||
* Add per-chain/table purging support to `firewallchain`
|
||||
* IPv4 specific
|
||||
* Add random port forwarding support
|
||||
* Add ipsec policy matching via `ipsec_dir` and `ipsec_policy`
|
||||
* IPv6 specific
|
||||
* Add support for hop limiting via `hop_limit` parameter
|
||||
* Add fragmentation matchers via `ishasmorefrags`, `islastfrag`, and `isfirstfrag`
|
||||
* Add support for conntrack stateful firewall matching via `ctstate`
|
||||
|
||||
##### Bugfixes:
|
||||
- Boolean fixups allowing false values
|
||||
- Better detection of unmanaged rules
|
||||
- Fix multiport rule detection
|
||||
- Fix sport/dport rule detection
|
||||
- Make INPUT, OUTPUT, and FORWARD not autorequired for firewall chain filter
|
||||
- Allow INPUT with the nat table
|
||||
- Fix `src_range` & `dst_range` order detection
|
||||
- Documentation clarifications
|
||||
- Fixes to spec tests
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.4.2 - 2013-09-10
|
||||
|
||||
Another attempt to fix the packaging issue. We think we understand exactly
|
||||
what is failing and this should work properly for the first time.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.4.1 - 2013-08-09
|
||||
|
||||
Bugfix release to fix a packaging issue that may have caused puppet module
|
||||
install commands to fail.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.4.0 - 2013-07-11
|
||||
|
||||
This release adds support for address type, src/dest ip ranges, and adds
|
||||
additional testing and bugfixes.
|
||||
|
||||
#### Features
|
||||
* Add `src_type` and `dst_type` attributes (Nick Stenning)
|
||||
* Add `src_range` and `dst_range` attributes (Lei Zhang)
|
||||
* Add SL and SLC operatingsystems as supported (Steve Traylen)
|
||||
|
||||
#### Bugfixes
|
||||
* Fix parser for bursts other than 5 (Chris Rutter)
|
||||
* Fix parser for -f in --comment (Georg Koester)
|
||||
* Add doc headers to class files (Dan Carley)
|
||||
* Fix lint warnings/errors (Wolf Noble)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.3.1 - 2013/6/10
|
||||
|
||||
This minor release provides some bugfixes and additional tests.
|
||||
|
||||
#### Changes
|
||||
|
||||
* Update tests for rspec-system-puppet 2 (Ken Barber)
|
||||
* Update rspec-system tests for rspec-system-puppet 1.5 (Ken Barber)
|
||||
* Ensure all services have 'hasstatus => true' for Puppet 2.6 (Ken Barber)
|
||||
* Accept pre-existing rule with invalid name (Joe Julian)
|
||||
* Swap log_prefix and log_level order to match the way it's saved (Ken Barber)
|
||||
* Fix log test to replicate bug #182 (Ken Barber)
|
||||
* Split argments while maintaining quoted strings (Joe Julian)
|
||||
* Add more log param tests (Ken Barber)
|
||||
* Add extra tests for logging parameters (Ken Barber)
|
||||
* Clarify OS support (Ken Barber)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.3.0 - 2013/4/25
|
||||
|
||||
This release introduces support for Arch Linux and extends support for Fedora 15 and up. There are also lots of bugs fixed and improved testing to prevent regressions.
|
||||
|
||||
##### Changes
|
||||
|
||||
* Fix error reporting for insane hostnames (Tomas Doran)
|
||||
* Support systemd on Fedora 15 and up (Eduardo Gutierrez)
|
||||
* Move examples to docs (Ken Barber)
|
||||
* Add support for Arch Linux platform (Ingmar Steen)
|
||||
* Add match rule for fragments (Georg Koester)
|
||||
* Fix boolean rules being recognized as changed (Georg Koester)
|
||||
* Same rules now get deleted (Anastasis Andronidis)
|
||||
* Socket params test (Ken Barber)
|
||||
* Ensure parameter can disable firewall (Marc Tardif)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.2.1 - 2012/3/13
|
||||
|
||||
This maintenance release introduces the new README layout, and fixes a bug with iptables_persistent_version.
|
||||
|
||||
##### Changes
|
||||
|
||||
* (GH-139) Throw away STDERR from dpkg-query in Fact
|
||||
* Update README to be consistent with module documentation template
|
||||
* Fix failing spec tests due to dpkg change in iptables_persistent_version
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.2.0 - 2012/3/3
|
||||
|
||||
This release introduces automatic persistence, removing the need for the previous manual dependency requirement for persistent the running rules to the OS persistence file.
|
||||
|
||||
Previously you would have required the following in your site.pp (or some other global location):
|
||||
|
||||
# Always persist firewall rules
|
||||
exec { 'persist-firewall':
|
||||
command => $operatingsystem ? {
|
||||
'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4',
|
||||
/(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables',
|
||||
},
|
||||
refreshonly => true,
|
||||
}
|
||||
Firewall {
|
||||
notify => Exec['persist-firewall'],
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
Firewallchain {
|
||||
notify => Exec['persist-firewall'],
|
||||
}
|
||||
resources { "firewall":
|
||||
purge => true
|
||||
}
|
||||
|
||||
You only need:
|
||||
|
||||
class { 'firewall': }
|
||||
Firewall {
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
|
||||
To install pre-requisites and to create dependencies on your pre & post rules. Consult the README for more information.
|
||||
|
||||
##### Changes
|
||||
|
||||
* Firewall class manifests (Dan Carley)
|
||||
* Firewall and firewallchain persistence (Dan Carley)
|
||||
* (GH-134) Autorequire iptables related packages (Dan Carley)
|
||||
* Typo in #persist_iptables OS normalisation (Dan Carley)
|
||||
* Tests for #persist_iptables (Dan Carley)
|
||||
* (GH-129) Replace errant return in autoreq block (Dan Carley)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.1.1 - 2012/2/28
|
||||
|
||||
This release primarily fixes changing parameters in 3.x
|
||||
|
||||
##### Changes
|
||||
|
||||
* (GH-128) Change method_missing usage to define_method for 3.x compatibility
|
||||
* Update travis.yml gem specifications to actually test 2.6
|
||||
* Change source in Gemfile to use a specific URL for Ruby 2.0.0 compatibility
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.1.0 - 2012/2/24
|
||||
|
||||
This release is somewhat belated, so no summary as there are far too many changes this time around. Hopefully we won't fall this far behind again :-).
|
||||
|
||||
##### Changes
|
||||
|
||||
* Add support for MARK target and set-mark property (Johan Huysmans)
|
||||
* Fix broken call to super for ruby-1.9.2 in munge (Ken Barber)
|
||||
* simple fix of the error message for allowed values of the jump property (Daniel Black)
|
||||
* Adding OSPF(v3) protocol to puppetlabs-firewall (Arnoud Vermeer)
|
||||
* Display multi-value: port, sport, dport and state command seperated (Daniel Black)
|
||||
* Require jump=>LOG for log params (Daniel Black)
|
||||
* Reject and document icmp => "any" (Dan Carley)
|
||||
* add firewallchain type and iptables_chain provider (Daniel Black)
|
||||
* Various fixes for firewallchain resource (Ken Barber)
|
||||
* Modify firewallchain name to be chain:table:protocol (Ken Barber)
|
||||
* Fix allvalidchain iteration (Ken Barber)
|
||||
* Firewall autorequire Firewallchains (Dan Carley)
|
||||
* Tests and docstring for chain autorequire (Dan Carley)
|
||||
* Fix README so setup instructions actually work (Ken Barber)
|
||||
* Support vlan interfaces (interface containing ".") (Johan Huysmans)
|
||||
* Add tests for VLAN support for iniface/outiface (Ken Barber)
|
||||
* Add the table when deleting rules (Johan Huysmans)
|
||||
* Fix tests since we are now prefixing -t)
|
||||
* Changed 'jump' to 'action', commands to lower case (Jason Short)
|
||||
* Support interface names containing "+" (Simon Deziel)
|
||||
* Fix for when iptables-save spews out "FATAL" errors (Sharif Nassar)
|
||||
* Fix for incorrect limit command arguments for ip6tables provider (Michael Hsu)
|
||||
* Document Util::Firewall.host_to_ip (Dan Carley)
|
||||
* Nullify addresses with zero prefixlen (Dan Carley)
|
||||
* Add support for --tcp-flags (Thomas Vander Stichele)
|
||||
* Make tcp_flags support a feature (Ken Barber)
|
||||
* OUTPUT is a valid chain for the mangle table (Adam Gibbins)
|
||||
* Enable travis-ci support (Ken Barber)
|
||||
* Convert an existing test to CIDR (Dan Carley)
|
||||
* Normalise iptables-save to CIDR (Dan Carley)
|
||||
* be clearer about what distributions we support (Ken Barber)
|
||||
* add gre protocol to list of acceptable protocols (Jason Hancock)
|
||||
* Added pkttype property (Ashley Penney)
|
||||
* Fix mark to not repeat rules with iptables 1.4.1+ (Sharif Nassar)
|
||||
* Stub iptables_version for now so tests run on non-Linux hosts (Ken Barber)
|
||||
* Stub iptables facts for set_mark tests (Dan Carley)
|
||||
* Update formatting of README to meet Puppet Labs best practices (Will Hopper)
|
||||
* Support for ICMP6 type code resolutions (Dan Carley)
|
||||
* Insert order hash included chains from different tables (Ken Barber)
|
||||
* rspec 2.11 compatibility (Jonathan Boyett)
|
||||
* Add missing class declaration in README (sfozz)
|
||||
* array_matching is contraindicated (Sharif Nassar)
|
||||
* Convert port Fixnum into strings (Sharif Nassar)
|
||||
* Update test framework to the modern age (Ken Barber)
|
||||
* working with ip6tables support (wuwx)
|
||||
* Remove gemfile.lock and add to gitignore (William Van Hevelingen)
|
||||
* Update travis and gemfile to be like stdlib travis files (William Van Hevelingen)
|
||||
* Add support for -m socket option (Ken Barber)
|
||||
* Add support for single --sport and --dport parsing (Ken Barber)
|
||||
* Fix tests for Ruby 1.9.3 from 3e13bf3 (Dan Carley)
|
||||
* Mock Resolv.getaddress in #host_to_ip (Dan Carley)
|
||||
* Update docs for source and dest - they are not arrays (Ken Barber)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.0.4 - 2011/12/05
|
||||
|
||||
This release adds two new parameters, 'uid' and 'gid'. As a part of the owner module, these params allow you to specify a uid, username, gid, or group got a match:
|
||||
|
||||
firewall { '497 match uid':
|
||||
port => '123',
|
||||
proto => 'mangle',
|
||||
chain => 'OUTPUT',
|
||||
action => 'drop'
|
||||
uid => '123'
|
||||
}
|
||||
|
||||
This release also adds value munging for the 'log_level', 'source', and 'destination' parameters. The 'source' and 'destination' now support hostnames:
|
||||
|
||||
firewall { '498 accept from puppetlabs.com':
|
||||
port => '123',
|
||||
proto => 'tcp',
|
||||
source => 'puppetlabs.com',
|
||||
action => 'accept'
|
||||
}
|
||||
|
||||
|
||||
The 'log_level' parameter now supports using log level names, such as 'warn', 'debug', and 'panic':
|
||||
|
||||
firewall { '499 logging':
|
||||
port => '123',
|
||||
proto => 'udp',
|
||||
log_level => 'debug',
|
||||
action => 'drop'
|
||||
}
|
||||
|
||||
Additional changes include iptables and ip6tables version facts, general whitespace cleanup, and adding additional unit tests.
|
||||
|
||||
##### Changes
|
||||
|
||||
* (#10957) add iptables_version and ip6tables_version facts
|
||||
* (#11093) Improve log_level property so it converts names to numbers
|
||||
* (#10723) Munge hostnames and IPs to IPs with CIDR
|
||||
* (#10718) Add owner-match support
|
||||
* (#10997) Add fixtures for ipencap
|
||||
* (#11034) Whitespace cleanup
|
||||
* (#10690) add port property support to ip6tables
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.0.3 - 2011/11/12
|
||||
|
||||
This release introduces a new parameter 'port' which allows you to set both
|
||||
source and destination ports for a match:
|
||||
|
||||
firewall { "500 allow NTP requests":
|
||||
port => "123",
|
||||
proto => "udp",
|
||||
action => "accept",
|
||||
}
|
||||
|
||||
We also have the limit parameter finally working:
|
||||
|
||||
firewall { "500 limit HTTP requests":
|
||||
dport => 80,
|
||||
proto => tcp,
|
||||
limit => "60/sec",
|
||||
burst => 30,
|
||||
action => accept,
|
||||
}
|
||||
|
||||
State ordering has been fixed now, and more characters are allowed in the
|
||||
namevar:
|
||||
|
||||
* Alphabetical
|
||||
* Numbers
|
||||
* Punctuation
|
||||
* Whitespace
|
||||
|
||||
##### Changes
|
||||
|
||||
* (#10693) Ensure -m limit is added for iptables when using 'limit' param
|
||||
* (#10690) Create new port property
|
||||
* (#10700) allow additional characters in comment string
|
||||
* (#9082) Sort iptables --state option values internally to keep it consistent across runs
|
||||
* (#10324) Remove extraneous whitespace from iptables rule line in spec tests
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.0.2 - 2011/10/26
|
||||
|
||||
This is largely a maintanence and cleanup release, but includes the ability to
|
||||
specify ranges of ports in the sport/dport parameter:
|
||||
|
||||
firewall { "500 allow port range":
|
||||
dport => ["3000-3030","5000-5050"],
|
||||
sport => ["1024-65535"],
|
||||
action => "accept",
|
||||
}
|
||||
|
||||
##### Changes
|
||||
|
||||
* (#10295) Work around bug #4248 whereby the puppet/util paths are not being loaded correctly on the puppetmaster
|
||||
* (#10002) Change to dport and sport to handle ranges, and fix handling of name to name to port
|
||||
* (#10263) Fix tests on Puppet 2.6.x
|
||||
* (#10163) Cleanup some of the inline documentation and README file to align with general forge usage
|
||||
|
||||
---------------------------------------
|
||||
|
||||
### 0.0.1 - 2011/10/18
|
||||
|
||||
Initial release.
|
||||
|
||||
##### Changes
|
||||
|
||||
* (#9362) Create action property and perform transformation for accept, drop, reject value for iptables jump parameter
|
||||
* (#10088) Provide a customised version of CONTRIBUTING.md
|
||||
* (#10026) Re-arrange provider and type spec files to align with Puppet
|
||||
* (#10026) Add aliases for test,specs,tests to Rakefile and provide -T as default
|
||||
* (#9439) fix parsing and deleting existing rules
|
||||
* (#9583) Fix provider detection for gentoo and unsupported linuxes for the iptables provider
|
||||
* (#9576) Stub provider so it works properly outside of Linux
|
||||
* (#9576) Align spec framework with Puppet core
|
||||
* and lots of other earlier development tasks ...
|
87
modules/firewall/CONTRIBUTING.md
Normal file
87
modules/firewall/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,87 @@
|
|||
# How to contribute
|
||||
|
||||
Third-party patches are essential for keeping puppet great. We simply can't
|
||||
access the huge number of platforms and myriad configurations for running
|
||||
puppet. We want to keep it as easy as possible to contribute changes that
|
||||
get things working in your environment. There are a few guidelines that we
|
||||
need contributors to follow so that we can have a chance of keeping on
|
||||
top of things.
|
||||
|
||||
## Getting Started
|
||||
|
||||
* Make sure you have a [Jira account](http://tickets.puppetlabs.com)
|
||||
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
||||
* Submit a ticket for your issue, assuming one does not already exist.
|
||||
* Clearly describe the issue including steps to reproduce when it is a bug.
|
||||
* Make sure you fill in the earliest version that you know has the issue.
|
||||
* Fork the repository on GitHub
|
||||
|
||||
## Making Changes
|
||||
|
||||
* Create a topic branch from where you want to base your work.
|
||||
* This is usually the master branch.
|
||||
* Only target release branches if you are certain your fix must be on that
|
||||
branch.
|
||||
* To quickly create a topic branch based on master; `git branch
|
||||
fix/master/my_contribution master` then checkout the new branch with `git
|
||||
checkout fix/master/my_contribution`. Please avoid working directly on the
|
||||
`master` branch.
|
||||
* Make commits of logical units.
|
||||
* Check for unnecessary whitespace with `git diff --check` before committing.
|
||||
* Make sure your commit messages are in the proper format.
|
||||
|
||||
````
|
||||
(MODULES-1234) Make the example in CONTRIBUTING imperative and concrete
|
||||
|
||||
Without this patch applied the example commit message in the CONTRIBUTING
|
||||
document is not a concrete example. This is a problem because the
|
||||
contributor is left to imagine what the commit message should look like
|
||||
based on a description rather than an example. This patch fixes the
|
||||
problem by making the example concrete and imperative.
|
||||
|
||||
The first line is a real life imperative statement with a ticket number
|
||||
from our issue tracker. The body describes the behavior without the patch,
|
||||
why this is a problem, and how the patch fixes the problem when applied.
|
||||
````
|
||||
|
||||
* Make sure you have added the necessary tests for your changes.
|
||||
* Run _all_ the tests to assure nothing else was accidentally broken.
|
||||
|
||||
## Making Trivial Changes
|
||||
|
||||
### Documentation
|
||||
|
||||
For changes of a trivial nature to comments and documentation, it is not
|
||||
always necessary to create a new ticket in Jira. In this case, it is
|
||||
appropriate to start the first line of a commit with '(doc)' instead of
|
||||
a ticket number.
|
||||
|
||||
````
|
||||
(doc) Add documentation commit example to CONTRIBUTING
|
||||
|
||||
There is no example for contributing a documentation commit
|
||||
to the Puppet repository. This is a problem because the contributor
|
||||
is left to assume how a commit of this nature may appear.
|
||||
|
||||
The first line is a real life imperative statement with '(doc)' in
|
||||
place of what would have been the ticket number in a
|
||||
non-documentation related commit. The body describes the nature of
|
||||
the new documentation or comments added.
|
||||
````
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
* Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla).
|
||||
* Push your changes to a topic branch in your fork of the repository.
|
||||
* Submit a pull request to the repository in the puppetlabs organization.
|
||||
* Update your Jira ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge).
|
||||
* Include a link to the pull request in the ticket.
|
||||
|
||||
# Additional Resources
|
||||
|
||||
* [More information on contributing](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing)
|
||||
* [Bug tracker (Jira)](http://tickets.puppetlabs.com)
|
||||
* [Contributor License Agreement](http://links.puppetlabs.com/cla)
|
||||
* [General GitHub documentation](http://help.github.com/)
|
||||
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
||||
* #puppet-dev IRC channel on freenode.org
|
18
modules/firewall/Gemfile
Normal file
18
modules/firewall/Gemfile
Normal file
|
@ -0,0 +1,18 @@
|
|||
source ENV['GEM_SOURCE'] || "https://rubygems.org"
|
||||
|
||||
group :development, :test do
|
||||
gem 'puppetlabs_spec_helper', :require => false
|
||||
gem 'rspec-puppet', :require => false
|
||||
gem 'serverspec', :require => false
|
||||
gem 'beaker-rspec', :require => false
|
||||
gem 'puppet-lint', :require => false
|
||||
gem 'pry', :require => false
|
||||
end
|
||||
|
||||
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
||||
gem 'puppet', puppetversion, :require => false
|
||||
else
|
||||
gem 'puppet', :require => false
|
||||
end
|
||||
|
||||
# vim:ft=ruby
|
157
modules/firewall/Gemfile.lock
Normal file
157
modules/firewall/Gemfile.lock
Normal file
|
@ -0,0 +1,157 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (2.2.8)
|
||||
addressable (2.3.6)
|
||||
archive-tar-minitar (0.5.2)
|
||||
autoparse (0.3.3)
|
||||
addressable (>= 2.3.1)
|
||||
extlib (>= 0.9.15)
|
||||
multi_json (>= 1.0.0)
|
||||
aws-sdk (1.40.1)
|
||||
json (~> 1.4)
|
||||
nokogiri (>= 1.4.4)
|
||||
beaker (1.11.0)
|
||||
aws-sdk (~> 1.38)
|
||||
blimpy (~> 0.6)
|
||||
docker-api
|
||||
fission (~> 0.4)
|
||||
google-api-client (~> 0.7.1)
|
||||
inifile (~> 2.0)
|
||||
json (~> 1.8)
|
||||
mime-types (~> 1.25)
|
||||
net-scp (~> 1.1)
|
||||
net-ssh (~> 2.6)
|
||||
nokogiri (= 1.5.10)
|
||||
rbvmomi (= 1.8.1)
|
||||
unf (~> 0.1)
|
||||
beaker-rspec (2.2.4)
|
||||
beaker (~> 1.10)
|
||||
rspec (~> 2.14)
|
||||
serverspec (~> 1.0)
|
||||
specinfra (~> 1.0)
|
||||
blimpy (0.6.7)
|
||||
fog
|
||||
minitar
|
||||
thor
|
||||
builder (3.2.2)
|
||||
coderay (1.1.0)
|
||||
diff-lcs (1.2.5)
|
||||
docker-api (1.10.10)
|
||||
archive-tar-minitar
|
||||
excon (>= 0.28)
|
||||
json
|
||||
excon (0.33.0)
|
||||
extlib (0.9.16)
|
||||
facter (2.0.1)
|
||||
CFPropertyList (~> 2.2.6)
|
||||
faraday (0.9.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fission (0.5.0)
|
||||
CFPropertyList (~> 2.2)
|
||||
fog (1.11.1)
|
||||
builder
|
||||
excon (~> 0.20)
|
||||
formatador (~> 0.2.0)
|
||||
json (~> 1.7)
|
||||
mime-types
|
||||
net-scp (~> 1.1)
|
||||
net-ssh (>= 2.1.3)
|
||||
nokogiri (~> 1.5.0)
|
||||
ruby-hmac
|
||||
formatador (0.2.4)
|
||||
google-api-client (0.7.1)
|
||||
addressable (>= 2.3.2)
|
||||
autoparse (>= 0.3.3)
|
||||
extlib (>= 0.9.15)
|
||||
faraday (>= 0.9.0)
|
||||
jwt (>= 0.1.5)
|
||||
launchy (>= 2.1.1)
|
||||
multi_json (>= 1.0.0)
|
||||
retriable (>= 1.4)
|
||||
signet (>= 0.5.0)
|
||||
uuidtools (>= 2.1.0)
|
||||
hiera (1.3.2)
|
||||
json_pure
|
||||
highline (1.6.21)
|
||||
inifile (2.0.2)
|
||||
json (1.8.1)
|
||||
json_pure (1.8.1)
|
||||
jwt (1.0.0)
|
||||
launchy (2.4.2)
|
||||
addressable (~> 2.3)
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
minitar (0.5.4)
|
||||
mocha (1.0.0)
|
||||
metaclass (~> 0.0.1)
|
||||
multi_json (1.10.0)
|
||||
multipart-post (2.0.0)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.9.1)
|
||||
nokogiri (1.5.10)
|
||||
pry (0.9.12.6)
|
||||
coderay (~> 1.0)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
puppet (3.5.1)
|
||||
facter (> 1.6, < 3)
|
||||
hiera (~> 1.0)
|
||||
json_pure
|
||||
rgen (~> 0.6.5)
|
||||
puppet-lint (0.3.2)
|
||||
puppetlabs_spec_helper (0.4.1)
|
||||
mocha (>= 0.10.5)
|
||||
rake
|
||||
rspec (>= 2.9.0)
|
||||
rspec-puppet (>= 0.1.1)
|
||||
rake (10.3.1)
|
||||
rbvmomi (1.8.1)
|
||||
builder
|
||||
nokogiri (>= 1.4.1)
|
||||
trollop
|
||||
retriable (1.4.1)
|
||||
rgen (0.6.6)
|
||||
rspec (2.14.1)
|
||||
rspec-core (~> 2.14.0)
|
||||
rspec-expectations (~> 2.14.0)
|
||||
rspec-mocks (~> 2.14.0)
|
||||
rspec-core (2.14.8)
|
||||
rspec-expectations (2.14.5)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec-mocks (2.14.6)
|
||||
rspec-puppet (1.0.1)
|
||||
rspec
|
||||
ruby-hmac (0.4.0)
|
||||
serverspec (1.6.0)
|
||||
highline
|
||||
net-ssh
|
||||
rspec (~> 2.13)
|
||||
specinfra (~> 1.11)
|
||||
signet (0.5.0)
|
||||
addressable (>= 2.2.3)
|
||||
faraday (>= 0.9.0.rc5)
|
||||
jwt (>= 0.1.5)
|
||||
multi_json (>= 1.0.0)
|
||||
slop (3.5.0)
|
||||
specinfra (1.12.0)
|
||||
thor (0.19.1)
|
||||
trollop (2.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.6)
|
||||
uuidtools (2.1.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
beaker-rspec
|
||||
pry
|
||||
puppet
|
||||
puppet-lint
|
||||
puppetlabs_spec_helper
|
||||
rspec-puppet
|
||||
serverspec
|
25
modules/firewall/LICENSE
Normal file
25
modules/firewall/LICENSE
Normal file
|
@ -0,0 +1,25 @@
|
|||
Puppet Firewall Module - Puppet module for managing Firewalls
|
||||
|
||||
Copyright (C) 2011-2013 Puppet Labs, Inc.
|
||||
Copyright (C) 2011 Jonathan Boyett
|
||||
Copyright (C) 2011 Media Temple, Inc.
|
||||
|
||||
Some of the iptables code was taken from puppet-iptables which was:
|
||||
|
||||
Copyright (C) 2011 Bob.sh Limited
|
||||
Copyright (C) 2008 Camptocamp Association
|
||||
Copyright (C) 2007 Dmitri Priimak
|
||||
|
||||
Puppet Labs can be contacted at: info@puppetlabs.com
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
8
modules/firewall/Modulefile
Normal file
8
modules/firewall/Modulefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
name 'puppetlabs-firewall'
|
||||
version '1.1.2'
|
||||
source 'git://github.com/puppetlabs/puppetlabs-firewall.git'
|
||||
author 'puppetlabs'
|
||||
license 'ASL 2.0'
|
||||
summary 'Firewall Module'
|
||||
description 'Manages Firewalls such as iptables'
|
||||
project_page 'http://forge.puppetlabs.com/puppetlabs/firewall'
|
429
modules/firewall/README.markdown
Normal file
429
modules/firewall/README.markdown
Normal file
|
@ -0,0 +1,429 @@
|
|||
#firewall
|
||||
|
||||
[](https://travis-ci.org/puppetlabs/puppetlabs-firewall)
|
||||
|
||||
####Table of Contents
|
||||
|
||||
1. [Overview - What is the Firewall module?](#overview)
|
||||
2. [Module Description - What does the module do?](#module-description)
|
||||
3. [Setup - The basics of getting started with Firewall](#setup)
|
||||
* [What Firewall affects](#what-firewall-affects)
|
||||
* [Setup Requirements](#setup-requirements)
|
||||
* [Beginning with Firewall](#beginning-with-firewall)
|
||||
* [Upgrading](#upgrading)
|
||||
4. [Usage - Configuration and customization options](#usage)
|
||||
* [Default rules - Setting up general configurations for all firewalls](#default-rules)
|
||||
* [Application-specific rules - Options for configuring and managing firewalls across applications](#application-specific-rules)
|
||||
* [Other Rules](#other-rules)
|
||||
5. [Reference - An under-the-hood peek at what the module is doing](#reference)
|
||||
6. [Limitations - OS compatibility, etc.](#limitations)
|
||||
7. [Development - Guide for contributing to the module](#development)
|
||||
* [Tests - Testing your configuration](#tests)
|
||||
|
||||
##Overview
|
||||
|
||||
The Firewall module lets you manage firewall rules with Puppet.
|
||||
|
||||
##Module Description
|
||||
|
||||
PuppetLabs' Firewall introduces the resource `firewall`, which is used to manage and configure firewall rules from within the Puppet DSL. This module offers support for iptables, ip6tables, and ebtables.
|
||||
|
||||
The module also introduces the resource `firewallchain`, which allows you to manage chains or firewall lists. At the moment, only iptables and ip6tables chains are supported.
|
||||
|
||||
##Setup
|
||||
|
||||
###What Firewall affects:
|
||||
|
||||
* every node running a firewall
|
||||
* system's firewall settings
|
||||
* connection settings for managed nodes
|
||||
* unmanaged resources (get purged)
|
||||
* site.pp
|
||||
|
||||
###Setup Requirements
|
||||
|
||||
Firewall uses Ruby-based providers, so you must have [pluginsync enabled](http://docs.puppetlabs.com/guides/plugins_in_modules.html#enabling-pluginsync).
|
||||
|
||||
###Beginning with Firewall
|
||||
|
||||
To begin, you need to provide some initial top-scope configuration to ensure your firewall configurations are ordered properly and you do not lock yourself out of your box or lose any configuration.
|
||||
|
||||
Persistence of rules between reboots is handled automatically, although there are known issues with ip6tables on older Debian/Ubuntu, as well as known issues with ebtables.
|
||||
|
||||
In your `site.pp` (or some similarly top-scope file), set up a metatype to purge unmanaged firewall resources. This will clear any existing rules and make sure that only rules defined in Puppet exist on the machine.
|
||||
|
||||
resources { "firewall":
|
||||
purge => true
|
||||
}
|
||||
|
||||
Next, set up the default parameters for all of the firewall rules you will be establishing later. These defaults will ensure that the pre and post classes (you will be setting up in just a moment) are run in the correct order to avoid locking you out of your box during the first puppet run.
|
||||
|
||||
Firewall {
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
|
||||
You also need to declare the `my_fw::pre` & `my_fw::post` classes so that dependencies are satisfied. This can be achieved using an External Node Classifier or the following
|
||||
|
||||
class { ['my_fw::pre', 'my_fw::post']: }
|
||||
|
||||
Finally, you should include the `firewall` class to ensure the correct packages are installed.
|
||||
|
||||
class { 'firewall': }
|
||||
|
||||
Now to create the `my_fw::pre` and `my_fw::post` classes. Firewall acts on your running firewall, making immediate changes as the catalog executes. Defining default pre and post rules allows you provide global defaults for your hosts before and after any custom rules; it is also required to avoid locking yourself out of your own boxes when Puppet runs. This approach employs a whitelist setup, so you can define what rules you want and everything else is ignored rather than removed.
|
||||
|
||||
The `pre` class should be located in `my_fw/manifests/pre.pp` and should contain any default rules to be applied first.
|
||||
|
||||
class my_fw::pre {
|
||||
Firewall {
|
||||
require => undef,
|
||||
}
|
||||
|
||||
# Default firewall rules
|
||||
firewall { '000 accept all icmp':
|
||||
proto => 'icmp',
|
||||
action => 'accept',
|
||||
}->
|
||||
firewall { '001 accept all to lo interface':
|
||||
proto => 'all',
|
||||
iniface => 'lo',
|
||||
action => 'accept',
|
||||
}->
|
||||
firewall { '002 accept related established rules':
|
||||
proto => 'all',
|
||||
state => ['RELATED', 'ESTABLISHED'],
|
||||
action => 'accept',
|
||||
}
|
||||
}
|
||||
|
||||
The rules in `pre` should allow basic networking (such as ICMP and TCP), as well as ensure that existing connections are not closed.
|
||||
|
||||
The `post` class should be located in `my_fw/manifests/post.pp` and include any default rules to be applied last.
|
||||
|
||||
class my_fw::post {
|
||||
firewall { '999 drop all':
|
||||
proto => 'all',
|
||||
action => 'drop',
|
||||
before => undef,
|
||||
}
|
||||
}
|
||||
|
||||
To put it all together: the `require` parameter in `Firewall {}` ensures `my_fw::pre` is run before any other rules and the `before` parameter ensures `my_fw::post` is run after any other rules. So the run order is:
|
||||
|
||||
* run the rules in `my_fw::pre`
|
||||
* run your rules (defined in code)
|
||||
* run the rules in `my_fw::post`
|
||||
|
||||
###Upgrading
|
||||
|
||||
####Upgrading from version 0.2.0 and newer
|
||||
|
||||
Upgrade the module with the puppet module tool as normal:
|
||||
|
||||
puppet module upgrade puppetlabs/firewall
|
||||
|
||||
####Upgrading from version 0.1.1 and older
|
||||
|
||||
Start by upgrading the module using the puppet module tool:
|
||||
|
||||
puppet module upgrade puppetlabs/firewall
|
||||
|
||||
Previously, you would have required the following in your `site.pp` (or some other global location):
|
||||
|
||||
# Always persist firewall rules
|
||||
exec { 'persist-firewall':
|
||||
command => $operatingsystem ? {
|
||||
'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4',
|
||||
/(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables',
|
||||
},
|
||||
refreshonly => true,
|
||||
}
|
||||
Firewall {
|
||||
notify => Exec['persist-firewall'],
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
Firewallchain {
|
||||
notify => Exec['persist-firewall'],
|
||||
}
|
||||
resources { "firewall":
|
||||
purge => true
|
||||
}
|
||||
|
||||
With the latest version, we now have in-built persistence, so this is no longer needed. However, you will still need some basic setup to define pre & post rules.
|
||||
|
||||
resources { "firewall":
|
||||
purge => true
|
||||
}
|
||||
Firewall {
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
class { ['my_fw::pre', 'my_fw::post']: }
|
||||
class { 'firewall': }
|
||||
|
||||
Consult the the documentation below for more details around the classes `my_fw::pre` and `my_fw::post`.
|
||||
|
||||
##Usage
|
||||
|
||||
There are two kinds of firewall rules you can use with Firewall: default rules and application-specific rules. Default rules apply to general firewall settings, whereas application-specific rules manage firewall settings of a specific application, node, etc.
|
||||
|
||||
All rules employ a numbering system in the resource's title that is used for ordering. When titling your rules, make sure you prefix the rule with a number.
|
||||
|
||||
000 this runs first
|
||||
999 this runs last
|
||||
|
||||
###Default rules
|
||||
|
||||
You can place default rules in either `my_fw::pre` or `my_fw::post`, depending on when you would like them to run. Rules placed in the `pre` class will run first, rules in the `post` class, last.
|
||||
|
||||
Depending on the provider, the title of the rule can be stored using the comment feature of the underlying firewall subsystem. Values can match `/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/`.
|
||||
|
||||
####Examples of default rules
|
||||
|
||||
Basic accept ICMP request example:
|
||||
|
||||
firewall { "000 accept all icmp requests":
|
||||
proto => "icmp",
|
||||
action => "accept",
|
||||
}
|
||||
|
||||
Drop all:
|
||||
|
||||
firewall { "999 drop all other requests":
|
||||
action => "drop",
|
||||
}
|
||||
|
||||
###Application-specific rules
|
||||
|
||||
Puppet doesn't care where you define rules, and this means that you can place
|
||||
your firewall resources as close to the applications and services that you
|
||||
manage as you wish. If you use the [roles and profiles
|
||||
pattern](https://puppetlabs.com/learn/roles-profiles-introduction) then it
|
||||
would make sense to create your firewall rules in the profiles, so that they
|
||||
remain close to the services managed by the profile.
|
||||
|
||||
An example of this might be:
|
||||
|
||||
```puppet
|
||||
class profile::apache {
|
||||
include apache
|
||||
apache::vhost { 'mysite': ensure => present }
|
||||
|
||||
firewall { '100 allow http and https access':
|
||||
port => [80, 443],
|
||||
proto => tcp,
|
||||
action => accept,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
However, if you're not using that pattern then you can place them directly into
|
||||
the individual module that manages a service, such as:
|
||||
|
||||
```puppet
|
||||
class apache {
|
||||
firewall { '100 allow http and https access':
|
||||
port => [80, 443],
|
||||
proto => tcp,
|
||||
action => accept,
|
||||
}
|
||||
# ... the rest of your code ...
|
||||
}
|
||||
```
|
||||
|
||||
This means if someone includes either the profile:
|
||||
|
||||
```puppet
|
||||
include profile::apache
|
||||
```
|
||||
|
||||
Or the module, if you're not using roles and profiles:
|
||||
|
||||
```puppet
|
||||
include ::apache
|
||||
```
|
||||
|
||||
Then they would automatically get appropriate firewall rules.
|
||||
|
||||
###Other rules
|
||||
|
||||
You can also apply firewall rules to specific nodes. Usually, you will want to put the firewall rule in another class and apply that class to a node. But you can apply a rule to a node.
|
||||
|
||||
node 'foo.bar.com' {
|
||||
firewall { '111 open port 111':
|
||||
dport => 111
|
||||
}
|
||||
}
|
||||
|
||||
You can also do more complex things with the `firewall` resource. Here we are doing some NAT configuration.
|
||||
|
||||
firewall { '100 snat for network foo2':
|
||||
chain => 'POSTROUTING',
|
||||
jump => 'MASQUERADE',
|
||||
proto => 'all',
|
||||
outiface => "eth0",
|
||||
source => '10.1.2.0/24',
|
||||
table => 'nat',
|
||||
}
|
||||
|
||||
In the below example, we are creating a new chain and forwarding any port 5000 access to it.
|
||||
|
||||
firewall { '100 forward to MY_CHAIN':
|
||||
chain => 'INPUT',
|
||||
jump => 'MY_CHAIN',
|
||||
}
|
||||
# The namevar here is in the format chain_name:table:protocol
|
||||
firewallchain { 'MY_CHAIN:filter:IPv4':
|
||||
ensure => present,
|
||||
}
|
||||
firewall { '100 my rule':
|
||||
chain => 'MY_CHAIN',
|
||||
action => 'accept',
|
||||
proto => 'tcp',
|
||||
dport => 5000,
|
||||
}
|
||||
|
||||
###Additional Information
|
||||
|
||||
You can access the inline documentation:
|
||||
|
||||
puppet describe firewall
|
||||
|
||||
Or
|
||||
|
||||
puppet doc -r type
|
||||
(and search for firewall)
|
||||
|
||||
##Reference
|
||||
|
||||
Classes:
|
||||
|
||||
* [firewall](#class-firewall)
|
||||
|
||||
Types:
|
||||
|
||||
* [firewall](#type-firewall)
|
||||
* [firewallchain](#type-firewallchain)
|
||||
|
||||
Facts:
|
||||
|
||||
* [ip6tables_version](#fact-ip6tablesversion)
|
||||
* [iptables_version](#fact-iptablesversion)
|
||||
* [iptables_persistent_version](#fact-iptablespersistentversion)
|
||||
|
||||
###Class: firewall
|
||||
|
||||
This class is provided to do the basic setup tasks required for using the firewall resources.
|
||||
|
||||
At the moment this takes care of:
|
||||
|
||||
* iptables-persistent package installation
|
||||
|
||||
You should include the class for nodes that need to use the resources in this module. For example
|
||||
|
||||
class { 'firewall': }
|
||||
|
||||
####`ensure`
|
||||
|
||||
Indicates the state of `iptables` on your system, allowing you to disable `iptables` if desired.
|
||||
|
||||
Can either be `running` or `stopped`. Default to `running`.
|
||||
|
||||
###Type: firewall
|
||||
|
||||
This type provides the capability to manage firewall rules within puppet.
|
||||
|
||||
For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge:
|
||||
|
||||
<http://forge.puppetlabs.com/puppetlabs/firewall#types>
|
||||
|
||||
###Type:: firewallchain
|
||||
|
||||
This type provides the capability to manage rule chains for firewalls.
|
||||
|
||||
For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge:
|
||||
|
||||
<http://forge.puppetlabs.com/puppetlabs/firewall#types>
|
||||
|
||||
###Fact: ip6tables_version
|
||||
|
||||
The module provides a Facter fact that can be used to determine what the default version of ip6tables is for your operating system/distribution.
|
||||
|
||||
###Fact: iptables_version
|
||||
|
||||
The module provides a Facter fact that can be used to determine what the default version of iptables is for your operating system/distribution.
|
||||
|
||||
###Fact: iptables_persistent_version
|
||||
|
||||
Retrieves the version of iptables-persistent from your OS. This is a Debian/Ubuntu specific fact.
|
||||
|
||||
##Limitations
|
||||
|
||||
###SLES
|
||||
|
||||
The `socket` parameter is not supported on SLES. In this release it will cause
|
||||
the catalog to fail with iptables failures, rather than correctly warn you that
|
||||
the features are unusable.
|
||||
|
||||
###Oracle Enterprise Linux
|
||||
|
||||
The `socket` and `owner` parameters are unsupported on Oracle Enterprise Linux
|
||||
when the "Unbreakable" kernel is used. These may function correctly when using
|
||||
the stock RedHat kernel instead. Declaring either of these parameters on an
|
||||
unsupported system will result in iptable rules failing to apply.
|
||||
|
||||
###Other
|
||||
|
||||
Bugs can be reported using JIRA issues
|
||||
|
||||
<http://tickets.puppetlabs.com>
|
||||
|
||||
##Development
|
||||
|
||||
Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve.
|
||||
|
||||
We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
|
||||
|
||||
You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing)
|
||||
|
||||
For this particular module, please also read CONTRIBUTING.md before contributing.
|
||||
|
||||
Currently we support:
|
||||
|
||||
* iptables
|
||||
* ip6tables
|
||||
* ebtables (chains only)
|
||||
|
||||
But plans are to support lots of other firewall implementations:
|
||||
|
||||
* FreeBSD (ipf)
|
||||
* Mac OS X (ipfw)
|
||||
* OpenBSD (pf)
|
||||
* Cisco (ASA and basic access lists)
|
||||
|
||||
If you have knowledge in these technologies, know how to code, and wish to contribute to this project, we would welcome the help.
|
||||
|
||||
###Testing
|
||||
|
||||
Make sure you have:
|
||||
|
||||
* rake
|
||||
* bundler
|
||||
|
||||
Install the necessary gems:
|
||||
|
||||
bundle install
|
||||
|
||||
And run the tests from the root of the source code:
|
||||
|
||||
rake test
|
||||
|
||||
If you have a copy of Vagrant 1.1.0 you can also run the system tests:
|
||||
|
||||
RSPEC_SET=debian-606-x64 rake spec:system
|
||||
RSPEC_SET=centos-58-x64 rake spec:system
|
||||
|
||||
*Note:* system testing is fairly alpha at this point, your mileage may vary.
|
14
modules/firewall/Rakefile
Normal file
14
modules/firewall/Rakefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'puppetlabs_spec_helper/rake_tasks'
|
||||
|
||||
require 'puppet-lint/tasks/puppet-lint'
|
||||
PuppetLint.configuration.ignore_paths = ['vendor/**/*.pp']
|
||||
|
||||
task :default do
|
||||
sh %{rake -T}
|
||||
end
|
||||
|
||||
desc 'Run reasonably quick tests for CI'
|
||||
task :ci => [
|
||||
:lint,
|
||||
:spec,
|
||||
]
|
72
modules/firewall/checksums.json
Normal file
72
modules/firewall/checksums.json
Normal file
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"CHANGELOG.md": "74a76346f2d59dad54791724a3619b8c",
|
||||
"CONTRIBUTING.md": "abdca3447afde207929d49c4c65eb7e7",
|
||||
"Gemfile": "9c34a2c45945aa2601a98a521364b524",
|
||||
"Gemfile.lock": "cbcf4134a7fa9e8d796f967739082543",
|
||||
"LICENSE": "ade7f2bb88b5b4f034152822222ec314",
|
||||
"Modulefile": "e5a482fea468afc196e17bd31aba1eb7",
|
||||
"README.markdown": "9aa129c5afecefeaedc1e642f9d40dee",
|
||||
"Rakefile": "f939a830f3e97bea4ebfa5773af890d2",
|
||||
"lib/facter/ip6tables_version.rb": "091123ad703f1706686bca4398c5b06f",
|
||||
"lib/facter/iptables_persistent_version.rb": "b7a47827cd3d3bb1acbd526a31da3acb",
|
||||
"lib/facter/iptables_version.rb": "facbd760223f236538b731c1d1f6cf8f",
|
||||
"lib/puppet/provider/firewall/ip6tables.rb": "06198524b5237bcf9222bce72535d817",
|
||||
"lib/puppet/provider/firewall/iptables.rb": "470fdc741c7d60843573b23a2c9fc321",
|
||||
"lib/puppet/provider/firewall.rb": "32d2f5e5dcc082986b82ef26a119038b",
|
||||
"lib/puppet/provider/firewallchain/iptables_chain.rb": "194bde3d3992a37aa7d76e431d251178",
|
||||
"lib/puppet/type/firewall.rb": "88739d191642568aab222bca3fb79b8a",
|
||||
"lib/puppet/type/firewallchain.rb": "548676cc7da53598eb24268ebac38a0d",
|
||||
"lib/puppet/util/firewall.rb": "6f7667742d9f6d192cd202be0014dd85",
|
||||
"lib/puppet/util/ipcidr.rb": "e1160dfd6e73fc5ef2bb8abc291f6fd5",
|
||||
"manifests/init.pp": "ba3e697f00fc3d4e7e5b9c7fdbc6a89d",
|
||||
"manifests/linux/archlinux.pp": "1257fe335ecafa0629b285dc8621cf75",
|
||||
"manifests/linux/debian.pp": "626f0fd23f2f451ca14e2b7f690675fe",
|
||||
"manifests/linux/redhat.pp": "cd4316ac5df986a15fafaafbcbb99a74",
|
||||
"manifests/linux.pp": "eb4a8970e7b90fa27b1b4968e2cb77b4",
|
||||
"metadata.json": "2151a0c4602b2e4be575282d44989c21",
|
||||
"spec/acceptance/change_source_spec.rb": "86279f2f4ad9e0532c9ffab00db8e009",
|
||||
"spec/acceptance/class_spec.rb": "bf8fabb101db46d438fa3aedf35bba08",
|
||||
"spec/acceptance/connlimit_spec.rb": "b0b6600381479e692d0f50215cc15121",
|
||||
"spec/acceptance/connmark_spec.rb": "cf4233e7327af69f0d4431d63140edf1",
|
||||
"spec/acceptance/firewall_spec.rb": "7ee0d49fa4f471d6190e84205941aab1",
|
||||
"spec/acceptance/firewallchain_spec.rb": "1fb101c958bde31ac371e15052dfd6f9",
|
||||
"spec/acceptance/ip6_fragment_spec.rb": "0fed30601238313a51dbc1115840e2bc",
|
||||
"spec/acceptance/isfragment_spec.rb": "80d254c6c74e8dfc2a787115ec2b2ae5",
|
||||
"spec/acceptance/nodesets/centos-59-x64-pe.yml": "255100fb63e3b7c204e24dc9464ae904",
|
||||
"spec/acceptance/nodesets/centos-59-x64.yml": "879c50ab100b927bdbbc130f23baaff7",
|
||||
"spec/acceptance/nodesets/centos-64-x64-fusion.yml": "f5da18ffa623c6a13d9f48521b1bfa39",
|
||||
"spec/acceptance/nodesets/centos-64-x64-pe.yml": "ec075d95760df3d4702abea1ce0a829b",
|
||||
"spec/acceptance/nodesets/centos-64-x64.yml": "092dd2c588a9f87fa1fb12997c0723ef",
|
||||
"spec/acceptance/nodesets/debian-607-x64.yml": "d566bf76f534e2af7c9a4605316d232c",
|
||||
"spec/acceptance/nodesets/debian-70rc1-x64.yml": "31ccca73af7b74e1cc2fb0035c230b2c",
|
||||
"spec/acceptance/nodesets/default.yml": "092dd2c588a9f87fa1fb12997c0723ef",
|
||||
"spec/acceptance/nodesets/fedora-18-x64.yml": "acc126fa764c39a3b1df36e9224a21d9",
|
||||
"spec/acceptance/nodesets/sles-11sp1-x64.yml": "fa0046bd89c1ab4ba9521ad79db234cd",
|
||||
"spec/acceptance/nodesets/ubuntu-server-10044-x64.yml": "dc0da2d2449f66c8fdae16593811504f",
|
||||
"spec/acceptance/nodesets/ubuntu-server-12042-x64.yml": "d30d73e34cd50b043c7d14e305955269",
|
||||
"spec/acceptance/nodesets/ubuntu-server-1404-x64.yml": "ea006afd1329a2d7a8e35d8287ec1658",
|
||||
"spec/acceptance/params_spec.rb": "d86987bfc2156ed840c8b1ca33a92e0d",
|
||||
"spec/acceptance/purge_spec.rb": "1a8327cc7203c6ebfd0ee1c7c8578367",
|
||||
"spec/acceptance/resource_cmd_spec.rb": "9b03213492b13fbb309189246b69cadd",
|
||||
"spec/acceptance/rules_spec.rb": "739538383e33dd711a66531189aa10d4",
|
||||
"spec/acceptance/socket_spec.rb": "ee160ba60e3451f7a6a386e38f1f0de7",
|
||||
"spec/acceptance/standard_usage_spec.rb": "71002dbb3939fc1bbec7f9045db0b30b",
|
||||
"spec/acceptance/unsupported_spec.rb": "5540063193905d4e8235d6042e8633fc",
|
||||
"spec/fixtures/ip6tables/conversion_hash.rb": "0d88a4226fdf28dd55e30f7e6d4a1cb7",
|
||||
"spec/fixtures/iptables/conversion_hash.rb": "c63c630866e543e6f5e23dc30ec53212",
|
||||
"spec/spec_helper.rb": "faae8467928b93bd251a1a66e1eedbe5",
|
||||
"spec/spec_helper_acceptance.rb": "5a6eeb4ed87fa18a2a066fa03c5ec6df",
|
||||
"spec/unit/classes/firewall_linux_archlinux_spec.rb": "1c600a9852ec328b14cb15b0630ed5ff",
|
||||
"spec/unit/classes/firewall_linux_debian_spec.rb": "6334936fb16223cf15f637083c67850e",
|
||||
"spec/unit/classes/firewall_linux_redhat_spec.rb": "f41b21caf6948f3ac08f42c1bc59ba1b",
|
||||
"spec/unit/classes/firewall_linux_spec.rb": "5ea19b1d9b98f5d47d15f6c89c55a5c8",
|
||||
"spec/unit/classes/firewall_spec.rb": "65e27f1c85066641d65d52c35c1d46cf",
|
||||
"spec/unit/facter/iptables_persistent_version_spec.rb": "e08c68b6400c51735a3ac1cc4acf2d15",
|
||||
"spec/unit/facter/iptables_spec.rb": "5265be1e6ec652184de8b314ae396d1a",
|
||||
"spec/unit/puppet/provider/iptables_chain_spec.rb": "5b5dbc4cea409f6f40ef3f18e46fa270",
|
||||
"spec/unit/puppet/provider/iptables_spec.rb": "2d0ed593e9e6996162b3abe4569dd9c6",
|
||||
"spec/unit/puppet/type/firewall_spec.rb": "a57dc53289de01f80611292f5367b4ae",
|
||||
"spec/unit/puppet/type/firewallchain_spec.rb": "ea82405b12b9c0069a6b38a48a6a544a",
|
||||
"spec/unit/puppet/util/firewall_spec.rb": "7fe65c0ab8ea7438ec99a6fab0e2c619",
|
||||
"spec/unit/puppet/util/ipcidr_spec.rb": "1a6eeb2dd7c9634fcfb60d8ead6e1d79"
|
||||
}
|
11
modules/firewall/lib/facter/ip6tables_version.rb
Normal file
11
modules/firewall/lib/facter/ip6tables_version.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
Facter.add(:ip6tables_version) do
|
||||
confine :kernel => :linux
|
||||
setcode do
|
||||
version = Facter::Util::Resolution.exec('ip6tables --version')
|
||||
if version
|
||||
version.match(/\d+\.\d+\.\d+/).to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
15
modules/firewall/lib/facter/iptables_persistent_version.rb
Normal file
15
modules/firewall/lib/facter/iptables_persistent_version.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
Facter.add(:iptables_persistent_version) do
|
||||
confine :operatingsystem => %w{Debian Ubuntu}
|
||||
setcode do
|
||||
# Throw away STDERR because dpkg >= 1.16.7 will make some noise if the
|
||||
# package isn't currently installed.
|
||||
cmd = "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null"
|
||||
version = Facter::Util::Resolution.exec(cmd)
|
||||
|
||||
if version.nil? or !version.match(/\d+\.\d+/)
|
||||
nil
|
||||
else
|
||||
version
|
||||
end
|
||||
end
|
||||
end
|
11
modules/firewall/lib/facter/iptables_version.rb
Normal file
11
modules/firewall/lib/facter/iptables_version.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
Facter.add(:iptables_version) do
|
||||
confine :kernel => :linux
|
||||
setcode do
|
||||
version = Facter::Util::Resolution.exec('iptables --version')
|
||||
if version
|
||||
version.match(/\d+\.\d+\.\d+/).to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
34
modules/firewall/lib/puppet/provider/firewall.rb
Normal file
34
modules/firewall/lib/puppet/provider/firewall.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
class Puppet::Provider::Firewall < Puppet::Provider
|
||||
|
||||
# Prefetch our rule list. This is ran once every time before any other
|
||||
# action (besides initialization of each object).
|
||||
def self.prefetch(resources)
|
||||
debug("[prefetch(resources)]")
|
||||
instances.each do |prov|
|
||||
if resource = resources[prov.name] || resources[prov.name.downcase]
|
||||
resource.provider = prov
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Look up the current status. This allows us to conventiently look up
|
||||
# existing status with properties[:foo].
|
||||
def properties
|
||||
if @property_hash.empty?
|
||||
@property_hash = query || {:ensure => :absent}
|
||||
@property_hash[:ensure] = :absent if @property_hash.empty?
|
||||
end
|
||||
@property_hash.dup
|
||||
end
|
||||
|
||||
# Pull the current state of the list from the full list. We're
|
||||
# getting some double entendre here....
|
||||
def query
|
||||
self.class.instances.each do |instance|
|
||||
if instance.name == self.name or instance.name.downcase == self.name
|
||||
return instance.properties
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
136
modules/firewall/lib/puppet/provider/firewall/ip6tables.rb
Normal file
136
modules/firewall/lib/puppet/provider/firewall/ip6tables.rb
Normal file
|
@ -0,0 +1,136 @@
|
|||
Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :iptables do
|
||||
@doc = "Ip6tables type provider"
|
||||
|
||||
has_feature :iptables
|
||||
has_feature :connection_limiting
|
||||
has_feature :hop_limiting
|
||||
has_feature :rate_limiting
|
||||
has_feature :recent_limiting
|
||||
has_feature :snat
|
||||
has_feature :dnat
|
||||
has_feature :interface_match
|
||||
has_feature :icmp_match
|
||||
has_feature :owner
|
||||
has_feature :state_match
|
||||
has_feature :reject_type
|
||||
has_feature :log_level
|
||||
has_feature :log_prefix
|
||||
has_feature :mark
|
||||
has_feature :tcp_flags
|
||||
has_feature :pkttype
|
||||
has_feature :ishasmorefrags
|
||||
has_feature :islastfrag
|
||||
has_feature :isfirstfrag
|
||||
|
||||
optional_commands({
|
||||
:ip6tables => 'ip6tables',
|
||||
:ip6tables_save => 'ip6tables-save',
|
||||
})
|
||||
|
||||
def initialize(*args)
|
||||
if Facter.fact('ip6tables_version').value.match /1\.3\.\d/
|
||||
raise ArgumentError, 'The ip6tables provider is not supported on version 1.3 of iptables'
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self.iptables(*args)
|
||||
ip6tables(*args)
|
||||
end
|
||||
|
||||
def self.iptables_save(*args)
|
||||
ip6tables_save(*args)
|
||||
end
|
||||
|
||||
@protocol = "IPv6"
|
||||
|
||||
@resource_map = {
|
||||
:burst => "--limit-burst",
|
||||
:connlimit_above => "-m connlimit --connlimit-above",
|
||||
:connlimit_mask => "--connlimit-mask",
|
||||
:connmark => "-m connmark --mark",
|
||||
:ctstate => "-m conntrack --ctstate",
|
||||
:destination => "-d",
|
||||
:dport => "-m multiport --dports",
|
||||
:gid => "-m owner --gid-owner",
|
||||
:icmp => "-m icmp6 --icmpv6-type",
|
||||
:iniface => "-i",
|
||||
:jump => "-j",
|
||||
:hop_limit => "-m hl --hl-eq",
|
||||
:limit => "-m limit --limit",
|
||||
:log_level => "--log-level",
|
||||
:log_prefix => "--log-prefix",
|
||||
:name => "-m comment --comment",
|
||||
:outiface => "-o",
|
||||
:port => '-m multiport --ports',
|
||||
:proto => "-p",
|
||||
:rdest => "--rdest",
|
||||
:reap => "--reap",
|
||||
:recent => "-m recent",
|
||||
:reject => "--reject-with",
|
||||
:rhitcount => "--hitcount",
|
||||
:rname => "--name",
|
||||
:rseconds => "--seconds",
|
||||
:rsource => "--rsource",
|
||||
:rttl => "--rttl",
|
||||
:source => "-s",
|
||||
:state => "-m state --state",
|
||||
:sport => "-m multiport --sports",
|
||||
:table => "-t",
|
||||
:todest => "--to-destination",
|
||||
:toports => "--to-ports",
|
||||
:tosource => "--to-source",
|
||||
:uid => "-m owner --uid-owner",
|
||||
:pkttype => "-m pkttype --pkt-type",
|
||||
:ishasmorefrags => "-m frag --fragid 0 --fragmore",
|
||||
:islastfrag => "-m frag --fragid 0 --fraglast",
|
||||
:isfirstfrag => "-m frag --fragid 0 --fragfirst",
|
||||
}
|
||||
|
||||
# These are known booleans that do not take a value, but we want to munge
|
||||
# to true if they exist.
|
||||
@known_booleans = [:ishasmorefrags, :islastfrag, :isfirstfrag, :rsource, :rdest, :reap, :rttl]
|
||||
|
||||
# Create property methods dynamically
|
||||
(@resource_map.keys << :chain << :table << :action).each do |property|
|
||||
if @known_booleans.include?(property) then
|
||||
# The boolean properties default to '' which should be read as false
|
||||
define_method "#{property}" do
|
||||
@property_hash[property] = :false if @property_hash[property] == nil
|
||||
@property_hash[property.to_sym]
|
||||
end
|
||||
else
|
||||
define_method "#{property}" do
|
||||
@property_hash[property.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
if property == :chain
|
||||
define_method "#{property}=" do |value|
|
||||
if @property_hash[:chain] != value
|
||||
raise ArgumentError, "Modifying the chain for existing rules is not supported."
|
||||
end
|
||||
end
|
||||
else
|
||||
define_method "#{property}=" do |value|
|
||||
@property_hash[:needs_change] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the order of resources as they appear in iptables-save output,
|
||||
# we need it to properly parse and apply rules, if the order of resource
|
||||
# changes between puppet runs, the changed rules will be re-applied again.
|
||||
# This order can be determined by going through iptables source code or just tweaking and trying manually
|
||||
# (Note: on my CentOS 6.4 ip6tables-save returns -m frag on the place
|
||||
# I put it when calling the command. So compability with manual changes
|
||||
# not provided with current parser [georg.koester])
|
||||
@resource_list = [:table, :source, :destination, :iniface, :outiface,
|
||||
:proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :gid, :uid, :sport, :dport,
|
||||
:port, :pkttype, :name, :state, :ctstate, :icmp, :hop_limit, :limit, :burst,
|
||||
:recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :rsource, :rdest,
|
||||
:jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject,
|
||||
:connlimit_above, :connlimit_mask, :connmark]
|
||||
|
||||
end
|
501
modules/firewall/lib/puppet/provider/firewall/iptables.rb
Normal file
501
modules/firewall/lib/puppet/provider/firewall/iptables.rb
Normal file
|
@ -0,0 +1,501 @@
|
|||
require 'puppet/provider/firewall'
|
||||
require 'digest/md5'
|
||||
|
||||
Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Firewall do
|
||||
include Puppet::Util::Firewall
|
||||
|
||||
@doc = "Iptables type provider"
|
||||
|
||||
has_feature :iptables
|
||||
has_feature :connection_limiting
|
||||
has_feature :rate_limiting
|
||||
has_feature :recent_limiting
|
||||
has_feature :snat
|
||||
has_feature :dnat
|
||||
has_feature :interface_match
|
||||
has_feature :icmp_match
|
||||
has_feature :owner
|
||||
has_feature :state_match
|
||||
has_feature :reject_type
|
||||
has_feature :log_level
|
||||
has_feature :log_prefix
|
||||
has_feature :mark
|
||||
has_feature :tcp_flags
|
||||
has_feature :pkttype
|
||||
has_feature :isfragment
|
||||
has_feature :socket
|
||||
has_feature :address_type
|
||||
has_feature :iprange
|
||||
has_feature :ipsec_dir
|
||||
has_feature :ipsec_policy
|
||||
has_feature :mask
|
||||
|
||||
optional_commands({
|
||||
:iptables => 'iptables',
|
||||
:iptables_save => 'iptables-save',
|
||||
})
|
||||
|
||||
defaultfor :kernel => :linux
|
||||
|
||||
iptables_version = Facter.fact('iptables_version').value
|
||||
if (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') < 0)
|
||||
mark_flag = '--set-mark'
|
||||
else
|
||||
mark_flag = '--set-xmark'
|
||||
end
|
||||
|
||||
@protocol = "IPv4"
|
||||
|
||||
@resource_map = {
|
||||
:burst => "--limit-burst",
|
||||
:connlimit_above => "-m connlimit --connlimit-above",
|
||||
:connlimit_mask => "--connlimit-mask",
|
||||
:connmark => "-m connmark --mark",
|
||||
:ctstate => "-m conntrack --ctstate",
|
||||
:destination => "-d",
|
||||
:dst_type => "-m addrtype --dst-type",
|
||||
:dst_range => "-m iprange --dst-range",
|
||||
:dport => ["-m multiport --dports", "--dport"],
|
||||
:gid => "-m owner --gid-owner",
|
||||
:icmp => "-m icmp --icmp-type",
|
||||
:iniface => "-i",
|
||||
:jump => "-j",
|
||||
:limit => "-m limit --limit",
|
||||
:log_level => "--log-level",
|
||||
:log_prefix => "--log-prefix",
|
||||
:name => "-m comment --comment",
|
||||
:outiface => "-o",
|
||||
:port => '-m multiport --ports',
|
||||
:proto => "-p",
|
||||
:random => "--random",
|
||||
:rdest => "--rdest",
|
||||
:reap => "--reap",
|
||||
:recent => "-m recent",
|
||||
:reject => "--reject-with",
|
||||
:rhitcount => "--hitcount",
|
||||
:rname => "--name",
|
||||
:rseconds => "--seconds",
|
||||
:rsource => "--rsource",
|
||||
:rttl => "--rttl",
|
||||
:set_mark => mark_flag,
|
||||
:socket => "-m socket",
|
||||
:source => "-s",
|
||||
:src_type => "-m addrtype --src-type",
|
||||
:src_range => "-m iprange --src-range",
|
||||
:sport => ["-m multiport --sports", "--sport"],
|
||||
:state => "-m state --state",
|
||||
:table => "-t",
|
||||
:tcp_flags => "-m tcp --tcp-flags",
|
||||
:todest => "--to-destination",
|
||||
:toports => "--to-ports",
|
||||
:tosource => "--to-source",
|
||||
:uid => "-m owner --uid-owner",
|
||||
:pkttype => "-m pkttype --pkt-type",
|
||||
:isfragment => "-f",
|
||||
:ipsec_dir => "-m policy --dir",
|
||||
:ipsec_policy => "--pol",
|
||||
:mask => '--mask',
|
||||
}
|
||||
|
||||
# These are known booleans that do not take a value, but we want to munge
|
||||
# to true if they exist.
|
||||
@known_booleans = [
|
||||
:isfragment,
|
||||
:random,
|
||||
:rdest,
|
||||
:reap,
|
||||
:rsource,
|
||||
:rttl,
|
||||
:socket
|
||||
]
|
||||
|
||||
|
||||
# Create property methods dynamically
|
||||
(@resource_map.keys << :chain << :table << :action).each do |property|
|
||||
if @known_booleans.include?(property) then
|
||||
# The boolean properties default to '' which should be read as false
|
||||
define_method "#{property}" do
|
||||
@property_hash[property] = :false if @property_hash[property] == nil
|
||||
@property_hash[property.to_sym]
|
||||
end
|
||||
else
|
||||
define_method "#{property}" do
|
||||
@property_hash[property.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
if property == :chain
|
||||
define_method "#{property}=" do |value|
|
||||
if @property_hash[:chain] != value
|
||||
raise ArgumentError, "Modifying the chain for existing rules is not supported."
|
||||
end
|
||||
end
|
||||
else
|
||||
define_method "#{property}=" do |value|
|
||||
@property_hash[:needs_change] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the order of resources as they appear in iptables-save output,
|
||||
# we need it to properly parse and apply rules, if the order of resource
|
||||
# changes between puppet runs, the changed rules will be re-applied again.
|
||||
# This order can be determined by going through iptables source code or just tweaking and trying manually
|
||||
@resource_list = [
|
||||
:table, :source, :destination, :iniface, :outiface, :proto, :isfragment,
|
||||
:src_range, :dst_range, :tcp_flags, :gid, :uid, :sport, :dport, :port,
|
||||
:dst_type, :src_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy,
|
||||
:state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap,
|
||||
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :jump, :todest,
|
||||
:tosource, :toports, :random, :log_prefix, :log_level, :reject, :set_mark,
|
||||
:connlimit_above, :connlimit_mask, :connmark
|
||||
]
|
||||
|
||||
def insert
|
||||
debug 'Inserting rule %s' % resource[:name]
|
||||
iptables insert_args
|
||||
end
|
||||
|
||||
def update
|
||||
debug 'Updating rule %s' % resource[:name]
|
||||
iptables update_args
|
||||
end
|
||||
|
||||
def delete
|
||||
debug 'Deleting rule %s' % resource[:name]
|
||||
iptables delete_args
|
||||
end
|
||||
|
||||
def exists?
|
||||
properties[:ensure] != :absent
|
||||
end
|
||||
|
||||
# Flush the property hash once done.
|
||||
def flush
|
||||
debug("[flush]")
|
||||
if @property_hash.delete(:needs_change)
|
||||
notice("Properties changed - updating rule")
|
||||
update
|
||||
end
|
||||
persist_iptables(self.class.instance_variable_get(:@protocol))
|
||||
@property_hash.clear
|
||||
end
|
||||
|
||||
def self.instances
|
||||
debug "[instances]"
|
||||
table = nil
|
||||
rules = []
|
||||
counter = 1
|
||||
|
||||
# String#lines would be nice, but we need to support Ruby 1.8.5
|
||||
iptables_save.split("\n").each do |line|
|
||||
unless line =~ /^\#\s+|^\:\S+|^COMMIT|^FATAL/
|
||||
if line =~ /^\*/
|
||||
table = line.sub(/\*/, "")
|
||||
else
|
||||
if hash = rule_to_hash(line, table, counter)
|
||||
rules << new(hash)
|
||||
counter += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rules
|
||||
end
|
||||
|
||||
def self.rule_to_hash(line, table, counter)
|
||||
hash = {}
|
||||
keys = []
|
||||
values = line.dup
|
||||
|
||||
####################
|
||||
# PRE-PARSE CLUDGING
|
||||
####################
|
||||
|
||||
# --tcp-flags takes two values; we cheat by adding " around it
|
||||
# so it behaves like --comment
|
||||
values = values.sub(/--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1 \2"')
|
||||
# we do a similar thing for negated address masks (source and destination).
|
||||
values = values.sub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"')
|
||||
# the actual rule will have the ! mark before the option.
|
||||
values = values.sub(/(!)\s*(-\S+)\s*(\S*)/, '\2 "\1 \3"')
|
||||
# The match extension for tcp & udp are optional and throws off the @resource_map.
|
||||
values = values.sub(/-m (tcp|udp) (--(s|d)port|-m multiport)/, '\2')
|
||||
|
||||
# Trick the system for booleans
|
||||
@known_booleans.each do |bool|
|
||||
# append "true" because all params are expected to have values
|
||||
if bool == :isfragment then
|
||||
# -f requires special matching:
|
||||
# only replace those -f that are not followed by an l to
|
||||
# distinguish between -f and the '-f' inside of --tcp-flags.
|
||||
values = values.sub(/-f(?!l)(?=.*--comment)/, '-f true')
|
||||
else
|
||||
values = values.sub(/#{@resource_map[bool]}/, "#{@resource_map[bool]} true")
|
||||
end
|
||||
end
|
||||
|
||||
############
|
||||
# Populate parser_list with used value, in the correct order
|
||||
############
|
||||
map_index={}
|
||||
@resource_map.each_pair do |map_k,map_v|
|
||||
[map_v].flatten.each do |v|
|
||||
ind=values.index(/\s#{v}/)
|
||||
next unless ind
|
||||
map_index[map_k]=ind
|
||||
end
|
||||
end
|
||||
# Generate parser_list based on the index of the found option
|
||||
parser_list=[]
|
||||
map_index.sort_by{|k,v| v}.each{|mapi| parser_list << mapi.first }
|
||||
|
||||
############
|
||||
# MAIN PARSE
|
||||
############
|
||||
|
||||
# Here we iterate across our values to generate an array of keys
|
||||
parser_list.reverse.each do |k|
|
||||
resource_map_key = @resource_map[k]
|
||||
[resource_map_key].flatten.each do |opt|
|
||||
if values.slice!(/\s#{opt}/)
|
||||
keys << k
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Manually remove chain
|
||||
values.slice!('-A')
|
||||
keys << :chain
|
||||
|
||||
# Here we generate the main hash
|
||||
keys.zip(values.scan(/"[^"]*"|\S+/).reverse) { |f, v| hash[f] = v.gsub(/"/, '') }
|
||||
|
||||
#####################
|
||||
# POST PARSE CLUDGING
|
||||
#####################
|
||||
|
||||
# Normalise all rules to CIDR notation.
|
||||
[:source, :destination].each do |prop|
|
||||
next if hash[prop].nil?
|
||||
m = hash[prop].match(/(!?)\s?(.*)/)
|
||||
neg = "! " if m[1] == "!"
|
||||
hash[prop] = "#{neg}#{Puppet::Util::IPCidr.new(m[2]).cidr}"
|
||||
end
|
||||
|
||||
[:dport, :sport, :port, :state, :ctstate].each do |prop|
|
||||
hash[prop] = hash[prop].split(',') if ! hash[prop].nil?
|
||||
end
|
||||
|
||||
# Convert booleans removing the previous cludge we did
|
||||
@known_booleans.each do |bool|
|
||||
if hash[bool] != nil then
|
||||
if hash[bool] != "true" then
|
||||
raise "Parser error: #{bool} was meant to be a boolean but received value: #{hash[bool]}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Our type prefers hyphens over colons for ranges so ...
|
||||
# Iterate across all ports replacing colons with hyphens so that ranges match
|
||||
# the types expectations.
|
||||
[:dport, :sport, :port].each do |prop|
|
||||
next unless hash[prop]
|
||||
hash[prop] = hash[prop].collect do |elem|
|
||||
elem.gsub(/:/,'-')
|
||||
end
|
||||
end
|
||||
|
||||
# States should always be sorted. This ensures that the output from
|
||||
# iptables-save and user supplied resources is consistent.
|
||||
hash[:state] = hash[:state].sort unless hash[:state].nil?
|
||||
hash[:ctstate] = hash[:ctstate].sort unless hash[:ctstate].nil?
|
||||
|
||||
# This forces all existing, commentless rules or rules with invalid comments to be moved
|
||||
# to the bottom of the stack.
|
||||
# Puppet-firewall requires that all rules have comments (resource names) and match this
|
||||
# regex and will fail if a rule in iptables does not have a comment. We get around this
|
||||
# by appending a high level
|
||||
if ! hash[:name]
|
||||
num = 9000 + counter
|
||||
hash[:name] = "#{num} #{Digest::MD5.hexdigest(line)}"
|
||||
elsif not /^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/ =~ hash[:name]
|
||||
num = 9000 + counter
|
||||
hash[:name] = "#{num} #{/([[:alpha:][:digit:][:punct:][:space:]]+)/.match(hash[:name])[1]}"
|
||||
end
|
||||
|
||||
# Iptables defaults to log_level '4', so it is omitted from the output of iptables-save.
|
||||
# If the :jump value is LOG and you don't have a log-level set, we assume it to be '4'.
|
||||
if hash[:jump] == 'LOG' && ! hash[:log_level]
|
||||
hash[:log_level] = '4'
|
||||
end
|
||||
|
||||
# Iptables defaults to burst '5', so it is ommitted from the output of iptables-save.
|
||||
# If the :limit value is set and you don't have a burst set, we assume it to be '5'.
|
||||
if hash[:limit] && ! hash[:burst]
|
||||
hash[:burst] = '5'
|
||||
end
|
||||
|
||||
hash[:line] = line
|
||||
hash[:provider] = self.name.to_s
|
||||
hash[:table] = table
|
||||
hash[:ensure] = :present
|
||||
|
||||
# Munge some vars here ...
|
||||
|
||||
# Proto should equal 'all' if undefined
|
||||
hash[:proto] = "all" if !hash.include?(:proto)
|
||||
|
||||
# If the jump parameter is set to one of: ACCEPT, REJECT or DROP then
|
||||
# we should set the action parameter instead.
|
||||
if ['ACCEPT','REJECT','DROP'].include?(hash[:jump]) then
|
||||
hash[:action] = hash[:jump].downcase
|
||||
hash.delete(:jump)
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def insert_args
|
||||
args = []
|
||||
args << ["-I", resource[:chain], insert_order]
|
||||
args << general_args
|
||||
args
|
||||
end
|
||||
|
||||
def update_args
|
||||
args = []
|
||||
args << ["-R", resource[:chain], insert_order]
|
||||
args << general_args
|
||||
args
|
||||
end
|
||||
|
||||
def delete_args
|
||||
# Split into arguments
|
||||
line = properties[:line].gsub(/\-A/, '-D').split(/\s(?=(?:[^"]|"[^"]*")*$)/).map{|v| v.gsub(/"/, '')}
|
||||
line.unshift("-t", properties[:table])
|
||||
end
|
||||
|
||||
# This method takes the resource, and attempts to generate the command line
|
||||
# arguments for iptables.
|
||||
def general_args
|
||||
debug "Current resource: %s" % resource.class
|
||||
|
||||
args = []
|
||||
resource_list = self.class.instance_variable_get('@resource_list')
|
||||
resource_map = self.class.instance_variable_get('@resource_map')
|
||||
known_booleans = self.class.instance_variable_get('@known_booleans')
|
||||
|
||||
resource_list.each do |res|
|
||||
resource_value = nil
|
||||
if (resource[res]) then
|
||||
resource_value = resource[res]
|
||||
# If socket is true then do not add the value as -m socket is standalone
|
||||
if known_booleans.include?(res) then
|
||||
if resource[res] == :true then
|
||||
resource_value = nil
|
||||
else
|
||||
# If the property is not :true then we don't want to add the value
|
||||
# to the args list
|
||||
next
|
||||
end
|
||||
end
|
||||
elsif res == :jump and resource[:action] then
|
||||
# In this case, we are substituting jump for action
|
||||
resource_value = resource[:action].to_s.upcase
|
||||
else
|
||||
next
|
||||
end
|
||||
|
||||
args << [resource_map[res]].flatten.first.split(' ')
|
||||
|
||||
# On negations, the '!' has to be before the option (eg: "! -d 1.2.3.4")
|
||||
if resource_value.is_a?(String) and resource_value.sub!(/^!\s*/, '') then
|
||||
# we do this after adding the 'dash' argument because of ones like "-m multiport --dports", where we want it before the "--dports" but after "-m multiport".
|
||||
# so we insert before whatever the last argument is
|
||||
args.insert(-2, '!')
|
||||
end
|
||||
|
||||
|
||||
# For sport and dport, convert hyphens to colons since the type
|
||||
# expects hyphens for ranges of ports.
|
||||
if [:sport, :dport, :port].include?(res) then
|
||||
resource_value = resource_value.collect do |elem|
|
||||
elem.gsub(/-/, ':')
|
||||
end
|
||||
end
|
||||
|
||||
# our tcp_flags takes a single string with comma lists separated
|
||||
# by space
|
||||
# --tcp-flags expects two arguments
|
||||
if res == :tcp_flags
|
||||
one, two = resource_value.split(' ')
|
||||
args << one
|
||||
args << two
|
||||
elsif resource_value.is_a?(Array)
|
||||
args << resource_value.join(',')
|
||||
elsif !resource_value.nil?
|
||||
args << resource_value
|
||||
end
|
||||
end
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
def insert_order
|
||||
debug("[insert_order]")
|
||||
rules = []
|
||||
|
||||
# Find list of current rules based on chain and table
|
||||
self.class.instances.each do |rule|
|
||||
if rule.chain == resource[:chain].to_s and rule.table == resource[:table].to_s
|
||||
rules << rule.name
|
||||
end
|
||||
end
|
||||
|
||||
# No rules at all? Just bail now.
|
||||
return 1 if rules.empty?
|
||||
|
||||
# Add our rule to the end of the array of known rules
|
||||
my_rule = resource[:name].to_s
|
||||
rules << my_rule
|
||||
|
||||
unmanaged_rule_regex = /^9[0-9]{3}\s[a-f0-9]{32}$/
|
||||
# Find if this is a new rule or an existing rule, then find how many
|
||||
# unmanaged rules preceed it.
|
||||
if rules.length == rules.uniq.length
|
||||
# This is a new rule so find its ordered location.
|
||||
new_rule_location = rules.sort.uniq.index(my_rule)
|
||||
if new_rule_location == 0
|
||||
# The rule will be the first rule in the chain because nothing came
|
||||
# before it.
|
||||
offset_rule = rules[0]
|
||||
else
|
||||
# This rule will come after other managed rules, so find the rule
|
||||
# immediately preceeding it.
|
||||
offset_rule = rules.sort.uniq[new_rule_location - 1]
|
||||
end
|
||||
else
|
||||
# This is a pre-existing rule, so find the offset from the original
|
||||
# ordering.
|
||||
offset_rule = my_rule
|
||||
end
|
||||
# Count how many unmanaged rules are ahead of the target rule so we know
|
||||
# how much to add to the insert order
|
||||
unnamed_offset = rules[0..rules.index(offset_rule)].inject(0) do |sum,rule|
|
||||
# This regex matches the names given to unmanaged rules (a number
|
||||
# 9000-9999 followed by an MD5 hash).
|
||||
sum + (rule.match(unmanaged_rule_regex) ? 1 : 0)
|
||||
end
|
||||
|
||||
# We want our rule to come before unmanaged rules if it's not a 9-rule
|
||||
if offset_rule.match(unmanaged_rule_regex) and ! my_rule.match(/^9/)
|
||||
unnamed_offset -= 1
|
||||
end
|
||||
|
||||
# Insert our new or updated rule in the correct order of named rules, but
|
||||
# offset for unnamed rules.
|
||||
rules.reject{|r|r.match(unmanaged_rule_regex)}.sort.index(my_rule) + 1 + unnamed_offset
|
||||
end
|
||||
end
|
|
@ -0,0 +1,178 @@
|
|||
Puppet::Type.type(:firewallchain).provide :iptables_chain do
|
||||
include Puppet::Util::Firewall
|
||||
|
||||
@doc = "Iptables chain provider"
|
||||
|
||||
has_feature :iptables_chain
|
||||
has_feature :policy
|
||||
|
||||
optional_commands({
|
||||
:iptables => 'iptables',
|
||||
:iptables_save => 'iptables-save',
|
||||
:ip6tables => 'ip6tables',
|
||||
:ip6tables_save => 'ip6tables-save',
|
||||
:ebtables => 'ebtables',
|
||||
:ebtables_save => 'ebtables-save',
|
||||
})
|
||||
|
||||
defaultfor :kernel => :linux
|
||||
|
||||
# chain name is greedy so we anchor from the end.
|
||||
# [\d+:\d+] doesn't exist on ebtables
|
||||
Mapping = {
|
||||
:IPv4 => {
|
||||
:tables => method(:iptables),
|
||||
:save => method(:iptables_save),
|
||||
:re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/,
|
||||
},
|
||||
:IPv6 => {
|
||||
:tables => method(:ip6tables),
|
||||
:save => method(:ip6tables_save),
|
||||
:re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/,
|
||||
},
|
||||
:ethernet => {
|
||||
:tables => method(:ebtables),
|
||||
:save => method(:ebtables_save),
|
||||
:re => /^:(.+)\s(\S+)$/,
|
||||
}
|
||||
}
|
||||
InternalChains = /^(PREROUTING|POSTROUTING|BROUTING|INPUT|FORWARD|OUTPUT)$/
|
||||
Tables = 'nat|mangle|filter|raw|rawpost|broute'
|
||||
Nameformat = /^(.+):(#{Tables}):(IP(v[46])?|ethernet)$/
|
||||
|
||||
def create
|
||||
allvalidchains do |t, chain, table, protocol|
|
||||
if chain =~ InternalChains
|
||||
# can't create internal chains
|
||||
warning "Attempting to create internal chain #{@resource[:name]}"
|
||||
end
|
||||
if properties[:ensure] == protocol
|
||||
debug "Skipping Inserting chain #{chain} on table #{table} (#{protocol}) already exists"
|
||||
else
|
||||
debug "Inserting chain #{chain} on table #{table} (#{protocol}) using #{t}"
|
||||
t.call ['-t',table,'-N',chain]
|
||||
unless @resource[:policy].nil?
|
||||
t.call ['-t',table,'-P',chain,@resource[:policy].to_s.upcase]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
allvalidchains do |t, chain, table|
|
||||
if chain =~ InternalChains
|
||||
# can't delete internal chains
|
||||
warning "Attempting to destroy internal chain #{@resource[:name]}"
|
||||
end
|
||||
debug "Deleting chain #{chain} on table #{table}"
|
||||
t.call ['-t',table,'-X',chain]
|
||||
end
|
||||
end
|
||||
|
||||
def exists?
|
||||
allvalidchains do |t, chain|
|
||||
if chain =~ InternalChains
|
||||
# If the chain isn't present, it's likely because the module isn't loaded.
|
||||
# If this is true, then we fall into 2 cases
|
||||
# 1) It'll be loaded on demand
|
||||
# 2) It won't be loaded on demand, and we throw an error
|
||||
# This is the intended behavior as it's not the provider's job to load kernel modules
|
||||
# So we pretend it exists...
|
||||
return true
|
||||
end
|
||||
end
|
||||
properties[:ensure] == :present
|
||||
end
|
||||
|
||||
def policy=(value)
|
||||
return if value == :empty
|
||||
allvalidchains do |t, chain, table|
|
||||
p = ['-t',table,'-P',chain,value.to_s.upcase]
|
||||
debug "[set policy] #{t} #{p}"
|
||||
t.call p
|
||||
end
|
||||
end
|
||||
|
||||
def policy
|
||||
debug "[get policy] #{@resource[:name]} =#{@property_hash[:policy].to_s.downcase}"
|
||||
return @property_hash[:policy].to_s.downcase
|
||||
end
|
||||
|
||||
def self.prefetch(resources)
|
||||
debug("[prefetch(resources)]")
|
||||
instances.each do |prov|
|
||||
if resource = resources[prov.name]
|
||||
resource.provider = prov
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def flush
|
||||
debug("[flush]")
|
||||
persist_iptables(@resource[:name].match(Nameformat)[3])
|
||||
# Clear the property hash so we re-initialize with updated values
|
||||
@property_hash.clear
|
||||
end
|
||||
|
||||
# Look up the current status. This allows us to conventiently look up
|
||||
# existing status with properties[:foo].
|
||||
def properties
|
||||
if @property_hash.empty?
|
||||
@property_hash = query || {:ensure => :absent}
|
||||
end
|
||||
@property_hash.dup
|
||||
end
|
||||
|
||||
# Pull the current state of the list from the full list.
|
||||
def query
|
||||
self.class.instances.each do |instance|
|
||||
if instance.name == self.name
|
||||
debug "query found #{self.name}" % instance.properties.inspect
|
||||
return instance.properties
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def self.instances
|
||||
debug "[instances]"
|
||||
table = nil
|
||||
chains = []
|
||||
|
||||
Mapping.each { |p, c|
|
||||
begin
|
||||
c[:save].call.each_line do |line|
|
||||
if line =~ c[:re] then
|
||||
name = $1 + ':' + (table == 'filter' ? 'filter' : table) + ':' + p.to_s
|
||||
policy = $2 == '-' ? nil : $2.downcase.to_sym
|
||||
|
||||
chains << new({
|
||||
:name => name,
|
||||
:policy => policy,
|
||||
:ensure => :present,
|
||||
})
|
||||
|
||||
debug "[instance] '#{name}' #{policy}"
|
||||
elsif line =~ /^\*(\S+)/
|
||||
table = $1
|
||||
else
|
||||
next
|
||||
end
|
||||
end
|
||||
rescue Puppet::Error
|
||||
# ignore command not found for ebtables or anything that doesn't exist
|
||||
end
|
||||
}
|
||||
|
||||
chains
|
||||
end
|
||||
|
||||
def allvalidchains
|
||||
@resource[:name].match(Nameformat)
|
||||
chain = $1
|
||||
table = $2
|
||||
protocol = $3
|
||||
yield Mapping[protocol.to_sym][:tables],chain,table,protocol.to_sym
|
||||
end
|
||||
|
||||
end
|
1077
modules/firewall/lib/puppet/type/firewall.rb
Normal file
1077
modules/firewall/lib/puppet/type/firewall.rb
Normal file
File diff suppressed because it is too large
Load diff
222
modules/firewall/lib/puppet/type/firewallchain.rb
Normal file
222
modules/firewall/lib/puppet/type/firewallchain.rb
Normal file
|
@ -0,0 +1,222 @@
|
|||
# This is a workaround for bug: #4248 whereby ruby files outside of the normal
|
||||
# provider/type path do not load until pluginsync has occured on the puppetmaster
|
||||
#
|
||||
# In this case I'm trying the relative path first, then falling back to normal
|
||||
# mechanisms. This should be fixed in future versions of puppet but it looks
|
||||
# like we'll need to maintain this for some time perhaps.
|
||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..",".."))
|
||||
require 'puppet/util/firewall'
|
||||
|
||||
Puppet::Type.newtype(:firewallchain) do
|
||||
include Puppet::Util::Firewall
|
||||
|
||||
@doc = <<-EOS
|
||||
This type provides the capability to manage rule chains for firewalls.
|
||||
|
||||
Currently this supports only iptables, ip6tables and ebtables on Linux. And
|
||||
provides support for setting the default policy on chains and tables that
|
||||
allow it.
|
||||
|
||||
**Autorequires:**
|
||||
If Puppet is managing the iptables or iptables-persistent packages, and
|
||||
the provider is iptables_chain, the firewall resource will autorequire
|
||||
those packages to ensure that any required binaries are installed.
|
||||
EOS
|
||||
|
||||
feature :iptables_chain, "The provider provides iptables chain features."
|
||||
feature :policy, "Default policy (inbuilt chains only)"
|
||||
|
||||
ensurable do
|
||||
defaultvalues
|
||||
defaultto :present
|
||||
end
|
||||
|
||||
newparam(:name) do
|
||||
desc <<-EOS
|
||||
The canonical name of the chain.
|
||||
|
||||
For iptables the format must be {chain}:{table}:{protocol}.
|
||||
EOS
|
||||
isnamevar
|
||||
|
||||
validate do |value|
|
||||
if value !~ Nameformat then
|
||||
raise ArgumentError, "Inbuilt chains must be in the form {chain}:{table}:{protocol} where {table} is one of FILTER, NAT, MANGLE, RAW, RAWPOST, BROUTE or empty (alias for filter), chain can be anything without colons or one of PREROUTING, POSTROUTING, BROUTING, INPUT, FORWARD, OUTPUT for the inbuilt chains, and {protocol} being IPv4, IPv6, ethernet (ethernet bridging) got '#{value}' table:'#{$1}' chain:'#{$2}' protocol:'#{$3}'"
|
||||
else
|
||||
chain = $1
|
||||
table = $2
|
||||
protocol = $3
|
||||
case table
|
||||
when 'filter'
|
||||
if chain =~ /^(PREROUTING|POSTROUTING|BROUTING)$/
|
||||
raise ArgumentError, "INPUT, OUTPUT and FORWARD are the only inbuilt chains that can be used in table 'filter'"
|
||||
end
|
||||
when 'mangle'
|
||||
if chain =~ InternalChains && chain == 'BROUTING'
|
||||
raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, FORWARD and OUTPUT are the only inbuilt chains that can be used in table 'mangle'"
|
||||
end
|
||||
when 'nat'
|
||||
if chain =~ /^(BROUTING|FORWARD)$/
|
||||
raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, and OUTPUT are the only inbuilt chains that can be used in table 'nat'"
|
||||
end
|
||||
if protocol =~/^(IP(v6)?)?$/
|
||||
raise ArgumentError, "table nat isn't valid in IPv6. You must specify ':IPv4' as the name suffix"
|
||||
end
|
||||
when 'raw'
|
||||
if chain =~ /^(POSTROUTING|BROUTING|INPUT|FORWARD)$/
|
||||
raise ArgumentError,'PREROUTING and OUTPUT are the only inbuilt chains in the table \'raw\''
|
||||
end
|
||||
when 'broute'
|
||||
if protocol != 'ethernet'
|
||||
raise ArgumentError,'BROUTE is only valid with protocol \'ethernet\''
|
||||
end
|
||||
if chain =~ /^PREROUTING|POSTROUTING|INPUT|FORWARD|OUTPUT$/
|
||||
raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'broute\''
|
||||
end
|
||||
end
|
||||
if chain == 'BROUTING' && ( protocol != 'ethernet' || table!='broute')
|
||||
raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\' with protocol \'ethernet\' i.e. \'broute:BROUTING:enternet\''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:policy) do
|
||||
desc <<-EOS
|
||||
This is the action to when the end of the chain is reached.
|
||||
It can only be set on inbuilt chains (INPUT, FORWARD, OUTPUT,
|
||||
PREROUTING, POSTROUTING) and can be one of:
|
||||
|
||||
* accept - the packet is accepted
|
||||
* drop - the packet is dropped
|
||||
* queue - the packet is passed userspace
|
||||
* return - the packet is returned to calling (jump) queue
|
||||
or the default of inbuilt chains
|
||||
EOS
|
||||
newvalues(:accept, :drop, :queue, :return)
|
||||
defaultto do
|
||||
# ethernet chain have an ACCEPT default while other haven't got an
|
||||
# allowed value
|
||||
if @resource[:name] =~ /:ethernet$/
|
||||
:accept
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:purge, :boolean => true) do
|
||||
desc <<-EOS
|
||||
Purge unmanaged firewall rules in this chain
|
||||
EOS
|
||||
newvalues(:false, :true)
|
||||
defaultto :false
|
||||
end
|
||||
|
||||
newparam(:ignore) do
|
||||
desc <<-EOS
|
||||
Regex to perform on firewall rules to exempt unmanaged rules from purging (when enabled).
|
||||
This is matched against the output of `iptables-save`.
|
||||
|
||||
This can be a single regex, or an array of them.
|
||||
To support flags, use the ruby inline flag mechanism.
|
||||
Meaning a regex such as
|
||||
/foo/i
|
||||
can be written as
|
||||
'(?i)foo' or '(?i:foo)'
|
||||
|
||||
Full example:
|
||||
firewallchain { 'INPUT:filter:IPv4':
|
||||
purge => true,
|
||||
ignore => [
|
||||
'-j fail2ban-ssh', # ignore the fail2ban jump rule
|
||||
'--comment "[^"]*(?i:ignore)[^"]*"', # ignore any rules with "ignore" (case insensitive) in the comment in the rule
|
||||
],
|
||||
}
|
||||
EOS
|
||||
|
||||
validate do |value|
|
||||
unless value.is_a?(Array) or value.is_a?(String) or value == false
|
||||
self.devfail "Ignore must be a string or an Array"
|
||||
end
|
||||
end
|
||||
munge do |patterns| # convert into an array of {Regex}es
|
||||
patterns = [patterns] if patterns.is_a?(String)
|
||||
patterns.map{|p| Regexp.new(p)}
|
||||
end
|
||||
end
|
||||
|
||||
# Classes would be a better abstraction, pending:
|
||||
# http://projects.puppetlabs.com/issues/19001
|
||||
autorequire(:package) do
|
||||
case value(:provider)
|
||||
when :iptables_chain
|
||||
%w{iptables iptables-persistent}
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
validate do
|
||||
debug("[validate]")
|
||||
|
||||
value(:name).match(Nameformat)
|
||||
chain = $1
|
||||
table = $2
|
||||
protocol = $3
|
||||
|
||||
# Check that we're not removing an internal chain
|
||||
if chain =~ InternalChains && value(:ensure) == :absent
|
||||
self.fail "Cannot remove in-built chains"
|
||||
end
|
||||
|
||||
if value(:policy).nil? && protocol == 'ethernet'
|
||||
self.fail "you must set a non-empty policy on all ethernet table chains"
|
||||
end
|
||||
|
||||
# Check that we're not setting a policy on a user chain
|
||||
if chain !~ InternalChains &&
|
||||
!value(:policy).nil? &&
|
||||
protocol != 'ethernet'
|
||||
|
||||
self.fail "policy can only be set on in-built chains (with the exception of ethernet chains) (table:#{table} chain:#{chain} protocol:#{protocol})"
|
||||
end
|
||||
|
||||
# no DROP policy on nat table
|
||||
if table == 'nat' &&
|
||||
value(:policy) == :drop
|
||||
|
||||
self.fail 'The "nat" table is not intended for filtering, the use of DROP is therefore inhibited'
|
||||
end
|
||||
end
|
||||
|
||||
def generate
|
||||
return [] unless self.purge?
|
||||
|
||||
value(:name).match(Nameformat)
|
||||
chain = $1
|
||||
table = $2
|
||||
protocol = $3
|
||||
|
||||
provider = case protocol
|
||||
when 'IPv4'
|
||||
:iptables
|
||||
when 'IPv6'
|
||||
:ip6tables
|
||||
end
|
||||
|
||||
# gather a list of all rules present on the system
|
||||
rules_resources = Puppet::Type.type(:firewall).instances
|
||||
|
||||
# Keep only rules in this chain
|
||||
rules_resources.delete_if { |res| (res[:provider] != provider or res.provider.properties[:table].to_s != table or res.provider.properties[:chain] != chain) }
|
||||
|
||||
# Remove rules which match our ignore filter
|
||||
rules_resources.delete_if {|res| value(:ignore).find_index{|f| res.provider.properties[:line].match(f)}} if value(:ignore)
|
||||
|
||||
# We mark all remaining rules for deletion, and then let the catalog override us on rules which should be present
|
||||
rules_resources.each {|res| res[:ensure] = :absent}
|
||||
|
||||
rules_resources
|
||||
end
|
||||
end
|
225
modules/firewall/lib/puppet/util/firewall.rb
Normal file
225
modules/firewall/lib/puppet/util/firewall.rb
Normal file
|
@ -0,0 +1,225 @@
|
|||
require 'socket'
|
||||
require 'resolv'
|
||||
require 'puppet/util/ipcidr'
|
||||
|
||||
# Util module for puppetlabs-firewall
|
||||
module Puppet::Util::Firewall
|
||||
# Translate the symbolic names for icmp packet types to integers
|
||||
def icmp_name_to_number(value_icmp, protocol)
|
||||
if value_icmp =~ /\d{1,2}$/
|
||||
value_icmp
|
||||
elsif protocol == 'inet'
|
||||
case value_icmp
|
||||
when "echo-reply" then "0"
|
||||
when "destination-unreachable" then "3"
|
||||
when "source-quench" then "4"
|
||||
when "redirect" then "6"
|
||||
when "echo-request" then "8"
|
||||
when "router-advertisement" then "9"
|
||||
when "router-solicitation" then "10"
|
||||
when "time-exceeded" then "11"
|
||||
when "parameter-problem" then "12"
|
||||
when "timestamp-request" then "13"
|
||||
when "timestamp-reply" then "14"
|
||||
when "address-mask-request" then "17"
|
||||
when "address-mask-reply" then "18"
|
||||
else nil
|
||||
end
|
||||
elsif protocol == 'inet6'
|
||||
case value_icmp
|
||||
when "destination-unreachable" then "1"
|
||||
when "time-exceeded" then "3"
|
||||
when "parameter-problem" then "4"
|
||||
when "echo-request" then "128"
|
||||
when "echo-reply" then "129"
|
||||
when "router-solicitation" then "133"
|
||||
when "router-advertisement" then "134"
|
||||
when "redirect" then "137"
|
||||
else nil
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "unsupported protocol family '#{protocol}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Convert log_level names to their respective numbers
|
||||
def log_level_name_to_number(value)
|
||||
#TODO make this 0-7 only
|
||||
if value =~ /\d/
|
||||
value
|
||||
else
|
||||
case value
|
||||
when "panic" then "0"
|
||||
when "alert" then "1"
|
||||
when "crit" then "2"
|
||||
when "err" then "3"
|
||||
when "error" then "3"
|
||||
when "warn" then "4"
|
||||
when "warning" then "4"
|
||||
when "not" then "5"
|
||||
when "notice" then "5"
|
||||
when "info" then "6"
|
||||
when "debug" then "7"
|
||||
else nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This method takes a string and a protocol and attempts to convert
|
||||
# it to a port number if valid.
|
||||
#
|
||||
# If the string already contains a port number or perhaps a range of ports
|
||||
# in the format 22:1000 for example, it simply returns the string and does
|
||||
# nothing.
|
||||
def string_to_port(value, proto)
|
||||
proto = proto.to_s
|
||||
unless proto =~ /^(tcp|udp)$/
|
||||
proto = 'tcp'
|
||||
end
|
||||
|
||||
if value.kind_of?(String)
|
||||
if value.match(/^\d+(-\d+)?$/)
|
||||
return value
|
||||
else
|
||||
return Socket.getservbyname(value, proto).to_s
|
||||
end
|
||||
else
|
||||
Socket.getservbyname(value.to_s, proto).to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Takes an address and returns it in CIDR notation.
|
||||
#
|
||||
# If the address is:
|
||||
#
|
||||
# - A hostname:
|
||||
# It will be resolved
|
||||
# - An IPv4 address:
|
||||
# It will be qualified with a /32 CIDR notation
|
||||
# - An IPv6 address:
|
||||
# It will be qualified with a /128 CIDR notation
|
||||
# - An IP address with a CIDR notation:
|
||||
# It will be normalised
|
||||
# - An IP address with a dotted-quad netmask:
|
||||
# It will be converted to CIDR notation
|
||||
# - Any address with a resulting prefix length of zero:
|
||||
# It will return nil which is equivilent to not specifying an address
|
||||
#
|
||||
def host_to_ip(value)
|
||||
begin
|
||||
value = Puppet::Util::IPCidr.new(value)
|
||||
rescue
|
||||
value = Puppet::Util::IPCidr.new(Resolv.getaddress(value))
|
||||
end
|
||||
|
||||
return nil if value.prefixlen == 0
|
||||
value.cidr
|
||||
end
|
||||
|
||||
# Takes an address mask and converts the host portion to CIDR notation.
|
||||
#
|
||||
# This takes into account you can negate a mask but follows all rules
|
||||
# defined in host_to_ip for the host/address part.
|
||||
#
|
||||
def host_to_mask(value)
|
||||
match = value.match /(!)\s?(.*)$/
|
||||
return host_to_ip(value) unless match
|
||||
|
||||
cidr = host_to_ip(match[2])
|
||||
return nil if cidr == nil
|
||||
"#{match[1]} #{cidr}"
|
||||
end
|
||||
|
||||
# Validates the argument is int or hex, and returns valid hex
|
||||
# conversion of the value or nil otherwise.
|
||||
def to_hex32(value)
|
||||
begin
|
||||
value = Integer(value)
|
||||
if value.between?(0, 0xffffffff)
|
||||
return '0x' + value.to_s(16)
|
||||
end
|
||||
rescue ArgumentError
|
||||
# pass
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def persist_iptables(proto)
|
||||
debug("[persist_iptables]")
|
||||
|
||||
# Basic normalisation for older Facter
|
||||
os_key = Facter.value(:osfamily)
|
||||
os_key ||= case Facter.value(:operatingsystem)
|
||||
when 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer'
|
||||
'RedHat'
|
||||
when 'Debian', 'Ubuntu'
|
||||
'Debian'
|
||||
else
|
||||
Facter.value(:operatingsystem)
|
||||
end
|
||||
|
||||
# Older iptables-persistent doesn't provide save action.
|
||||
if os_key == 'Debian'
|
||||
persist_ver = Facter.value(:iptables_persistent_version)
|
||||
if (persist_ver and Puppet::Util::Package.versioncmp(persist_ver, '0.5.0') < 0)
|
||||
os_key = 'Debian_manual'
|
||||
end
|
||||
end
|
||||
|
||||
# Fedora 15 and newer use systemd to persist iptable rules
|
||||
if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'Fedora' && Facter.value(:operatingsystemrelease).to_i >= 15
|
||||
os_key = 'Fedora'
|
||||
end
|
||||
|
||||
# RHEL 7 and newer also use systemd to persist iptable rules
|
||||
if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'RedHat' && Facter.value(:operatingsystemrelease).to_i >= 7
|
||||
os_key = 'Fedora'
|
||||
end
|
||||
|
||||
cmd = case os_key.to_sym
|
||||
when :RedHat
|
||||
case proto.to_sym
|
||||
when :IPv4
|
||||
%w{/sbin/service iptables save}
|
||||
when :IPv6
|
||||
%w{/sbin/service ip6tables save}
|
||||
end
|
||||
when :Fedora
|
||||
case proto.to_sym
|
||||
when :IPv4
|
||||
%w{/usr/libexec/iptables/iptables.init save}
|
||||
when :IPv6
|
||||
%w{/usr/libexec/iptables/ip6tables.init save}
|
||||
end
|
||||
when :Debian
|
||||
case proto.to_sym
|
||||
when :IPv4, :IPv6
|
||||
%w{/usr/sbin/service iptables-persistent save}
|
||||
end
|
||||
when :Debian_manual
|
||||
case proto.to_sym
|
||||
when :IPv4
|
||||
["/bin/sh", "-c", "/sbin/iptables-save > /etc/iptables/rules"]
|
||||
end
|
||||
when :Archlinux
|
||||
case proto.to_sym
|
||||
when :IPv4
|
||||
["/bin/sh", "-c", "/usr/sbin/iptables-save > /etc/iptables/iptables.rules"]
|
||||
when :IPv6
|
||||
["/bin/sh", "-c", "/usr/sbin/ip6tables-save > /etc/iptables/ip6tables.rules"]
|
||||
end
|
||||
end
|
||||
|
||||
# Catch unsupported OSs from the case statement above.
|
||||
if cmd.nil?
|
||||
debug('firewall: Rule persistence is not supported for this type/OS')
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
execute(cmd)
|
||||
rescue Puppet::ExecutionFailure => detail
|
||||
warning("Unable to persist firewall rules: #{detail}")
|
||||
end
|
||||
end
|
||||
end
|
42
modules/firewall/lib/puppet/util/ipcidr.rb
Normal file
42
modules/firewall/lib/puppet/util/ipcidr.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
require 'ipaddr'
|
||||
|
||||
# IPCidr object wrapper for IPAddr
|
||||
module Puppet
|
||||
module Util
|
||||
class IPCidr < IPAddr
|
||||
def initialize(ipaddr)
|
||||
begin
|
||||
super(ipaddr)
|
||||
rescue ArgumentError => e
|
||||
if e.message =~ /invalid address/
|
||||
raise ArgumentError, "Invalid address from IPAddr.new: #{ipaddr}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def netmask
|
||||
_to_string(@mask_addr)
|
||||
end
|
||||
|
||||
def prefixlen
|
||||
m = case @family
|
||||
when Socket::AF_INET
|
||||
IN4MASK
|
||||
when Socket::AF_INET6
|
||||
IN6MASK
|
||||
else
|
||||
raise "unsupported address family"
|
||||
end
|
||||
return $1.length if /\A(1*)(0*)\z/ =~ (@mask_addr & m).to_s(2)
|
||||
raise "bad addr_mask format"
|
||||
end
|
||||
|
||||
def cidr
|
||||
cidr = sprintf("%s/%s", self.to_s, self.prefixlen)
|
||||
cidr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
modules/firewall/manifests/init.pp
Normal file
36
modules/firewall/manifests/init.pp
Normal file
|
@ -0,0 +1,36 @@
|
|||
# = Class: firewall
|
||||
#
|
||||
# Manages packages and services required by the firewall type/provider.
|
||||
#
|
||||
# This class includes the appropriate sub-class for your operating system,
|
||||
# where supported.
|
||||
#
|
||||
# == Parameters:
|
||||
#
|
||||
# [*ensure*]
|
||||
# Ensure parameter passed onto Service[] resources.
|
||||
# Default: running
|
||||
#
|
||||
class firewall (
|
||||
$ensure = running
|
||||
) {
|
||||
case $ensure {
|
||||
/^(running|stopped)$/: {
|
||||
# Do nothing.
|
||||
}
|
||||
default: {
|
||||
fail("${title}: Ensure value '${ensure}' is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
case $::kernel {
|
||||
'Linux': {
|
||||
class { "${title}::linux":
|
||||
ensure => $ensure,
|
||||
}
|
||||
}
|
||||
default: {
|
||||
fail("${title}: Kernel '${::kernel}' is not currently supported")
|
||||
}
|
||||
}
|
||||
}
|
51
modules/firewall/manifests/linux.pp
Normal file
51
modules/firewall/manifests/linux.pp
Normal file
|
@ -0,0 +1,51 @@
|
|||
# = Class: firewall::linux
|
||||
#
|
||||
# Installs the `iptables` package for Linux operating systems and includes
|
||||
# the appropriate sub-class for any distribution specific services and
|
||||
# additional packages.
|
||||
#
|
||||
# == Parameters:
|
||||
#
|
||||
# [*ensure*]
|
||||
# Ensure parameter passed onto Service[] resources. When `running` the
|
||||
# service will be started on boot, and when `stopped` it will not.
|
||||
# Default: running
|
||||
#
|
||||
class firewall::linux (
|
||||
$ensure = running
|
||||
) {
|
||||
$enable = $ensure ? {
|
||||
running => true,
|
||||
stopped => false,
|
||||
}
|
||||
|
||||
package { 'iptables':
|
||||
ensure => present,
|
||||
}
|
||||
|
||||
case $::operatingsystem {
|
||||
'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos',
|
||||
'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': {
|
||||
class { "${title}::redhat":
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
require => Package['iptables'],
|
||||
}
|
||||
}
|
||||
'Debian', 'Ubuntu': {
|
||||
class { "${title}::debian":
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
require => Package['iptables'],
|
||||
}
|
||||
}
|
||||
'Archlinux': {
|
||||
class { "${title}::archlinux":
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
require => Package['iptables'],
|
||||
}
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
}
|
41
modules/firewall/manifests/linux/archlinux.pp
Normal file
41
modules/firewall/manifests/linux/archlinux.pp
Normal file
|
@ -0,0 +1,41 @@
|
|||
# = Class: firewall::linux::archlinux
|
||||
#
|
||||
# Manages `iptables` and `ip6tables` services, and creates files used for
|
||||
# persistence, on Arch Linux systems.
|
||||
#
|
||||
# == Parameters:
|
||||
#
|
||||
# [*ensure*]
|
||||
# Ensure parameter passed onto Service[] resources.
|
||||
# Default: running
|
||||
#
|
||||
# [*enable*]
|
||||
# Enable parameter passed onto Service[] resources.
|
||||
# Default: true
|
||||
#
|
||||
class firewall::linux::archlinux (
|
||||
$ensure = 'running',
|
||||
$enable = true
|
||||
) {
|
||||
service { 'iptables':
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
hasstatus => true,
|
||||
}
|
||||
|
||||
service { 'ip6tables':
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
hasstatus => true,
|
||||
}
|
||||
|
||||
file { '/etc/iptables/iptables.rules':
|
||||
ensure => present,
|
||||
before => Service['iptables'],
|
||||
}
|
||||
|
||||
file { '/etc/iptables/ip6tables.rules':
|
||||
ensure => present,
|
||||
before => Service['ip6tables'],
|
||||
}
|
||||
}
|
44
modules/firewall/manifests/linux/debian.pp
Normal file
44
modules/firewall/manifests/linux/debian.pp
Normal file
|
@ -0,0 +1,44 @@
|
|||
# = Class: firewall::linux::debian
|
||||
#
|
||||
# Installs the `iptables-persistent` package for Debian-alike systems. This
|
||||
# allows rules to be stored to file and restored on boot.
|
||||
#
|
||||
# == Parameters:
|
||||
#
|
||||
# [*ensure*]
|
||||
# Ensure parameter passed onto Service[] resources.
|
||||
# Default: running
|
||||
#
|
||||
# [*enable*]
|
||||
# Enable parameter passed onto Service[] resources.
|
||||
# Default: true
|
||||
#
|
||||
class firewall::linux::debian (
|
||||
$ensure = running,
|
||||
$enable = true
|
||||
) {
|
||||
package { 'iptables-persistent':
|
||||
ensure => present,
|
||||
}
|
||||
|
||||
if($::operatingsystemrelease =~ /^6\./ and $enable == true
|
||||
and versioncmp($::iptables_persistent_version, '0.5.0') < 0 ) {
|
||||
# This fixes a bug in the iptables-persistent LSB headers in 6.x, without it
|
||||
# we lose idempotency
|
||||
exec { 'iptables-persistent-enable':
|
||||
logoutput => on_failure,
|
||||
command => '/usr/sbin/update-rc.d iptables-persistent enable',
|
||||
unless => '/usr/bin/test -f /etc/rcS.d/S*iptables-persistent',
|
||||
require => Package['iptables-persistent'],
|
||||
}
|
||||
} else {
|
||||
# This isn't a real service/daemon. The start action loads rules, so just
|
||||
# needs to be called on system boot.
|
||||
service { 'iptables-persistent':
|
||||
ensure => undef,
|
||||
enable => $enable,
|
||||
hasstatus => true,
|
||||
require => Package['iptables-persistent'],
|
||||
}
|
||||
}
|
||||
}
|
40
modules/firewall/manifests/linux/redhat.pp
Normal file
40
modules/firewall/manifests/linux/redhat.pp
Normal file
|
@ -0,0 +1,40 @@
|
|||
# = Class: firewall::linux::redhat
|
||||
#
|
||||
# Manages the `iptables` service on RedHat-alike systems.
|
||||
#
|
||||
# == Parameters:
|
||||
#
|
||||
# [*ensure*]
|
||||
# Ensure parameter passed onto Service[] resources.
|
||||
# Default: running
|
||||
#
|
||||
# [*enable*]
|
||||
# Enable parameter passed onto Service[] resources.
|
||||
# Default: true
|
||||
#
|
||||
class firewall::linux::redhat (
|
||||
$ensure = running,
|
||||
$enable = true
|
||||
) {
|
||||
|
||||
# RHEL 7 and later and Fedora 15 and later require the iptables-services
|
||||
# package, which provides the /usr/libexec/iptables/iptables.init used by
|
||||
# lib/puppet/util/firewall.rb.
|
||||
if $::operatingsystem == RedHat and $::operatingsystemrelease >= 7 {
|
||||
package { 'iptables-services':
|
||||
ensure => present,
|
||||
}
|
||||
}
|
||||
|
||||
if ($::operatingsystem == 'Fedora' and (( $::operatingsystemrelease =~ /^\d+/ and $::operatingsystemrelease >= 15 ) or $::operatingsystemrelease == "Rawhide")) {
|
||||
package { 'iptables-services':
|
||||
ensure => present,
|
||||
}
|
||||
}
|
||||
|
||||
service { 'iptables':
|
||||
ensure => $ensure,
|
||||
enable => $enable,
|
||||
hasstatus => true,
|
||||
}
|
||||
}
|
79
modules/firewall/metadata.json
Normal file
79
modules/firewall/metadata.json
Normal file
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"name": "puppetlabs-firewall",
|
||||
"version": "1.1.2",
|
||||
"author": "puppetlabs",
|
||||
"summary": "Firewall Module",
|
||||
"license": "ASL 2.0",
|
||||
"source": "git://github.com/puppetlabs/puppetlabs-firewall.git",
|
||||
"project_page": "http://forge.puppetlabs.com/puppetlabs/firewall",
|
||||
"issues_url": "https://github.com/puppetlabs/puppetlabs-firewall/issues",
|
||||
"operatingsystem_support": [
|
||||
{
|
||||
"operatingsystem": "RedHat",
|
||||
"operatingsystemrelease": [
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "CentOS",
|
||||
"operatingsystemrelease": [
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "OracleLinux",
|
||||
"operatingsystemrelease": [
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "Scientific",
|
||||
"operatingsystemrelease": [
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "SLES",
|
||||
"operatingsystemrelease": [
|
||||
"11 SP1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "Debian",
|
||||
"operatingsystemrelease": [
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"operatingsystem": "Ubuntu",
|
||||
"operatingsystemrelease": [
|
||||
"10.04",
|
||||
"12.04",
|
||||
"14.04"
|
||||
]
|
||||
}
|
||||
],
|
||||
"requirements": [
|
||||
{
|
||||
"name": "pe",
|
||||
"version_requirement": ">= 3.2.0 < 3.4.0"
|
||||
},
|
||||
{
|
||||
"name": "puppet",
|
||||
"version_requirement": "3.x"
|
||||
}
|
||||
],
|
||||
"description": "Manages Firewalls such as iptables",
|
||||
"dependencies": [
|
||||
|
||||
]
|
||||
}
|
77
modules/firewall/spec/acceptance/change_source_spec.rb
Normal file
77
modules/firewall/spec/acceptance/change_source_spec.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
describe 'reset' do
|
||||
it 'deletes all rules' do
|
||||
shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when unmanaged rules exist' do
|
||||
it 'applies with 8.0.0.1 first' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '101 test source changes':
|
||||
proto => tcp,
|
||||
port => '101',
|
||||
action => accept,
|
||||
source => '8.0.0.1',
|
||||
}
|
||||
firewall { '100 test source static':
|
||||
proto => tcp,
|
||||
port => '100',
|
||||
action => accept,
|
||||
source => '8.0.0.2',
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
end
|
||||
|
||||
it 'adds a unmanaged rule without a comment' do
|
||||
shell('iptables -A INPUT -t filter -s 8.0.0.3/32 -p tcp -m multiport --ports 102 -j ACCEPT')
|
||||
expect(shell('iptables-save').stdout).to match(/-A INPUT -s 8\.0\.0\.3(\/32)? -p tcp -m multiport --ports 102 -j ACCEPT/)
|
||||
end
|
||||
|
||||
it 'contains the changable 8.0.0.1 rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.1(\/32)? -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/)
|
||||
end
|
||||
end
|
||||
it 'contains the static 8.0.0.2 rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2(\/32)? -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/)
|
||||
end
|
||||
end
|
||||
|
||||
it 'changes to 8.0.0.4 second' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '101 test source changes':
|
||||
proto => tcp,
|
||||
port => '101',
|
||||
action => accept,
|
||||
source => '8.0.0.4',
|
||||
}
|
||||
EOS
|
||||
|
||||
expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/Notice: \/Stage\[main\]\/Main\/Firewall\[101 test source changes\]\/source: source changed '8\.0\.0\.1\/32' to '8\.0\.0\.4\/32'/)
|
||||
end
|
||||
|
||||
it 'does not contain the old changing 8.0.0.1 rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to_not match(/8\.0\.0\.1/)
|
||||
end
|
||||
end
|
||||
it 'contains the staic 8.0.0.2 rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2(\/32)? -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/)
|
||||
end
|
||||
end
|
||||
it 'contains the changing new 8.0.0.4 rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.4(\/32)? -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
modules/firewall/spec/acceptance/class_spec.rb
Normal file
27
modules/firewall/spec/acceptance/class_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe "firewall class:", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
it 'should run successfully' do
|
||||
pp = "class { 'firewall': }"
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'ensure => stopped:' do
|
||||
pp = "class { 'firewall': ensure => stopped }"
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'ensure => running:' do
|
||||
pp = "class { 'firewall': ensure => running }"
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
end
|
55
modules/firewall/spec/acceptance/connlimit_spec.rb
Normal file
55
modules/firewall/spec/acceptance/connlimit_spec.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
|
||||
describe 'connlimit_above' do
|
||||
context '10' do
|
||||
it 'applies' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '500 - test':
|
||||
proto => tcp,
|
||||
dport => '22',
|
||||
connlimit_above => '10',
|
||||
action => reject,
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
end
|
||||
|
||||
it 'should contain the rule' do
|
||||
shell('iptables-save') do |r|
|
||||
#connlimit-saddr is added in Ubuntu 14.04.
|
||||
expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "500 - test" -m connlimit --connlimit-above 10 --connlimit-mask 32 (--connlimit-saddr )?-j REJECT --reject-with icmp-port-unreachable/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'connlimit_mask' do
|
||||
context '24' do
|
||||
it 'applies' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '501 - test':
|
||||
proto => tcp,
|
||||
dport => '22',
|
||||
connlimit_above => '10',
|
||||
connlimit_mask => '24',
|
||||
action => reject,
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
end
|
||||
|
||||
it 'should contain the rule' do
|
||||
shell('iptables-save') do |r|
|
||||
#connlimit-saddr is added in Ubuntu 14.04.
|
||||
expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "501 - test" -m connlimit --connlimit-above 10 --connlimit-mask 24 (--connlimit-saddr )?-j REJECT --reject-with icmp-port-unreachable/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
modules/firewall/spec/acceptance/connmark_spec.rb
Normal file
27
modules/firewall/spec/acceptance/connmark_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
|
||||
describe 'connmark' do
|
||||
context '50' do
|
||||
it 'applies' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '502 - test':
|
||||
proto => 'all',
|
||||
connmark => '0x1',
|
||||
action => reject,
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
end
|
||||
|
||||
it 'should contain the rule' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/-A INPUT -m comment --comment "502 - test" -m connmark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1618
modules/firewall/spec/acceptance/firewall_spec.rb
Normal file
1618
modules/firewall/spec/acceptance/firewall_spec.rb
Normal file
File diff suppressed because it is too large
Load diff
125
modules/firewall/spec/acceptance/firewallchain_spec.rb
Normal file
125
modules/firewall/spec/acceptance/firewallchain_spec.rb
Normal file
|
@ -0,0 +1,125 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'puppet resource firewallchain command:', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
describe 'ensure' do
|
||||
context 'present' do
|
||||
it 'applies cleanly' do
|
||||
pp = <<-EOS
|
||||
firewallchain { 'MY_CHAIN:filter:IPv4':
|
||||
ensure => present,
|
||||
}
|
||||
EOS
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'finds the chain' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/MY_CHAIN/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'absent' do
|
||||
it 'applies cleanly' do
|
||||
pp = <<-EOS
|
||||
firewallchain { 'MY_CHAIN:filter:IPv4':
|
||||
ensure => absent,
|
||||
}
|
||||
EOS
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'fails to find the chain' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to_not match(/MY_CHAIN/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# XXX purge => false is not yet implemented
|
||||
#context 'adding a firewall rule to a chain:' do
|
||||
# it 'applies cleanly' do
|
||||
# pp = <<-EOS
|
||||
# firewallchain { 'MY_CHAIN:filter:IPv4':
|
||||
# ensure => present,
|
||||
# }
|
||||
# firewall { '100 my rule':
|
||||
# chain => 'MY_CHAIN',
|
||||
# action => 'accept',
|
||||
# proto => 'tcp',
|
||||
# dport => 5000,
|
||||
# }
|
||||
# EOS
|
||||
# # Run it twice and test for idempotency
|
||||
# apply_manifest(pp, :catch_failures => true)
|
||||
# apply_manifest(pp, :catch_changes => true)
|
||||
# end
|
||||
#end
|
||||
|
||||
#context 'not purge firewallchain chains:' do
|
||||
# it 'does not purge the rule' do
|
||||
# pp = <<-EOS
|
||||
# firewallchain { 'MY_CHAIN:filter:IPv4':
|
||||
# ensure => present,
|
||||
# purge => false,
|
||||
# before => Resources['firewall'],
|
||||
# }
|
||||
# resources { 'firewall':
|
||||
# purge => true,
|
||||
# }
|
||||
# EOS
|
||||
# # Run it twice and test for idempotency
|
||||
# apply_manifest(pp, :catch_failures => true) do |r|
|
||||
# expect(r.stdout).to_not match(/removed/)
|
||||
# expect(r.stderr).to eq('')
|
||||
# end
|
||||
# apply_manifest(pp, :catch_changes => true)
|
||||
# end
|
||||
|
||||
# it 'still has the rule' do
|
||||
# pp = <<-EOS
|
||||
# firewall { '100 my rule':
|
||||
# chain => 'MY_CHAIN',
|
||||
# action => 'accept',
|
||||
# proto => 'tcp',
|
||||
# dport => 5000,
|
||||
# }
|
||||
# EOS
|
||||
# # Run it twice and test for idempotency
|
||||
# apply_manifest(pp, :catch_changes => true)
|
||||
# end
|
||||
#end
|
||||
|
||||
describe 'policy' do
|
||||
after :all do
|
||||
shell('iptables -t filter -P FORWARD ACCEPT')
|
||||
end
|
||||
|
||||
context 'DROP' do
|
||||
it 'applies cleanly' do
|
||||
pp = <<-EOS
|
||||
firewallchain { 'FORWARD:filter:IPv4':
|
||||
policy => 'drop',
|
||||
}
|
||||
EOS
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'finds the chain' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/FORWARD DROP/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
114
modules/firewall/spec/acceptance/ip6_fragment_spec.rb
Normal file
114
modules/firewall/spec/acceptance/ip6_fragment_spec.rb
Normal file
|
@ -0,0 +1,114 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
if default['platform'] =~ /el-5/
|
||||
describe "firewall ip6tables doesn't work on 1.3.5 because --comment is missing", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
before :all do
|
||||
ip6tables_flush_all_tables
|
||||
end
|
||||
|
||||
it "can't use ip6tables" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '599 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
provider => 'ip6tables',
|
||||
}
|
||||
EOS
|
||||
expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/ip6tables provider is not supported/)
|
||||
end
|
||||
end
|
||||
else
|
||||
describe 'firewall ishasmorefrags/islastfrag/isfirstfrag properties', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
before :all do
|
||||
ip6tables_flush_all_tables
|
||||
end
|
||||
|
||||
shared_examples "is idempotent" do |values, line_match|
|
||||
it "changes the values to #{values}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '599 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
provider => 'ip6tables',
|
||||
#{values}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('ip6tables-save') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
shared_examples "doesn't change" do |values, line_match|
|
||||
it "doesn't change the values to #{values}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '599 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
provider => 'ip6tables',
|
||||
#{values}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('ip6tables-save') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'adding a rule' do
|
||||
context 'when unset' do
|
||||
before :all do
|
||||
ip6tables_flush_all_tables
|
||||
end
|
||||
it_behaves_like 'is idempotent', '', /-A INPUT -p tcp -m comment --comment "599 - test"/
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :all do
|
||||
ip6tables_flush_all_tables
|
||||
end
|
||||
it_behaves_like "is idempotent", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/
|
||||
end
|
||||
context 'when set to false' do
|
||||
before :all do
|
||||
ip6tables_flush_all_tables
|
||||
end
|
||||
it_behaves_like "is idempotent", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/
|
||||
end
|
||||
end
|
||||
describe 'editing a rule' do
|
||||
context 'when unset or false' do
|
||||
before :each do
|
||||
ip6tables_flush_all_tables
|
||||
shell('ip6tables -A INPUT -p tcp -m comment --comment "599 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "doesn't change", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "is idempotent", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/
|
||||
end
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :each do
|
||||
ip6tables_flush_all_tables
|
||||
shell('ip6tables -A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "is idempotent", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "doesn't change", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
92
modules/firewall/spec/acceptance/isfragment_spec.rb
Normal file
92
modules/firewall/spec/acceptance/isfragment_spec.rb
Normal file
|
@ -0,0 +1,92 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'firewall isfragment property', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
|
||||
shared_examples "is idempotent" do |value, line_match|
|
||||
it "changes the value to #{value}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '597 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
#{value}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
shared_examples "doesn't change" do |value, line_match|
|
||||
it "doesn't change the value to #{value}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '597 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
#{value}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'adding a rule' do
|
||||
context 'when unset' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like 'is idempotent', '', /-A INPUT -p tcp -m comment --comment "597 - test"/
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like 'is idempotent', 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/
|
||||
end
|
||||
context 'when set to false' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like "is idempotent", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/
|
||||
end
|
||||
end
|
||||
describe 'editing a rule' do
|
||||
context 'when unset or false' do
|
||||
before :each do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -A INPUT -p tcp -m comment --comment "597 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "doesn't change", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "is idempotent", 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/
|
||||
end
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :each do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -A INPUT -p tcp -f -m comment --comment "597 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "is idempotent", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "doesn't change", 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
HOSTS:
|
||||
centos-59-x64:
|
||||
roles:
|
||||
- master
|
||||
- database
|
||||
- console
|
||||
platform: el-5-x86_64
|
||||
box : centos-59-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: pe
|
10
modules/firewall/spec/acceptance/nodesets/centos-59-x64.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/centos-59-x64.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
centos-59-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: el-5-x86_64
|
||||
box : centos-59-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: foss
|
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
centos-64-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: el-6-x86_64
|
||||
box : centos-64-x64-fusion503-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-fusion503-nocm.box
|
||||
hypervisor : fusion
|
||||
CONFIG:
|
||||
type: foss
|
|
@ -0,0 +1,12 @@
|
|||
HOSTS:
|
||||
centos-64-x64:
|
||||
roles:
|
||||
- master
|
||||
- database
|
||||
- dashboard
|
||||
platform: el-6-x86_64
|
||||
box : centos-64-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: pe
|
10
modules/firewall/spec/acceptance/nodesets/centos-64-x64.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/centos-64-x64.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
centos-64-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: el-6-x86_64
|
||||
box : centos-64-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: foss
|
10
modules/firewall/spec/acceptance/nodesets/debian-607-x64.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/debian-607-x64.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
debian-607-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: debian-6-amd64
|
||||
box : debian-607-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: git
|
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
debian-70rc1-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: debian-7-amd64
|
||||
box : debian-70rc1-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: git
|
10
modules/firewall/spec/acceptance/nodesets/default.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/default.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
centos-64-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: el-6-x86_64
|
||||
box : centos-64-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: foss
|
10
modules/firewall/spec/acceptance/nodesets/fedora-18-x64.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/fedora-18-x64.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
fedora-18-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: fedora-18-x86_64
|
||||
box : fedora-18-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: git
|
10
modules/firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml
Normal file
10
modules/firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
sles-11sp1-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: sles-11-x86_64
|
||||
box : sles-11sp1-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: git
|
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
ubuntu-server-10044-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: ubuntu-10.04-amd64
|
||||
box : ubuntu-server-10044-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: git
|
|
@ -0,0 +1,10 @@
|
|||
HOSTS:
|
||||
ubuntu-server-12042-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: ubuntu-12.04-amd64
|
||||
box : ubuntu-server-12042-x64-vbox4210-nocm
|
||||
box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: foss
|
|
@ -0,0 +1,9 @@
|
|||
HOSTS:
|
||||
ubuntu-server-1404-x64:
|
||||
roles:
|
||||
- master
|
||||
platform: ubuntu-14.04-64
|
||||
box: puppetlabs/ubuntu-14.04-64-nocm
|
||||
hypervisor : vagrant
|
||||
CONFIG:
|
||||
type: foss
|
154
modules/firewall/spec/acceptance/params_spec.rb
Normal file
154
modules/firewall/spec/acceptance/params_spec.rb
Normal file
|
@ -0,0 +1,154 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe "param based tests:", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
# Takes a hash and converts it into a firewall resource
|
||||
def pp(params)
|
||||
name = params.delete('name') || '100 test'
|
||||
pm = <<-EOS
|
||||
firewall { '#{name}':
|
||||
EOS
|
||||
|
||||
params.each do |k,v|
|
||||
pm += <<-EOS
|
||||
#{k} => #{v},
|
||||
EOS
|
||||
end
|
||||
|
||||
pm += <<-EOS
|
||||
}
|
||||
EOS
|
||||
pm
|
||||
end
|
||||
|
||||
it 'test various params', :unless => (default['platform'].match(/el-5/) || fact('operatingsystem') == 'SLES') do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm = pp({
|
||||
'table' => "'raw'",
|
||||
'socket' => 'true',
|
||||
'chain' => "'PREROUTING'",
|
||||
'jump' => 'LOG',
|
||||
'log_level' => 'debug',
|
||||
})
|
||||
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'test log rule' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm = pp({
|
||||
'name' => '998 log all',
|
||||
'proto' => 'all',
|
||||
'jump' => 'LOG',
|
||||
'log_level' => 'debug',
|
||||
})
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'test log rule - changing names' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm1 = pp({
|
||||
'name' => '004 log all INVALID packets',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
'ctstate' => 'INVALID',
|
||||
'jump' => 'LOG',
|
||||
'log_level' => '3',
|
||||
'log_prefix' => '"IPTABLES dropped invalid: "',
|
||||
})
|
||||
|
||||
ppm2 = pp({
|
||||
'name' => '003 log all INVALID packets',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
'ctstate' => 'INVALID',
|
||||
'jump' => 'LOG',
|
||||
'log_level' => '3',
|
||||
'log_prefix' => '"IPTABLES dropped invalid: "',
|
||||
})
|
||||
|
||||
expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2)
|
||||
|
||||
ppm = <<-EOS + "\n" + ppm2
|
||||
resources { 'firewall':
|
||||
purge => true,
|
||||
}
|
||||
EOS
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
|
||||
end
|
||||
|
||||
it 'test chain - changing names' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm1 = pp({
|
||||
'name' => '004 with a chain',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
})
|
||||
|
||||
ppm2 = pp({
|
||||
'name' => '004 with a chain',
|
||||
'chain' => 'OUTPUT',
|
||||
'proto' => 'all',
|
||||
})
|
||||
|
||||
apply_manifest(ppm1, :expect_changes => true)
|
||||
|
||||
ppm = <<-EOS + "\n" + ppm2
|
||||
resources { 'firewall':
|
||||
purge => true,
|
||||
}
|
||||
EOS
|
||||
expect(apply_manifest(ppm2, :expect_failures => true).stderr).to match(/is not supported/)
|
||||
end
|
||||
|
||||
it 'test log rule - idempotent' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm1 = pp({
|
||||
'name' => '004 log all INVALID packets',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
'ctstate' => 'INVALID',
|
||||
'jump' => 'LOG',
|
||||
'log_level' => '3',
|
||||
'log_prefix' => '"IPTABLES dropped invalid: "',
|
||||
})
|
||||
|
||||
expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2)
|
||||
expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'test src_range rule' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm = pp({
|
||||
'name' => '997 block src ip range',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
'action' => 'drop',
|
||||
'src_range' => '"10.0.0.1-10.0.0.10"',
|
||||
})
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'test dst_range rule' do
|
||||
iptables_flush_all_tables
|
||||
|
||||
ppm = pp({
|
||||
'name' => '998 block dst ip range',
|
||||
'chain' => 'INPUT',
|
||||
'proto' => 'all',
|
||||
'action' => 'drop',
|
||||
'dst_range' => '"10.0.0.2-10.0.0.20"',
|
||||
})
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2)
|
||||
expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
end
|
124
modules/firewall/spec/acceptance/purge_spec.rb
Normal file
124
modules/firewall/spec/acceptance/purge_spec.rb
Normal file
|
@ -0,0 +1,124 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe "purge tests:", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
context('resources purge') do
|
||||
before(:all) do
|
||||
iptables_flush_all_tables
|
||||
|
||||
shell('iptables -A INPUT -s 1.2.1.2')
|
||||
shell('iptables -A INPUT -s 1.2.1.2')
|
||||
end
|
||||
|
||||
it 'make sure duplicate existing rules get purged' do
|
||||
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
resources { 'firewall':
|
||||
purge => true,
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :expect_changes => true)
|
||||
end
|
||||
|
||||
it 'saves' do
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to_not match(/1\.2\.1\.2/)
|
||||
expect(r.stderr).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context('chain purge') do
|
||||
before(:each) do
|
||||
iptables_flush_all_tables
|
||||
|
||||
shell('iptables -A INPUT -p tcp -s 1.2.1.1')
|
||||
shell('iptables -A INPUT -p udp -s 1.2.1.1')
|
||||
shell('iptables -A OUTPUT -s 1.2.1.2 -m comment --comment "010 output-1.2.1.2"')
|
||||
end
|
||||
|
||||
it 'purges only the specified chain' do
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
firewallchain { 'INPUT:filter:IPv4':
|
||||
purge => true,
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :expect_changes => true)
|
||||
|
||||
shell('iptables-save') do |r|
|
||||
expect(r.stdout).to match(/010 output-1\.2\.1\.2/)
|
||||
expect(r.stdout).to_not match(/1\.2\.1\.1/)
|
||||
expect(r.stderr).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
it 'ignores managed rules' do
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
firewallchain { 'OUTPUT:filter:IPv4':
|
||||
purge => true,
|
||||
}
|
||||
firewall { '010 output-1.2.1.2':
|
||||
chain => 'OUTPUT',
|
||||
proto => 'all',
|
||||
source => '1.2.1.2',
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'ignores specified rules' do
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
firewallchain { 'INPUT:filter:IPv4':
|
||||
purge => true,
|
||||
ignore => [
|
||||
'-s 1\.2\.1\.1',
|
||||
],
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'adds managed rules with ignored rules' do
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
firewallchain { 'INPUT:filter:IPv4':
|
||||
purge => true,
|
||||
ignore => [
|
||||
'-s 1\.2\.1\.1',
|
||||
],
|
||||
}
|
||||
firewall { '014 input-1.2.1.6':
|
||||
chain => 'INPUT',
|
||||
proto => 'all',
|
||||
source => '1.2.1.6',
|
||||
}
|
||||
-> firewall { '013 input-1.2.1.5':
|
||||
chain => 'INPUT',
|
||||
proto => 'all',
|
||||
source => '1.2.1.5',
|
||||
}
|
||||
-> firewall { '012 input-1.2.1.4':
|
||||
chain => 'INPUT',
|
||||
proto => 'all',
|
||||
source => '1.2.1.4',
|
||||
}
|
||||
-> firewall { '011 input-1.2.1.3':
|
||||
chain => 'INPUT',
|
||||
proto => 'all',
|
||||
source => '1.2.1.3',
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
|
||||
expect(shell('iptables-save').stdout).to match(/-A INPUT -s 1\.2\.1\.1(\/32)? -p tcp\s?\n-A INPUT -s 1\.2\.1\.1(\/32)? -p udp/)
|
||||
end
|
||||
end
|
||||
end
|
92
modules/firewall/spec/acceptance/resource_cmd_spec.rb
Normal file
92
modules/firewall/spec/acceptance/resource_cmd_spec.rb
Normal file
|
@ -0,0 +1,92 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
# Here we want to test the the resource commands ability to work with different
|
||||
# existing ruleset scenarios. This will give the parsing capabilities of the
|
||||
# code a good work out.
|
||||
describe 'puppet resource firewall command:', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
context 'make sure it returns no errors when executed on a clean machine' do
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
# don't check stdout, some boxes come with rules, that is normal
|
||||
# don't check stderr, puppet throws deprecation warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'flush iptables and make sure it returns nothing afterwards' do
|
||||
before(:all) do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
|
||||
# No rules, means no output thanks. And no errors as well.
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
r.stdout.should == "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'accepts rules without comments' do
|
||||
before(:all) do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80')
|
||||
end
|
||||
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
# don't check stdout, testing preexisting rules, output is normal
|
||||
# don't check stderr, puppet throws deprecation warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'accepts rules with invalid comments' do
|
||||
before(:all) do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http"')
|
||||
end
|
||||
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
# don't check stdout, testing preexisting rules, output is normal
|
||||
# don't check stderr, puppet throws deprecation warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'accepts rules with negation' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535')
|
||||
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535')
|
||||
shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE')
|
||||
end
|
||||
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
# don't check stdout, testing preexisting rules, output is normal
|
||||
# don't check stderr, puppet throws deprecation warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'accepts rules with match extension tcp flag' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -t mangle -A PREROUTING -d 1.2.3.4 -p tcp -m tcp -m multiport --dports 80,443,8140 -j MARK --set-mark 42')
|
||||
end
|
||||
|
||||
it do
|
||||
shell('puppet resource firewall') do |r|
|
||||
r.exit_code.should be_zero
|
||||
# don't check stdout, testing preexisting rules, output is normal
|
||||
# don't check stderr, puppet throws deprecation warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
252
modules/firewall/spec/acceptance/rules_spec.rb
Normal file
252
modules/firewall/spec/acceptance/rules_spec.rb
Normal file
|
@ -0,0 +1,252 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'complex ruleset 1', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
|
||||
after :all do
|
||||
shell('iptables -t filter -P INPUT ACCEPT')
|
||||
shell('iptables -t filter -P FORWARD ACCEPT')
|
||||
shell('iptables -t filter -P OUTPUT ACCEPT')
|
||||
shell('iptables -t filter --flush')
|
||||
end
|
||||
|
||||
it 'applies cleanly' do
|
||||
pp = <<-EOS
|
||||
firewall { '090 forward allow local':
|
||||
chain => 'FORWARD',
|
||||
proto => 'all',
|
||||
source => '10.0.0.0/8',
|
||||
destination => '10.0.0.0/8',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '100 forward standard allow tcp':
|
||||
chain => 'FORWARD',
|
||||
source => '10.0.0.0/8',
|
||||
destination => '!10.0.0.0/8',
|
||||
proto => 'tcp',
|
||||
state => 'NEW',
|
||||
port => [80,443,21,20,22,53,123,43,873,25,465],
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '100 forward standard allow udp':
|
||||
chain => 'FORWARD',
|
||||
source => '10.0.0.0/8',
|
||||
destination => '!10.0.0.0/8',
|
||||
proto => 'udp',
|
||||
port => [53,123],
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '100 forward standard allow icmp':
|
||||
chain => 'FORWARD',
|
||||
source => '10.0.0.0/8',
|
||||
destination => '!10.0.0.0/8',
|
||||
proto => 'icmp',
|
||||
action => 'accept',
|
||||
}
|
||||
|
||||
firewall { '090 ignore ipsec':
|
||||
table => 'nat',
|
||||
chain => 'POSTROUTING',
|
||||
outiface => 'eth0',
|
||||
ipsec_policy => 'ipsec',
|
||||
ipsec_dir => 'out',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '093 ignore 10.0.0.0/8':
|
||||
table => 'nat',
|
||||
chain => 'POSTROUTING',
|
||||
outiface => 'eth0',
|
||||
destination => '10.0.0.0/8',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '093 ignore 172.16.0.0/12':
|
||||
table => 'nat',
|
||||
chain => 'POSTROUTING',
|
||||
outiface => 'eth0',
|
||||
destination => '172.16.0.0/12',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '093 ignore 192.168.0.0/16':
|
||||
table => 'nat',
|
||||
chain => 'POSTROUTING',
|
||||
outiface => 'eth0',
|
||||
destination => '192.168.0.0/16',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '100 masq outbound':
|
||||
table => 'nat',
|
||||
chain => 'POSTROUTING',
|
||||
outiface => 'eth0',
|
||||
jump => 'MASQUERADE',
|
||||
}
|
||||
firewall { '101 redirect port 1':
|
||||
table => 'nat',
|
||||
chain => 'PREROUTING',
|
||||
iniface => 'eth0',
|
||||
proto => 'tcp',
|
||||
dport => '1',
|
||||
toports => '22',
|
||||
jump => 'REDIRECT',
|
||||
}
|
||||
EOS
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
|
||||
it 'contains appropriate rules' do
|
||||
shell('iptables-save') do |r|
|
||||
[
|
||||
/INPUT ACCEPT/,
|
||||
/FORWARD ACCEPT/,
|
||||
/OUTPUT ACCEPT/,
|
||||
/-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) -d 10.0.0.0\/(8|255\.0\.0\.0) -m comment --comment \"090 forward allow local\" -j ACCEPT/,
|
||||
/-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"100 forward standard allow icmp\" -j ACCEPT/,
|
||||
/-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p tcp -m multiport --ports 80,443,21,20,22,53,123,43,873,25,465 -m comment --comment \"100 forward standard allow tcp\" -m state --state NEW -j ACCEPT/,
|
||||
/-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p udp -m multiport --ports 53,123 -m comment --comment \"100 forward standard allow udp\" -j ACCEPT/
|
||||
].each do |line|
|
||||
expect(r.stdout).to match(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'complex ruleset 2' do
|
||||
after :all do
|
||||
shell('iptables -t filter -P INPUT ACCEPT')
|
||||
shell('iptables -t filter -P FORWARD ACCEPT')
|
||||
shell('iptables -t filter -P OUTPUT ACCEPT')
|
||||
shell('iptables -t filter --flush')
|
||||
expect(shell('iptables -t filter -X LOCAL_INPUT').stderr).to eq("")
|
||||
expect(shell('iptables -t filter -X LOCAL_INPUT_PRE').stderr).to eq("")
|
||||
end
|
||||
|
||||
it 'applies cleanly' do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
|
||||
Firewall {
|
||||
proto => 'all',
|
||||
stage => 'pre',
|
||||
}
|
||||
Firewallchain {
|
||||
stage => 'pre',
|
||||
purge => 'true',
|
||||
ignore => [
|
||||
'--comment "[^"]*(?i:ignore)[^"]*"',
|
||||
],
|
||||
}
|
||||
|
||||
firewall { '010 INPUT allow established and related':
|
||||
proto => 'all',
|
||||
state => ['ESTABLISHED', 'RELATED'],
|
||||
action => 'accept',
|
||||
before => Firewallchain['INPUT:filter:IPv4'],
|
||||
}
|
||||
firewall { '012 accept loopback':
|
||||
iniface => 'lo',
|
||||
action => 'accept',
|
||||
before => Firewallchain['INPUT:filter:IPv4'],
|
||||
}
|
||||
firewall { '020 ssh':
|
||||
proto => 'tcp',
|
||||
dport => '22',
|
||||
state => 'NEW',
|
||||
action => 'accept',
|
||||
before => Firewallchain['INPUT:filter:IPv4'],
|
||||
}
|
||||
|
||||
firewall { '013 icmp echo-request':
|
||||
proto => 'icmp',
|
||||
icmp => 'echo-request',
|
||||
action => 'accept',
|
||||
source => '10.0.0.0/8',
|
||||
}
|
||||
firewall { '013 icmp destination-unreachable':
|
||||
proto => 'icmp',
|
||||
icmp => 'destination-unreachable',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '013 icmp time-exceeded':
|
||||
proto => 'icmp',
|
||||
icmp => 'time-exceeded',
|
||||
action => 'accept',
|
||||
}
|
||||
firewall { '999 reject':
|
||||
action => 'reject',
|
||||
reject => 'icmp-host-prohibited',
|
||||
}
|
||||
|
||||
|
||||
firewallchain { 'LOCAL_INPUT_PRE:filter:IPv4': }
|
||||
firewall { '001 LOCAL_INPUT_PRE':
|
||||
jump => 'LOCAL_INPUT_PRE',
|
||||
require => Firewallchain['LOCAL_INPUT_PRE:filter:IPv4'],
|
||||
}
|
||||
firewallchain { 'LOCAL_INPUT:filter:IPv4': }
|
||||
firewall { '900 LOCAL_INPUT':
|
||||
jump => 'LOCAL_INPUT',
|
||||
require => Firewallchain['LOCAL_INPUT:filter:IPv4'],
|
||||
}
|
||||
firewallchain { 'INPUT:filter:IPv4':
|
||||
policy => 'drop',
|
||||
ignore => [
|
||||
'-j fail2ban-ssh',
|
||||
'--comment "[^"]*(?i:ignore)[^"]*"',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
firewall { '010 allow established and related':
|
||||
chain => 'FORWARD',
|
||||
proto => 'all',
|
||||
state => ['ESTABLISHED','RELATED'],
|
||||
action => 'accept',
|
||||
before => Firewallchain['FORWARD:filter:IPv4'],
|
||||
}
|
||||
firewallchain { 'FORWARD:filter:IPv4':
|
||||
policy => 'drop',
|
||||
}
|
||||
|
||||
firewallchain { 'OUTPUT:filter:IPv4': }
|
||||
|
||||
|
||||
# purge unknown rules from mangle table
|
||||
firewallchain { ['PREROUTING:mangle:IPv4', 'INPUT:mangle:IPv4', 'FORWARD:mangle:IPv4', 'OUTPUT:mangle:IPv4', 'POSTROUTING:mangle:IPv4']: }
|
||||
|
||||
# and the nat table
|
||||
firewallchain { ['PREROUTING:nat:IPv4', 'INPUT:nat:IPv4', 'OUTPUT:nat:IPv4', 'POSTROUTING:nat:IPv4']: }
|
||||
EOS
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
it 'contains appropriate rules' do
|
||||
shell('iptables-save') do |r|
|
||||
[
|
||||
/INPUT DROP/,
|
||||
/FORWARD DROP/,
|
||||
/OUTPUT ACCEPT/,
|
||||
/LOCAL_INPUT/,
|
||||
/LOCAL_INPUT_PRE/,
|
||||
/-A INPUT -m comment --comment \"001 LOCAL_INPUT_PRE\" -j LOCAL_INPUT_PRE/,
|
||||
/-A INPUT -m comment --comment \"010 INPUT allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/,
|
||||
/-A INPUT -i lo -m comment --comment \"012 accept loopback\" -j ACCEPT/,
|
||||
/-A INPUT -p icmp -m comment --comment \"013 icmp destination-unreachable\" -m icmp --icmp-type 3 -j ACCEPT/,
|
||||
/-A INPUT -s 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"013 icmp echo-request\" -m icmp --icmp-type 8 -j ACCEPT/,
|
||||
/-A INPUT -p icmp -m comment --comment \"013 icmp time-exceeded\" -m icmp --icmp-type 11 -j ACCEPT/,
|
||||
/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment \"020 ssh\" -m state --state NEW -j ACCEPT/,
|
||||
/-A INPUT -m comment --comment \"900 LOCAL_INPUT\" -j LOCAL_INPUT/,
|
||||
/-A INPUT -m comment --comment \"999 reject\" -j REJECT --reject-with icmp-host-prohibited/,
|
||||
/-A FORWARD -m comment --comment \"010 allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/
|
||||
].each do |line|
|
||||
expect(r.stdout).to match(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
97
modules/firewall/spec/acceptance/socket_spec.rb
Normal file
97
modules/firewall/spec/acceptance/socket_spec.rb
Normal file
|
@ -0,0 +1,97 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
# RHEL5 does not support -m socket
|
||||
describe 'firewall socket property', :unless => (UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) || default['platform'] =~ /el-5/ || fact('operatingsystem') == 'SLES') do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
|
||||
shared_examples "is idempotent" do |value, line_match|
|
||||
it "changes the value to #{value}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '598 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
chain => 'PREROUTING',
|
||||
table => 'raw',
|
||||
#{value}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('iptables-save -t raw') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
shared_examples "doesn't change" do |value, line_match|
|
||||
it "doesn't change the value to #{value}" do
|
||||
pp = <<-EOS
|
||||
class { '::firewall': }
|
||||
firewall { '598 - test':
|
||||
ensure => present,
|
||||
proto => 'tcp',
|
||||
chain => 'PREROUTING',
|
||||
table => 'raw',
|
||||
#{value}
|
||||
}
|
||||
EOS
|
||||
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
|
||||
shell('iptables-save -t raw') do |r|
|
||||
expect(r.stdout).to match(/#{line_match}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'adding a rule' do
|
||||
context 'when unset' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like 'is idempotent', '', /-A PREROUTING -p tcp -m comment --comment "598 - test"/
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like 'is idempotent', 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/
|
||||
end
|
||||
context 'when set to false' do
|
||||
before :all do
|
||||
iptables_flush_all_tables
|
||||
end
|
||||
it_behaves_like "is idempotent", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/
|
||||
end
|
||||
end
|
||||
describe 'editing a rule' do
|
||||
context 'when unset or false' do
|
||||
before :each do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -t raw -A PREROUTING -p tcp -m comment --comment "598 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "doesn't change", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "is idempotent", 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/
|
||||
end
|
||||
end
|
||||
context 'when set to true' do
|
||||
before :each do
|
||||
iptables_flush_all_tables
|
||||
shell('iptables -t raw -A PREROUTING -p tcp -m socket -m comment --comment "598 - test"')
|
||||
end
|
||||
context 'and current value is false' do
|
||||
it_behaves_like "is idempotent", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/
|
||||
end
|
||||
context 'and current value is true' do
|
||||
it_behaves_like "doesn't change", 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
55
modules/firewall/spec/acceptance/standard_usage_spec.rb
Normal file
55
modules/firewall/spec/acceptance/standard_usage_spec.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
# Some tests for the standard recommended usage
|
||||
describe 'standard usage tests:', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
it 'applies twice' do
|
||||
pp = <<-EOS
|
||||
class my_fw::pre {
|
||||
Firewall {
|
||||
require => undef,
|
||||
}
|
||||
|
||||
# Default firewall rules
|
||||
firewall { '000 accept all icmp':
|
||||
proto => 'icmp',
|
||||
action => 'accept',
|
||||
}->
|
||||
firewall { '001 accept all to lo interface':
|
||||
proto => 'all',
|
||||
iniface => 'lo',
|
||||
action => 'accept',
|
||||
}->
|
||||
firewall { '002 accept related established rules':
|
||||
proto => 'all',
|
||||
ctstate => ['RELATED', 'ESTABLISHED'],
|
||||
action => 'accept',
|
||||
}
|
||||
}
|
||||
class my_fw::post {
|
||||
firewall { '999 drop all':
|
||||
proto => 'all',
|
||||
action => 'drop',
|
||||
before => undef,
|
||||
}
|
||||
}
|
||||
resources { "firewall":
|
||||
purge => true
|
||||
}
|
||||
Firewall {
|
||||
before => Class['my_fw::post'],
|
||||
require => Class['my_fw::pre'],
|
||||
}
|
||||
class { ['my_fw::pre', 'my_fw::post']: }
|
||||
class { 'firewall': }
|
||||
firewall { '500 open up port 22':
|
||||
action => 'accept',
|
||||
proto => 'tcp',
|
||||
dport => 22,
|
||||
}
|
||||
EOS
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
|
||||
end
|
||||
end
|
10
modules/firewall/spec/acceptance/unsupported_spec.rb
Normal file
10
modules/firewall/spec/acceptance/unsupported_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'unsupported distributions and OSes', :if => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
|
||||
it 'should fail' do
|
||||
pp = <<-EOS
|
||||
class { 'firewall': }
|
||||
EOS
|
||||
expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/not currently supported/i)
|
||||
end
|
||||
end
|
107
modules/firewall/spec/fixtures/ip6tables/conversion_hash.rb
vendored
Normal file
107
modules/firewall/spec/fixtures/ip6tables/conversion_hash.rb
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
# These hashes allow us to iterate across a series of test data
|
||||
# creating rspec examples for each parameter to ensure the input :line
|
||||
# extrapolates to the desired value for the parameter in question. And
|
||||
# vice-versa
|
||||
|
||||
# This hash is for testing a line conversion to a hash of parameters
|
||||
# which will be used to create a resource.
|
||||
ARGS_TO_HASH6 = {
|
||||
'source_destination_ipv6_no_cidr' => {
|
||||
:line => '-A INPUT -s 2001:db8:85a3::8a2e:370:7334 -d 2001:db8:85a3::8a2e:370:7334 -m comment --comment "000 source destination ipv6 no cidr"',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:params => {
|
||||
:source => '2001:db8:85a3::8a2e:370:7334/128',
|
||||
:destination => '2001:db8:85a3::8a2e:370:7334/128',
|
||||
},
|
||||
},
|
||||
'source_destination_ipv6_netmask' => {
|
||||
:line => '-A INPUT -s 2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -d 2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -m comment --comment "000 source destination ipv6 netmask"',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:params => {
|
||||
:source => '2001:db8:1234::/48',
|
||||
:destination => '2001:db8:4321::/48',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# This hash is for testing converting a hash to an argument line.
|
||||
HASH_TO_ARGS6 = {
|
||||
'zero_prefixlen_ipv6' => {
|
||||
:params => {
|
||||
:name => '100 zero prefix length ipv6',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:source => '::/0',
|
||||
:destination => '::/0',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv6'],
|
||||
},
|
||||
'source_destination_ipv4_no_cidr' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv4 no cidr',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:source => '1.1.1.1',
|
||||
:destination => '2.2.2.2',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '1.1.1.1/32', '-d', '2.2.2.2/32', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 no cidr'],
|
||||
},
|
||||
'source_destination_ipv6_no_cidr' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv6 no cidr',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:source => '2001:db8:1234::',
|
||||
:destination => '2001:db8:4321::',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '2001:db8:1234::/128', '-d', '2001:db8:4321::/128', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 no cidr'],
|
||||
},
|
||||
'source_destination_ipv6_netmask' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv6 netmask',
|
||||
:table => 'filter',
|
||||
:provider => 'ip6tables',
|
||||
:source => '2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000',
|
||||
:destination => '2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '2001:db8:1234::/48', '-d', '2001:db8:4321::/48', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 netmask'],
|
||||
},
|
||||
'frag_ishasmorefrags' => {
|
||||
:params => {
|
||||
:name => "100 has more fragments",
|
||||
:ishasmorefrags => true,
|
||||
:provider => 'ip6tables',
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fragmore", "-m", "comment", "--comment", "100 has more fragments"],
|
||||
},
|
||||
'frag_islastfrag' => {
|
||||
:params => {
|
||||
:name => "100 last fragment",
|
||||
:islastfrag => true,
|
||||
:provider => 'ip6tables',
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fraglast", "-m", "comment", "--comment", "100 last fragment"],
|
||||
},
|
||||
'frag_isfirstfrags' => {
|
||||
:params => {
|
||||
:name => "100 first fragment",
|
||||
:isfirstfrag => true,
|
||||
:provider => 'ip6tables',
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fragfirst", "-m", "comment", "--comment", "100 first fragment"],
|
||||
},
|
||||
'hop_limit' => {
|
||||
:params => {
|
||||
:name => "100 hop limit",
|
||||
:hop_limit => 255,
|
||||
:provider => 'ip6tables',
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 hop limit", "-m", "hl", "--hl-eq", 255],
|
||||
},
|
||||
}
|
934
modules/firewall/spec/fixtures/iptables/conversion_hash.rb
vendored
Normal file
934
modules/firewall/spec/fixtures/iptables/conversion_hash.rb
vendored
Normal file
|
@ -0,0 +1,934 @@
|
|||
# These hashes allow us to iterate across a series of test data
|
||||
# creating rspec examples for each parameter to ensure the input :line
|
||||
# extrapolates to the desired value for the parameter in question. And
|
||||
# vice-versa
|
||||
|
||||
# This hash is for testing a line conversion to a hash of parameters
|
||||
# which will be used to create a resource.
|
||||
ARGS_TO_HASH = {
|
||||
'dport_and_sport' => {
|
||||
:line => '-A nova-compute-FORWARD -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp --sport 68 --dport 67 -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'nova-compute-FORWARD',
|
||||
:source => '0.0.0.0/32',
|
||||
:destination => '255.255.255.255/32',
|
||||
:sport => ['68'],
|
||||
:dport => ['67'],
|
||||
:proto => 'udp',
|
||||
},
|
||||
},
|
||||
'long_rule_1' => {
|
||||
:line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:compare_all => true,
|
||||
:params => {
|
||||
:action => "accept",
|
||||
:chain => "INPUT",
|
||||
:destination => "1.1.1.1/32",
|
||||
:dport => ["7061","7062"],
|
||||
:ensure => :present,
|
||||
:line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT',
|
||||
:name => "000 allow foo",
|
||||
:proto => "tcp",
|
||||
:provider => "iptables",
|
||||
:source => "1.1.1.1/32",
|
||||
:sport => ["7061","7062"],
|
||||
:table => "filter",
|
||||
},
|
||||
},
|
||||
'action_drop_1' => {
|
||||
:line => '-A INPUT -m comment --comment "000 allow foo" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:jump => nil,
|
||||
:action => "drop",
|
||||
},
|
||||
},
|
||||
'action_reject_1' => {
|
||||
:line => '-A INPUT -m comment --comment "000 allow foo" -j REJECT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:jump => nil,
|
||||
:action => "reject",
|
||||
},
|
||||
},
|
||||
'action_nil_1' => {
|
||||
:line => '-A INPUT -m comment --comment "000 allow foo"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:jump => nil,
|
||||
:action => nil,
|
||||
},
|
||||
},
|
||||
'jump_custom_chain_1' => {
|
||||
:line => '-A INPUT -m comment --comment "000 allow foo" -j custom_chain',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:jump => "custom_chain",
|
||||
:action => nil,
|
||||
},
|
||||
},
|
||||
'source_destination_ipv4_no_cidr' => {
|
||||
:line => '-A INPUT -s 1.1.1.1 -d 2.2.2.2 -m comment --comment "000 source destination ipv4 no cidr"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '1.1.1.1/32',
|
||||
:destination => '2.2.2.2/32',
|
||||
},
|
||||
},
|
||||
'source_destination_ipv6_no_cidr' => {
|
||||
:line => '-A INPUT -s 2001:db8:85a3::8a2e:370:7334 -d 2001:db8:85a3::8a2e:370:7334 -m comment --comment "000 source destination ipv6 no cidr"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '2001:db8:85a3::8a2e:370:7334/128',
|
||||
:destination => '2001:db8:85a3::8a2e:370:7334/128',
|
||||
},
|
||||
},
|
||||
'source_destination_ipv4_netmask' => {
|
||||
:line => '-A INPUT -s 1.1.1.0/255.255.255.0 -d 2.2.0.0/255.255.0.0 -m comment --comment "000 source destination ipv4 netmask"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '1.1.1.0/24',
|
||||
:destination => '2.2.0.0/16',
|
||||
},
|
||||
},
|
||||
'source_destination_ipv6_netmask' => {
|
||||
:line => '-A INPUT -s 2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -d 2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -m comment --comment "000 source destination ipv6 netmask"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '2001:db8:1234::/48',
|
||||
:destination => '2001:db8:4321::/48',
|
||||
},
|
||||
},
|
||||
'source_destination_negate_source' => {
|
||||
:line => '-A INPUT ! -s 1.1.1.1 -d 2.2.2.2 -m comment --comment "000 negated source address"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '! 1.1.1.1/32',
|
||||
:destination => '2.2.2.2/32',
|
||||
},
|
||||
},
|
||||
'source_destination_negate_destination' => {
|
||||
:line => '-A INPUT -s 1.1.1.1 ! -d 2.2.2.2 -m comment --comment "000 negated destination address"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '1.1.1.1/32',
|
||||
:destination => '! 2.2.2.2/32',
|
||||
},
|
||||
},
|
||||
'source_destination_negate_destination_alternative' => {
|
||||
:line => '-A INPUT -s 1.1.1.1 -d ! 2.2.2.2 -m comment --comment "000 negated destination address alternative"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '1.1.1.1/32',
|
||||
:destination => '! 2.2.2.2/32',
|
||||
},
|
||||
},
|
||||
'dport_range_1' => {
|
||||
:line => '-A INPUT -m multiport --dports 1:1024 -m comment --comment "000 allow foo"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dport => ["1-1024"],
|
||||
},
|
||||
},
|
||||
'dport_range_2' => {
|
||||
:line => '-A INPUT -m multiport --dports 15,512:1024 -m comment --comment "000 allow foo"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dport => ["15","512-1024"],
|
||||
},
|
||||
},
|
||||
'sport_range_1' => {
|
||||
:line => '-A INPUT -m multiport --sports 1:1024 -m comment --comment "000 allow foo"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:sport => ["1-1024"],
|
||||
},
|
||||
},
|
||||
'sport_range_2' => {
|
||||
:line => '-A INPUT -m multiport --sports 15,512:1024 -m comment --comment "000 allow foo"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:sport => ["15","512-1024"],
|
||||
},
|
||||
},
|
||||
'dst_type_1' => {
|
||||
:line => '-A INPUT -m addrtype --dst-type LOCAL',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dst_type => 'LOCAL',
|
||||
},
|
||||
},
|
||||
'src_type_1' => {
|
||||
:line => '-A INPUT -m addrtype --src-type LOCAL',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:src_type => 'LOCAL',
|
||||
},
|
||||
},
|
||||
'dst_range_1' => {
|
||||
:line => '-A INPUT -m iprange --dst-range 10.0.0.2-10.0.0.20',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dst_range => '10.0.0.2-10.0.0.20',
|
||||
},
|
||||
},
|
||||
'src_range_1' => {
|
||||
:line => '-A INPUT -m iprange --src-range 10.0.0.2-10.0.0.20',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:src_range => '10.0.0.2-10.0.0.20',
|
||||
},
|
||||
},
|
||||
'tcp_flags_1' => {
|
||||
:line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"',
|
||||
:table => 'filter',
|
||||
:compare_all => true,
|
||||
:chain => 'INPUT',
|
||||
:proto => 'tcp',
|
||||
:params => {
|
||||
:chain => "INPUT",
|
||||
:ensure => :present,
|
||||
:line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"',
|
||||
:name => "000 initiation",
|
||||
:proto => "tcp",
|
||||
:provider => "iptables",
|
||||
:table => "filter",
|
||||
:tcp_flags => "SYN,RST,ACK,FIN SYN",
|
||||
},
|
||||
},
|
||||
'state_returns_sorted_values' => {
|
||||
:line => '-A INPUT -m state --state INVALID,RELATED,ESTABLISHED',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:state => ['ESTABLISHED', 'INVALID', 'RELATED'],
|
||||
:action => nil,
|
||||
},
|
||||
},
|
||||
'ctstate_returns_sorted_values' => {
|
||||
:line => '-A INPUT -m conntrack --ctstate INVALID,RELATED,ESTABLISHED',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:ctstate => ['ESTABLISHED', 'INVALID', 'RELATED'],
|
||||
:action => nil,
|
||||
},
|
||||
},
|
||||
'comment_string_character_validation' => {
|
||||
:line => '-A INPUT -s 192.168.0.1/32 -m comment --comment "000 allow from 192.168.0.1, please"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:source => '192.168.0.1/32',
|
||||
},
|
||||
},
|
||||
'log_level_debug' => {
|
||||
:line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG --log-level 7',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:state => ['NEW'],
|
||||
:log_level => '7',
|
||||
:jump => 'LOG'
|
||||
},
|
||||
},
|
||||
'log_level_warn' => {
|
||||
:line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:state => ['NEW'],
|
||||
:log_level => '4',
|
||||
:jump => 'LOG'
|
||||
},
|
||||
},
|
||||
'load_limit_module_and_implicit_burst' => {
|
||||
:line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 15/hour',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dport => ['123'],
|
||||
:limit => '15/hour',
|
||||
:burst => '5'
|
||||
},
|
||||
},
|
||||
'limit_with_explicit_burst' => {
|
||||
:line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 30/hour --limit-burst 10',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:dport => ['123'],
|
||||
:limit => '30/hour',
|
||||
:burst => '10'
|
||||
},
|
||||
},
|
||||
'proto_ipencap' => {
|
||||
:line => '-A INPUT -p ipencap -m comment --comment "0100 INPUT accept ipencap"',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:proto => 'ipencap',
|
||||
}
|
||||
},
|
||||
'load_uid_owner_filter_module' => {
|
||||
:line => '-A OUTPUT -m owner --uid-owner root -m comment --comment "057 OUTPUT uid root only" -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:uid => 'root',
|
||||
:chain => 'OUTPUT',
|
||||
},
|
||||
},
|
||||
'load_uid_owner_postrouting_module' => {
|
||||
:line => '-t mangle -A POSTROUTING -m owner --uid-owner root -m comment --comment "057 POSTROUTING uid root only" -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'POSTROUTING',
|
||||
:uid => 'root',
|
||||
},
|
||||
},
|
||||
'load_gid_owner_filter_module' => {
|
||||
:line => '-A OUTPUT -m owner --gid-owner root -m comment --comment "057 OUTPUT gid root only" -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:gid => 'root',
|
||||
},
|
||||
},
|
||||
'load_gid_owner_postrouting_module' => {
|
||||
:line => '-t mangle -A POSTROUTING -m owner --gid-owner root -m comment --comment "057 POSTROUTING gid root only" -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'POSTROUTING',
|
||||
:gid => 'root',
|
||||
},
|
||||
},
|
||||
'mark_set-mark' => {
|
||||
:line => '-t mangle -A PREROUTING -j MARK --set-xmark 0x3e8/0xffffffff',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:jump => 'MARK',
|
||||
:chain => 'PREROUTING',
|
||||
:set_mark => '0x3e8/0xffffffff',
|
||||
}
|
||||
},
|
||||
'iniface_1' => {
|
||||
:line => '-A INPUT -i eth0 -m comment --comment "060 iniface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth0',
|
||||
},
|
||||
},
|
||||
'iniface_with_vlans_1' => {
|
||||
:line => '-A INPUT -i eth0.234 -m comment --comment "060 iniface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth0.234',
|
||||
},
|
||||
},
|
||||
'iniface_with_plus_1' => {
|
||||
:line => '-A INPUT -i eth+ -m comment --comment "060 iniface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth+',
|
||||
},
|
||||
},
|
||||
'outiface_1' => {
|
||||
:line => '-A OUTPUT -o eth0 -m comment --comment "060 outiface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth0',
|
||||
},
|
||||
},
|
||||
'outiface_with_vlans_1' => {
|
||||
:line => '-A OUTPUT -o eth0.234 -m comment --comment "060 outiface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth0.234',
|
||||
},
|
||||
},
|
||||
'outiface_with_plus_1' => {
|
||||
:line => '-A OUTPUT -o eth+ -m comment --comment "060 outiface" -j DROP',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth+',
|
||||
},
|
||||
},
|
||||
'pkttype multicast' => {
|
||||
:line => '-A INPUT -m pkttype --pkt-type multicast -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:pkttype => 'multicast',
|
||||
},
|
||||
},
|
||||
'socket_option' => {
|
||||
:line => '-A PREROUTING -m socket -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'PREROUTING',
|
||||
:socket => true,
|
||||
},
|
||||
},
|
||||
'isfragment_option' => {
|
||||
:line => '-A INPUT -f -m comment --comment "010 a-f comment with dashf" -j ACCEPT',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:name => '010 a-f comment with dashf',
|
||||
:action => 'accept',
|
||||
:isfragment => true,
|
||||
},
|
||||
},
|
||||
'single_tcp_sport' => {
|
||||
:line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --sport 20443 -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:source => "10.94.100.46/32",
|
||||
:proto => "tcp",
|
||||
:sport => ["20443"],
|
||||
},
|
||||
},
|
||||
'single_udp_sport' => {
|
||||
:line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --sport 20443 -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:source => "10.94.100.46/32",
|
||||
:proto => "udp",
|
||||
:sport => ["20443"],
|
||||
},
|
||||
},
|
||||
'single_tcp_dport' => {
|
||||
:line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --dport 20443 -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:source => "10.94.100.46/32",
|
||||
:proto => "tcp",
|
||||
:dport => ["20443"],
|
||||
},
|
||||
},
|
||||
'single_udp_dport' => {
|
||||
:line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --dport 20443 -j ACCEPT',
|
||||
:table => 'mangle',
|
||||
:params => {
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:source => "10.94.100.46/32",
|
||||
:proto => "udp",
|
||||
:dport => ["20443"],
|
||||
},
|
||||
},
|
||||
'connlimit_above' => {
|
||||
:line => '-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "061 REJECT connlimit_above 10" -m connlimit --connlimit-above 10 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:proto => 'tcp',
|
||||
:dport => ["22"],
|
||||
:connlimit_above => '10',
|
||||
:action => 'reject',
|
||||
},
|
||||
},
|
||||
'connlimit_above_with_connlimit_mask' => {
|
||||
:line => '-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "061 REJECT connlimit_above 10 with mask 24" -m connlimit --connlimit-above 10 --connlimit-mask 24 -j REJECT --reject-with icmp-port-unreachable',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:proto => 'tcp',
|
||||
:dport => ["22"],
|
||||
:connlimit_above => '10',
|
||||
:connlimit_mask => '24',
|
||||
:action => 'reject',
|
||||
},
|
||||
},
|
||||
'connmark' => {
|
||||
:line => '-A INPUT -m comment --comment "062 REJECT connmark" -m connmark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable',
|
||||
:table => 'filter',
|
||||
:params => {
|
||||
:proto => 'all',
|
||||
:connmark => '0x1',
|
||||
:action => 'reject',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# This hash is for testing converting a hash to an argument line.
|
||||
HASH_TO_ARGS = {
|
||||
'long_rule_1' => {
|
||||
:params => {
|
||||
:action => "accept",
|
||||
:chain => "INPUT",
|
||||
:destination => "1.1.1.1",
|
||||
:dport => ["7061","7062"],
|
||||
:ensure => :present,
|
||||
:name => "000 allow foo",
|
||||
:proto => "tcp",
|
||||
:source => "1.1.1.1",
|
||||
:sport => ["7061","7062"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "1.1.1.1/32", "-p", :tcp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061,7062", "-m", "comment", "--comment", "000 allow foo", "-j", "ACCEPT"],
|
||||
},
|
||||
'long_rule_2' => {
|
||||
:params => {
|
||||
:chain => "INPUT",
|
||||
:destination => "2.10.13.3/24",
|
||||
:dport => ["7061"],
|
||||
:ensure => :present,
|
||||
:jump => "my_custom_chain",
|
||||
:name => "700 allow bar",
|
||||
:proto => "udp",
|
||||
:source => "1.1.1.1",
|
||||
:sport => ["7061","7062"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "2.10.13.0/24", "-p", :udp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061", "-m", "comment", "--comment", "700 allow bar", "-j", "my_custom_chain"],
|
||||
},
|
||||
'no_action' => {
|
||||
:params => {
|
||||
:name => "100 no action",
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment",
|
||||
"100 no action"],
|
||||
},
|
||||
'zero_prefixlen_ipv4' => {
|
||||
:params => {
|
||||
:name => '100 zero prefix length ipv4',
|
||||
:table => 'filter',
|
||||
:source => '0.0.0.0/0',
|
||||
:destination => '0.0.0.0/0',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv4'],
|
||||
},
|
||||
'zero_prefixlen_ipv6' => {
|
||||
:params => {
|
||||
:name => '100 zero prefix length ipv6',
|
||||
:table => 'filter',
|
||||
:source => '::/0',
|
||||
:destination => '::/0',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv6'],
|
||||
},
|
||||
'source_destination_ipv4_no_cidr' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv4 no cidr',
|
||||
:table => 'filter',
|
||||
:source => '1.1.1.1',
|
||||
:destination => '2.2.2.2',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '1.1.1.1/32', '-d', '2.2.2.2/32', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 no cidr'],
|
||||
},
|
||||
'source_destination_ipv6_no_cidr' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv6 no cidr',
|
||||
:table => 'filter',
|
||||
:source => '2001:db8:1234::',
|
||||
:destination => '2001:db8:4321::',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '2001:db8:1234::/128', '-d', '2001:db8:4321::/128', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 no cidr'],
|
||||
},
|
||||
'source_destination_ipv4_netmask' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv4 netmask',
|
||||
:table => 'filter',
|
||||
:source => '1.1.1.0/255.255.255.0',
|
||||
:destination => '2.2.0.0/255.255.0.0',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '1.1.1.0/24', '-d', '2.2.0.0/16', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 netmask'],
|
||||
},
|
||||
'source_destination_ipv6_netmask' => {
|
||||
:params => {
|
||||
:name => '000 source destination ipv6 netmask',
|
||||
:table => 'filter',
|
||||
:source => '2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000',
|
||||
:destination => '2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000',
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '2001:db8:1234::/48', '-d', '2001:db8:4321::/48', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 netmask'],
|
||||
},
|
||||
'sport_range_1' => {
|
||||
:params => {
|
||||
:name => "100 sport range",
|
||||
:sport => ["1-1024"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "1:1024", "-m", "comment", "--comment", "100 sport range"],
|
||||
},
|
||||
'sport_range_2' => {
|
||||
:params => {
|
||||
:name => "100 sport range",
|
||||
:sport => ["15","512-1024"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"],
|
||||
},
|
||||
'dport_range_1' => {
|
||||
:params => {
|
||||
:name => "100 sport range",
|
||||
:dport => ["1-1024"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "1:1024", "-m", "comment", "--comment", "100 sport range"],
|
||||
},
|
||||
'dport_range_2' => {
|
||||
:params => {
|
||||
:name => "100 sport range",
|
||||
:dport => ["15","512-1024"],
|
||||
:table => "filter",
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"],
|
||||
},
|
||||
'dst_type_1' => {
|
||||
:params => {
|
||||
:name => '000 dst_type',
|
||||
:table => 'filter',
|
||||
:dst_type => 'LOCAL',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', :LOCAL, '-m', 'comment', '--comment', '000 dst_type'],
|
||||
},
|
||||
'src_type_1' => {
|
||||
:params => {
|
||||
:name => '000 src_type',
|
||||
:table => 'filter',
|
||||
:src_type => 'LOCAL',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', :LOCAL, '-m', 'comment', '--comment', '000 src_type'],
|
||||
},
|
||||
'dst_range_1' => {
|
||||
:params => {
|
||||
:name => '000 dst_range',
|
||||
:table => 'filter',
|
||||
:dst_range => '10.0.0.1-10.0.0.10',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-m', 'comment', '--comment', '000 dst_range'],
|
||||
},
|
||||
'src_range_1' => {
|
||||
:params => {
|
||||
:name => '000 src_range',
|
||||
:table => 'filter',
|
||||
:dst_range => '10.0.0.1-10.0.0.10',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-m', 'comment', '--comment', '000 src_range'],
|
||||
},
|
||||
'tcp_flags_1' => {
|
||||
:params => {
|
||||
:name => "000 initiation",
|
||||
:tcp_flags => "SYN,RST,ACK,FIN SYN",
|
||||
:table => "filter",
|
||||
},
|
||||
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "tcp", "--tcp-flags", "SYN,RST,ACK,FIN", "SYN", "-m", "comment", "--comment", "000 initiation",]
|
||||
},
|
||||
'states_set_from_array' => {
|
||||
:params => {
|
||||
:name => "100 states_set_from_array",
|
||||
:table => "filter",
|
||||
:state => ['ESTABLISHED', 'INVALID']
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 states_set_from_array",
|
||||
"-m", "state", "--state", "ESTABLISHED,INVALID"],
|
||||
},
|
||||
'ctstates_set_from_array' => {
|
||||
:params => {
|
||||
:name => "100 ctstates_set_from_array",
|
||||
:table => "filter",
|
||||
:ctstate => ['ESTABLISHED', 'INVALID']
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 ctstates_set_from_array",
|
||||
"-m", "conntrack", "--ctstate", "ESTABLISHED,INVALID"],
|
||||
},
|
||||
'comment_string_character_validation' => {
|
||||
:params => {
|
||||
:name => "000 allow from 192.168.0.1, please",
|
||||
:table => 'filter',
|
||||
:source => '192.168.0.1'
|
||||
},
|
||||
:args => ['-t', :filter, '-s', '192.168.0.1/32', '-p', :tcp, '-m', 'comment', '--comment', '000 allow from 192.168.0.1, please'],
|
||||
},
|
||||
'port_property' => {
|
||||
:params => {
|
||||
:name => '001 port property',
|
||||
:table => 'filter',
|
||||
:port => '80',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--ports', '80', '-m', 'comment', '--comment', '001 port property'],
|
||||
},
|
||||
'log_level_debug' => {
|
||||
:params => {
|
||||
:name => '956 INPUT log-level',
|
||||
:table => 'filter',
|
||||
:state => 'NEW',
|
||||
:jump => 'LOG',
|
||||
:log_level => 'debug'
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '7'],
|
||||
},
|
||||
'log_level_warn' => {
|
||||
:params => {
|
||||
:name => '956 INPUT log-level',
|
||||
:table => 'filter',
|
||||
:state => 'NEW',
|
||||
:jump => 'LOG',
|
||||
:log_level => 'warn'
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '4'],
|
||||
},
|
||||
'load_limit_module_and_implicit_burst' => {
|
||||
:params => {
|
||||
:name => '057 INPUT limit NTP',
|
||||
:table => 'filter',
|
||||
:dport => '123',
|
||||
:limit => '15/hour'
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '15/hour'],
|
||||
},
|
||||
'limit_with_explicit_burst' => {
|
||||
:params => {
|
||||
:name => '057 INPUT limit NTP',
|
||||
:table => 'filter',
|
||||
:dport => '123',
|
||||
:limit => '30/hour',
|
||||
:burst => '10'
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '30/hour', '--limit-burst', '10'],
|
||||
},
|
||||
'proto_ipencap' => {
|
||||
:params => {
|
||||
:name => '0100 INPUT accept ipencap',
|
||||
:table => 'filter',
|
||||
:proto => 'ipencap',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :ipencap, '-m', 'comment', '--comment', '0100 INPUT accept ipencap'],
|
||||
},
|
||||
'load_uid_owner_filter_module' => {
|
||||
:params => {
|
||||
:name => '057 OUTPUT uid root only',
|
||||
:table => 'filter',
|
||||
:uid => 'root',
|
||||
:action => 'accept',
|
||||
:chain => 'OUTPUT',
|
||||
:proto => 'all',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT uid root only', '-j', 'ACCEPT'],
|
||||
},
|
||||
'load_uid_owner_postrouting_module' => {
|
||||
:params => {
|
||||
:name => '057 POSTROUTING uid root only',
|
||||
:table => 'mangle',
|
||||
:uid => 'root',
|
||||
:action => 'accept',
|
||||
:chain => 'POSTROUTING',
|
||||
:proto => 'all',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING uid root only', '-j', 'ACCEPT'],
|
||||
},
|
||||
'load_gid_owner_filter_module' => {
|
||||
:params => {
|
||||
:name => '057 OUTPUT gid root only',
|
||||
:table => 'filter',
|
||||
:chain => 'OUTPUT',
|
||||
:gid => 'root',
|
||||
:action => 'accept',
|
||||
:proto => 'all',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT gid root only', '-j', 'ACCEPT'],
|
||||
},
|
||||
'load_gid_owner_postrouting_module' => {
|
||||
:params => {
|
||||
:name => '057 POSTROUTING gid root only',
|
||||
:table => 'mangle',
|
||||
:gid => 'root',
|
||||
:action => 'accept',
|
||||
:chain => 'POSTROUTING',
|
||||
:proto => 'all',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING gid root only', '-j', 'ACCEPT'],
|
||||
},
|
||||
'mark_set-mark_int' => {
|
||||
:params => {
|
||||
:name => '058 set-mark 1000',
|
||||
:table => 'mangle',
|
||||
:jump => 'MARK',
|
||||
:chain => 'PREROUTING',
|
||||
:set_mark => '1000',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 1000', '-j', 'MARK', '--set-xmark', '0x3e8/0xffffffff'],
|
||||
},
|
||||
'mark_set-mark_hex' => {
|
||||
:params => {
|
||||
:name => '058 set-mark 0x32',
|
||||
:table => 'mangle',
|
||||
:jump => 'MARK',
|
||||
:chain => 'PREROUTING',
|
||||
:set_mark => '0x32',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'],
|
||||
},
|
||||
'mark_set-mark_hex_with_hex_mask' => {
|
||||
:params => {
|
||||
:name => '058 set-mark 0x32/0xffffffff',
|
||||
:table => 'mangle',
|
||||
:jump => 'MARK',
|
||||
:chain => 'PREROUTING',
|
||||
:set_mark => '0x32/0xffffffff',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/0xffffffff', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'],
|
||||
},
|
||||
'mark_set-mark_hex_with_mask' => {
|
||||
:params => {
|
||||
:name => '058 set-mark 0x32/4',
|
||||
:table => 'mangle',
|
||||
:jump => 'MARK',
|
||||
:chain => 'PREROUTING',
|
||||
:set_mark => '0x32/4',
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/4', '-j', 'MARK', '--set-xmark', '0x32/0x4'],
|
||||
},
|
||||
'iniface_1' => {
|
||||
:params => {
|
||||
:name => '060 iniface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth0',
|
||||
},
|
||||
:args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"],
|
||||
},
|
||||
'iniface_with_vlans_1' => {
|
||||
:params => {
|
||||
:name => '060 iniface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth0.234',
|
||||
},
|
||||
:args => ["-t", :filter, "-i", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"],
|
||||
},
|
||||
'iniface_with_plus_1' => {
|
||||
:params => {
|
||||
:name => '060 iniface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth+',
|
||||
},
|
||||
:args => ["-t", :filter, "-i", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"],
|
||||
},
|
||||
'outiface_1' => {
|
||||
:params => {
|
||||
:name => '060 outiface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth0',
|
||||
},
|
||||
:args => ["-t", :filter, "-o", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"],
|
||||
},
|
||||
'outiface_with_vlans_1' => {
|
||||
:params => {
|
||||
:name => '060 outiface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth0.234',
|
||||
},
|
||||
:args => ["-t", :filter, "-o", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"],
|
||||
},
|
||||
'outiface_with_plus_1' => {
|
||||
:params => {
|
||||
:name => '060 outiface',
|
||||
:table => 'filter',
|
||||
:action => 'drop',
|
||||
:chain => 'OUTPUT',
|
||||
:outiface => 'eth+',
|
||||
},
|
||||
:args => ["-t", :filter, "-o", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"],
|
||||
},
|
||||
'pkttype multicast' => {
|
||||
:params => {
|
||||
:name => '062 pkttype multicast',
|
||||
:table => "filter",
|
||||
:action => 'accept',
|
||||
:chain => 'INPUT',
|
||||
:iniface => 'eth0',
|
||||
:pkttype => 'multicast',
|
||||
},
|
||||
:args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "pkttype", "--pkt-type", :multicast, "-m", "comment", "--comment", "062 pkttype multicast", "-j", "ACCEPT"],
|
||||
},
|
||||
'socket_option' => {
|
||||
:params => {
|
||||
:name => '050 socket option',
|
||||
:table => 'mangle',
|
||||
:action => 'accept',
|
||||
:chain => 'PREROUTING',
|
||||
:socket => true,
|
||||
},
|
||||
:args => ['-t', :mangle, '-p', :tcp, '-m', 'socket', '-m', 'comment', '--comment', '050 socket option', '-j', 'ACCEPT'],
|
||||
},
|
||||
'isfragment_option' => {
|
||||
:params => {
|
||||
:name => '050 isfragment option',
|
||||
:table => 'filter',
|
||||
:proto => :all,
|
||||
:action => 'accept',
|
||||
:isfragment => true,
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :all, '-f', '-m', 'comment', '--comment', '050 isfragment option', '-j', 'ACCEPT'],
|
||||
},
|
||||
'isfragment_option not changing -f in comment' => {
|
||||
:params => {
|
||||
:name => '050 testcomment-with-fdashf',
|
||||
:table => 'filter',
|
||||
:proto => :all,
|
||||
:action => 'accept',
|
||||
},
|
||||
:args => ['-t', :filter, '-p', :all, '-m', 'comment', '--comment', '050 testcomment-with-fdashf', '-j', 'ACCEPT'],
|
||||
},
|
||||
'connlimit_above' => {
|
||||
:params => {
|
||||
:name => '061 REJECT connlimit_above 10',
|
||||
:table => 'filter',
|
||||
:proto => 'tcp',
|
||||
:dport => ["22"],
|
||||
:connlimit_above => '10',
|
||||
:action => 'reject',
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "22", "-m", "comment", "--comment", "061 REJECT connlimit_above 10", "-j", "REJECT", "-m", "connlimit", "--connlimit-above", "10"],
|
||||
},
|
||||
'connlimit_above_with_connlimit_mask' => {
|
||||
:params => {
|
||||
:name => '061 REJECT connlimit_above 10 with mask 24',
|
||||
:table => 'filter',
|
||||
:proto => 'tcp',
|
||||
:dport => ["22"],
|
||||
:connlimit_above => '10',
|
||||
:connlimit_mask => '24',
|
||||
:action => 'reject',
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "22", "-m", "comment", "--comment", "061 REJECT connlimit_above 10 with mask 24", "-j", "REJECT", "-m", "connlimit", "--connlimit-above", "10", "--connlimit-mask", "24"],
|
||||
},
|
||||
'connmark' => {
|
||||
:params => {
|
||||
:name => '062 REJECT connmark',
|
||||
:table => 'filter',
|
||||
:proto => 'all',
|
||||
:connmark => '0x1',
|
||||
:action => 'reject',
|
||||
},
|
||||
:args => ["-t", :filter, "-p", :all, "-m", "comment", "--comment", "062 REJECT connmark", "-j", "REJECT", "-m", "connmark", "--mark", "0x1"],
|
||||
},
|
||||
}
|
29
modules/firewall/spec/spec_helper.rb
Normal file
29
modules/firewall/spec/spec_helper.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
dir = File.expand_path(File.dirname(__FILE__))
|
||||
$LOAD_PATH.unshift File.join(dir, 'lib')
|
||||
|
||||
# Don't want puppet getting the command line arguments for rake or autotest
|
||||
ARGV.clear
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
require 'rspec-puppet'
|
||||
|
||||
Bundler.require :default, :test
|
||||
|
||||
require 'pathname'
|
||||
require 'tmpdir'
|
||||
|
||||
Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour|
|
||||
require behaviour.relative_path_from(Pathname.new(dir))
|
||||
end
|
||||
|
||||
fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.tty = true
|
||||
config.mock_with :rspec do |c|
|
||||
c.syntax = :expect
|
||||
end
|
||||
config.module_path = File.join(fixture_path, 'modules')
|
||||
config.manifest_dir = File.join(fixture_path, 'manifests')
|
||||
end
|
44
modules/firewall/spec/spec_helper_acceptance.rb
Normal file
44
modules/firewall/spec/spec_helper_acceptance.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
require 'beaker-rspec'
|
||||
|
||||
def iptables_flush_all_tables
|
||||
['filter', 'nat', 'mangle', 'raw'].each do |t|
|
||||
expect(shell("iptables -t #{t} -F").stderr).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
def ip6tables_flush_all_tables
|
||||
['filter'].each do |t|
|
||||
expect(shell("ip6tables -t #{t} -F").stderr).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no'
|
||||
if hosts.first.is_pe?
|
||||
install_pe
|
||||
else
|
||||
install_puppet
|
||||
end
|
||||
hosts.each do |host|
|
||||
on host, "mkdir -p #{host['distmoduledir']}"
|
||||
end
|
||||
end
|
||||
|
||||
UNSUPPORTED_PLATFORMS = ['windows','Solaris','Darwin']
|
||||
|
||||
RSpec.configure do |c|
|
||||
# Project root
|
||||
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
||||
|
||||
# Readable test descriptions
|
||||
c.formatter = :documentation
|
||||
|
||||
# Configure all nodes in nodeset
|
||||
c.before :suite do
|
||||
# Install module and dependencies
|
||||
puppet_module_install(:source => proj_root, :module_name => 'firewall')
|
||||
hosts.each do |host|
|
||||
shell('/bin/touch /etc/puppet/hiera.yaml')
|
||||
shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'firewall::linux::archlinux', :type => :class do
|
||||
it { should contain_service('iptables').with(
|
||||
:ensure => 'running',
|
||||
:enable => 'true'
|
||||
)}
|
||||
it { should contain_service('ip6tables').with(
|
||||
:ensure => 'running',
|
||||
:enable => 'true'
|
||||
)}
|
||||
|
||||
context 'ensure => stopped' do
|
||||
let(:params) {{ :ensure => 'stopped' }}
|
||||
it { should contain_service('iptables').with(
|
||||
:ensure => 'stopped'
|
||||
)}
|
||||
it { should contain_service('ip6tables').with(
|
||||
:ensure => 'stopped'
|
||||
)}
|
||||
end
|
||||
|
||||
context 'enable => false' do
|
||||
let(:params) {{ :enable => 'false' }}
|
||||
it { should contain_service('iptables').with(
|
||||
:enable => 'false'
|
||||
)}
|
||||
it { should contain_service('ip6tables').with(
|
||||
:enable => 'false'
|
||||
)}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'firewall::linux::debian', :type => :class do
|
||||
it { should contain_package('iptables-persistent').with(
|
||||
:ensure => 'present'
|
||||
)}
|
||||
it { should contain_service('iptables-persistent').with(
|
||||
:ensure => nil,
|
||||
:enable => 'true',
|
||||
:require => 'Package[iptables-persistent]'
|
||||
)}
|
||||
|
||||
context 'enable => false' do
|
||||
let(:params) {{ :enable => 'false' }}
|
||||
it { should contain_service('iptables-persistent').with(
|
||||
:enable => 'false'
|
||||
)}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'firewall::linux::redhat', :type => :class do
|
||||
it { should contain_service('iptables').with(
|
||||
:ensure => 'running',
|
||||
:enable => 'true'
|
||||
)}
|
||||
|
||||
context 'ensure => stopped' do
|
||||
let(:params) {{ :ensure => 'stopped' }}
|
||||
it { should contain_service('iptables').with(
|
||||
:ensure => 'stopped'
|
||||
)}
|
||||
end
|
||||
|
||||
context 'enable => false' do
|
||||
let(:params) {{ :enable => 'false' }}
|
||||
it { should contain_service('iptables').with(
|
||||
:enable => 'false'
|
||||
)}
|
||||
end
|
||||
end
|
30
modules/firewall/spec/unit/classes/firewall_linux_spec.rb
Normal file
30
modules/firewall/spec/unit/classes/firewall_linux_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'firewall::linux', :type => :class do
|
||||
let(:facts_default) {{ :kernel => 'Linux' }}
|
||||
it { should contain_package('iptables').with_ensure('present') }
|
||||
|
||||
context 'RedHat like' do
|
||||
%w{RedHat CentOS Fedora}.each do |os|
|
||||
context "operatingsystem => #{os}" do
|
||||
releases = (os == 'Fedora' ? [14,15,'Rawhide'] : [6,7])
|
||||
releases.each do |osrel|
|
||||
context "operatingsystemrelease => #{osrel}" do
|
||||
let(:facts) { facts_default.merge({ :operatingsystem => os,
|
||||
:operatingsystemrelease => osrel}) }
|
||||
it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Debian like' do
|
||||
%w{Debian Ubuntu}.each do |os|
|
||||
context "operatingsystem => #{os}" do
|
||||
let(:facts) { facts_default.merge({ :operatingsystem => os }) }
|
||||
it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
modules/firewall/spec/unit/classes/firewall_spec.rb
Normal file
25
modules/firewall/spec/unit/classes/firewall_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'firewall', :type => :class do
|
||||
context 'kernel => Linux' do
|
||||
let(:facts) {{ :kernel => 'Linux' }}
|
||||
it { should contain_class('firewall::linux').with_ensure('running') }
|
||||
end
|
||||
|
||||
context 'kernel => Windows' do
|
||||
let(:facts) {{ :kernel => 'Windows' }}
|
||||
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
|
||||
end
|
||||
|
||||
context 'ensure => stopped' do
|
||||
let(:facts) {{ :kernel => 'Linux' }}
|
||||
let(:params) {{ :ensure => 'stopped' }}
|
||||
it { should contain_class('firewall::linux').with_ensure('stopped') }
|
||||
end
|
||||
|
||||
context 'ensure => test' do
|
||||
let(:facts) {{ :kernel => 'Linux' }}
|
||||
let(:params) {{ :ensure => 'test' }}
|
||||
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Facter::Util::Fact iptables_persistent_version" do
|
||||
before { Facter.clear }
|
||||
let(:dpkg_cmd) { "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" }
|
||||
|
||||
{
|
||||
"Debian" => "0.0.20090701",
|
||||
"Ubuntu" => "0.5.3ubuntu2",
|
||||
}.each do |os, ver|
|
||||
describe "#{os} package installed" do
|
||||
before {
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return(os)
|
||||
allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd).
|
||||
and_return(ver)
|
||||
}
|
||||
it { Facter.fact(:iptables_persistent_version).value.should == ver }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Ubuntu package not installed' do
|
||||
before {
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
|
||||
allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd).
|
||||
and_return(nil)
|
||||
}
|
||||
it { Facter.fact(:iptables_persistent_version).value.should be_nil }
|
||||
end
|
||||
|
||||
describe 'CentOS not supported' do
|
||||
before { allow(Facter.fact(:operatingsystem)).to receive(:value).
|
||||
and_return("CentOS") }
|
||||
it { Facter.fact(:iptables_persistent_version).value.should be_nil }
|
||||
end
|
||||
end
|
23
modules/firewall/spec/unit/facter/iptables_spec.rb
Normal file
23
modules/firewall/spec/unit/facter/iptables_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Facter::Util::Fact" do
|
||||
before {
|
||||
Facter.clear
|
||||
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
|
||||
allow(Facter.fact(:kernelrelease)).to receive(:value).and_return('2.6')
|
||||
}
|
||||
|
||||
describe 'iptables_version' do
|
||||
it {
|
||||
allow(Facter::Util::Resolution).to receive(:exec).with('iptables --version').
|
||||
and_return('iptables v1.4.7')
|
||||
Facter.fact(:iptables_version).value.should == '1.4.7'
|
||||
}
|
||||
end
|
||||
|
||||
describe 'ip6tables_version' do
|
||||
before { allow(Facter::Util::Resolution).to receive(:exec).
|
||||
with('ip6tables --version').and_return('ip6tables v1.4.7') }
|
||||
it { Facter.fact(:ip6tables_version).value.should == '1.4.7' }
|
||||
end
|
||||
end
|
227
modules/firewall/spec/unit/puppet/provider/iptables_chain_spec.rb
Executable file
227
modules/firewall/spec/unit/puppet/provider/iptables_chain_spec.rb
Executable file
|
@ -0,0 +1,227 @@
|
|||
#!/usr/bin/env rspec
|
||||
|
||||
require 'spec_helper'
|
||||
if Puppet.version < '3.4.0'
|
||||
require 'puppet/provider/confine/exists'
|
||||
else
|
||||
require 'puppet/confine/exists'
|
||||
end
|
||||
|
||||
describe 'iptables chain provider detection' do
|
||||
if Puppet.version < '3.4.0'
|
||||
let(:exists) {
|
||||
Puppet::Provider::Confine::Exists
|
||||
}
|
||||
else
|
||||
let(:exists) {
|
||||
Puppet::Confine::Exists
|
||||
}
|
||||
end
|
||||
|
||||
before :each do
|
||||
# Reset the default provider
|
||||
Puppet::Type.type(:firewallchain).defaultprovider = nil
|
||||
end
|
||||
|
||||
it "should default to iptables provider if /sbin/(eb|ip|ip6)tables[-save] exists" do
|
||||
# Stub lookup for /sbin/iptables & /sbin/iptables-save
|
||||
allow(exists).to receive(:which).with("ebtables").
|
||||
and_return "/sbin/ebtables"
|
||||
allow(exists).to receive(:which).with("ebtables-save").
|
||||
and_return "/sbin/ebtables-save"
|
||||
|
||||
allow(exists).to receive(:which).with("iptables").
|
||||
and_return "/sbin/iptables"
|
||||
allow(exists).to receive(:which).with("iptables-save").
|
||||
and_return "/sbin/iptables-save"
|
||||
|
||||
allow(exists).to receive(:which).with("ip6tables").
|
||||
and_return "/sbin/ip6tables"
|
||||
allow(exists).to receive(:which).with("ip6tables-save").
|
||||
and_return "/sbin/ip6tables-save"
|
||||
|
||||
# Every other command should return false so we don't pick up any
|
||||
# other providers
|
||||
allow(exists).to receive(:which).with() { |value|
|
||||
value !~ /(eb|ip|ip6)tables(-save)?$/
|
||||
}.and_return false
|
||||
|
||||
# Create a resource instance and make sure the provider is iptables
|
||||
resource = Puppet::Type.type(:firewallchain).new({
|
||||
:name => 'test:filter:IPv4',
|
||||
})
|
||||
expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewallchain::ProviderIptables_chain")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'iptables chain provider' do
|
||||
let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) }
|
||||
let(:resource) {
|
||||
Puppet::Type.type(:firewallchain).new({
|
||||
:name => ':test:',
|
||||
})
|
||||
}
|
||||
|
||||
before :each do
|
||||
allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider
|
||||
allow(provider).to receive(:command).with(:ebtables_save).and_return "/sbin/ebtables-save"
|
||||
allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save"
|
||||
allow(provider).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save"
|
||||
end
|
||||
|
||||
it 'should be able to get a list of existing rules' do
|
||||
# Pretend to return nil from iptables
|
||||
allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return("")
|
||||
allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return("")
|
||||
allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return("")
|
||||
|
||||
provider.instances.each do |chain|
|
||||
expect(chain).to be_instance_of(provider)
|
||||
expect(chain.properties[:provider].to_s).to eq(provider.name.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'iptables chain resource parsing' do
|
||||
let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) }
|
||||
|
||||
before :each do
|
||||
ebtables = ['BROUTE:BROUTING:ethernet',
|
||||
'BROUTE:broute:ethernet',
|
||||
':INPUT:ethernet',
|
||||
':FORWARD:ethernet',
|
||||
':OUTPUT:ethernet',
|
||||
':filter:ethernet',
|
||||
':filterdrop:ethernet',
|
||||
':filterreturn:ethernet',
|
||||
'NAT:PREROUTING:ethernet',
|
||||
'NAT:OUTPUT:ethernet',
|
||||
'NAT:POSTROUTING:ethernet',
|
||||
]
|
||||
allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return('
|
||||
*broute
|
||||
:BROUTING ACCEPT
|
||||
:broute ACCEPT
|
||||
|
||||
*filter
|
||||
:INPUT ACCEPT
|
||||
:FORWARD ACCEPT
|
||||
:OUTPUT ACCEPT
|
||||
:filter ACCEPT
|
||||
:filterdrop DROP
|
||||
:filterreturn RETURN
|
||||
|
||||
*nat
|
||||
:PREROUTING ACCEPT
|
||||
:OUTPUT ACCEPT
|
||||
:POSTROUTING ACCEPT
|
||||
')
|
||||
|
||||
iptables = [
|
||||
'raw:PREROUTING:IPv4',
|
||||
'raw:OUTPUT:IPv4',
|
||||
'raw:raw:IPv4',
|
||||
'mangle:PREROUTING:IPv4',
|
||||
'mangle:INPUT:IPv4',
|
||||
'mangle:FORWARD:IPv4',
|
||||
'mangle:OUTPUT:IPv4',
|
||||
'mangle:POSTROUTING:IPv4',
|
||||
'mangle:mangle:IPv4',
|
||||
'NAT:PREROUTING:IPv4',
|
||||
'NAT:OUTPUT:IPv4',
|
||||
'NAT:POSTROUTING:IPv4',
|
||||
'NAT:mangle:IPv4',
|
||||
'NAT:mangle:IPv4',
|
||||
'NAT:mangle:IPv4',
|
||||
':$5()*&%\'"^$): :IPv4',
|
||||
]
|
||||
allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return('
|
||||
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
|
||||
*raw
|
||||
:PREROUTING ACCEPT [12:1780]
|
||||
:OUTPUT ACCEPT [19:1159]
|
||||
:raw - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:20:06 2012
|
||||
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
|
||||
*mangle
|
||||
:PREROUTING ACCEPT [12:1780]
|
||||
:INPUT ACCEPT [12:1780]
|
||||
:FORWARD ACCEPT [0:0]
|
||||
:OUTPUT ACCEPT [19:1159]
|
||||
:POSTROUTING ACCEPT [19:1159]
|
||||
:mangle - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:20:06 2012
|
||||
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
|
||||
*nat
|
||||
:PREROUTING ACCEPT [2242:639750]
|
||||
:OUTPUT ACCEPT [5176:326206]
|
||||
:POSTROUTING ACCEPT [5162:325382]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:20:06 2012
|
||||
# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012
|
||||
*filter
|
||||
:INPUT ACCEPT [0:0]
|
||||
:FORWARD DROP [0:0]
|
||||
:OUTPUT ACCEPT [5673:420879]
|
||||
:$5()*&%\'"^$): - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:20:06 2012
|
||||
')
|
||||
ip6tables = [
|
||||
'raw:PREROUTING:IPv6',
|
||||
'raw:OUTPUT:IPv6',
|
||||
'raw:ff:IPv6',
|
||||
'mangle:PREROUTING:IPv6',
|
||||
'mangle:INPUT:IPv6',
|
||||
'mangle:FORWARD:IPv6',
|
||||
'mangle:OUTPUT:IPv6',
|
||||
'mangle:POSTROUTING:IPv6',
|
||||
'mangle:ff:IPv6',
|
||||
':INPUT:IPv6',
|
||||
':FORWARD:IPv6',
|
||||
':OUTPUT:IPv6',
|
||||
':test:IPv6',
|
||||
]
|
||||
allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return('
|
||||
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
|
||||
*raw
|
||||
:PREROUTING ACCEPT [2173:489241]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
:ff - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:31:39 2012
|
||||
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
|
||||
*mangle
|
||||
:PREROUTING ACCEPT [2301:518373]
|
||||
:INPUT ACCEPT [0:0]
|
||||
:FORWARD ACCEPT [0:0]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
:POSTROUTING ACCEPT [0:0]
|
||||
:ff - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:31:39 2012
|
||||
# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012
|
||||
*filter
|
||||
:INPUT ACCEPT [0:0]
|
||||
:FORWARD DROP [0:0]
|
||||
:OUTPUT ACCEPT [20:1292]
|
||||
:test - [0:0]
|
||||
COMMIT
|
||||
# Completed on Mon Jan 2 01:31:39 2012
|
||||
')
|
||||
@all = ebtables + iptables + ip6tables
|
||||
# IPv4 and IPv6 names also exist as resources {table}:{chain}:IP and {table}:{chain}:
|
||||
iptables.each { |name| @all += [ name[0..-3], name[0..-5] ] }
|
||||
ip6tables.each { |name| @all += [ name[0..-3], name[0..-5] ] }
|
||||
end
|
||||
|
||||
it 'should have all in parsed resources' do
|
||||
provider.instances.each do |resource|
|
||||
@all.include?(resource.name)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
410
modules/firewall/spec/unit/puppet/provider/iptables_spec.rb
Normal file
410
modules/firewall/spec/unit/puppet/provider/iptables_spec.rb
Normal file
|
@ -0,0 +1,410 @@
|
|||
#!/usr/bin/env rspec
|
||||
|
||||
require 'spec_helper'
|
||||
if Puppet.version < '3.4.0'
|
||||
require 'puppet/provider/confine/exists'
|
||||
else
|
||||
require 'puppet/confine/exists'
|
||||
end
|
||||
|
||||
describe 'iptables provider detection' do
|
||||
if Puppet.version < '3.4.0'
|
||||
let(:exists) {
|
||||
Puppet::Provider::Confine::Exists
|
||||
}
|
||||
else
|
||||
let(:exists) {
|
||||
Puppet::Confine::Exists
|
||||
}
|
||||
end
|
||||
|
||||
before :each do
|
||||
# Reset the default provider
|
||||
Puppet::Type.type(:firewall).defaultprovider = nil
|
||||
end
|
||||
|
||||
it "should default to iptables provider if /sbin/iptables[-save] exists" do
|
||||
# Stub lookup for /sbin/iptables & /sbin/iptables-save
|
||||
allow(exists).to receive(:which).with("iptables").
|
||||
and_return "/sbin/iptables"
|
||||
allow(exists).to receive(:which).with("iptables-save").
|
||||
and_return "/sbin/iptables-save"
|
||||
|
||||
# Every other command should return false so we don't pick up any
|
||||
# other providers
|
||||
allow(exists).to receive(:which).with() { |value|
|
||||
! ["iptables","iptables-save"].include?(value)
|
||||
}.and_return false
|
||||
|
||||
# Create a resource instance and make sure the provider is iptables
|
||||
resource = Puppet::Type.type(:firewall).new({
|
||||
:name => '000 test foo',
|
||||
})
|
||||
expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewall::ProviderIptables")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'iptables provider' do
|
||||
let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) }
|
||||
let(:resource) {
|
||||
Puppet::Type.type(:firewall).new({
|
||||
:name => '000 test foo',
|
||||
:action => 'accept',
|
||||
})
|
||||
}
|
||||
|
||||
before :each do
|
||||
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return provider
|
||||
allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save"
|
||||
|
||||
# Stub iptables version
|
||||
allow(Facter.fact(:iptables_version)).to receive(:value).and_return("1.4.2")
|
||||
|
||||
allow(Puppet::Util::Execution).to receive(:execute).and_return ""
|
||||
allow(Puppet::Util).to receive(:which).with("iptables-save").
|
||||
and_return "/sbin/iptables-save"
|
||||
end
|
||||
|
||||
it 'should be able to get a list of existing rules' do
|
||||
provider.instances.each do |rule|
|
||||
expect(rule).to be_instance_of(provider)
|
||||
expect(rule.properties[:provider].to_s).to eq(provider.name.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should ignore lines with fatal errors' do
|
||||
allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/iptables-save']).
|
||||
and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory")
|
||||
|
||||
expect(provider.instances.length).to be_zero
|
||||
end
|
||||
|
||||
describe '#insert_order' do
|
||||
let(:iptables_save_output) { [
|
||||
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -m comment --comment "200 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT'
|
||||
] }
|
||||
let(:resources) do
|
||||
iptables_save_output.each_with_index.collect { |l,index| provider.rule_to_hash(l, 'filter', index) }
|
||||
end
|
||||
let(:providers) do
|
||||
resources.collect { |r| provider.new(r) }
|
||||
end
|
||||
it 'understands offsets for adding rules to the beginning' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(1) # 1-indexed
|
||||
end
|
||||
it 'understands offsets for editing rules at the beginning' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(1)
|
||||
end
|
||||
it 'understands offsets for adding rules to the middle' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '101 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(2)
|
||||
end
|
||||
it 'understands offsets for editing rules at the middle' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '200 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(2)
|
||||
end
|
||||
it 'understands offsets for adding rules to the end' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '301 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(4)
|
||||
end
|
||||
it 'understands offsets for editing rules at the end' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '300 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(3)
|
||||
end
|
||||
|
||||
context 'with unname rules between' do
|
||||
let(:iptables_save_output) { [
|
||||
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT',
|
||||
] }
|
||||
it 'understands offsets for adding rules before unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(1)
|
||||
end
|
||||
it 'understands offsets for editing rules before unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(1)
|
||||
end
|
||||
it 'understands offsets for adding rules between managed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '120 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(2)
|
||||
end
|
||||
it 'understands offsets for adding rules between unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '151 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(3)
|
||||
end
|
||||
it 'understands offsets for adding rules after unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '351 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(7)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unname rules before and after' do
|
||||
let(:iptables_save_output) { [
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 050 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 090 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 400 -j ACCEPT',
|
||||
'-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 450 -j ACCEPT',
|
||||
] }
|
||||
it 'understands offsets for adding rules before unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(1)
|
||||
end
|
||||
it 'understands offsets for editing rules before unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(3)
|
||||
end
|
||||
it 'understands offsets for adding rules between managed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '120 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(4)
|
||||
end
|
||||
it 'understands offsets for adding rules between unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '151 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(5)
|
||||
end
|
||||
it 'understands offsets for adding rules after unnamed rules' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '351 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(9)
|
||||
end
|
||||
it 'understands offsets for adding rules at the end' do
|
||||
resource = Puppet::Type.type(:firewall).new({ :name => '950 test', })
|
||||
allow(resource.provider.class).to receive(:instances).and_return(providers)
|
||||
expect(resource.provider.insert_order).to eq(11)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Load in ruby hash for test fixtures.
|
||||
load 'spec/fixtures/iptables/conversion_hash.rb'
|
||||
|
||||
describe 'when converting rules to resources' do
|
||||
ARGS_TO_HASH.each do |test_name,data|
|
||||
describe "for test data '#{test_name}'" do
|
||||
let(:resource) { provider.rule_to_hash(data[:line], data[:table], 0) }
|
||||
|
||||
# If this option is enabled, make sure the parameters exactly match
|
||||
if data[:compare_all] then
|
||||
it "the parameter hash keys should be the same as returned by rules_to_hash" do
|
||||
expect(resource.keys).to match_array(data[:params].keys)
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate across each parameter, creating an example for comparison
|
||||
data[:params].each do |param_name, param_value|
|
||||
it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do
|
||||
# booleans get cludged to string "true"
|
||||
if param_value == true then
|
||||
expect(resource[param_name]).to be_true
|
||||
else
|
||||
expect(resource[param_name]).to eq(data[:params][param_name])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when working out general_args' do
|
||||
HASH_TO_ARGS.each do |test_name,data|
|
||||
describe "for test data '#{test_name}'" do
|
||||
let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) }
|
||||
let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) }
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'general_args should be valid' do
|
||||
expect(instance.general_args.flatten).to eq(data[:args])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when converting rules without comments to resources' do
|
||||
let(:sample_rule) {
|
||||
'-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT'
|
||||
}
|
||||
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'rule name contains a MD5 sum of the line' do
|
||||
expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}")
|
||||
end
|
||||
|
||||
it 'parsed the rule arguments correctly' do
|
||||
expect(resource[:chain]).to eq('INPUT')
|
||||
expect(resource[:source]).to eq('1.1.1.1/32')
|
||||
expect(resource[:destination]).to eq('1.1.1.1/32')
|
||||
expect(resource[:proto]).to eq('tcp')
|
||||
expect(resource[:dport]).to eq(['7061', '7062'])
|
||||
expect(resource[:sport]).to eq(['7061', '7062'])
|
||||
expect(resource[:action]).to eq('accept')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when converting existing rules generates by system-config-firewall-tui to resources' do
|
||||
let(:sample_rule) {
|
||||
# as generated by iptables-save from rules created with system-config-firewall-tui
|
||||
'-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT'
|
||||
}
|
||||
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'rule name contains a MD5 sum of the line' do
|
||||
expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}")
|
||||
end
|
||||
|
||||
it 'parse arguments' do
|
||||
expect(resource[:chain]).to eq('INPUT')
|
||||
expect(resource[:proto]).to eq('tcp')
|
||||
expect(resource[:dport]).to eq(['22'])
|
||||
expect(resource[:state]).to eq(['NEW'])
|
||||
expect(resource[:action]).to eq('accept')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when creating resources' do
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'insert_args should be an array' do
|
||||
expect(instance.insert_args.class).to eq(Array)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when modifying resources' do
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'update_args should be an array' do
|
||||
expect(instance.update_args.class).to eq(Array)
|
||||
end
|
||||
|
||||
it 'fails when modifying the chain' do
|
||||
expect { instance.chain = "OUTPUT" }.to raise_error(/is not supported/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when deleting resources' do
|
||||
let(:sample_rule) {
|
||||
'-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT'
|
||||
}
|
||||
let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) }
|
||||
let(:instance) { provider.new(resource) }
|
||||
|
||||
it 'resource[:line] looks like the original rule' do
|
||||
resource[:line] == sample_rule
|
||||
end
|
||||
|
||||
it 'delete_args is an array' do
|
||||
expect(instance.delete_args.class).to eq(Array)
|
||||
end
|
||||
|
||||
it 'delete_args is the same as the rule string when joined' do
|
||||
expect(instance.delete_args.join(' ')).to eq(sample_rule.gsub(/\-A/,
|
||||
'-t filter -D'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ip6tables provider' do
|
||||
let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) }
|
||||
let(:resource) {
|
||||
Puppet::Type.type(:firewall).new({
|
||||
:name => '000 test foo',
|
||||
:action => 'accept',
|
||||
:provider => "ip6tables",
|
||||
})
|
||||
}
|
||||
|
||||
before :each do
|
||||
allow(Puppet::Type::Firewall).to receive(:ip6tables).and_return provider6
|
||||
allow(provider6).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save"
|
||||
|
||||
# Stub iptables version
|
||||
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return '1.4.7'
|
||||
|
||||
allow(Puppet::Util::Execution).to receive(:execute).and_return ''
|
||||
allow(Puppet::Util).to receive(:which).with("ip6tables-save").
|
||||
and_return "/sbin/ip6tables-save"
|
||||
end
|
||||
|
||||
it 'should be able to get a list of existing rules' do
|
||||
provider6.instances.each do |rule|
|
||||
rule.should be_instance_of(provider6)
|
||||
rule.properties[:provider6].to_s.should == provider6.name.to_s
|
||||
end
|
||||
end
|
||||
|
||||
it 'should ignore lines with fatal errors' do
|
||||
allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/ip6tables-save']).
|
||||
and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory")
|
||||
provider6.instances.length.should == 0
|
||||
end
|
||||
|
||||
# Load in ruby hash for test fixtures.
|
||||
load 'spec/fixtures/ip6tables/conversion_hash.rb'
|
||||
|
||||
describe 'when converting rules to resources' do
|
||||
ARGS_TO_HASH6.each do |test_name,data|
|
||||
describe "for test data '#{test_name}'" do
|
||||
let(:resource) { provider6.rule_to_hash(data[:line], data[:table], 0) }
|
||||
|
||||
# If this option is enabled, make sure the parameters exactly match
|
||||
if data[:compare_all] then
|
||||
it "the parameter hash keys should be the same as returned by rules_to_hash" do
|
||||
resource.keys.should =~ data[:params].keys
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate across each parameter, creating an example for comparison
|
||||
data[:params].each do |param_name, param_value|
|
||||
it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do
|
||||
resource[param_name].should == data[:params][param_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when working out general_args' do
|
||||
HASH_TO_ARGS6.each do |test_name,data|
|
||||
describe "for test data '#{test_name}'" do
|
||||
let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) }
|
||||
let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) }
|
||||
let(:instance) { provider6.new(resource) }
|
||||
|
||||
it 'general_args should be valid' do
|
||||
instance.general_args.flatten.should == data[:args]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
650
modules/firewall/spec/unit/puppet/type/firewall_spec.rb
Executable file
650
modules/firewall/spec/unit/puppet/type/firewall_spec.rb
Executable file
|
@ -0,0 +1,650 @@
|
|||
#!/usr/bin/env rspec
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
firewall = Puppet::Type.type(:firewall)
|
||||
|
||||
describe firewall do
|
||||
before :each do
|
||||
@class = firewall
|
||||
@provider = double 'provider'
|
||||
allow(@provider).to receive(:name).and_return(:iptables)
|
||||
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return @provider
|
||||
|
||||
@resource = @class.new({:name => '000 test foo'})
|
||||
|
||||
# Stub iptables version
|
||||
allow(Facter.fact(:iptables_version)).to receive(:value).and_return('1.4.2')
|
||||
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return('1.4.2')
|
||||
|
||||
# Stub confine facts
|
||||
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian')
|
||||
end
|
||||
|
||||
it 'should have :name be its namevar' do
|
||||
@class.key_attributes.should == [:name]
|
||||
end
|
||||
|
||||
describe ':name' do
|
||||
it 'should accept a name' do
|
||||
@resource[:name] = '000-test-foo'
|
||||
@resource[:name].should == '000-test-foo'
|
||||
end
|
||||
|
||||
it 'should not accept a name with non-ASCII chars' do
|
||||
lambda { @resource[:name] = '%*#^(#$' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':action' do
|
||||
it "should have no default" do
|
||||
res = @class.new(:name => "000 test")
|
||||
res.parameters[:action].should == nil
|
||||
end
|
||||
|
||||
[:accept, :drop, :reject].each do |action|
|
||||
it "should accept value #{action}" do
|
||||
@resource[:action] = action
|
||||
@resource[:action].should == action
|
||||
end
|
||||
end
|
||||
|
||||
it 'should fail when value is not recognized' do
|
||||
lambda { @resource[:action] = 'not valid' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':chain' do
|
||||
[:INPUT, :FORWARD, :OUTPUT, :PREROUTING, :POSTROUTING].each do |chain|
|
||||
it "should accept chain value #{chain}" do
|
||||
@resource[:chain] = chain
|
||||
@resource[:chain].should == chain
|
||||
end
|
||||
end
|
||||
|
||||
it 'should fail when the chain value is not recognized' do
|
||||
lambda { @resource[:chain] = 'not valid' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':table' do
|
||||
[:nat, :mangle, :filter, :raw].each do |table|
|
||||
it "should accept table value #{table}" do
|
||||
@resource[:table] = table
|
||||
@resource[:table].should == table
|
||||
end
|
||||
end
|
||||
|
||||
it "should fail when table value is not recognized" do
|
||||
lambda { @resource[:table] = 'not valid' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':proto' do
|
||||
[:tcp, :udp, :icmp, :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all].each do |proto|
|
||||
it "should accept proto value #{proto}" do
|
||||
@resource[:proto] = proto
|
||||
@resource[:proto].should == proto
|
||||
end
|
||||
end
|
||||
|
||||
it "should fail when proto value is not recognized" do
|
||||
lambda { @resource[:proto] = 'foo' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':jump' do
|
||||
it "should have no default" do
|
||||
res = @class.new(:name => "000 test")
|
||||
res.parameters[:jump].should == nil
|
||||
end
|
||||
|
||||
['QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK'].each do |jump|
|
||||
it "should accept jump value #{jump}" do
|
||||
@resource[:jump] = jump
|
||||
@resource[:jump].should == jump
|
||||
end
|
||||
end
|
||||
|
||||
['ACCEPT', 'DROP', 'REJECT'].each do |jump|
|
||||
it "should now fail when value #{jump}" do
|
||||
lambda { @resource[:jump] = jump }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
it "should fail when jump value is not recognized" do
|
||||
lambda { @resource[:jump] = '%^&*' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
[:source, :destination].each do |addr|
|
||||
describe addr do
|
||||
it "should accept a #{addr} as a string" do
|
||||
@resource[addr] = '127.0.0.1'
|
||||
@resource[addr].should == '127.0.0.1/32'
|
||||
end
|
||||
['0.0.0.0/0', '::/0'].each do |prefix|
|
||||
it "should be nil for zero prefix length address #{prefix}" do
|
||||
@resource[addr] = prefix
|
||||
@resource[addr].should == nil
|
||||
end
|
||||
end
|
||||
it "should accept a negated #{addr} as a string" do
|
||||
@resource[addr] = '! 127.0.0.1'
|
||||
@resource[addr].should == '! 127.0.0.1/32'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[:dport, :sport].each do |port|
|
||||
describe port do
|
||||
it "should accept a #{port} as string" do
|
||||
@resource[port] = '22'
|
||||
@resource[port].should == ['22']
|
||||
end
|
||||
|
||||
it "should accept a #{port} as an array" do
|
||||
@resource[port] = ['22','23']
|
||||
@resource[port].should == ['22','23']
|
||||
end
|
||||
|
||||
it "should accept a #{port} as a number" do
|
||||
@resource[port] = 22
|
||||
@resource[port].should == ['22']
|
||||
end
|
||||
|
||||
it "should accept a #{port} as a hyphen separated range" do
|
||||
@resource[port] = ['22-1000']
|
||||
@resource[port].should == ['22-1000']
|
||||
end
|
||||
|
||||
it "should accept a #{port} as a combination of arrays of single and " \
|
||||
"hyphen separated ranges" do
|
||||
|
||||
@resource[port] = ['22-1000','33','3000-4000']
|
||||
@resource[port].should == ['22-1000','33','3000-4000']
|
||||
end
|
||||
|
||||
it "should convert a port name for #{port} to its number" do
|
||||
@resource[port] = 'ssh'
|
||||
@resource[port].should == ['22']
|
||||
end
|
||||
|
||||
it "should not accept something invalid for #{port}" do
|
||||
expect { @resource[port] = 'something odd' }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/)
|
||||
end
|
||||
|
||||
it "should not accept something invalid in an array for #{port}" do
|
||||
expect { @resource[port] = ['something odd','something even odder'] }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[:dst_type, :src_type].each do |addrtype|
|
||||
describe addrtype do
|
||||
it "should have no default" do
|
||||
res = @class.new(:name => "000 test")
|
||||
res.parameters[addrtype].should == nil
|
||||
end
|
||||
end
|
||||
|
||||
[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE,
|
||||
:UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].each do |type|
|
||||
it "should accept #{addrtype} value #{type}" do
|
||||
@resource[addrtype] = type
|
||||
@resource[addrtype].should == type
|
||||
end
|
||||
end
|
||||
|
||||
it "should fail when #{addrtype} value is not recognized" do
|
||||
lambda { @resource[addrtype] = 'foo' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
[:iniface, :outiface].each do |iface|
|
||||
describe iface do
|
||||
it "should accept #{iface} value as a string" do
|
||||
@resource[iface] = 'eth1'
|
||||
@resource[iface].should == 'eth1'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[:tosource, :todest].each do |addr|
|
||||
describe addr do
|
||||
it "should accept #{addr} value as a string" do
|
||||
@resource[addr] = '127.0.0.1'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ':log_level' do
|
||||
values = {
|
||||
'panic' => '0',
|
||||
'alert' => '1',
|
||||
'crit' => '2',
|
||||
'err' => '3',
|
||||
'warn' => '4',
|
||||
'warning' => '4',
|
||||
'not' => '5',
|
||||
'notice' => '5',
|
||||
'info' => '6',
|
||||
'debug' => '7'
|
||||
}
|
||||
|
||||
values.each do |k,v|
|
||||
it {
|
||||
@resource[:log_level] = k
|
||||
@resource[:log_level].should == v
|
||||
}
|
||||
|
||||
it {
|
||||
@resource[:log_level] = 3
|
||||
@resource[:log_level].should == 3
|
||||
}
|
||||
|
||||
it { lambda { @resource[:log_level] = 'foo' }.should raise_error(Puppet::Error) }
|
||||
end
|
||||
end
|
||||
|
||||
describe ':icmp' do
|
||||
icmp_codes = {
|
||||
:iptables => {
|
||||
'0' => 'echo-reply',
|
||||
'3' => 'destination-unreachable',
|
||||
'4' => 'source-quench',
|
||||
'6' => 'redirect',
|
||||
'8' => 'echo-request',
|
||||
'9' => 'router-advertisement',
|
||||
'10' => 'router-solicitation',
|
||||
'11' => 'time-exceeded',
|
||||
'12' => 'parameter-problem',
|
||||
'13' => 'timestamp-request',
|
||||
'14' => 'timestamp-reply',
|
||||
'17' => 'address-mask-request',
|
||||
'18' => 'address-mask-reply'
|
||||
},
|
||||
:ip6tables => {
|
||||
'1' => 'destination-unreachable',
|
||||
'3' => 'time-exceeded',
|
||||
'4' => 'parameter-problem',
|
||||
'128' => 'echo-request',
|
||||
'129' => 'echo-reply',
|
||||
'133' => 'router-solicitation',
|
||||
'134' => 'router-advertisement',
|
||||
'137' => 'redirect'
|
||||
}
|
||||
}
|
||||
icmp_codes.each do |provider, values|
|
||||
describe provider do
|
||||
values.each do |k,v|
|
||||
it 'should convert icmp string to number' do
|
||||
@resource[:provider] = provider
|
||||
@resource[:provider].should == provider
|
||||
@resource[:icmp] = v
|
||||
@resource[:icmp].should == k
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'should accept values as integers' do
|
||||
@resource[:icmp] = 9
|
||||
@resource[:icmp].should == 9
|
||||
end
|
||||
|
||||
it 'should fail if icmp type is "any"' do
|
||||
lambda { @resource[:icmp] = 'any' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
|
||||
it 'should fail if icmp type cannot be mapped to a numeric' do
|
||||
lambda { @resource[:icmp] = 'foo' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':state' do
|
||||
it 'should accept value as a string' do
|
||||
@resource[:state] = :INVALID
|
||||
@resource[:state].should == [:INVALID]
|
||||
end
|
||||
|
||||
it 'should accept value as an array' do
|
||||
@resource[:state] = [:INVALID, :NEW]
|
||||
@resource[:state].should == [:INVALID, :NEW]
|
||||
end
|
||||
|
||||
it 'should sort values alphabetically' do
|
||||
@resource[:state] = [:NEW, :ESTABLISHED]
|
||||
@resource[:state].should == [:ESTABLISHED, :NEW]
|
||||
end
|
||||
end
|
||||
|
||||
describe ':ctstate' do
|
||||
it 'should accept value as a string' do
|
||||
@resource[:ctstate] = :INVALID
|
||||
@resource[:ctstate].should == [:INVALID]
|
||||
end
|
||||
|
||||
it 'should accept value as an array' do
|
||||
@resource[:ctstate] = [:INVALID, :NEW]
|
||||
@resource[:ctstate].should == [:INVALID, :NEW]
|
||||
end
|
||||
|
||||
it 'should sort values alphabetically' do
|
||||
@resource[:ctstate] = [:NEW, :ESTABLISHED]
|
||||
@resource[:ctstate].should == [:ESTABLISHED, :NEW]
|
||||
end
|
||||
end
|
||||
|
||||
describe ':burst' do
|
||||
it 'should accept numeric values' do
|
||||
@resource[:burst] = 12
|
||||
@resource[:burst].should == 12
|
||||
end
|
||||
|
||||
it 'should fail if value is not numeric' do
|
||||
lambda { @resource[:burst] = 'foo' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe ':recent' do
|
||||
['set', 'update', 'rcheck', 'remove'].each do |recent|
|
||||
it "should accept recent value #{recent}" do
|
||||
@resource[:recent] = recent
|
||||
@resource[:recent].should == "--#{recent}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ':action and :jump' do
|
||||
it 'should allow only 1 to be set at a time' do
|
||||
expect {
|
||||
@class.new(
|
||||
:name => "001-test",
|
||||
:action => "accept",
|
||||
:jump => "custom_chain"
|
||||
)
|
||||
}.to raise_error(Puppet::Error, /Only one of the parameters 'action' and 'jump' can be set$/)
|
||||
end
|
||||
end
|
||||
describe ':gid and :uid' do
|
||||
it 'should allow me to set uid' do
|
||||
@resource[:uid] = 'root'
|
||||
@resource[:uid].should == 'root'
|
||||
end
|
||||
it 'should allow me to set uid as an array, and silently hide my error' do
|
||||
@resource[:uid] = ['root', 'bobby']
|
||||
@resource[:uid].should == 'root'
|
||||
end
|
||||
it 'should allow me to set gid' do
|
||||
@resource[:gid] = 'root'
|
||||
@resource[:gid].should == 'root'
|
||||
end
|
||||
it 'should allow me to set gid as an array, and silently hide my error' do
|
||||
@resource[:gid] = ['root', 'bobby']
|
||||
@resource[:gid].should == 'root'
|
||||
end
|
||||
end
|
||||
|
||||
describe ':set_mark' do
|
||||
['1.3.2', '1.4.2'].each do |iptables_version|
|
||||
describe "with iptables #{iptables_version}" do
|
||||
before {
|
||||
Facter.clear
|
||||
allow(Facter.fact(:iptables_version)).to receive(:value).and_return iptables_version
|
||||
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return iptables_version
|
||||
}
|
||||
|
||||
if iptables_version == '1.3.2'
|
||||
it 'should allow me to set set-mark without mask' do
|
||||
@resource[:set_mark] = '0x3e8'
|
||||
@resource[:set_mark].should == '0x3e8'
|
||||
end
|
||||
it 'should convert int to hex without mask' do
|
||||
@resource[:set_mark] = '1000'
|
||||
@resource[:set_mark].should == '0x3e8'
|
||||
end
|
||||
it 'should fail if mask is present' do
|
||||
lambda { @resource[:set_mark] = '0x3e8/0xffffffff'}.should raise_error(
|
||||
Puppet::Error, /iptables version #{iptables_version} does not support masks on MARK rules$/
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if iptables_version == '1.4.2'
|
||||
it 'should allow me to set set-mark with mask' do
|
||||
@resource[:set_mark] = '0x3e8/0xffffffff'
|
||||
@resource[:set_mark].should == '0x3e8/0xffffffff'
|
||||
end
|
||||
it 'should convert int to hex and add a 32 bit mask' do
|
||||
@resource[:set_mark] = '1000'
|
||||
@resource[:set_mark].should == '0x3e8/0xffffffff'
|
||||
end
|
||||
it 'should add a 32 bit mask' do
|
||||
@resource[:set_mark] = '0x32'
|
||||
@resource[:set_mark].should == '0x32/0xffffffff'
|
||||
end
|
||||
it 'should use the mask provided' do
|
||||
@resource[:set_mark] = '0x32/0x4'
|
||||
@resource[:set_mark].should == '0x32/0x4'
|
||||
end
|
||||
it 'should use the mask provided and convert int to hex' do
|
||||
@resource[:set_mark] = '1000/0x4'
|
||||
@resource[:set_mark].should == '0x3e8/0x4'
|
||||
end
|
||||
it 'should fail if mask value is more than 32 bits' do
|
||||
lambda { @resource[:set_mark] = '1/4294967296'}.should raise_error(
|
||||
Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/
|
||||
)
|
||||
end
|
||||
it 'should fail if mask is malformed' do
|
||||
lambda { @resource[:set_mark] = '1000/0xq4'}.should raise_error(
|
||||
Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
['/', '1000/', 'pwnie'].each do |bad_mark|
|
||||
it "should fail with malformed mark '#{bad_mark}'" do
|
||||
lambda { @resource[:set_mark] = bad_mark}.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
it 'should fail if mark value is more than 32 bits' do
|
||||
lambda { @resource[:set_mark] = '4294967296'}.should raise_error(
|
||||
Puppet::Error, /MARK value must be integer or hex between 0 and 0xffffffff$/
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[:chain, :jump].each do |param|
|
||||
describe param do
|
||||
it 'should autorequire fwchain when table and provider are undefined' do
|
||||
@resource[param] = 'FOO'
|
||||
@resource[:table].should == :filter
|
||||
@resource[:provider].should == :iptables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
it 'should autorequire fwchain when table is undefined and provider is ip6tables' do
|
||||
@resource[param] = 'FOO'
|
||||
@resource[:table].should == :filter
|
||||
@resource[:provider] = :ip6tables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv6')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
it 'should autorequire fwchain when table is raw and provider is undefined' do
|
||||
@resource[param] = 'FOO'
|
||||
@resource[:table] = :raw
|
||||
@resource[:provider].should == :iptables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv4')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
it 'should autorequire fwchain when table is raw and provider is ip6tables' do
|
||||
@resource[param] = 'FOO'
|
||||
@resource[:table] = :raw
|
||||
@resource[:provider] = :ip6tables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv6')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
# test where autorequire is still needed (table != filter)
|
||||
['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain|
|
||||
it "should autorequire fwchain #{test_chain} when table is mangle and provider is undefined" do
|
||||
@resource[param] = test_chain
|
||||
@resource[:table] = :mangle
|
||||
@resource[:provider].should == :iptables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv4")
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
it "should autorequire fwchain #{test_chain} when table is mangle and provider is ip6tables" do
|
||||
@resource[param] = test_chain
|
||||
@resource[:table] = :mangle
|
||||
@resource[:provider] = :ip6tables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv6")
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == chain.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
end
|
||||
|
||||
# test of case where autorequire should not happen
|
||||
['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain|
|
||||
|
||||
it "should not autorequire fwchain #{test_chain} when table and provider are undefined" do
|
||||
@resource[param] = test_chain
|
||||
@resource[:table].should == :filter
|
||||
@resource[:provider].should == :iptables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv4")
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.should == nil
|
||||
end
|
||||
|
||||
it "should not autorequire fwchain #{test_chain} when table is undefined and provider is ip6tables" do
|
||||
@resource[param] = test_chain
|
||||
@resource[:table].should == :filter
|
||||
@resource[:provider] = :ip6tables
|
||||
|
||||
chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv6")
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain
|
||||
rel = @resource.autorequire[0]
|
||||
rel.should == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ":chain and :jump" do
|
||||
it 'should autorequire independent fwchains' do
|
||||
@resource[:chain] = 'FOO'
|
||||
@resource[:jump] = 'BAR'
|
||||
@resource[:table].should == :filter
|
||||
@resource[:provider].should == :iptables
|
||||
|
||||
chain_foo = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4')
|
||||
chain_bar = Puppet::Type.type(:firewallchain).new(:name => 'BAR:filter:IPv4')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource chain_foo
|
||||
catalog.add_resource chain_bar
|
||||
rel = @resource.autorequire
|
||||
rel[0].source.ref.should == chain_foo.ref
|
||||
rel[0].target.ref.should == @resource.ref
|
||||
rel[1].source.ref.should == chain_bar.ref
|
||||
rel[1].target.ref.should == @resource.ref
|
||||
end
|
||||
end
|
||||
|
||||
describe ':pkttype' do
|
||||
[:multicast, :broadcast, :unicast].each do |pkttype|
|
||||
it "should accept pkttype value #{pkttype}" do
|
||||
@resource[:pkttype] = pkttype
|
||||
@resource[:pkttype].should == pkttype
|
||||
end
|
||||
end
|
||||
|
||||
it 'should fail when the pkttype value is not recognized' do
|
||||
lambda { @resource[:pkttype] = 'not valid' }.should raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'autorequire packages' do
|
||||
[:iptables, :ip6tables].each do |provider|
|
||||
it "provider #{provider} should autorequire package iptables" do
|
||||
@resource[:provider] = provider
|
||||
@resource[:provider].should == provider
|
||||
package = Puppet::Type.type(:package).new(:name => 'iptables')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
catalog.add_resource package
|
||||
rel = @resource.autorequire[0]
|
||||
rel.source.ref.should == package.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
|
||||
it "provider #{provider} should autorequire packages iptables and iptables-persistent" do
|
||||
@resource[:provider] = provider
|
||||
@resource[:provider].should == provider
|
||||
packages = [
|
||||
Puppet::Type.type(:package).new(:name => 'iptables'),
|
||||
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
|
||||
]
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource @resource
|
||||
packages.each do |package|
|
||||
catalog.add_resource package
|
||||
end
|
||||
packages.zip(@resource.autorequire) do |package, rel|
|
||||
rel.source.ref.should == package.ref
|
||||
rel.target.ref.should == @resource.ref
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
185
modules/firewall/spec/unit/puppet/type/firewallchain_spec.rb
Executable file
185
modules/firewall/spec/unit/puppet/type/firewallchain_spec.rb
Executable file
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env rspec
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
firewallchain = Puppet::Type.type(:firewallchain)
|
||||
|
||||
describe firewallchain do
|
||||
before(:each) do
|
||||
# Stub confine facts
|
||||
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian')
|
||||
end
|
||||
let(:klass) { firewallchain }
|
||||
let(:provider) {
|
||||
prov = double 'provider'
|
||||
allow(prov).to receive(:name).and_return(:iptables_chain)
|
||||
prov
|
||||
}
|
||||
let(:resource) {
|
||||
allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider
|
||||
klass.new({:name => 'INPUT:filter:IPv4', :policy => :accept })
|
||||
}
|
||||
|
||||
it 'should have :name be its namevar' do
|
||||
klass.key_attributes.should == [:name]
|
||||
end
|
||||
|
||||
describe ':name' do
|
||||
{'nat' => ['PREROUTING', 'POSTROUTING', 'INPUT', 'OUTPUT'],
|
||||
'mangle' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ],
|
||||
'filter' => ['INPUT','OUTPUT','FORWARD'],
|
||||
'raw' => [ 'PREROUTING', 'OUTPUT'],
|
||||
'broute' => ['BROUTING']
|
||||
}.each_pair do |table, allowedinternalchains|
|
||||
['IPv4', 'IPv6', 'ethernet'].each do |protocol|
|
||||
[ 'test', '$5()*&%\'"^$09):' ].each do |chainname|
|
||||
name = "#{chainname}:#{table}:#{protocol}"
|
||||
if table == 'nat' && protocol == 'IPv6'
|
||||
it "should fail #{name}" do
|
||||
expect { resource[:name] = name }.to raise_error(Puppet::Error)
|
||||
end
|
||||
elsif protocol != 'ethernet' && table == 'broute'
|
||||
it "should fail #{name}" do
|
||||
expect { resource[:name] = name }.to raise_error(Puppet::Error)
|
||||
end
|
||||
else
|
||||
it "should accept name #{name}" do
|
||||
resource[:name] = name
|
||||
resource[:name].should == name
|
||||
end
|
||||
end
|
||||
end # chainname
|
||||
end # protocol
|
||||
|
||||
[ 'PREROUTING', 'POSTROUTING', 'BROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ].each do |internalchain|
|
||||
name = internalchain + ':' + table + ':'
|
||||
if internalchain == 'BROUTING'
|
||||
name += 'ethernet'
|
||||
elsif table == 'nat'
|
||||
name += 'IPv4'
|
||||
else
|
||||
name += 'IPv4'
|
||||
end
|
||||
if allowedinternalchains.include? internalchain
|
||||
it "should allow #{name}" do
|
||||
resource[:name] = name
|
||||
resource[:name].should == name
|
||||
end
|
||||
else
|
||||
it "should fail #{name}" do
|
||||
expect { resource[:name] = name }.to raise_error(Puppet::Error)
|
||||
end
|
||||
end
|
||||
end # internalchain
|
||||
|
||||
end # table, allowedinternalchainnames
|
||||
|
||||
it 'should fail with invalid table names' do
|
||||
expect { resource[:name] = 'wrongtablename:test:IPv4' }.to raise_error(Puppet::Error)
|
||||
end
|
||||
|
||||
it 'should fail with invalid protocols names' do
|
||||
expect { resource[:name] = 'test:filter:IPv5' }.to raise_error(Puppet::Error)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ':policy' do
|
||||
|
||||
[:accept, :drop, :queue, :return].each do |policy|
|
||||
it "should accept policy #{policy}" do
|
||||
resource[:policy] = policy
|
||||
resource[:policy].should == policy
|
||||
end
|
||||
end
|
||||
|
||||
it 'should fail when value is not recognized' do
|
||||
expect { resource[:policy] = 'not valid' }.to raise_error(Puppet::Error)
|
||||
end
|
||||
|
||||
[:accept, :drop, :queue, :return].each do |policy|
|
||||
it "non-inbuilt chains should not accept policy #{policy}" do
|
||||
expect { klass.new({:name => 'testchain:filter:IPv4', :policy => policy }) }.to raise_error(Puppet::Error)
|
||||
end
|
||||
it "non-inbuilt chains can accept policies on protocol = ethernet (policy #{policy})" do
|
||||
klass.new({:name => 'testchain:filter:ethernet', :policy => policy })
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'autorequire packages' do
|
||||
it "provider iptables_chain should autorequire package iptables" do
|
||||
resource[:provider].should == :iptables_chain
|
||||
package = Puppet::Type.type(:package).new(:name => 'iptables')
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource resource
|
||||
catalog.add_resource package
|
||||
rel = resource.autorequire[0]
|
||||
rel.source.ref.should == package.ref
|
||||
rel.target.ref.should == resource.ref
|
||||
end
|
||||
|
||||
it "provider iptables_chain should autorequire packages iptables and iptables-persistent" do
|
||||
resource[:provider].should == :iptables_chain
|
||||
packages = [
|
||||
Puppet::Type.type(:package).new(:name => 'iptables'),
|
||||
Puppet::Type.type(:package).new(:name => 'iptables-persistent')
|
||||
]
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
catalog.add_resource resource
|
||||
packages.each do |package|
|
||||
catalog.add_resource package
|
||||
end
|
||||
packages.zip(resource.autorequire) do |package, rel|
|
||||
rel.source.ref.should == package.ref
|
||||
rel.target.ref.should == resource.ref
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'purge iptables rules' do
|
||||
before(:each) do
|
||||
allow(Puppet::Type.type(:firewall).provider(:iptables)).to receive(:iptables_save).and_return(<<EOS
|
||||
# Completed on Sun Jan 5 19:30:21 2014
|
||||
# Generated by iptables-save v1.4.12 on Sun Jan 5 19:30:21 2014
|
||||
*filter
|
||||
:INPUT DROP [0:0]
|
||||
:FORWARD DROP [0:0]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
:LOCAL_FORWARD - [0:0]
|
||||
:LOCAL_FORWARD_PRE - [0:0]
|
||||
:LOCAL_INPUT - [0:0]
|
||||
:LOCAL_INPUT_PRE - [0:0]
|
||||
:fail2ban-ssh - [0:0]
|
||||
-A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh
|
||||
-A INPUT -i lo -m comment --comment "012 accept loopback" -j ACCEPT
|
||||
-A INPUT -p tcp -m multiport --dports 22 -m comment --comment "020 ssh" -j ACCEPT
|
||||
-A OUTPUT -d 1.2.1.2 -j DROP
|
||||
-A fail2ban-ssh -j RETURN
|
||||
COMMIT
|
||||
# Completed on Sun Jan 5 19:30:21 2014
|
||||
EOS
|
||||
)
|
||||
end
|
||||
|
||||
it 'should generate iptables resources' do
|
||||
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4', :purge => true)
|
||||
|
||||
expect(resource.generate.size).to eq(3)
|
||||
end
|
||||
|
||||
it 'should not generate ignored iptables rules' do
|
||||
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4', :purge => true, :ignore => ['-j fail2ban-ssh'])
|
||||
|
||||
expect(resource.generate.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'should not generate iptables resources when not enabled' do
|
||||
resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4')
|
||||
|
||||
expect(resource.generate.size).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
197
modules/firewall/spec/unit/puppet/util/firewall_spec.rb
Normal file
197
modules/firewall/spec/unit/puppet/util/firewall_spec.rb
Normal file
|
@ -0,0 +1,197 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Puppet::Util::Firewall' do
|
||||
let(:resource) {
|
||||
type = Puppet::Type.type(:firewall)
|
||||
provider = double 'provider'
|
||||
allow(provider).to receive(:name).and_return(:iptables)
|
||||
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return(provider)
|
||||
type.new({:name => '000 test foo'})
|
||||
}
|
||||
|
||||
before(:each) { resource }
|
||||
|
||||
describe '#host_to_ip' do
|
||||
subject { resource }
|
||||
specify {
|
||||
expect(Resolv).to receive(:getaddress).with('puppetlabs.com').and_return('96.126.112.51')
|
||||
subject.host_to_ip('puppetlabs.com').should == '96.126.112.51/32'
|
||||
}
|
||||
specify { subject.host_to_ip('96.126.112.51').should == '96.126.112.51/32' }
|
||||
specify { subject.host_to_ip('96.126.112.51/32').should == '96.126.112.51/32' }
|
||||
specify { subject.host_to_ip('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' }
|
||||
specify { subject.host_to_ip('2001:db8:1234::/48').should == '2001:db8:1234::/48' }
|
||||
specify { subject.host_to_ip('0.0.0.0/0').should == nil }
|
||||
specify { subject.host_to_ip('::/0').should == nil }
|
||||
end
|
||||
|
||||
describe '#host_to_mask' do
|
||||
subject { resource }
|
||||
specify {
|
||||
expect(Resolv).to receive(:getaddress).at_least(:once).with('puppetlabs.com').and_return('96.126.112.51')
|
||||
subject.host_to_mask('puppetlabs.com').should == '96.126.112.51/32'
|
||||
subject.host_to_mask('!puppetlabs.com').should == '! 96.126.112.51/32'
|
||||
}
|
||||
specify { subject.host_to_mask('96.126.112.51').should == '96.126.112.51/32' }
|
||||
specify { subject.host_to_mask('!96.126.112.51').should == '! 96.126.112.51/32' }
|
||||
specify { subject.host_to_mask('96.126.112.51/32').should == '96.126.112.51/32' }
|
||||
specify { subject.host_to_mask('! 96.126.112.51/32').should == '! 96.126.112.51/32' }
|
||||
specify { subject.host_to_mask('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' }
|
||||
specify { subject.host_to_mask('!2001:db8:85a3:0:0:8a2e:370:7334').should == '! 2001:db8:85a3::8a2e:370:7334/128' }
|
||||
specify { subject.host_to_mask('2001:db8:1234::/48').should == '2001:db8:1234::/48' }
|
||||
specify { subject.host_to_mask('! 2001:db8:1234::/48').should == '! 2001:db8:1234::/48' }
|
||||
specify { subject.host_to_mask('0.0.0.0/0').should == nil }
|
||||
specify { subject.host_to_mask('!0.0.0.0/0').should == nil }
|
||||
specify { subject.host_to_mask('::/0').should == nil }
|
||||
specify { subject.host_to_mask('! ::/0').should == nil }
|
||||
end
|
||||
|
||||
describe '#icmp_name_to_number' do
|
||||
describe 'proto unsupported' do
|
||||
subject { resource }
|
||||
|
||||
%w{inet5 inet8 foo}.each do |proto|
|
||||
it "should reject invalid proto #{proto}" do
|
||||
expect { subject.icmp_name_to_number('echo-reply', proto) }.
|
||||
to raise_error(ArgumentError, "unsupported protocol family '#{proto}'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'proto IPv4' do
|
||||
proto = 'inet'
|
||||
subject { resource }
|
||||
specify { subject.icmp_name_to_number('echo-reply', proto).should == '0' }
|
||||
specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '3' }
|
||||
specify { subject.icmp_name_to_number('source-quench', proto).should == '4' }
|
||||
specify { subject.icmp_name_to_number('redirect', proto).should == '6' }
|
||||
specify { subject.icmp_name_to_number('echo-request', proto).should == '8' }
|
||||
specify { subject.icmp_name_to_number('router-advertisement', proto).should == '9' }
|
||||
specify { subject.icmp_name_to_number('router-solicitation', proto).should == '10' }
|
||||
specify { subject.icmp_name_to_number('time-exceeded', proto).should == '11' }
|
||||
specify { subject.icmp_name_to_number('parameter-problem', proto).should == '12' }
|
||||
specify { subject.icmp_name_to_number('timestamp-request', proto).should == '13' }
|
||||
specify { subject.icmp_name_to_number('timestamp-reply', proto).should == '14' }
|
||||
specify { subject.icmp_name_to_number('address-mask-request', proto).should == '17' }
|
||||
specify { subject.icmp_name_to_number('address-mask-reply', proto).should == '18' }
|
||||
end
|
||||
|
||||
describe 'proto IPv6' do
|
||||
proto = 'inet6'
|
||||
subject { resource }
|
||||
specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '1' }
|
||||
specify { subject.icmp_name_to_number('time-exceeded', proto).should == '3' }
|
||||
specify { subject.icmp_name_to_number('parameter-problem', proto).should == '4' }
|
||||
specify { subject.icmp_name_to_number('echo-request', proto).should == '128' }
|
||||
specify { subject.icmp_name_to_number('echo-reply', proto).should == '129' }
|
||||
specify { subject.icmp_name_to_number('router-solicitation', proto).should == '133' }
|
||||
specify { subject.icmp_name_to_number('router-advertisement', proto).should == '134' }
|
||||
specify { subject.icmp_name_to_number('redirect', proto).should == '137' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#string_to_port' do
|
||||
subject { resource }
|
||||
specify { subject.string_to_port('80','tcp').should == '80' }
|
||||
specify { subject.string_to_port(80,'tcp').should == '80' }
|
||||
specify { subject.string_to_port('http','tcp').should == '80' }
|
||||
specify { subject.string_to_port('domain','udp').should == '53' }
|
||||
end
|
||||
|
||||
describe '#to_hex32' do
|
||||
subject { resource }
|
||||
specify { subject.to_hex32('0').should == '0x0' }
|
||||
specify { subject.to_hex32('0x32').should == '0x32' }
|
||||
specify { subject.to_hex32('42').should == '0x2a' }
|
||||
specify { subject.to_hex32('4294967295').should == '0xffffffff' }
|
||||
specify { subject.to_hex32('4294967296').should == nil }
|
||||
specify { subject.to_hex32('-1').should == nil }
|
||||
specify { subject.to_hex32('bananas').should == nil }
|
||||
end
|
||||
|
||||
describe '#persist_iptables' do
|
||||
before { Facter.clear }
|
||||
subject { resource }
|
||||
|
||||
describe 'when proto is IPv4' do
|
||||
let(:proto) { 'IPv4' }
|
||||
|
||||
it 'should exec /sbin/service if running RHEL 6 or earlier' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6')
|
||||
|
||||
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should exec for systemd if running RHEL 7 or greater' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7')
|
||||
|
||||
expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save})
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should exec for systemd if running Fedora 15 or greater' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Fedora')
|
||||
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('15')
|
||||
|
||||
expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save})
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should exec for CentOS identified from operatingsystem' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
|
||||
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should exec for Archlinux identified from osfamily' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux')
|
||||
expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules'])
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should raise a warning when exec fails' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat')
|
||||
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6')
|
||||
|
||||
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}).
|
||||
and_raise(Puppet::ExecutionFailure, 'some error')
|
||||
expect(subject).to receive(:warning).with('Unable to persist firewall rules: some error')
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when proto is IPv6' do
|
||||
let(:proto) { 'IPv6' }
|
||||
|
||||
it 'should exec for newer Ubuntu' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
|
||||
allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.5.3ubuntu2')
|
||||
expect(subject).to receive(:execute).with(%w{/usr/sbin/service iptables-persistent save})
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should not exec for older Ubuntu which does not support IPv6' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
|
||||
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
|
||||
allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.0.20090701')
|
||||
expect(subject).to receive(:execute).never
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
|
||||
it 'should not exec for Suse which is not supported' do
|
||||
allow(Facter.fact(:osfamily)).to receive(:value).and_return('Suse')
|
||||
expect(subject).to receive(:execute).never
|
||||
subject.persist_iptables(proto)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
67
modules/firewall/spec/unit/puppet/util/ipcidr_spec.rb
Normal file
67
modules/firewall/spec/unit/puppet/util/ipcidr_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Puppet::Util::IPCidr' do
|
||||
describe 'ipv4 address' do
|
||||
before { @ipaddr = Puppet::Util::IPCidr.new('96.126.112.51') }
|
||||
subject { @ipaddr }
|
||||
specify { subject.cidr.should == '96.126.112.51/32' }
|
||||
specify { subject.prefixlen.should == 32 }
|
||||
specify { subject.netmask.should == '255.255.255.255' }
|
||||
end
|
||||
|
||||
describe 'single ipv4 address with cidr' do
|
||||
before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.51/32') }
|
||||
subject { @ipcidr }
|
||||
specify { subject.cidr.should == '96.126.112.51/32' }
|
||||
specify { subject.prefixlen.should == 32 }
|
||||
specify { subject.netmask.should == '255.255.255.255' }
|
||||
end
|
||||
|
||||
describe 'ipv4 address range with cidr' do
|
||||
before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.0/24') }
|
||||
subject { @ipcidr }
|
||||
specify { subject.cidr.should == '96.126.112.0/24' }
|
||||
specify { subject.prefixlen.should == 24 }
|
||||
specify { subject.netmask.should == '255.255.255.0' }
|
||||
end
|
||||
|
||||
describe 'ipv4 open range with cidr' do
|
||||
before { @ipcidr = Puppet::Util::IPCidr.new('0.0.0.0/0') }
|
||||
subject { @ipcidr }
|
||||
specify { subject.cidr.should == '0.0.0.0/0' }
|
||||
specify { subject.prefixlen.should == 0 }
|
||||
specify { subject.netmask.should == '0.0.0.0' }
|
||||
end
|
||||
|
||||
describe 'ipv6 address' do
|
||||
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334') }
|
||||
subject { @ipaddr }
|
||||
specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' }
|
||||
specify { subject.prefixlen.should == 128 }
|
||||
specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }
|
||||
end
|
||||
|
||||
describe 'single ipv6 addr with cidr' do
|
||||
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334/128') }
|
||||
subject { @ipaddr }
|
||||
specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' }
|
||||
specify { subject.prefixlen.should == 128 }
|
||||
specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }
|
||||
end
|
||||
|
||||
describe 'ipv6 addr range with cidr' do
|
||||
before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:1234::/48') }
|
||||
subject { @ipaddr }
|
||||
specify { subject.cidr.should == '2001:db8:1234::/48' }
|
||||
specify { subject.prefixlen.should == 48 }
|
||||
specify { subject.netmask.should == 'ffff:ffff:ffff:0000:0000:0000:0000:0000' }
|
||||
end
|
||||
|
||||
describe 'ipv6 open range with cidr' do
|
||||
before { @ipaddr = Puppet::Util::IPCidr.new('::/0') }
|
||||
subject { @ipaddr }
|
||||
specify { subject.cidr.should == '::/0' }
|
||||
specify { subject.prefixlen.should == 0 }
|
||||
specify { subject.netmask.should == '0000:0000:0000:0000:0000:0000:0000:0000' }
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue