Merge pull request #38 from Jjk422/new_firewall_service_module

Iptables firewall configure
This commit is contained in:
Tom
2016-07-19 14:34:23 +01:00
committed by GitHub
102 changed files with 14142 additions and 0 deletions

View File

@@ -0,0 +1,602 @@
## Supported Release 1.8.1
### Summary
This release documents an important issue with mcollective that may impact users of the firewall module. Workarounds are suggested as part of this advisory until mcollective can be patched.
#### Bugfixes
- Add mcollective rule-reversal known limitation
## Supported Release 1.8.0
### Summary
This release includes numerous features, bugfixes and other improvements including better handling when trying to delete already absent rules.
#### Features
- Added new 'pkg_ensure' parameter to allow the updating of the iptables package.
- Added new 'log_uid' property.
- Added 'sctp' to the 'proto' property.
- Added support for IPv6 NAT in Linux kernels >= 3.7.
- Added support for the security table.
#### Bugfixes
- (MODULES-2783) Replaced hardcoded iptables service references with $service_name variable.
- (MODULES-1341) Recover when deleting absent rules.
- (MODULES-3032) Facter flush is called to clear Facter cache get up to date value for ':iptables_persistent_version'.
- (MODULES-2159) Fixed idempotency issue when using connlimit.
- Fixed the handling of chain names that contain '-f'.
#### Improvements
- Numerous unit and acceptance test improvements.
- Improved handling/use of the '$::iptables_persistent_version' custom fact.
- Better handling of operating systems that use SELinux.
## Supported Release 1.7.2
### Summary
Small release for support of newer PE versions. This increments the version of PE in the metadata.json file.
## 2015-08-25 - Supported Release 1.7.1
### Summary
This is a bugfix release to deprecate the port parameter. Using the unspecific 'port' parameter can lead to firewall rules that are unexpectedly too lax. It is recommended to always use the specific dport and sport parameters to avoid this ambiguity.
#### Bugfixes
- Deprecate the port parameter
## 2015-07-28 - Supported Release 1.7.0
### Summary
This release includes numerous features, bugfixes and other improvements including Puppet 4 & PE 2015.2 support as well as ClusterIP and DSCP jump target support.
#### Features
- Puppet 4 and PE 2015.2 official support
- ClusterIP jump target (including options) now supported
- DSCP jump target (including options) now supported
- SLES 10 now compatible (but not supported)
#### Bugfixes
- (MODULES-1967) Parse escape sequences from iptables
- (MODULES-1592) Allow src_type and dst_type prefixed with '!' to pass validation
- (MODULES-2186) - iptables rules with -A in comment now supported
- (MODULES-1976) Revise rule name validation for ruby 1.9
- Fix installation hang on Debian Jessie
- Fix for physdev idempotency on EL5
#### Improvements
- Documentation improvements
- Enforce the seluser on selinux systems
- All the relevent services are now autorequired by the firewall and firewallchain types
- Replace Facter.fact().value() calls with Facter.value() to support Facter 3
## 2015-05-19 - Supported Release 1.6.0
### Summary
This release includes support for TEE, MSS, the time ipt module, Debian 8 support, and a number of test fixes and other improvements.
#### Features
- Add TEE support
- Add MSS support (including clamp-mss-to-pmtu support)
- Add support for the time ipt module (-m time)
- Add support for Debian 8
- Add support for ICMPv6 types 'neighbour-{solicitation,advertisement}'
- Add support for ICMPv6 type 'too-big'
- Add support for new 'match_mark' property
- Added 'ipv4' and 'ipv6' options to 'proto' property
#### Bugfixes
- Fix for Systemd-based OSes where systemd needs restarted before being able to pick up new services (MODULES-1984)
- Arch Linux package management fix
## 2015-03-31 - Supported Release 1.5.0
### Summary
This release includes physdev_is_bridged support, checksum_fill support, basic Gentoo compatibility, and a number of test fixes and improvements.
#### Features
- Add `physdev_is_bridged` support
- Add `checksum_fill` support
- Add basic Gentoo compatibility (unsupported)
#### Bugfixes
- Implementation for resource map munging to allow a single ipt module to be used multiple times in a single rule on older versions of iptables (MODULES-1808)
- Test fixes
## 2015-01-27 - Supported Release 1.4.0
### Summary
This release includes physdev support, the ability to look up usernames from uuid, and a number of bugfixes
#### Features
- Add `netmap` feature
- Add `physdev` support
- Add ability to look up username from uuid (MODULES-753, MODULES-1688)
#### Bugfixes
- Sync iptables/ip6tables providers (MODULES-1612)
- Fix package names for Amazon and Ubuntu 14.10 (MODULES-1029)
- Fix overly aggressive gsub when `ensure => absent` (MODULES-1453)
- Unable to parse `-m (tcp|udp)` rules (MODULES-1552)
- Fix ip6tables provider when `iptables-ipv6` package isn't installed for EL6 (MODULES-633)
- Test fixes
## 2014-12-16 - Supported Release 1.3.0
### Summary
This release includes a number of bugfixes and features, including fixing `tcp_flags` support, and added support for interface aliases, negation for iniface and outiface, and extra configurability for packages and service names.
#### Features
- Add support for interface aliases (eth0:0) (MODULES-1469)
- Add negation for iniface, outiface (MODULES-1470)
- Make package and service names configurable (MODULES-1309)
#### Bugfixes
- Fix test regexes for EL5 (MODULES-1565)
- Fix `tcp_flags` support for ip6tables (MODULES-556)
- Don't arbitrarily limit `set_mark` for certain chains
## 2014-11-04 - Supported Release 1.2.0
### Summary
This release has a number of new features and bugfixes, including rule inversion, future parser support, improved EL7 support, and the ability to purge ip6tables rules.
#### Features
- Documentation updates!
- Test updates!
- Add ipset support
- Enable rule inversion
- Future parser support
- Improved support for EL7
- Support netfilter-persistent
- Add support for statistics module
- Add support for mac address source rules
- Add cbt protocol
#### Bugfixes
- Incorrect use of `source => :iptables` in the ip6tables provider was making it impossible to purge ip6tables rules (MODULES-41)
- Don't require `toports` when `jump => 'REDIRECT'` (MODULES-1086)
- Don't limit which chains iniface and outiface parameters can be used in
- Don't fail on rules added with ipsec/strongswan (MODULES-796)
## 2014-07-08 - Supported Release 1.1.3
### Summary
This is a supported release with test coverage enhancements.
#### Bugfixes
- Confine to supported kernels
## 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 ...

View File

@@ -0,0 +1,220 @@
Checklist (and a short version for the impatient)
=================================================
* Commits:
- Make commits of logical units.
- Check for unnecessary whitespace with "git diff --check" before
committing.
- Commit using Unix line endings (check the settings around "crlf" in
git-config(1)).
- Do not check in commented out code or unneeded files.
- The first line of the commit message should be a short
description (50 characters is the soft limit, excluding ticket
number(s)), and should skip the full stop.
- Associate the issue in the message. The first line should include
the issue number in the form "(#XXXX) Rest of message".
- The body should provide a meaningful commit message, which:
- uses the imperative, present tense: "change", not "changed" or
"changes".
- includes motivation for the change, and contrasts its
implementation with the previous behavior.
- Make sure that you have tests for the bug you are fixing, or
feature you are adding.
- Make sure the test suites passes after your commit:
`bundle exec rspec spec/acceptance` More information on [testing](#Testing) below
- When introducing a new feature, make sure it is properly
documented in the README.md
* Submission:
* Pre-requisites:
- Make sure you have a [GitHub account](https://github.com/join)
- [Create a ticket](https://tickets.puppetlabs.com/secure/CreateIssue!default.jspa), or [watch the ticket](https://tickets.puppetlabs.com/browse/) you are patching for.
* Preferred method:
- Fork the repository on GitHub.
- Push your changes to a topic branch in your fork of the
repository. (the format ticket/1234-short_description_of_change is
usually preferred for this project).
- Submit a pull request to the repository in the puppetlabs
organization.
The long version
================
1. Make separate commits for logically separate changes.
Please break your commits down into logically consistent units
which include new or changed tests relevant to the rest of the
change. The goal of doing this is to make the diff easier to
read for whoever is reviewing your code. In general, the easier
your diff is to read, the more likely someone will be happy to
review it and get it into the code base.
If you are going to refactor a piece of code, please do so as a
separate commit from your feature or bug fix changes.
We also really appreciate changes that include tests to make
sure the bug is not re-introduced, and that the feature is not
accidentally broken.
Describe the technical detail of the change(s). If your
description starts to get too long, that is a good sign that you
probably need to split up your commit into more finely grained
pieces.
Commits which plainly describe the things which help
reviewers check the patch and future developers understand the
code are much more likely to be merged in with a minimum of
bike-shedding or requested changes. Ideally, the commit message
would include information, and be in a form suitable for
inclusion in the release notes for the version of Puppet that
includes them.
Please also check that you are not introducing any trailing
whitespace or other "whitespace errors". You can do this by
running "git diff --check" on your changes before you commit.
2. Sending your patches
To submit your changes via a GitHub pull request, we _highly_
recommend that you have them on a topic branch, instead of
directly on "master".
It makes things much easier to keep track of, especially if
you decide to work on another thing before your first change
is merged in.
GitHub has some pretty good
[general documentation](http://help.github.com/) on using
their site. They also have documentation on
[creating pull requests](http://help.github.com/send-pull-requests/).
In general, after pushing your topic branch up to your
repository on GitHub, you can switch to the branch in the
GitHub UI and click "Pull Request" towards the top of the page
in order to open a pull request.
3. Update the related GitHub issue.
If there is a GitHub issue associated with the change you
submitted, then you should update the ticket to include the
location of your branch, along with any other commentary you
may wish to make.
Testing
=======
Getting Started
---------------
Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby
package manager such as [bundler](http://bundler.io/) what Ruby packages,
or Gems, are required to build, develop, and test this software.
Please make sure you have [bundler installed](http://bundler.io/#getting-started)
on your system, then use it to install all dependencies needed for this project,
by running
```shell
% bundle install
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/..
Using rake (10.1.0)
Using builder (3.2.2)
-- 8><-- many more --><8 --
Using rspec-system-puppet (2.2.0)
Using serverspec (0.6.3)
Using rspec-system-serverspec (1.0.0)
Using bundler (1.3.5)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
```
NOTE some systems may require you to run this command with sudo.
If you already have those gems installed, make sure they are up-to-date:
```shell
% bundle update
```
With all dependencies in place and up-to-date we can now run the tests:
```shell
% bundle exec rake spec
```
This will execute all the [rspec tests](http://rspec-puppet.com/) tests
under [spec/defines](./spec/defines), [spec/classes](./spec/classes),
and so on. rspec tests may have the same kind of dependencies as the
module they are testing. While the module defines in its [Modulefile](./Modulefile),
rspec tests define them in [.fixtures.yml](./fixtures.yml).
Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker)
tests. These tests spin up a virtual machine under
[VirtualBox](https://www.virtualbox.org/)) with, controlling it with
[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test
scenarios. In order to run these, you will need both of those tools
installed on your system.
You can run them by issuing the following command
```shell
% bundle exec rake spec_clean
% bundle exec rspec spec/acceptance
```
This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml),
install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb)
and then run all the tests under [spec/acceptance](./spec/acceptance).
Writing Tests
-------------
XXX getting started writing tests.
If you have commit access to the repository
===========================================
Even if you have commit access to the repository, you will still need to
go through the process above, and have someone else review and merge
in your changes. The rule is that all changes must be reviewed by a
developer on the project (that did not write the code) to ensure that
all changes go through a code review process.
Having someone other than the author of the topic branch recorded as
performing the merge is the record that they performed the code
review.
Additional Resources
====================
* [Getting additional help](http://puppetlabs.com/community/get-help)
* [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests)
* [Patchwork](https://patchwork.puppetlabs.com)
* [General GitHub documentation](http://help.github.com/)
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)

View File

@@ -0,0 +1,48 @@
source ENV['GEM_SOURCE'] || "https://rubygems.org"
def location_for(place, fake_version = nil)
if place =~ /^(git[:@][^#]*)#(.*)/
[fake_version, { :git => $1, :branch => $2, :require => false }].compact
elsif place =~ /^file:\/\/(.*)/
['>= 0', { :path => File.expand_path($1), :require => false }]
else
[place, { :require => false }]
end
end
group :development, :unit_tests do
gem 'json', :require => false
gem 'metadata-json-lint', :require => false
gem 'puppet_facts', :require => false
gem 'puppet-blacksmith', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec-puppet', '>= 2.3.2', :require => false
gem 'simplecov', :require => false
end
group :system_tests do
gem 'beaker-puppet_install_helper', :require => false
if beaker_version = ENV['BEAKER_VERSION']
gem 'beaker', *location_for(beaker_version)
end
if beaker_rspec_version = ENV['BEAKER_RSPEC_VERSION']
gem 'beaker-rspec', *location_for(beaker_rspec_version)
else
gem 'beaker-rspec', :require => false
end
gem 'master_manipulator', :require => false
gem 'serverspec', :require => false
end
if facterversion = ENV['FACTER_GEM_VERSION']
gem 'facter', facterversion, :require => false
else
gem 'facter', :require => false
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby

View 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.

View File

@@ -0,0 +1,919 @@
#firewall
[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-firewall.png?branch=master)](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)
* [Additional Uses for the Firewall Module](#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 module introduces the `firewall` resource, which is used to manage and configure firewall rules from within the Puppet DSL. This module offers support for iptables and ip6tables. The module also introduces the `firewallchain` resource, which allows you to manage chains or firewall lists and ebtables for bridging support. At the moment, only iptables and ip6tables chains are supported.
The firewall module acts on your running firewall, making immediate changes as the catalog executes. Defining default pre and post rules allows you to provide global defaults for your hosts before and after any custom rules. Defining `pre` and `post` rules is also necessary to help you avoid locking yourself out of your own boxes when Puppet runs.
## Setup
### What firewall Affects
* Every node running a firewall
* Firewall settings in your system
* Connection settings for managed nodes
* Unmanaged resources (get purged)
### Setup Requirements
Firewall uses Ruby-based providers, so you must enable [pluginsync](http://docs.puppetlabs.com/guides/plugins_in_modules.html#enabling-pluginsync).
### Beginning with firewall
In the following two sections, you create new classes and then create firewall rules related to those classes. These steps are optional but provide a framework for firewall rules, which is helpful if youre just starting to create them.
If you already have rules in place, then you dont need to do these two sections. However, be aware of the ordering of your firewall rules. The module will dynamically apply rules in the order they appear in the catalog, meaning a deny rule could be applied before the allow rules. This might mean the module hasnt established some of the important connections, such as the connection to the Puppet master.
The following steps are designed to ensure that you keep your SSH and other connections, primarily your connection to your Puppet master. If you create the `pre` and `post` classes described in the first section, then you also need to create the rules described in the second section.
#### Create the `my_fw::pre` and `my_fw::post` Classes
This approach employs a whitelist setup, so you can define what rules you want and everything else is ignored rather than removed.
The code in this section does the following:
* The 'require' parameter in `firewall {}` ensures `my_fw::pre` is run before any other rules.
* In the `my_fw::post` class declaration, the 'before' parameter ensures `my_fw::post` is run after any other rules.
Therefore, the run order is:
* The rules in `my_fw::pre`
* Your rules (defined in code)
* The rules in `my_fw::post`
The rules in the `pre` and `post` classes are fairly general. These two classes ensure that you retain connectivity and that you drop unmatched packets appropriately. The rules you define in your manifests are likely specific to the applications you run.
1.) Add the `pre` class to my_fw/manifests/pre.pp. Your pre.pp file should contain any default rules to be applied first. The rules in this class should be added in the order you want them to run.2.
~~~puppet
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 reject local traffic not on loopback interface':
iniface => '! lo',
proto => 'all',
destination => '127.0.0.1/8',
action => 'reject',
}->
firewall { '003 accept related established rules':
proto => 'all',
state => ['RELATED', 'ESTABLISHED'],
action => 'accept',
}
}
~~~
The rules in `pre` should allow basic networking (such as ICMP and TCP) and ensure that existing connections are not closed.
2.) Add the `post` class to my_fw/manifests/post.pp and include any default rules to be applied last.
~~~puppet
class my_fw::post {
firewall { '999 drop all':
proto => 'all',
action => 'drop',
before => undef,
}
}
~~~
Alternatively, the [firewallchain](#type-firewallchain) type can be used to set the default policy:
~~~puppet
firewallchain { 'INPUT:filter:IPv4':
ensure => present,
policy => drop,
before => undef,
}
~~~
#### Create Firewall Rules
The rules you create here are helpful if you dont have any existing rules; they help you order your firewall configurations so you dont lock yourself out of your box.
Rules are persisted automatically between reboots, although there are known issues with ip6tables on older Debian/Ubuntu distributions. There are also known issues with ebtables.
1.) In site.pp or another top-scope file, add the following code to 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.
~~~puppet
resources { 'firewall':
purge => true,
}
~~~
To purge unmanaged firewall chains, also add:
~~~puppet
resources { 'firewallchain':
purge => true,
}
~~~
**Note** - If there are unmanaged rules in unmanaged chains, it will take two Puppet runs before the firewall chain is purged. This is different than the `purge` parameter available in `firewallchain`.
2.) Use the following code to set up the default parameters for all of the firewall rules you will establish later. These defaults will ensure that the `pre` and `post` classes are run in the correct order to avoid locking you out of your box during the first Puppet run.
~~~puppet
Firewall {
before => Class['my_fw::post'],
require => Class['my_fw::pre'],
}
~~~
3.) Then, declare the `my_fw::pre` and `my_fw::post` classes to satisfy dependencies. You can declare these classes using an External Node Classifier or the following code:
~~~puppet
class { ['my_fw::pre', 'my_fw::post']: }
~~~
4.) Include the `firewall` class to ensure the correct packages are installed.
~~~puppet
class { 'firewall': }
~~~
### Upgrading
Use these steps if you already have a version of the firewall module installed.
#### From version 0.2.0 and more recent
Upgrade the module with the puppet module tool as normal:
puppet module upgrade puppetlabs/firewall
## 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 for 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, for example, '000 accept all icmp requests'. _000_ runs first, _999_ 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, and rules in the `post` class, last.
In iptables, the title of the rule is stored using the comment feature of the underlying firewall subsystem. Values must match '/^\d+[[:graph:][:space:]]+$/'.
#### Examples of Default Rules
Basic accept ICMP request example:
~~~puppet
firewall { '000 accept all icmp requests':
proto => 'icmp',
action => 'accept',
}
~~~
Drop all:
~~~puppet
firewall { '999 drop all other requests':
action => 'drop',
}
~~~
#### Example of an IPv6 rule
IPv6 rules can be specified using the _ip6tables_ provider:
~~~puppet
firewall { '006 Allow inbound SSH (v6)':
dport => 22,
proto => tcp,
action => accept,
provider => 'ip6tables',
}
~~~
### 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
makes sense to create your firewall rules in the profiles, so they
remain close to the services managed by the profile.
This is an example of firewall rules in a profile:
~~~puppet
class profile::apache {
include apache
apache::vhost { 'mysite': ensure => present }
firewall { '100 allow http and https access':
dport => [80, 443],
proto => tcp,
action => accept,
}
}
~~~
### Rule inversion
Firewall rules may be inverted by prefixing the value of a parameter by "! ". If the value is an array, then every item in the array must be prefixed as iptables does not understand inverting a single value.
Parameters that understand inversion are: connmark, ctstate, destination, dport, dst\_range, dst\_type, iniface, outiface, port, proto, source, sport, src\_range, src\_type, and state.
Examples:
~~~puppet
firewall { '001 disallow esp protocol':
action => 'accept',
proto => '! esp',
}
firewall { '002 drop NEW external website packets with FIN/RST/ACK set and SYN unset':
chain => 'INPUT',
state => 'NEW',
action => 'drop',
proto => 'tcp',
sport => ['! http', '! 443'],
source => '! 10.0.0.0/8',
tcp_flags => '! FIN,SYN,RST,ACK SYN',
}
~~~
### Additional Uses for the Firewall Module
You can 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. Apply a rule to a node as follows:
~~~puppet
node 'some.node.com' {
firewall { '111 open port 111':
dport => 111,
}
}
~~~
You can also do more complex things with the `firewall` resource. This example sets up static NAT for the source network 10.1.2.0/24:
~~~puppet
firewall { '100 snat for network foo2':
chain => 'POSTROUTING',
jump => 'MASQUERADE',
proto => 'all',
outiface => 'eth0',
source => '10.1.2.0/24',
table => 'nat',
}
~~~
You can also change the TCP MSS value for VPN client traffic:
~~~puppet
firewall { '110 TCPMSS for VPN clients':
chain => 'FORWARD',
table => 'mangle',
source => '10.0.2.0/24',
proto => tcp,
tcp_flags => 'SYN,RST SYN',
mss => '1361:1541',
set_mss => '1360',
jump => 'TCPMSS',
}
~~~
The following will mirror all traffic sent to the server to a secondary host on the LAN with the TEE target:
~~~puppet
firewall { '503 Mirror traffic to IDS':
proto => all,
jump => 'TEE',
gateway => '10.0.0.2',
chain => 'PREROUTING',
table => 'mangle',
}
~~~
The following example creates a new chain and forwards any port 5000 access to it.
~~~puppet
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
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
Performs the basic setup tasks required for using the firewall resources.
At the moment this takes care of:
* iptables-persistent package installation
Include the `firewall` class for nodes that need to use the resources in this module:
class { 'firewall': }
#### ensure
Parameter that controls the state of the iptables service on your system, allowing you to disable iptables if you want.
`ensure` can either be 'running' or 'stopped'. Defaults to 'running'.
#### package
Specify the platform-specific package(s) to install. Defaults defined in `firewall::params`.
#### pkg_ensure
Parameter that controls the state of the iptables package on your system, allowing you to update it if you wish.
`ensure` can either be 'present' or 'latest'. Defaults to 'present'.
#### service
Specify the platform-specific service(s) to start or stop. Defaults defined in `firewall::params`.
###Type: firewall
This type enables you to manage firewall rules within Puppet.
#### Providers
**Note:** Not all features are available with all providers.
* `ip6tables`: Ip6tables type provider
* Required binaries: `ip6tables-save`, `ip6tables`.
* Supported features: `address_type`, `connection_limiting`, `dnat`, `hop_limiting`, `icmp_match`, `interface_match`, `iprange`, `ipsec_dir`, `ipsec_policy`, `ipset`, `iptables`, `isfirstfrag`, `ishasmorefrags`, `islastfrag`, `log_level`, `log_prefix`, `log_uid`, `mark`, `mask`, `mss`, `owner`, `pkttype`, `rate_limiting`, `recent_limiting`, `reject_type`, `snat`, `socket`, `state_match`, `tcp_flags`.
* `iptables`: Iptables type provider
* Required binaries: `iptables-save`, `iptables`.
* Default for `kernel` == `linux`.
* Supported features: `address_type`, `clusterip`, `connection_limiting`, `dnat`, `icmp_match`, `interface_match`, `iprange`, `ipsec_dir`, `ipsec_policy`, `ipset`, `iptables`, `isfragment`, `log_level`, `log_prefix`, `log_uid`, `mark`, `mask`, `mss`, `netmap`, `owner`, `pkttype`, `rate_limiting`, `recent_limiting`, `reject_type`, `snat`, `socket`, `state_match`, `tcp_flags`.
**Autorequires:**
If Puppet is managing the iptables or ip6tables chains specified in the `chain` or `jump` parameters, the firewall resource will autorequire those firewallchain resources.
If Puppet is managing the iptables or iptables-persistent packages, and the provider is iptables or ip6tables, the firewall resource will autorequire those packages to ensure that any required binaries are installed.
#### Features
* `address_type`: The ability to match on source or destination address type.
* `clusterip`: Configure a simple cluster of nodes that share a certain IP and MAC address without an explicit load balancer in front of them.
* `connection_limiting`: Connection limiting features.
* `dnat`: Destination NATing.
* `hop_limiting`: Hop limiting features.
* `icmp_match`: The ability to match ICMP types.
* `interface_match`: Interface matching.
* `iprange`: The ability to match on source or destination IP range.
* `ipsec_dir`: The ability to match IPsec policy direction.
* `ipsec_policy`: The ability to match IPsec policy.
* `iptables`: The provider provides iptables features.
* `isfirstfrag`: The ability to match the first fragment of a fragmented ipv6 packet.
* `isfragment`: The ability to match fragments.
* `ishasmorefrags`: The ability to match a non-last fragment of a fragmented ipv6 packet.
* `islastfrag`: The ability to match the last fragment of an ipv6 packet.
* `log_level`: The ability to control the log level.
* `log_prefix`: The ability to add prefixes to log messages.
* `log_uid`: The ability to log the userid of the process which generated the packet.
* `mark`: The ability to match or set the netfilter mark value associated with the packet.
* `mask`: The ability to match recent rules based on the ipv4 mask.
* `owner`: The ability to match owners.
* `pkttype`: The ability to match a packet type.
* `rate_limiting`: Rate limiting features.
* `recent_limiting`: The netfilter recent module.
* `reject_type`: The ability to control reject messages.
* `set_mss`: Set the TCP MSS of a packet.
* `snat`: Source NATing.
* `socket`: The ability to match open sockets.
* `state_match`: The ability to match stateful firewall states.
* `tcp_flags`: The ability to match on particular TCP flag settings.
* `netmap`: The ability to map entire subnets via source or destination nat rules.
#### Parameters
* `action`: This is the action to perform on a match. Valid values for this action are:
* 'accept': The packet is accepted.
* 'reject': The packet is rejected with a suitable ICMP response.
* 'drop': The packet is dropped.
If you specify no value it will simply match the rule but perform no action unless you provide a provider-specific parameter (such as `jump`).
* `burst`: Rate limiting burst value (per second) before limit checks apply. Values must match '/^\d+$/'. Requires the `rate_limiting` feature.
* `clusterip_new`: Create a new ClusterIP. You always have to set this on the first rule for a given ClusterIP. Requires the `clusterip` feature.
* `clusterip_hashmode`: Specify the hashing mode. Valid values are sourceip, sourceip-sourceport, sourceip-sourceport-destport. Requires the `clusterip` feature.
* `clusterip_clustermac`: Specify the ClusterIP MAC address. Has to be a link-layer multicast address. Requires the `clusterip` feature.
* `clusterip_total_nodes`: Number of total nodes within this cluster. Requires the `clusterip` feature.
* `clusterip_local_node`: Local node number within this cluster. Requires the `clusterip` feature.
* `clusterip_hash_init`: Specify the random seed used for hash initialization. Requires the `clusterip` feature.
* `chain`: Name of the chain to use. You can provide a user-based chain or use one of the following built-in chains:'INPUT','FORWARD','OUTPUT','PREROUTING', or 'POSTROUTING'. The default value is 'INPUT'. Values must match '/^[a-zA-Z0-9\-_]+$/'. Requires the `iptables` feature.
* `checksum_fill`: When using a `jump` value of 'CHECKSUM', this boolean makes sure that a checksum is calculated and filled in a packet that lacks a checksum. Valid values are 'true' or 'false'. Requires the `iptables` feature.
* `clamp_mss_to_pmtu`: Enables PMTU Clamping support when using a jump target of 'TCPMSS'. Valid values are 'true' or 'false'.
* `connlimit_above`: Connection limiting value for matched connections above n. Values must match '/^\d+$/'. Requires the `connection_limiting` feature.
* `connlimit_mask`: Connection limiting by subnet mask for matched connections. Apply a subnet mask of /0 to /32 for IPv4, and a subnet mask of /0 to /128 for IPv6. Values must match '/^\d+$/'. Requires the `connection_limiting` feature.
* `connmark`: Match the Netfilter mark value associated with the packet. Accepts values `mark/mask` or `mark`. These will be converted to hex if they are not hex already. Requires the `mark` feature.
* `ctstate`: Matches a packet based on its state in the firewall stateful inspection table, using the conntrack module. Valid values are: 'INVALID', 'ESTABLISHED', 'NEW', 'RELATED'. Requires the `state_match` feature.
* `date_start`: Start Date/Time for the rule to match, which must be in ISO 8601 "T" notation. The possible time range is '1970-01-01T00:00:00' to '2038-01-19T04:17:07'
* `date_stop`: End Date/Time for the rule to match, which must be in ISO 8601 "T" notation. The possible time range is '1970-01-01T00:00:00' to '2038-01-19T04:17:07'
* `destination`: The destination address to match. For example: `destination => '192.168.1.0/24'`. You can also negate a mask by putting ! in front. For example: `destination => '! 192.168.2.0/24'`. The destination can also be an IPv6 address if your provider supports it.
For some firewall providers you can pass a range of ports in the format: 'start number-end number'. For example, '1-1024' would cover ports 1 to 1024.
* `dport`: The destination port to match for this filter (if the protocol supports ports). Will accept a single element or an array. For some firewall providers you can pass a range of ports in the format: 'start number-end number'. For example, '1-1024' would cover ports 1 to 1024.
* `dst_range`: The destination IP range. For example: `dst_range => '192.168.1.1-192.168.1.10'`.
The destination IP range is must in 'IP1-IP2' format. Values in the range must be valid IPv4 or IPv6 addresses. Requires the `iprange` feature.
* `dst_type`: The destination address type. For example: `dst_type => 'LOCAL'`.
Valid values are:
* 'UNSPEC': an unspecified address
* 'UNICAST': a unicast address
* 'LOCAL': a local address
* 'BROADCAST': a broadcast address
* 'ANYCAST': an anycast packet
* 'MULTICAST': a multicast address
* 'BLACKHOLE': a blackhole address
* 'UNREACHABLE': an unreachable address
* 'PROHIBIT': a prohibited address
* 'THROW': an unroutable address
* 'XRESOLVE: an unresolvable address
Requires the `address_type` feature.
* `ensure`: Ensures that the resource is present. Valid values are 'present', 'absent'. The default is 'present'.
* `gateway`: Used with TEE target to mirror traffic of a machine to a secondary host on the LAN.
* `gid`: GID or Group owner matching rule. Accepts a string argument only, as iptables does not accept multiple gid in a single statement. Requires the `owner` feature.
* `hop_limit`: Hop limiting value for matched packets. Values must match '/^\d+$/'. Requires the `hop_limiting` feature.
* `icmp`: When matching ICMP packets, this indicates the type of ICMP packet to match. A value of 'any' is not supported. To match any type of ICMP packet, the parameter should be omitted or undefined. Requires the `icmp_match` feature.
* `iniface`: Input interface to filter on. Values must match '/^!?\s?[a-zA-Z0-9\-\._\+\:]+$/'. Requires the `interface_match` feature. Supports interface alias (eth0:0) and negation.
* `ipsec_dir`: Sets the ipsec policy direction. Valid values are 'in', 'out'. Requires the `ipsec_dir` feature.
* `ipsec_policy`: Sets the ipsec policy type. Valid values are 'none', 'ipsec'. Requires the `ipsec_policy` feature.
* `ipset`: Matches IP sets. Value must be 'ipset_name (src|dst|src,dst)' and can be negated by putting ! in front. Requires ipset kernel module.
* `isfirstfrag`: If true, matches when the packet is the first fragment of a fragmented ipv6 packet. Cannot be negated. Supported by ipv6 only. Valid values are 'true', 'false'. Requires the `isfirstfrag` feature.
* `isfragment`: If 'true', matches when the packet is a tcp fragment of a fragmented packet. Supported by iptables only. Valid values are 'true', 'false'. Requires features `isfragment`.
* `ishasmorefrags`: If 'true', matches when the packet has the 'more fragments' bit set. Supported by ipv6 only. Valid values are 'true', 'false'. Requires the `ishasmorefrags` feature.
* `islastfrag`: If true, matches when the packet is the last fragment of a fragmented ipv6 packet. Supported by ipv6 only. Valid values are 'true', 'false'. Requires the `islastfrag`.
* `jump`: The value for the iptables `--jump` parameter. Any valid chain name is allowed, but normal values are: 'QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK', 'TCPMSS', 'DSCP'.
For the values 'ACCEPT', 'DROP', and 'REJECT', you must use the generic `action` parameter. This is to enforce the use of generic parameters where possible for maximum cross-platform modeling.
If you set both `accept` and `jump` parameters, you will get an error, because only one of the options should be set. Requires the `iptables` feature.
* `kernel_timezone`: Use the kernel timezone instead of UTC to determine whether a packet meets the time regulations.
* `limit`: Rate limiting value for matched packets. The format is: 'rate/[/second/|/minute|/hour|/day]'. Example values are: '50/sec', '40/min', '30/hour', '10/day'. Requires the `rate_limiting` feature.
* `line`: Read-only property for caching the rule line.
* `log_level`: When combined with `jump => 'LOG'` specifies the system log level to log to. Requires the `log_level` feature.
* `log_prefix`: When combined with `jump => 'LOG'` specifies the log prefix to use when logging. Requires the `log_prefix` feature.
* `log_uid`: The ability to log the userid of the process which generated the packet.
* `mask`: Sets the mask to use when `recent` is enabled. Requires the `mask` feature.
* `month_days`: Only match on the given days of the month. Possible values are '1' to '31'. Note that specifying '31' will not match on months that do not have a 31st day; the same goes for 28- or 29-day February.
* `match_mark`: Match the Netfilter mark value associated with the packet. Accepts either of mark/mask or mark. These will be converted to hex if they are not already. Requires the `mark` feature.
* `mss`: Sets a given TCP MSS value or range to match.
* `name`: The canonical name of the rule. This name is also used for ordering, so make sure you prefix the rule with a number. For example:
~~~puppet
firewall { '000 this runs first':
# this rule will run first
}
firewall { '999 this runs last':
# this rule will run last
}
~~~
Depending on the provider, the name of the rule can be stored using the comment feature of the underlying firewall subsystem. Values must match '/^\d+[[:graph:][:space:]]+$/'.
* `outiface`: Output interface to filter on. Values must match '/^!?\s?[a-zA-Z0-9\-\._\+\:]+$/'. Requires the `interface_match` feature. Supports interface alias (eth0:0) and negation.
* `physdev_in`: Match if the packet is entering a bridge from the given interface. Values must match '/^[a-zA-Z0-9\-\._\+]+$/'.
* `physdev_out`: Match if the packet is leaving a bridge via the given interface. Values must match '/^[a-zA-Z0-9\-\._\+]+$/'.
* `physdev_is_bridged`: Match if the packet is transversing a bridge. Valid values are true or false.
* `pkttype`: Sets the packet type to match. Valid values are: 'unicast', 'broadcast', and'multicast'. Requires the `pkttype` feature.
* `port`: *DEPRECATED* Using the unspecific 'port' parameter can lead to firewall rules that are unexpectedly too lax. It is recommended to always use the specific dport and sport parameters to avoid this ambiguity. The destination or source port to match for this filter (if the protocol supports ports). Will accept a single element or an array. For some firewall providers you can pass a range of ports in the format: 'start number-end number'. For example, '1-1024' would cover ports 1 to 1024.
* `proto`: The specific protocol to match for this rule. This is 'tcp' by default. Valid values are:
* 'tcp'
* 'udp'
* 'icmp'
* 'ipv4'
* 'ipv6'
* 'ipv6-icmp'
* 'esp'
* 'ah'
* 'vrrp'
* 'igmp'
* 'ipencap'
* 'ospf'
* 'gre'
* 'all'
* `provider`: The specific backend to use for this firewall resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform. Available providers are ip6tables and iptables. See the [Providers](#providers) section above for details about these providers.
* `random`: When using a `jump` value of 'MASQUERADE', 'DNAT', 'REDIRECT', or 'SNAT', this boolean will enable randomized port mapping. Valid values are true or false. Requires the `dnat` feature.
* `rdest`: If boolean 'true', adds the destination IP address to the list. Valid values are true or false. Requires the `recent_limiting` feature and the `recent` parameter.
* `reap`: Can only be used in conjunction with the `rseconds` parameter. If boolean 'true', this will purge entries older than 'seconds' as specified in `rseconds`. Valid values are true or false. Requires the `recent_limiting` feature and the `recent` parameter.
* `recent`: Enable the recent module. Valid values are: 'set', 'update', 'rcheck', or 'remove'. For example:
~~~puppet
# If anyone's appeared on the 'badguy' blacklist within
# the last 60 seconds, drop their traffic, and update the timestamp.
firewall { '100 Drop badguy traffic':
recent => 'update',
rseconds => 60,
rsource => true,
rname => 'badguy',
action => 'DROP',
chain => 'FORWARD',
}
# No-one should be sending us traffic on eth0 from localhost
# Blacklist them
firewall { '101 blacklist strange traffic':
recent => 'set',
rsource => true,
rname => 'badguy',
destination => '127.0.0.0/8',
iniface => 'eth0',
action => 'DROP',
chain => 'FORWARD',
}
~~~
Requires the `recent_limiting` feature.
* `reject`: When combined with `jump => 'REJECT'`, you can specify a different ICMP response to be sent back to the packet sender. Requires the `reject_type` feature.
* `rhitcount`: Used in conjunction with `recent => 'update'` or `recent => 'rcheck'`. When used, this will narrow the match to happen only when the address is in the list and packets greater than or equal to the given value have been received. Requires the `recent_limiting` feature and the `recent` parameter.
* `rname`: Specify the name of the list. Takes a string argument. Requires the `recent_limiting` feature and the `recent` parameter.
* `rseconds`: Used in conjunction with `recent => 'rcheck'` or `recent => 'update'`. When used, this will narrow the match to only happen when the address is in the list and was seen within the last given number of seconds. Requires the `recent_limiting` feature and the `recent` parameter.
* `rsource`: If boolean 'true', adds the source IP address to the list. Valid values are 'true', 'false'. Requires the `recent_limiting` feature and the `recent` parameter.
* `rttl`: May only be used in conjunction with `recent => 'rcheck'` or `recent => 'update'`. If boolean 'true', this will narrow the match to happen only when the address is in the list and the TTL of the current packet matches that of the packet that hit the `recent => 'set'` rule. If you have problems with DoS attacks via bogus packets from fake source addresses, this parameter may help. Valid values are 'true', 'false'. Requires the `recent_limiting` feature and the `recent` parameter.
* `set_dscp`: When combined with `jump => 'DSCP'` specifies the dscp marking associated with the packet.
* `set_dscp_class`: When combined with `jump => 'DSCP'` specifies the class associated with the packet (valid values found here: http://www.cisco.com/c/en/us/support/docs/quality-of-service-qos/qos-packet-marking/10103-dscpvalues.html#packetclassification).
* `set_mark`: Set the Netfilter mark value associated with the packet. Accepts either 'mark/mask' or 'mark'. These will be converted to hex if they are not already. Requires the `mark` feature.
* `set_mss`: When combined with `jump => 'TCPMSS'` specifies the value of the MSS field.
* `socket`: If 'true', matches if an open socket can be found by doing a socket lookup on the packet. Valid values are 'true', 'false'. Requires the `socket` feature.
* `source`: The source address. For example: `source => '192.168.2.0/24'`. You can also negate a mask by putting ! in front. For example: `source => '! 192.168.2.0/24'`. The source can also be an IPv6 address if your provider supports it.
* `sport`: The source port to match for this filter (if the protocol supports ports). Will accept a single element or an array. For some firewall providers you can pass a range of ports in the format:'start number-end number'. For example, '1-1024' would cover ports 1 to 1024.
* `src_range`: The source IP range. For example: `src_range => '192.168.1.1-192.168.1.10'`. The source IP range must be in 'IP1-IP2' format. Values in the range must be valid IPv4 or IPv6 addresses. Requires the `iprange` feature.
* `src_type`: Specify the source address type. For example: `src_type => 'LOCAL'`.
Valid values are:
* 'UNSPEC': an unspecified address.
* 'UNICAST': a unicast address.
* 'LOCAL': a local address.
* 'BROADCAST': a broadcast address.
* 'ANYCAST': an anycast packet.
* 'MULTICAST': a multicast address.
* 'BLACKHOLE': a blackhole address.
* 'UNREACHABLE': an unreachable address.
* 'PROHIBIT': a prohibited address.
* 'THROW': an unroutable address.
* 'XRESOLVE': an unresolvable address.
Requires the `address_type` feature.
* `stat_every`: Match one packet every nth packet. Requires `stat_mode => 'nth'`
* `stat_mode`: Set the matching mode for statistic matching. Supported modes are `random` and `nth`.
* `stat_packet`: Set the initial counter value for the nth mode. Must be between 0 and the value of `stat_every`. Defaults to 0. Requires `stat_mode => 'nth'`
* `stat_probability`: Set the probability from 0 to 1 for a packet to be randomly matched. It works only with `stat_mode => 'random'`.
* `state`: Matches a packet based on its state in the firewall stateful inspection table. Valid values are: 'INVALID', 'ESTABLISHED', 'NEW', 'RELATED'. Requires the `state_match` feature.
* `table`: Table to use. Valid values are: 'nat', 'mangle', 'filter', 'raw', 'rawpost'. By default the setting is 'filter'. Requires the `iptables` feature.
* `tcp_flags`: Match when the TCP flags are as specified. Set as a string with a list of comma-separated flag names for the mask, then a space, then a comma-separated list of flags that should be set. The flags are: 'SYN', 'ACK', 'FIN', 'RST', 'URG', 'PSH', 'ALL', 'NONE'.
Note that you specify flags in the order that iptables `--list` rules would list them to avoid having Puppet think you changed the flags. For example, 'FIN,SYN,RST,ACK SYN' matches packets with the SYN bit set and the ACK, RST and FIN bits cleared. Such packets are used to request TCP connection initiation. Requires the `tcp_flags` feature.
* `time_contiguous`: When the `time_stop` value is smaller than the `time_start` value, match this as a single time period instead of distinct intervals.
* `time_start`: Start time for the rule to match. The possible time range is '00:00:00' to '23:59:59'. Leading zeroes are allowed (e.g. '06:03') and correctly interpreted as base-10.
* `time_stop`: End time for the rule to match. The possible time range is '00:00:00' to '23:59:59'. Leading zeroes are allowed (e.g. '06:03') and correctly interpreted as base-10.
* `todest`: When using `jump => 'DNAT'`, you can specify the new destination address using this parameter. Requires the `dnat` feature.
* `toports`: For DNAT this is the port that will replace the destination port. Requires the `dnat` feature.
* `tosource`: When using `jump => 'SNAT'`, you can specify the new source address using this parameter. Requires the `snat` feature.
* `to`: When using `jump => 'NETMAP'`, you can specify a source or destination subnet to nat to. Requires the `netmap` feature`.
* `uid`: UID or Username owner matching rule. Accepts a string argument only, as iptables does not accept multiple uid in a single statement. Requires the `owner` feature.
* `week_days`: Only match on the given weekdays. Possible values are 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'.
### Type: firewallchain
Enables you to manage rule chains for firewalls.
Currently this type supports only iptables, ip6tables, and ebtables on Linux. It also 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.
#### Providers
`iptables_chain` is the only provider that supports firewallchain.
#### Features
* `iptables_chain`: The provider provides iptables chain features.
* `policy`: Default policy (inbuilt chains only).
#### Parameters
* `ensure`: Ensures that the resource is present. Valid values are 'present', 'absent'.
* `ignore`: 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: a regex such as '/foo/i' can be written as '(?i)foo' or '(?i:foo)'. Only when purge is 'true'.
Full example:
~~~puppet
firewallchain { 'INPUT:filter:IPv4':
purge => true,
ignore => [
# ignore the fail2ban jump rule
'-j fail2ban-ssh',
# ignore any rules with "ignore" (case insensitive) in the comment in the rule
'--comment "[^"](?i:ignore)[^"]"',
],
}
~~~
* `name`: Specify the canonical name of the chain. For iptables the format must be {chain}:{table}:{protocol}.
* `policy`: Set the action the packet will perform when the end of the chain is reached. It can only be set on inbuilt chains ('INPUT', 'FORWARD', 'OUTPUT', 'PREROUTING', 'POSTROUTING'). Valid values are:
* '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 to the default of inbuilt chains.
* `provider`: The specific backend to use for this firewallchain resource. You will seldom need to specify this --- Puppet will usually discover the appropriate provider for your platform. The only available provider is:
`iptables_chain`: iptables chain provider
* Required binaries: `ebtables-save`, `ebtables`, `ip6tables-save`, `ip6tables`, `iptables-save`, `iptables`.
* Default for `kernel` == `linux`.
* Supported features: `iptables_chain`, `policy`.
* `purge`: Purge unmanaged firewall rules in this chain. Valid values are 'false', 'true'.
**Note** This `purge` is purging unmanaged rules in a firewall chain, not unmanaged firewall chains. To purge unmanaged firewall chains, use the following instead.
~~~puppet
resources { 'firewallchain':
purge => true,
}
~~~
### Fact: ip6tables_version
A Facter fact that can be used to determine what the default version of ip6tables is for your operating system/distribution.
### Fact: iptables_version
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.
### Debian 8 Support
As Puppet Enterprise itself does not yet support Debian 8, use of this module with Puppet Enterprise under a Debian 8
system should be regarded as experimental.
### Known Issues
#### MCollective causes PE to reverse firewall rule order
Firewall rules appear in reverse order if you use MCollective to run Puppet in Puppet Enterprise 2016.1, 2015.3, 2015.2, or 3.8.x.
If you use MCollective to kick off Puppet runs (`mco puppet runonce -I agent.example.com`) while also using the [`puppetlabs/firewall`](https://forge.puppet.com/puppetlabs/firewall) module, your firewall rules might be listed in reverse order.
In many firewall configurations, the last rule drops all packets. If the rule order is reversed, this rule is listed first and network connectivity fails.
To prevent this issue, do not use MCollective to kick off Puppet runs. Use any of the following instead:
* Run `puppet agent -t` on the command line.
* Use a cron job.
* Click [Run Puppet](https://docs.puppet.com/pe/2016.1/console_classes_groups_running_puppet.html#run-puppet-on-an-individual-node) in the console.
#### Reporting Issues
Report found bugs in JIRA:
<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 cant 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)
### 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:
RS_SET=ubuntu-1404-x64 rspec spec/acceptance
RS_SET=centos-64-x64 rspec spec/acceptance

View File

@@ -0,0 +1,11 @@
require 'puppet_blacksmith/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'
require 'puppetlabs_spec_helper/rake_tasks'
PuppetLint.configuration.fail_on_warnings = true
PuppetLint.configuration.send('relative')
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.send('disable_class_inherits_from_params_class')
PuppetLint.configuration.send('disable_documentation')
PuppetLint.configuration.send('disable_single_quote_string_with_variables')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]

View File

@@ -0,0 +1,100 @@
{
"CHANGELOG.md": "6107b02f7aaff96123903e98ca197e0f",
"CONTRIBUTING.md": "ad65d271f183b5adb9fdd58207939f5f",
"Gemfile": "4c929078ebcde3437eacd8b1d2687d71",
"LICENSE": "ade7f2bb88b5b4f034152822222ec314",
"README.markdown": "5587278f9a907d136ffcf690340b5107",
"Rakefile": "15f2f143e7515282edf801dc7e94a544",
"lib/facter/ip6tables_version.rb": "091123ad703f1706686bca4398c5b06f",
"lib/facter/iptables_persistent_version.rb": "8ea76fecb8032174823ef6fb846c83c9",
"lib/facter/iptables_version.rb": "facbd760223f236538b731c1d1f6cf8f",
"lib/puppet/provider/firewall/ip6tables.rb": "d4c147434e94cf7da020d05a76872aaf",
"lib/puppet/provider/firewall/iptables.rb": "679164a3d8f28850215a639869c9585b",
"lib/puppet/provider/firewall.rb": "32d2f5e5dcc082986b82ef26a119038b",
"lib/puppet/provider/firewallchain/iptables_chain.rb": "4d0432b240e8ecd46094a64462641e15",
"lib/puppet/type/firewall.rb": "4ba21ccd1f849609715a5ab2b0efa9cd",
"lib/puppet/type/firewallchain.rb": "9262061ade5c73329db773b769ee70a2",
"lib/puppet/util/firewall.rb": "9da19e4b7365e3267e81a6aca2e3fb86",
"lib/puppet/util/ipcidr.rb": "e1160dfd6e73fc5ef2bb8abc291f6fd5",
"manifests/init.pp": "527c0a1c0b136a2a7ea039b0e5d3aec8",
"manifests/linux/archlinux.pp": "692a236965597cfba264a2e361aa3522",
"manifests/linux/debian.pp": "8103642b9061d18fe5c70cd3af695bf7",
"manifests/linux/gentoo.pp": "225a291a80afacc8d5f01a21825c0c5a",
"manifests/linux/redhat.pp": "37108897663df4c1e23d0981ad74f5a6",
"manifests/linux.pp": "5a4a217057cb8a14560e6e265d830a7b",
"manifests/params.pp": "a2d631a8d169210b602a3b3d31764130",
"metadata.json": "abdc3d586960740d1e513077c3a25f1c",
"spec/acceptance/change_source_spec.rb": "68974434c7434340ed659ece23dd1668",
"spec/acceptance/class_spec.rb": "1c45fb508e35355f18125c458da03c3b",
"spec/acceptance/connlimit_spec.rb": "3cd6e1ce8a1bbf60a6301d874c39db8f",
"spec/acceptance/connmark_spec.rb": "1617263f5d1e036b63060f65b03a1d3d",
"spec/acceptance/firewall_bridging_spec.rb": "4a660611d2c9e3e4439cb37838b11783",
"spec/acceptance/firewall_clusterip_spec.rb": "2ff89c8bd5985d9133537b0c80ffbffa",
"spec/acceptance/firewall_dscp_spec.rb": "8b99f2095f53b04e276c4bfb309de47d",
"spec/acceptance/firewall_iptmodules_spec.rb": "4f2293976483fd3d17a686e975e0813a",
"spec/acceptance/firewall_mss_spec.rb": "b76ace05ee2ef9442d673f2a31da976d",
"spec/acceptance/firewall_spec.rb": "225d462320fb580df8ac0eea533b78b5",
"spec/acceptance/firewall_tee_spec.rb": "56e38741eb3aacb3f68565e2e386b220",
"spec/acceptance/firewall_time_spec.rb": "206cf26d6c5e0d013b0529b2edcc9f43",
"spec/acceptance/firewall_uid_spec.rb": "5a44538e247ab2234bbeca539558b10e",
"spec/acceptance/firewallchain_spec.rb": "978288d450b6cd5374182b894361791c",
"spec/acceptance/invert_spec.rb": "7931641b8594d219afdb3abe1e50b920",
"spec/acceptance/ip6_fragment_spec.rb": "22cf51568533fc918407a034cf9f1bc4",
"spec/acceptance/isfragment_spec.rb": "2ec3f2201d87371516939e40b4f60445",
"spec/acceptance/match_mark_spec.rb": "48b6fa5fe9373b5376101e5e074c21d6",
"spec/acceptance/nodesets/centos-7-x64.yml": "349128a7cfa9a953fb9af67cad52442a",
"spec/acceptance/nodesets/debian-8-x64.yml": "d2d2977900989f30086ad251a14a1f39",
"spec/acceptance/nodesets/default.yml": "b42da5a1ea0c964567ba7495574b8808",
"spec/acceptance/nodesets/docker/centos-7.yml": "8a3892807bdd62306ae4774f41ba11ae",
"spec/acceptance/nodesets/docker/debian-8.yml": "ac8e871d1068c96de5e85a89daaec6df",
"spec/acceptance/nodesets/docker/ubuntu-14.04.yml": "dc42ee922a96908d85b8f0f08203ce58",
"spec/acceptance/nodesets/new/aio/debian-8-64mda.yml": "3cb5b076f8ff1ce0123f7002d1132aa1",
"spec/acceptance/nodesets/new/aio/redhat-6-64mda.yml": "2da8ffed4efeb44fb78b686a06195116",
"spec/acceptance/nodesets/new/aio/redhat-7-64mda.yml": "200ba1439bddf26dd611b5f315e3b955",
"spec/acceptance/nodesets/new/aio/ubuntu-1404-64mda.yml": "41256594850e488f033ec30327139162",
"spec/acceptance/nodesets/new/pe/centos-5-64mda.yml": "192e4434be158fb238b8088f5d9adce0",
"spec/acceptance/nodesets/new/pe/centos-6-64mda.yml": "13947041961996723a56bfa35289919a",
"spec/acceptance/nodesets/new/pe/centos-7-64mda.yml": "5ff3e38e023c75ce2ace69a5ccadbf33",
"spec/acceptance/nodesets/new/pe/debian-6-64mda.yml": "ff79b672c5a532409c492ad0d45dc0ad",
"spec/acceptance/nodesets/new/pe/debian-7-64mda.yml": "63fefd7c9395781748a8b63d08dfd86c",
"spec/acceptance/nodesets/new/pe/debian-8-64mda.yml": "ed8e721a3d96556d92a9a1806171987b",
"spec/acceptance/nodesets/new/pe/oracle-6-64mda.yml": "3e0fbe6f9516023816c85884f0fdc839",
"spec/acceptance/nodesets/new/pe/oracle-7-64mda.yml": "b5f568cf11baafdf98f0127220527219",
"spec/acceptance/nodesets/new/pe/redhat-5-64mda.yml": "3c07dc9d83607f0f04a6903a77f5825d",
"spec/acceptance/nodesets/new/pe/redhat-6-64mda.yml": "886858b5f4ddcd484da013eca85d996e",
"spec/acceptance/nodesets/new/pe/redhat-7-64mda.yml": "199c73bd7860987d5af932164b5603e7",
"spec/acceptance/nodesets/new/pe/scientific-5-64mda.yml": "4d23f6b5aa75033ddd9077588ee605a9",
"spec/acceptance/nodesets/new/pe/scientific-6-64mda.yml": "342a75a0f2a8e45b7f317d34a35fdb77",
"spec/acceptance/nodesets/new/pe/scientific-7-64mda.yml": "5952c337b5dfd11ddbc28cd7936630db",
"spec/acceptance/nodesets/new/pe/sles-10-64mda.yml": "6b71985abd427432bdb2f9e8b76087fe",
"spec/acceptance/nodesets/new/pe/sles-11-64mda.yml": "5ed55d2324395308cab72ec6e0545967",
"spec/acceptance/nodesets/new/pe/sles-12-64mda.yml": "bc2fc9c59161c30b69fe907d65a138dc",
"spec/acceptance/nodesets/new/pe/ubuntu-1004-64mda.yml": "07bef5f739c27a6a62945adbd2e8813e",
"spec/acceptance/nodesets/new/pe/ubuntu-1204-64mda.yml": "c689063fd68a3c3ba3c47ced3016fa0e",
"spec/acceptance/nodesets/new/pe/ubuntu-1404-64mda.yml": "d0f11e054810e240ba4efdf3d08de815",
"spec/acceptance/params_spec.rb": "68260bd4d660ac7d0d48dbd0ad67db9e",
"spec/acceptance/purge_spec.rb": "300c5bfea39594d7ef770b7a68a231a7",
"spec/acceptance/resource_cmd_spec.rb": "a6f1ecf14dc07107f08fe4c86abd3757",
"spec/acceptance/rules_spec.rb": "6a331e90b860fc8ce7a77b4930bfe81e",
"spec/acceptance/socket_spec.rb": "20cb65db2dd2d78e244d04e2a8080f6e",
"spec/acceptance/standard_usage_spec.rb": "c8b32c414f664525e4db6379c933d56c",
"spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c",
"spec/spec_helper.rb": "2c6d82bcf3d61771154b1a2a4c3527f9",
"spec/spec_helper_acceptance.rb": "8a5b3695371ec0fe091a6dce233c03fe",
"spec/spec_helper_local.rb": "2282ca978a87d98971655e94b5e88860",
"spec/unit/classes/firewall_linux_archlinux_spec.rb": "3f9ce56d2069fb971423b3df24de1e3a",
"spec/unit/classes/firewall_linux_debian_spec.rb": "03f6a6eb35f8cc30b4932dddae9a8855",
"spec/unit/classes/firewall_linux_gentoo_spec.rb": "c23c922f2e07ff25ba9de032cb182983",
"spec/unit/classes/firewall_linux_redhat_spec.rb": "aeb5e9fbdf278748a9459efa8fe39dbb",
"spec/unit/classes/firewall_linux_spec.rb": "ebfd11aa2903c91aa9505851fe060dbb",
"spec/unit/classes/firewall_spec.rb": "493e711ba35eb305491f7723ee6e1c73",
"spec/unit/facter/iptables_persistent_version_spec.rb": "c1192cf6b7ffbec614b00e16d5cce0bd",
"spec/unit/facter/iptables_spec.rb": "5265be1e6ec652184de8b314ae396d1a",
"spec/unit/puppet/provider/ip6tables_spec.rb": "67b945e26f9e31c16fd410818649b072",
"spec/unit/puppet/provider/iptables_chain_spec.rb": "f45cc8b7ef1ea3264f1331d045b815a5",
"spec/unit/puppet/provider/iptables_spec.rb": "c6db5ca0fb03331ceab59fdb05d614c6",
"spec/unit/puppet/type/firewall_spec.rb": "3b06117740defc998d90695639a66b60",
"spec/unit/puppet/type/firewallchain_spec.rb": "9e53af354734ce00a7e14e4e0335954f",
"spec/unit/puppet/util/firewall_spec.rb": "f7d1e4b74561fda330a231fdf973a12e",
"spec/unit/puppet/util/ipcidr_spec.rb": "1a6eeb2dd7c9634fcfb60d8ead6e1d79"
}

View File

@@ -0,0 +1,85 @@
######################################################
##### Update - REMOVED WHEN DEPENDENCY CODE DONE #####
######################################################
exec { 'update':
command => '/usr/bin/apt-get update',
}
######################################################
##### Purge all existing firewall rules (if any) #####
######################################################
resources { 'firewall':
purge => true,
}
#####################################################
##### Default rules defined before custom rules #####
#####################################################
class 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 reject local traffic not on loopback interface':
iniface => '! lo',
proto => 'all',
destination => '127.0.0.1/8',
action => 'reject',
}->
firewall { '003 accept related established rules':
proto => 'all',
state => ['RELATED', 'ESTABLISHED'],
action => 'accept',
}
}
######################################################
##### Custom rules defined between default rules #####
######################################################
# firewall { '004 custom rule example':
# proto => 'all',
# state => ['RELATED', 'ESTABLISHED'],
# action => 'accept',
# }
#
# firewall { '005 custom rule example':
# proto => 'all',
# state => ['RELATED', 'ESTABLISHED'],
# action => 'accept',
# }
#
# firewall { '006 custom rule example':
# proto => 'all',
# state => ['RELATED', 'ESTABLISHED'],
# action => 'drop',
# }
####################################################
##### Default rules defined after custom rules #####
####################################################
class post {
firewall { '999 drop all':
proto => 'all',
action => 'drop',
before => undef,
}
}
Firewall {
before => Class['post'],
require => Class['pre'],
}
class { ['pre', 'post']: }
class { 'firewall': }

View 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

View File

@@ -0,0 +1,22 @@
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.
os = Facter.value(:operatingsystem)
os_release = Facter.value(:operatingsystemrelease)
if (os == 'Debian' and (Puppet::Util::Package.versioncmp(os_release, '8.0') >= 0)) or
(os == 'Ubuntu' and (Puppet::Util::Package.versioncmp(os_release, '14.10') >= 0))
cmd = "dpkg-query -Wf '${Version}' netfilter-persistent 2>/dev/null"
else
cmd = "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null"
end
version = Facter::Util::Resolution.exec(cmd)
if version.nil? or !version.match(/\d+\.\d+/)
nil
else
version
end
end
end

View 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

View 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

View File

@@ -0,0 +1,229 @@
Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :ip6tables 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 :log_uid
has_feature :mark
has_feature :mss
has_feature :tcp_flags
has_feature :pkttype
has_feature :ishasmorefrags
has_feature :islastfrag
has_feature :isfirstfrag
has_feature :socket
has_feature :address_type
has_feature :iprange
has_feature :ipsec_dir
has_feature :ipsec_policy
has_feature :mask
has_feature :ipset
optional_commands({
:ip6tables => 'ip6tables',
:ip6tables_save => 'ip6tables-save',
})
confine :kernel => :linux
ip6tables_version = Facter.value('ip6tables_version')
if (ip6tables_version and Puppet::Util::Package.versioncmp(ip6tables_version, '1.4.1') < 0)
mark_flag = '--set-mark'
else
mark_flag = '--set-xmark'
end
def initialize(*args)
ip6tables_version = Facter.value('ip6tables_version')
if ip6tables_version and ip6tables_version.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",
:checksum_fill => "--checksum-fill",
:clamp_mss_to_pmtu => "--clamp-mss-to-pmtu",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dport => ["-m multiport --dports", "--dport"],
:dst_range => '--dst-range',
:dst_type => "--dst-type",
:gateway => "--gateway",
:gid => "--gid-owner",
:hop_limit => "-m hl --hl-eq",
:icmp => "-m icmp6 --icmpv6-type",
:iniface => "-i",
:ipsec_dir => "-m policy --dir",
:ipsec_policy => "--pol",
:ipset => "-m set --match-set",
:isfirstfrag => "-m frag --fragid 0 --fragfirst",
:ishasmorefrags => "-m frag --fragid 0 --fragmore",
:islastfrag => "-m frag --fragid 0 --fraglast",
:jump => "-j",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:log_uid => "--log-uid",
:mask => "--mask",
:match_mark => "-m mark --mark",
:name => "-m comment --comment",
:mac_source => ["-m mac --mac-source", "--mac-source"],
:mss => "-m tcpmss --mss",
:outiface => "-o",
:pkttype => "-m pkttype --pkt-type",
: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",
:set_dscp => '--set-dscp',
:set_dscp_class => '--set-dscp-class',
:set_mark => mark_flag,
:set_mss => '--set-mss',
:socket => "-m socket",
:source => "-s",
:sport => ["-m multiport --sports", "--sport"],
:src_range => '--src-range',
:src_type => "--src-type",
:stat_every => '--every',
:stat_mode => "-m statistic --mode",
:stat_packet => '--packet',
:stat_probability => '--probability',
:state => "-m state --state",
:table => "-t",
:tcp_flags => "-m tcp --tcp-flags",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:uid => "--uid-owner",
:physdev_in => "--physdev-in",
:physdev_out => "--physdev-out",
:physdev_is_bridged => "--physdev-is-bridged",
:date_start => "--datestart",
:date_stop => "--datestop",
:time_start => "--timestart",
:time_stop => "--timestop",
:month_days => "--monthdays",
:week_days => "--weekdays",
:time_contiguous => "--contiguous",
:kernel_timezone => "--kerneltz",
}
# These are known booleans that do not take a value, but we want to munge
# to true if they exist.
@known_booleans = [
:checksum_fill,
:clamp_mss_to_pmtu,
:ishasmorefrags,
:islastfrag,
:isfirstfrag,
:log_uid,
:rsource,
:rdest,
:reap,
:rttl,
:socket,
:physdev_is_bridged,
:time_contiguous,
:kernel_timezone,
]
# Properties that use "-m <ipt module name>" (with the potential to have multiple
# arguments against the same IPT module) must be in this hash. The keys in this
# hash are the IPT module names, with the values being an array of the respective
# supported arguments for this IPT module.
#
# ** IPT Module arguments must be in order as they would appear in iptables-save **
#
# Exceptions:
# => multiport: (For some reason, the multiport arguments can't be)
# specified within the same "-m multiport", but works in seperate
# ones.
#
@module_to_argument_mapping = {
:physdev => [:physdev_in, :physdev_out, :physdev_is_bridged],
:addrtype => [:src_type, :dst_type],
:iprange => [:src_range, :dst_range],
:owner => [:uid, :gid],
:time => [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone]
}
# 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, :physdev_in,
:physdev_out, :physdev_is_bridged, :proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :src_range, :dst_range,
:tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port, :src_type,
:dst_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy, :state,
:ctstate, :icmp, :hop_limit, :limit, :burst, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clamp_mss_to_pmtu, :gateway, :todest,
:tosource, :toports, :checksum_fill, :log_level, :log_prefix, :log_uid, :reject, :set_mss, :set_dscp, :set_dscp_class, :mss,
:set_mark, :match_mark, :connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone]
end

View File

@@ -0,0 +1,741 @@
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 :netmap
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 :log_uid
has_feature :mark
has_feature :mss
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
has_feature :ipset
has_feature :clusterip
optional_commands({
:iptables => 'iptables',
:iptables_save => 'iptables-save',
})
defaultfor :kernel => :linux
confine :kernel => :linux
iptables_version = Facter.value('iptables_version')
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",
:checksum_fill => "--checksum-fill",
:clamp_mss_to_pmtu => "--clamp-mss-to-pmtu",
:connlimit_above => "-m connlimit --connlimit-above",
:connlimit_mask => "--connlimit-mask",
:connmark => "-m connmark --mark",
:ctstate => "-m conntrack --ctstate",
:destination => "-d",
:dport => ["-m multiport --dports", "--dport"],
:dst_range => "--dst-range",
:dst_type => "--dst-type",
:gateway => "--gateway",
:gid => "--gid-owner",
:icmp => "-m icmp --icmp-type",
:iniface => "-i",
:ipsec_dir => "-m policy --dir",
:ipsec_policy => "--pol",
:ipset => "-m set --match-set",
:isfragment => "-f",
:jump => "-j",
:limit => "-m limit --limit",
:log_level => "--log-level",
:log_prefix => "--log-prefix",
:log_uid => "--log-uid",
:mac_source => ["-m mac --mac-source", "--mac-source"],
:mask => '--mask',
:match_mark => "-m mark --mark",
:mss => '-m tcpmss --mss',
:name => "-m comment --comment",
:outiface => "-o",
:pkttype => "-m pkttype --pkt-type",
: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_dscp => '--set-dscp',
:set_dscp_class => '--set-dscp-class',
:set_mark => mark_flag,
:set_mss => '--set-mss',
:socket => "-m socket",
:source => "-s",
:sport => ["-m multiport --sports", "--sport"],
:src_range => "--src-range",
:src_type => "--src-type",
:stat_every => '--every',
:stat_mode => "-m statistic --mode",
:stat_packet => '--packet',
:stat_probability => '--probability',
:state => "-m state --state",
:table => "-t",
:tcp_flags => "-m tcp --tcp-flags",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
:to => "--to",
:uid => "--uid-owner",
:physdev_in => "--physdev-in",
:physdev_out => "--physdev-out",
:physdev_is_bridged => "--physdev-is-bridged",
:date_start => "--datestart",
:date_stop => "--datestop",
:time_start => "--timestart",
:time_stop => "--timestop",
:month_days => "--monthdays",
:week_days => "--weekdays",
:time_contiguous => "--contiguous",
:kernel_timezone => "--kerneltz",
:clusterip_new => "--new",
:clusterip_hashmode => "--hashmode",
:clusterip_clustermac => "--clustermac",
:clusterip_total_nodes => "--total-nodes",
:clusterip_local_node => "--local-node",
:clusterip_hash_init => "--hash-init",
}
# These are known booleans that do not take a value, but we want to munge
# to true if they exist.
@known_booleans = [
:checksum_fill,
:clamp_mss_to_pmtu,
:isfragment,
:log_uid,
:random,
:rdest,
:reap,
:rsource,
:rttl,
:socket,
:physdev_is_bridged,
:time_contiguous,
:kernel_timezone,
:clusterip_new,
]
# Properties that use "-m <ipt module name>" (with the potential to have multiple
# arguments against the same IPT module) must be in this hash. The keys in this
# hash are the IPT module names, with the values being an array of the respective
# supported arguments for this IPT module.
#
# ** IPT Module arguments must be in order as they would appear in iptables-save **
#
# Exceptions:
# => multiport: (For some reason, the multiport arguments can't be)
# specified within the same "-m multiport", but works in seperate
# ones.
#
@module_to_argument_mapping = {
:physdev => [:physdev_in, :physdev_out, :physdev_is_bridged],
:addrtype => [:src_type, :dst_type],
:iprange => [:src_range, :dst_range],
:owner => [:uid, :gid],
:time => [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone]
}
def self.munge_resource_map_from_existing_values(resource_map_original, compare)
resource_map_new = resource_map_original.clone
@module_to_argument_mapping.each do |ipt_module, arg_array|
arg_array.each do |argument|
if resource_map_original[argument].is_a?(Array)
if compare.include?(resource_map_original[argument].first)
resource_map_new[argument] = resource_map_original[argument].clone
resource_map_new[argument][0] = "-m #{ipt_module.to_s} #{resource_map_original[argument].first}"
break
end
else
if compare.include?(resource_map_original[argument])
resource_map_new[argument] = "-m #{ipt_module.to_s} #{resource_map_original[argument]}"
break
end
end
end
end
resource_map_new
end
def munge_resource_map_from_resource(resource_map_original, compare)
resource_map_new = resource_map_original.clone
module_to_argument_mapping = self.class.instance_variable_get('@module_to_argument_mapping')
module_to_argument_mapping.each do |ipt_module, arg_array|
arg_array.each do |argument|
if compare[argument]
if resource_map_original[argument].is_a?(Array)
resource_map_new[argument] = resource_map_original[argument].clone
resource_map_new[argument][0] = "-m #{ipt_module.to_s} #{resource_map_original[argument].first}"
else
resource_map_new[argument] = "-m #{ipt_module.to_s} #{resource_map_original[argument]}"
end
break
end
end
end
resource_map_new
end
# 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, :physdev_in, :physdev_out, :physdev_is_bridged, :proto, :isfragment,
:stat_mode, :stat_every, :stat_packet, :stat_probability,
:src_range, :dst_range, :tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port,
:src_type, :dst_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy,
:state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clusterip_new, :clusterip_hashmode,
:clusterip_clustermac, :clusterip_total_nodes, :clusterip_local_node, :clusterip_hash_init,
:clamp_mss_to_pmtu, :gateway, :set_mss, :set_dscp, :set_dscp_class, :todest, :tosource, :toports, :to, :checksum_fill, :random, :log_prefix,
:log_level, :log_uid, :reject, :set_mark, :match_mark, :mss, :connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop,
:month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone
]
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]
begin
iptables delete_args
rescue Puppet::ExecutionFailure => e
# Check to see if the iptables rule is already gone. This can sometimes
# happen as a side effect of other resource changes. If it's not gone,
# raise the error as per usual.
raise e unless self.resource.property(:ensure).insync?(:absent)
# If it's already gone, there is no error. Still record a change, but
# adjust the change message to indicate ambiguity over what work Puppet
# actually did to remove the resource, vs. what could have been a side
# effect of something else puppet did.
resource.property(:ensure).singleton_class.send(:define_method, :change_to_s) do |a,b|
"ensured absent"
end
end
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.gsub(/(!\s+)?--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1\2 \3"')
# ditto for --match-set
values = values.sub(/(!\s+)?--match-set (\S*) (\S*)/, '--match-set "\1\2 \3"')
# we do a similar thing for negated address masks (source and destination).
values = values.gsub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"')
# the actual rule will have the ! mark before the option.
values = values.gsub(/(!)\s*(-\S+)\s*(\S*)/, '\2 "\1 \3"')
# The match extension for tcp & udp are optional and throws off the @resource_map.
values = values.gsub(/(?!-m tcp --tcp-flags)-m (tcp|udp) /, '')
# There is a bug in EL5 which puts 2 spaces before physdev, so we fix it
values = values.gsub(/\s{2}--physdev/, ' --physdev')
# '--pol ipsec' takes many optional arguments; we cheat again by adding " around them
values = values.sub(/
--pol\sipsec
(\s--strict)?
(\s--reqid\s\S+)?
(\s--spi\s\S+)?
(\s--proto\s\S+)?
(\s--mode\s\S+)?
(\s--tunnel-dst\s\S+)?
(\s--tunnel-src\s\S+)?
(\s--next)?/x,
'--pol "ipsec\1\2\3\4\5\6\7\8" '
)
# on some iptables versions, --connlimit-saddr switch is added after the rule is applied
values = values.gsub(/--connlimit-saddr/, '')
resource_map = munge_resource_map_from_existing_values(@resource_map, values)
# 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(/\s-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}\s/)
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 by scanning arguments off the values
# string, handling any quoted characters present in the value, and then
# zipping the values with the array of keys.
keys.zip(values.scan(/("([^"\\]|\\.)*"|\S+)/).transpose[0].reverse) do |f, v|
if v =~ /^".*"$/ then
hash[f] = v.sub(/^"(.*)"$/, '\1').gsub(/\\(\\|'|")/, '\1')
else
hash[f] = v.dup
end
end
#####################
# POST PARSE CLUDGING
#####################
[:dport, :sport, :port, :state, :ctstate].each do |prop|
hash[prop] = hash[prop].split(',') if ! hash[prop].nil?
end
## clean up DSCP class to HEX mappings
valid_dscp_classes = {
'0x0a' => 'af11',
'0x0c' => 'af12',
'0x0e' => 'af13',
'0x12' => 'af21',
'0x14' => 'af22',
'0x16' => 'af23',
'0x1a' => 'af31',
'0x1c' => 'af32',
'0x1e' => 'af33',
'0x22' => 'af41',
'0x24' => 'af42',
'0x26' => 'af43',
'0x08' => 'cs1',
'0x10' => 'cs2',
'0x18' => 'cs3',
'0x20' => 'cs4',
'0x28' => 'cs5',
'0x30' => 'cs6',
'0x38' => 'cs7',
'0x2e' => 'ef'
}
[:set_dscp_class].each do |prop|
[:set_dscp].each do |dmark|
next unless hash[dmark]
hash[prop] = valid_dscp_classes[hash[dmark]]
end
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
# Invert any rules that are prefixed with a '!'
[
:connmark,
:ctstate,
:destination,
:dport,
:dst_range,
:dst_type,
:ipset,
:port,
:proto,
:source,
:sport,
:src_range,
:src_type,
:state,
].each do |prop|
if hash[prop] and hash[prop].is_a?(Array)
# find if any are negated, then negate all if so
should_negate = hash[prop].index do |value|
value.match(/^(!)\s+/)
end
hash[prop] = hash[prop].collect { |v|
"! #{v.sub(/^!\s+/,'')}"
} if should_negate
elsif hash[prop]
m = hash[prop].match(/^(!?)\s?(.*)/)
neg = "! " if m[1] == "!"
if [:source,:destination].include?(prop)
# Normalise all rules to CIDR notation.
hash[prop] = "#{neg}#{Puppet::Util::IPCidr.new(m[2]).cidr}"
else
hash[prop] = "#{neg}#{m[2]}"
end
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+[[:graph:][:space:]]+$/ =~ hash[:name]
num = 9000 + counter
hash[:name] = "#{num} #{/([[:graph:][: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')
known_booleans = self.class.instance_variable_get('@known_booleans')
resource_map = self.class.instance_variable_get('@resource_map')
resource_map = munge_resource_map_from_resource(resource_map, resource)
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(' ')
args = args.flatten
# 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, '!')
elsif resource_value.is_a?(Symbol) and resource_value.to_s.match(/^!/) then
#ruby 1.8.7 can't .match Symbols ------------------ ^
resource_value = resource_value.to_s.sub!(/^!\s*/, '').to_sym
args.insert(-2, '!')
elsif resource_value.is_a?(Array)
should_negate = resource_value.index do |value|
#ruby 1.8.7 can't .match symbols
value.to_s.match(/^(!)\s+/)
end
if should_negate
resource_value, wrong_values = resource_value.collect do |value|
if value.is_a?(String)
wrong = value if ! value.match(/^!\s+/)
[value.sub(/^!\s*/, ''),wrong]
else
[value,nil]
end
end.transpose
wrong_values = wrong_values.compact
if ! wrong_values.empty?
fail "All values of the '#{res}' property must be prefixed with a '!' when inverting, but '#{wrong_values.join("', '")}' #{wrong_values.length>1?"are":"is"} not prefixed; aborting"
end
args.insert(-2, '!')
end
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 or res == :ipset
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

View File

@@ -0,0 +1,179 @@
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
confine :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|security'
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
# 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, iptables-persistent, or iptables-services 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, SECURITY 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 Gem::Version.new(Facter['kernelmajversion'].value.dup) < Gem::Version.new('3.7') and 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
when 'security'
if chain =~ /^(PREROUTING|POSTROUTING|BROUTING)$/
raise ArgumentError, "INPUT, OUTPUT and FORWARD are the only inbuilt chains that can be used in table 'security'"
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 iptables-services}
else
[]
end
end
autorequire(:service) do
case value(:provider)
when :iptables, :ip6tables
%w{firewalld iptables ip6tables iptables-persistent netfilter-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

View File

@@ -0,0 +1,232 @@
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 "too-big" then "2"
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 "neighbour-solicitation" then "135"
when "neighbour-advertisement" then "136"
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
m = value.to_s.match(/^(!\s+)?(\S+)/)
if m[2].match(/^\d+(-\d+)?$/)
return "#{m[1]}#{m[2]}"
else
return "#{m[1]}#{Socket.getservbyname(m[2], 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'
# We need to call flush to clear Facter cache as it's possible the cached value will be nil due to the fact
# that the iptables-persistent package was potentially installed after the initial Fact gathering.
Facter.fact(:iptables_persistent_version).flush
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' && ['RedHat','CentOS','Scientific','SL','SLC','Ascendos','CloudLinux','PSBM','OracleLinux','OVS','OEL','XenServer'].include?(Facter.value(:operatingsystem)) && 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
if (persist_ver and Puppet::Util::Package.versioncmp(persist_ver, '1.0') > 0)
%w{/usr/sbin/service netfilter-persistent save}
else
%w{/usr/sbin/service iptables-persistent save}
end
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

View 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

View File

@@ -0,0 +1,42 @@
# = 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,
$pkg_ensure = present,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {
case $ensure {
/^(running|stopped)$/: {
# Do nothing.
}
default: {
fail("${title}: Ensure value '${ensure}' is not supported")
}
}
case $::kernel {
'Linux': {
class { "${title}::linux":
ensure => $ensure,
pkg_ensure => $pkg_ensure,
service_name => $service_name,
package_name => $package_name,
}
}
default: {
fail("${title}: Kernel '${::kernel}' is not currently supported")
}
}
}

View File

@@ -0,0 +1,69 @@
# = 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,
$pkg_ensure = present,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {
$enable = $ensure ? {
running => true,
stopped => false,
}
package { 'iptables':
ensure => $pkg_ensure,
}
case $::operatingsystem {
'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos',
'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': {
class { "${title}::redhat":
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
'Debian', 'Ubuntu': {
class { "${title}::debian":
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
'Archlinux': {
class { "${title}::archlinux":
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
'Gentoo': {
class { "${title}::gentoo":
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
default: {}
}
}

View File

@@ -0,0 +1,44 @@
# = 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_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
$package_ensure = $::firewall::params::package_ensure,
) inherits ::firewall::params {
if $package_name {
package { $package_name:
ensure => $package_ensure,
}
}
service { $service_name:
ensure => $ensure,
enable => $enable,
hasstatus => true,
}
file { '/etc/iptables/iptables.rules':
ensure => present,
before => Service[$service_name],
}
file { '/etc/iptables/ip6tables.rules':
ensure => present,
before => Service[$service_name],
}
}

View File

@@ -0,0 +1,56 @@
# = 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,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
$package_ensure = $::firewall::params::package_ensure,
) inherits ::firewall::params {
if $package_name {
#Fixes hang while installing iptables-persistent on debian 8
exec {'iptables-persistent-debconf':
command => "/bin/echo \"${package_name} ${package_name}/autosave_v4 boolean false\" | /usr/bin/debconf-set-selections && /bin/echo \"${package_name} ${package_name}/autosave_v6 boolean false\" | /usr/bin/debconf-set-selections",
refreshonly => true
}
package { $package_name:
ensure => $package_ensure,
require => Exec['iptables-persistent-debconf']
}
}
if($::operatingsystemrelease =~ /^6\./ and $enable == true and $::iptables_persistent_version
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[$package_name],
}
} else {
# This isn't a real service/daemon. The start action loads rules, so just
# needs to be called on system boot.
service { $service_name:
ensure => undef,
enable => $enable,
hasstatus => true,
require => Package[$package_name],
}
}
}

View File

@@ -0,0 +1,44 @@
# = Class: firewall::linux::gentoo
#
# Manages `iptables` and `ip6tables` services, and creates files used for
# persistence, on Gentoo Linux systems.
#
# == Parameters:
#
# [*ensure*]
# Ensure parameter passed onto Service[] resources.
# Default: running
#
# [*enable*]
# Enable parameter passed onto Service[] resources.
# Default: true
#
class firewall::linux::gentoo (
$ensure = 'running',
$enable = true,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
$package_ensure = $::firewall::params::package_ensure,
) inherits ::firewall::params {
if $package_name {
package { $package_name:
ensure => $package_ensure,
}
}
service { $service_name:
ensure => $ensure,
enable => $enable,
hasstatus => true,
}
file { '/var/lib/iptables/rules-save':
ensure => present,
before => Service[$service_name],
}
file { '/var/lib/iptables/rules-save6':
ensure => present,
before => Service[$service_name],
}
}

View File

@@ -0,0 +1,82 @@
# = 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,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
$package_ensure = $::firewall::params::package_ensure,
) inherits ::firewall::params {
# 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 != 'Amazon')
and (($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0)) {
service { 'firewalld':
ensure => stopped,
enable => false,
before => Package[$package_name],
}
}
if $package_name {
package { $package_name:
ensure => $package_ensure,
before => Service[$service_name],
}
}
if ($::operatingsystem != 'Amazon')
and (($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0)) {
if $ensure == 'running' {
exec { '/usr/bin/systemctl daemon-reload':
require => Package[$package_name],
before => Service[$service_name],
unless => "/usr/bin/systemctl is-active ${service_name}",
}
}
}
service { $service_name:
ensure => $ensure,
enable => $enable,
hasstatus => true,
require => File["/etc/sysconfig/${service_name}"],
}
# Redhat 7 selinux user context for /etc/sysconfig/iptables is set to unconfined_u
case $::selinux {
#lint:ignore:quoted_booleans
'true',true: {
case $::operatingsystemrelease {
/^(6|7)\..*/: { $seluser = 'unconfined_u' }
default: { $seluser = 'system_u' }
}
}
#lint:endignore
default: { $seluser = undef }
}
file { "/etc/sysconfig/${service_name}":
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
seluser => $seluser,
}
}

View File

@@ -0,0 +1,73 @@
class firewall::params {
$package_ensure = 'present'
case $::osfamily {
'RedHat': {
case $::operatingsystem {
'Amazon': {
$service_name = 'iptables'
$package_name = undef
}
'Fedora': {
if versioncmp($::operatingsystemrelease, '15') >= 0 {
$package_name = 'iptables-services'
} else {
$package_name = undef
}
$service_name = 'iptables'
}
default: {
if versioncmp($::operatingsystemrelease, '7.0') >= 0 {
$package_name = 'iptables-services'
} else {
$package_name = 'iptables-ipv6'
}
$service_name = 'iptables'
}
}
}
'Debian': {
case $::operatingsystem {
'Debian': {
if versioncmp($::operatingsystemrelease, '8.0') >= 0 {
$service_name = 'netfilter-persistent'
$package_name = 'iptables-persistent'
} else {
$service_name = 'iptables-persistent'
$package_name = 'iptables-persistent'
}
}
'Ubuntu': {
if versioncmp($::operatingsystemrelease, '14.10') >= 0 {
$service_name = 'netfilter-persistent'
$package_name = 'iptables-persistent'
} else {
$service_name = 'iptables-persistent'
$package_name = 'iptables-persistent'
}
}
default: {
$service_name = 'iptables-persistent'
$package_name = 'iptables-persistent'
}
}
}
'Gentoo': {
$service_name = ['iptables','ip6tables']
$package_name = 'net-firewall/iptables'
}
default: {
case $::operatingsystem {
'Archlinux': {
$service_name = ['iptables','ip6tables']
$package_name = undef
}
default: {
$service_name = 'iptables'
$package_name = undef
}
}
}
}
}

View File

@@ -0,0 +1,86 @@
{
"name": "puppetlabs-firewall",
"version": "1.8.1",
"author": "Puppet Labs",
"summary": "Manages Firewalls such as iptables",
"license": "Apache-2.0",
"source": "https://github.com/puppetlabs/puppetlabs-firewall",
"project_page": "http://github.com/puppetlabs/puppetlabs-firewall",
"issues_url": "https://tickets.puppetlabs.com/browse/MODULES",
"dependencies": [
],
"data_provider": null,
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "OracleLinux",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Scientific",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "SLES",
"operatingsystemrelease": [
"11 SP1",
"12"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"6",
"7",
"8"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"10.04",
"12.04",
"14.04"
]
},
{
"operatingsystem": "Gentoo",
"operatingsystemrelease": [
"1.0"
]
}
],
"requirements": [
{
"name": "pe",
"version_requirement": ">= 3.0.0 < 2015.4.0"
},
{
"name": "puppet",
"version_requirement": ">= 3.0.0 < 5.0.0"
}
]
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<service xmlns="http://www.github/cliffe/SecGen/service"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/service">
<name>Iptables firewall config</name>
<author>Jason Keighley</author>
<author>Puppet Labs, Inc.</author>
<author>Jonathan Boyett</author>
<author>Media Temple, Inc.</author>
<author>Bob.sh Limited</author>
<author>Camptocamp Association</author>
<author>Dmitri Priimak</author>
<module_license>Apache v2</module_license>
<description>Configuration of the Iptables firewall</description>
<type>firewall</type>
<platform>linux</platform>
<!--optional details-->
<reference>https://forge.puppet.com/puppetlabs/firewall</reference>
<software_name></software_name>
<software_license>Apache v2</software_license>
<!--Cannot co-exist with other iptables installations-->
<conflict>
<software_name></software_name>
</conflict>
</service>
<!-- DEPENDS ON BOTH unix-update AND IPtables BEING PRESENT ON THE SYSTEM -->

View File

@@ -0,0 +1,76 @@
require 'spec_helper_acceptance'
describe 'changing the source' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
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

View File

@@ -0,0 +1,33 @@
require 'spec_helper_acceptance'
describe "firewall class" do
it 'should run successfully' do
pp = "class { 'firewall': }"
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end
it 'ensure => stopped:' do
pp = "class { 'firewall': ensure => stopped }"
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end
it 'ensure => running:' do
pp = "class { 'firewall': ensure => running }"
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
if do_catch_changes
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
end
end
end

View File

@@ -0,0 +1,63 @@
require 'spec_helper_acceptance'
describe 'connlimit property' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
if default['platform'] !~ /sles-10/
describe 'connlimit_above' do
context '10' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '500 - test':
proto => tcp,
dport => '2222',
connlimit_above => '10',
action => reject,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
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 2222 -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 => '2222',
connlimit_above => '10',
connlimit_mask => '24',
action => reject,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
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 2222 -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
end

View File

@@ -0,0 +1,27 @@
require 'spec_helper_acceptance'
describe 'connmark property' 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

View File

@@ -0,0 +1,369 @@
require 'spec_helper_acceptance'
describe 'firewall bridging' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
describe 'iptables physdev tests' do
context 'physdev_in eth0' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '701 - test':
chain => 'FORWARD',
proto => tcp,
port => '701',
action => accept,
physdev_in => 'eth0',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 -m multiport --ports 701 -m comment --comment "701 - test" -j ACCEPT/)
end
end
end
context 'physdev_out eth1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '702 - test':
chain => 'FORWARD',
proto => tcp,
port => '702',
action => accept,
physdev_out => 'eth1',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 -m multiport --ports 702 -m comment --comment "702 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_out eth1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '703 - test':
chain => 'FORWARD',
proto => tcp,
port => '703',
action => accept,
physdev_in => 'eth0',
physdev_out => 'eth1',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 -m multiport --ports 703 -m comment --comment "703 - test" -j ACCEPT/)
end
end
end
context 'physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '704 - test':
chain => 'FORWARD',
proto => tcp,
port => '704',
action => accept,
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-is-bridged -m multiport --ports 704 -m comment --comment "704 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '705 - test':
chain => 'FORWARD',
proto => tcp,
port => '705',
action => accept,
physdev_in => 'eth0',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-is-bridged -m multiport --ports 705 -m comment --comment "705 - test" -j ACCEPT/)
end
end
end
context 'physdev_out eth1 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '706 - test':
chain => 'FORWARD',
proto => tcp,
port => '706',
action => accept,
physdev_out => 'eth1',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m multiport --ports 706 -m comment --comment "706 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_out eth1 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '707 - test':
chain => 'FORWARD',
proto => tcp,
port => '707',
action => accept,
physdev_in => 'eth0',
physdev_out => 'eth1',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m multiport --ports 707 -m comment --comment "707 - test" -j ACCEPT/)
end
end
end
end
#iptables version 1.3.5 is not suppored by the ip6tables provider
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'ip6tables physdev tests' do
context 'physdev_in eth0' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '701 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '701',
action => accept,
physdev_in => 'eth0',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 -m multiport --ports 701 -m comment --comment "701 - test" -j ACCEPT/)
end
end
end
context 'physdev_out eth1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '702 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '702',
action => accept,
physdev_out => 'eth1',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 -m multiport --ports 702 -m comment --comment "702 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_out eth1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '703 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '703',
action => accept,
physdev_in => 'eth0',
physdev_out => 'eth1',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 -m multiport --ports 703 -m comment --comment "703 - test" -j ACCEPT/)
end
end
end
context 'physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '704 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '704',
action => accept,
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-is-bridged -m multiport --ports 704 -m comment --comment "704 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '705 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '705',
action => accept,
physdev_in => 'eth0',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-is-bridged -m multiport --ports 705 -m comment --comment "705 - test" -j ACCEPT/)
end
end
end
context 'physdev_out eth1 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '706 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '706',
action => accept,
physdev_out => 'eth1',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m multiport --ports 706 -m comment --comment "706 - test" -j ACCEPT/)
end
end
end
context 'physdev_in eth0 and physdev_out eth1 and physdev_is_bridged' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '707 - test':
provider => 'ip6tables',
chain => 'FORWARD',
proto => tcp,
port => '707',
action => accept,
physdev_in => 'eth0',
physdev_out => 'eth1',
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m multiport --ports 707 -m comment --comment "707 - test" -j ACCEPT/)
end
end
end
end
end
end

View File

@@ -0,0 +1,49 @@
# These tests have been commented out, as there are suspicions that the clusterIP ipt module is causing system reboots.
# require 'spec_helper_acceptance'
# describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
# before(:all) do
# shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
# shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush')
# end
# # SLES doesn't have the CLUSTERIP module
# if default['platform'] !~ /sles/
# describe 'clusterip' do
# context 'cluster ipv4 test' do
# it 'applies' do
# pending("MODULES-2124 should be resolved for clusterip RHEL7 support") if default['platform'] =~ /el-7/
# pp = <<-EOS
# class { '::firewall': }
# firewall {
# '830 - clusterip test':
# chain => 'FORWARD',
# jump => 'CLUSTERIP',
# destination => '1.1.1.1',
# iniface => 'eth0',
# clusterip_new => true,
# clusterip_hashmode => "sourceip",
# clusterip_clustermac => "01:00:5E:00:00:00",
# clusterip_total_nodes => "2",
# clusterip_local_node => "1",
# clusterip_hash_init => "1337",
# }
# EOS
# apply_manifest(pp, :catch_failures => true)
# end
# it 'should contain the rule' do
# pending("MODULES-2124 should be resolved for clusterip RHEL7 support") if default['platform'] =~ /el-7/
# shell('iptables-save') do |r|
# expect(r.stdout).to match(/-A FORWARD -d (1.1.1.1\/32|1.1.1.1) -i eth0 -p tcp -m comment --comment "830 - clusterip test" -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:5E:00:00:00 --total-nodes 2 --local-node 1 --hash-init 1337/)
# end
# end
# end
# end
# end
# end

View File

@@ -0,0 +1,117 @@
require 'spec_helper_acceptance'
describe 'firewall DSCP' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
describe 'dscp ipv4 tests' do
context 'set_dscp 0x01' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1000 - set_dscp':
proto => 'tcp',
jump => 'DSCP',
set_dscp => '0x01',
port => '997',
chain => 'OUTPUT',
table => 'mangle',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('iptables-save -t mangle') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1000 - set_dscp" -j DSCP --set-dscp 0x01/)
end
end
end
context 'set_dscp_class EF' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1001 EF - set_dscp_class':
proto => 'tcp',
jump => 'DSCP',
port => '997',
set_dscp_class => 'EF',
chain => 'OUTPUT',
table => 'mangle',
}
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 OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1001 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/)
end
end
end
end
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'dscp ipv6 tests' do
context 'set_dscp 0x01' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1002 - set_dscp':
proto => 'tcp',
jump => 'DSCP',
set_dscp => '0x01',
port => '997',
chain => 'OUTPUT',
table => 'mangle',
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save -t mangle') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1002 - set_dscp" -j DSCP --set-dscp 0x01/)
end
end
end
context 'set_dscp_class EF' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'1003 EF - set_dscp_class':
proto => 'tcp',
jump => 'DSCP',
port => '997',
set_dscp_class => 'EF',
chain => 'OUTPUT',
table => 'mangle',
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1003 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/)
end
end
end
end
end
end

View File

@@ -0,0 +1,201 @@
require 'spec_helper_acceptance'
describe 'firewall iptmodules' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
describe 'iptables ipt_modules tests' do
context 'all the modules with multiple args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '801 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
uid => 0,
gid => 404,
src_range => "90.0.0.1-90.0.0.2",
dst_range => "100.0.0.1-100.0.0.2",
src_type => 'LOCAL',
dst_type => 'UNICAST',
physdev_in => "eth0",
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m iprange --src-range 90.0.0.1-90.0.0.2\s+--dst-range 100.0.0.1-100.0.0.2 -m owner --uid-owner (0|root) --gid-owner 404 -m multiport --dports 8080 -m addrtype --src-type LOCAL --dst-type UNICAST -m comment --comment "801 - ipt_modules tests" -j REJECT --reject-with icmp-port-unreachable/)
end
end
end
context 'all the modules with single args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '802 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
gid => 404,
dst_range => "100.0.0.1-100.0.0.2",
dst_type => 'UNICAST',
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m iprange --dst-range 100.0.0.1-100.0.0.2 -m owner --gid-owner 404 -m multiport --dports 8080 -m addrtype --dst-type UNICAST -m comment --comment "802 - ipt_modules tests" -j REJECT --reject-with icmp-port-unreachable/)
end
end
end
end
#iptables version 1.3.5 is not suppored by the ip6tables provider
if default['platform'] =~ /debian-7/ or default['platform'] =~ /ubuntu-14\.04/
describe 'ip6tables ipt_modules tests' do
context 'all the modules with multiple args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '801 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
provider => 'ip6tables',
uid => 0,
gid => 404,
src_range => "2001::-2002::",
dst_range => "2003::-2004::",
src_type => 'LOCAL',
dst_type => 'UNICAST',
physdev_in => "eth0",
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m iprange --src-range 2001::-2002::\s+--dst-range 2003::-2004:: -m owner --uid-owner (0|root) --gid-owner 404 -m multiport --dports 8080 -m addrtype --src-type LOCAL --dst-type UNICAST -m comment --comment "801 - ipt_modules tests" -j REJECT --reject-with icmp6-port-unreachable/)
end
end
end
context 'all the modules with single args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '802 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
provider => 'ip6tables',
gid => 404,
dst_range => "2003::-2004::",
dst_type => 'UNICAST',
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m iprange --dst-range 2003::-2004:: -m owner --gid-owner 404 -m multiport --dports 8080 -m addrtype --dst-type UNICAST -m comment --comment "802 - ipt_modules tests" -j REJECT --reject-with icmp6-port-unreachable/)
end
end
end
end
# Older OSes don't have addrtype so we leave those properties out.
# el-5 doesn't support ipv6 by default
elsif default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'ip6tables ipt_modules tests' do
context 'all the modules with multiple args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '801 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
provider => 'ip6tables',
uid => 0,
gid => 404,
src_range => "2001::-2002::",
dst_range => "2003::-2004::",
physdev_in => "eth0",
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m iprange --src-range 2001::-2002::\s+--dst-range 2003::-2004:: -m owner --uid-owner (0|root) --gid-owner 404 -m multiport --dports 8080 -m comment --comment "801 - ipt_modules tests" -j REJECT --reject-with icmp6-port-unreachable/)
end
end
end
context 'all the modules with single args' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '802 - ipt_modules tests':
proto => tcp,
dport => '8080',
action => reject,
chain => 'OUTPUT',
provider => 'ip6tables',
gid => 404,
dst_range => "2003::-2004::",
physdev_out => "eth1",
physdev_is_bridged => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m iprange --dst-range 2003::-2004:: -m owner --gid-owner 404 -m multiport --dports 8080 -m comment --comment "802 - ipt_modules tests" -j REJECT --reject-with icmp6-port-unreachable/)
end
end
end
end
end
end

View File

@@ -0,0 +1,117 @@
require 'spec_helper_acceptance'
describe 'firewall MSS' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
describe 'mss ipv4 tests' do
context '1360' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'502 - set_mss':
proto => 'tcp',
tcp_flags => 'SYN,RST SYN',
jump => 'TCPMSS',
set_mss => '1360',
mss => '1361:1541',
chain => 'FORWARD',
table => 'mangle',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('iptables-save -t mangle') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -m comment --comment "502 - set_mss" -m tcpmss --mss 1361:1541 -j TCPMSS --set-mss 1360/)
end
end
end
context 'clamp_mss_to_pmtu' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'503 - clamp_mss_to_pmtu':
proto => 'tcp',
chain => 'FORWARD',
tcp_flags => 'SYN,RST SYN',
jump => 'TCPMSS',
clamp_mss_to_pmtu => true,
}
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 FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -m comment --comment "503 - clamp_mss_to_pmtu" -j TCPMSS --clamp-mss-to-pmtu/)
end
end
end
end
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'mss ipv6 tests' do
context '1360' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'502 - set_mss':
proto => 'tcp',
tcp_flags => 'SYN,RST SYN',
jump => 'TCPMSS',
set_mss => '1360',
mss => '1361:1541',
chain => 'FORWARD',
table => 'mangle',
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save -t mangle') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -m comment --comment "502 - set_mss" -m tcpmss --mss 1361:1541 -j TCPMSS --set-mss 1360/)
end
end
end
context 'clamp_mss_to_pmtu' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'503 - clamp_mss_to_pmtu':
proto => 'tcp',
chain => 'FORWARD',
tcp_flags => 'SYN,RST SYN',
jump => 'TCPMSS',
clamp_mss_to_pmtu => true,
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -m comment --comment "503 - clamp_mss_to_pmtu" -j TCPMSS --clamp-mss-to-pmtu/)
end
end
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
require 'spec_helper_acceptance'
describe 'firewall tee' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
if default['platform'] =~ /ubuntu-1404/ or default['platform'] =~ /ubuntu-1204/ or default['platform'] =~ /debian-7/ or default['platform'] =~ /debian-8/ or default['platform'] =~ /el-7/
describe 'tee_gateway' do
context '10.0.0.2' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'810 - tee_gateway':
chain => 'PREROUTING',
table => 'mangle',
jump => 'TEE',
gateway => '10.0.0.2',
proto => all,
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('iptables-save -t mangle') do |r|
expect(r.stdout).to match(/-A PREROUTING -m comment --comment "810 - tee_gateway" -j TEE --gateway 10.0.0.2/)
end
end
end
end
describe 'tee_gateway6' do
context '2001:db8::1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall {
'811 - tee_gateway6':
chain => 'PREROUTING',
table => 'mangle',
jump => 'TEE',
gateway => '2001:db8::1',
proto => all,
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save -t mangle') do |r|
expect(r.stdout).to match(/-A PREROUTING -m comment --comment "811 - tee_gateway6" -j TEE --gateway 2001:db8::1/)
end
end
end
end
end
end

View File

@@ -0,0 +1,75 @@
require 'spec_helper_acceptance'
describe 'firewall time' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
if default['platform'] =~ /ubuntu-1404/ or default['platform'] =~ /debian-7/ or default['platform'] =~ /debian-8/ or default['platform'] =~ /el-7/
describe "time tests ipv4" do
context 'set all time parameters' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '805 - test':
proto => tcp,
dport => '8080',
action => accept,
chain => 'OUTPUT',
date_start => '2016-01-19T04:17:07',
date_stop => '2038-01-19T04:17:07',
time_start => '6:00',
time_stop => '17:00:00',
month_days => '7',
week_days => 'Tue',
kernel_timezone => true,
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --dports 8080 -m comment --comment "805 - test" -m time --timestart 06:00:00 --timestop 17:00:00 --monthdays 7 --weekdays Tue --datestart 2016-01-19T04:17:07 --datestop 2038-01-19T04:17:07 --kerneltz -j ACCEPT/)
end
end
end
end
describe "time tests ipv6" do
context 'set all time parameters' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '805 - test':
proto => tcp,
dport => '8080',
action => accept,
chain => 'OUTPUT',
date_start => '2016-01-19T04:17:07',
date_stop => '2038-01-19T04:17:07',
time_start => '6:00',
time_stop => '17:00:00',
month_days => '7',
week_days => 'Tue',
kernel_timezone => true,
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --dports 8080 -m comment --comment "805 - test" -m time --timestart 06:00:00 --timestop 17:00:00 --monthdays 7 --weekdays Tue --datestart 2016-01-19T04:17:07 --datestop 2038-01-19T04:17:07 --kerneltz -j ACCEPT/)
end
end
end
end
end
end

View File

@@ -0,0 +1,104 @@
require 'spec_helper_acceptance'
describe 'firewall uid' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
describe "uid tests" do
context 'uid set to root' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '801 - test':
chain => 'OUTPUT',
action => accept,
uid => 'root',
proto => 'all',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -m owner --uid-owner (0|root) -m comment --comment "801 - test" -j ACCEPT/)
end
end
end
context 'uid set to !root' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '802 - test':
chain => 'OUTPUT',
action => accept,
uid => '!root',
proto => 'all',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -m owner ! --uid-owner (0|root) -m comment --comment "802 - test" -j ACCEPT/)
end
end
end
context 'uid set to 0' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '803 - test':
chain => 'OUTPUT',
action => accept,
uid => '0',
proto => 'all',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -m owner --uid-owner (0|root) -m comment --comment "803 - test" -j ACCEPT/)
end
end
end
context 'uid set to !0' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '804 - test':
chain => 'OUTPUT',
action => accept,
uid => '!0',
proto => 'all',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rule' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/-A OUTPUT -m owner ! --uid-owner (0|root) -m comment --comment "804 - test" -j ACCEPT/)
end
end
end
end
end

View File

@@ -0,0 +1,127 @@
require 'spec_helper_acceptance'
describe 'puppet resource firewallchain command' do
before :all do
iptables_flush_all_tables
ip6tables_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 => do_catch_changes)
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 => do_catch_changes)
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 => do_catch_changes)
# 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 => do_catch_changes)
# 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 => do_catch_changes)
# 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 => do_catch_changes)
end
it 'finds the chain' do
shell('iptables-save') do |r|
expect(r.stdout).to match(/FORWARD DROP/)
end
end
end
end
end

View File

@@ -0,0 +1,61 @@
require 'spec_helper_acceptance'
describe 'firewall inverting' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
context "inverting rules" do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '601 disallow esp protocol':
action => 'accept',
proto => '! esp',
}
firewall { '602 drop NEW external website packets with FIN/RST/ACK set and SYN unset':
chain => 'INPUT',
state => 'NEW',
action => 'drop',
proto => 'tcp',
sport => ['! http', '! 443'],
source => '! 10.0.0.0/8',
tcp_flags => '! FIN,SYN,RST,ACK SYN',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'should contain the rules' do
shell('iptables-save') do |r|
if (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5') or (default['platform'] =~ /sles-10/)
expect(r.stdout).to match(/-A INPUT -p ! esp -m comment --comment "601 disallow esp protocol" -j ACCEPT/)
expect(r.stdout).to match(/-A INPUT -s ! 10\.0\.0\.0\/255\.0\.0\.0 -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m multiport --sports ! 80,443 -m comment --comment "602 drop NEW external website packets with FIN\/RST\/ACK set and SYN unset" -m state --state NEW -j DROP/)
else
expect(r.stdout).to match(/-A INPUT ! -p esp -m comment --comment "601 disallow esp protocol" -j ACCEPT/)
expect(r.stdout).to match(/-A INPUT ! -s 10\.0\.0\.0\/8 -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m multiport ! --sports 80,443 -m comment --comment "602 drop NEW external website packets with FIN\/RST\/ACK set and SYN unset" -m state --state NEW -j DROP/)
end
end
end
end
context "inverting partial array rules" do
it 'raises a failure' do
pp = <<-EOS
class { '::firewall': }
firewall { '603 drop 80,443 traffic':
chain => 'INPUT',
action => 'drop',
proto => 'tcp',
sport => ['! http', '443'],
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/is not prefixed/)
end
end
end
end

View File

@@ -0,0 +1,116 @@
require 'spec_helper_acceptance'
if default['platform'] =~ /el-5/ or default['platform'] =~ /sles-10/
describe "firewall ip6tables doesn't work on 1.3.5 because --comment is missing" do
before :all do
iptables_flush_all_tables
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' do
before :all do
iptables_flush_all_tables
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 => do_catch_changes)
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 => do_catch_changes)
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

View File

@@ -0,0 +1,93 @@
require 'spec_helper_acceptance'
describe 'firewall isfragment property' do
before :all do
iptables_flush_all_tables
ip6tables_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 => do_catch_changes)
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 => do_catch_changes)
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

View File

@@ -0,0 +1,57 @@
require 'spec_helper_acceptance'
describe 'firewall match marks' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
describe 'match_mark' do
context '0x1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '503 match_mark - test':
proto => 'all',
match_mark => '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 "503 match_mark - test" -m mark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable/)
end
end
end
end
describe 'match_mark_ip6' do
context '0x1' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '503 match_mark ip6tables - test':
proto => 'all',
match_mark => '0x1',
action => reject,
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
end
it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A INPUT -m comment --comment "503 match_mark ip6tables - test" -m mark --mark 0x1 -j REJECT --reject-with icmp6-port-unreachable/)
end
end
end
end
end
end

View File

@@ -0,0 +1,10 @@
HOSTS:
centos-7-x64:
roles:
- agent
- default
platform: redhat-7-x86_64
hypervisor: vagrant
box: puppetlabs/centos-7.2-64-nocm
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
debian-8-x64:
roles:
- agent
- default
platform: debian-8-amd64
hypervisor: vagrant
box: puppetlabs/debian-8.2-64-nocm
CONFIG:
type: foss

View File

@@ -0,0 +1,10 @@
HOSTS:
ubuntu-1404-x64:
roles:
- agent
- default
platform: ubuntu-14.04-amd64
hypervisor: vagrant
box: puppetlabs/ubuntu-14.04-64-nocm
CONFIG:
type: foss

View File

@@ -0,0 +1,12 @@
HOSTS:
centos-7-x64:
platform: el-7-x86_64
hypervisor: docker
image: centos:7
docker_preserve_image: true
docker_cmd: '["/usr/sbin/init"]'
# install various tools required to get the image up to usable levels
docker_image_commands:
- 'yum install -y crontabs tar wget openssl sysvinit-tools iproute which initscripts'
CONFIG:
trace_limit: 200

View File

@@ -0,0 +1,11 @@
HOSTS:
debian-8-x64:
platform: debian-8-amd64
hypervisor: docker
image: debian:8
docker_preserve_image: true
docker_cmd: '["/sbin/init"]'
docker_image_commands:
- 'apt-get update && apt-get install -y net-tools wget locales strace lsof && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen'
CONFIG:
trace_limit: 200

View File

@@ -0,0 +1,12 @@
HOSTS:
ubuntu-1404-x64:
platform: ubuntu-14.04-amd64
hypervisor: docker
image: ubuntu:14.04
docker_preserve_image: true
docker_cmd: '["/sbin/init"]'
docker_image_commands:
# ensure that upstart is booting correctly in the container
- 'rm /usr/sbin/policy-rc.d && rm /sbin/initctl && dpkg-divert --rename --remove /sbin/initctl && apt-get update && apt-get install -y net-tools wget && locale-gen en_US.UTF-8'
CONFIG:
trace_limit: 200

View File

@@ -0,0 +1,29 @@
---
HOSTS:
debian-8-amd64-agent:
roles:
- agent
- default
platform: debian-8-amd64
template: debian-8-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
type: aio
ssh:
timeout: 600

View File

@@ -0,0 +1,28 @@
---
HOSTS:
redhat-6-x86_64-agent:
roles:
- agent
- default
platform: el-6-x86_64
template: redhat-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
type: aio
ssh:
timeout: 600

View File

@@ -0,0 +1,28 @@
---
HOSTS:
redhat-7-x86_64-agent:
roles:
- agent
- default
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
type: aio
ssh:
timeout: 600

View File

@@ -0,0 +1,28 @@
---
HOSTS:
ubuntu-1404-agent:
roles:
- agent
- default
platform: ubuntu-14.04-amd64
template: Delivery/Quality Assurance/Templates/vCloud/ubuntu-1404-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
type: aio
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
centos-5-x86_64-agent:
roles:
- agent
- default
platform: el-5-x86_64
template: Delivery/Quality Assurance/Templates/vCloud/centos-5-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
centos-6-x86_64-agent:
roles:
- agent
- default
platform: el-6-x86_64
template: Delivery/Quality Assurance/Templates/vCloud/centos-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
centos-7-x86_64-agent:
roles:
- agent
- default
platform: el-7-x86_64
template: centos-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
debian-6-amd64-agent:
roles:
- agent
- default
platform: debian-6-amd64
template: debian-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
debian-7-amd64-agent:
roles:
- agent
- default
platform: debian-7-amd64
template: debian-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,28 @@
---
HOSTS:
debian-8-amd64-agent:
roles:
- agent
- default
platform: debian-8-amd64
template: debian-8-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
oracle-6-x86_64-agent:
roles:
- agent
- default
platform: el-6-x86_64
template: oracle-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
oracle-7-x86_64-agent:
roles:
- agent
- default
platform: el-7-x86_64
template: oracle-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
redhat-5-x86_64-agent:
roles:
- agent
- default
platform: el-5-x86_64
template: redhat-5-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
redhat-6-x86_64-agent:
roles:
- agent
- default
platform: el-6-x86_64
template: redhat-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
redhat-7-x86_64-agent:
roles:
- agent
- default
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
scientific-5-x86_64-agent:
roles:
- agent
- default
platform: el-5-x86_64
template: scientific-5-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
scientific-6-x86_64-agent:
roles:
- agent
- default
platform: el-6-x86_64
template: scientific-6-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
scientific-7-x86_64-agent:
roles:
- agent
- default
platform: el-7-x86_64
template: scientific-7-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
sles-10-x86_64-agent:
roles:
- agent
- default
platform: sles-10-x86_64
template: sles-10-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
sles-11-x86_64-agent:
roles:
- agent
- default
platform: sles-11-x86_64
template: sles-11-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
sles-12-x86_64-agent:
roles:
- agent
- default
platform: sles-12-x86_64
template: sles-12-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
ubuntu-1004-agent:
roles:
- agent
- default
platform: ubuntu-10.04-amd64
template: ubuntu-1004-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
ubuntu-1204-agent:
roles:
- agent
- default
platform: ubuntu-12.04-amd64
template: Delivery/Quality Assurance/Templates/vCloud/ubuntu-1204-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,27 @@
---
HOSTS:
ubuntu-1404-agent:
roles:
- agent
- default
platform: ubuntu-14.04-amd64
template: Delivery/Quality Assurance/Templates/vCloud/ubuntu-1404-x86_64
hypervisor: vcloud
redhat-7-x86_64-master:
roles:
- master
- dashboard
- database
- agent
platform: el-7-x86_64
template: redhat-7-x86_64
hypervisor: vcloud
CONFIG:
nfs_server: none
consoleport: 443
datastore: instance0
folder: Delivery/Quality Assurance/Enterprise/Dynamic
resourcepool: delivery/Quality Assurance/Enterprise/Dynamic
pooling_api: http://vcloud.delivery.puppetlabs.net/
ssh:
timeout: 600

View File

@@ -0,0 +1,152 @@
require 'spec_helper_acceptance'
describe 'param based tests' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
it 'test various params', :unless => (default['platform'].match(/el-5/) || fact('operatingsystem') == 'SLES') do
iptables_flush_all_tables
ppm = <<-EOS
firewall { '100 test':
table => 'raw',
socket => 'true',
chain => 'PREROUTING',
jump => 'LOG',
log_level => 'debug',
}
EOS
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 = <<-EOS
firewall { '998 log all':
proto => 'all',
jump => 'LOG',
log_level => 'debug',
}
EOS
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 = <<-EOS
firewall { '004 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS
ppm2 = <<-EOS
firewall { '003 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS
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 = <<-EOS
firewall { '004 with a chain':
chain => 'INPUT',
proto => 'all',
}
EOS
ppm2 = <<-EOS
firewall { '004 with a chain':
chain => 'OUTPUT',
proto => 'all',
}
EOS
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 = <<-EOS
firewall { '004 log all INVALID packets':
chain => 'INPUT',
proto => 'all',
ctstate => 'INVALID',
jump => 'LOG',
log_level => '3',
log_prefix => 'IPTABLES dropped invalid: ',
}
EOS
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 = <<-EOS
firewall { '997 block src ip range':
chain => 'INPUT',
proto => 'all',
action => 'drop',
src_range => '10.0.0.1-10.0.0.10',
}
EOS
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 = <<-EOS
firewall { '998 block dst ip range':
chain => 'INPUT',
proto => 'all',
action => 'drop',
dst_range => '10.0.0.2-10.0.0.20',
}
EOS
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

View File

@@ -0,0 +1,235 @@
require 'spec_helper_acceptance'
describe 'purge tests' do
before :all do
iptables_flush_all_tables
ip6tables_flush_all_tables
end
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('ipv4 chain purge') do
after(:all) do
iptables_flush_all_tables
end
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 => do_catch_changes)
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 => do_catch_changes)
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
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
context 'ipv6 chain purge' do
after(:all) do
ip6tables_flush_all_tables
end
before(:each) do
ip6tables_flush_all_tables
shell('ip6tables -A INPUT -p tcp -s 1::42')
shell('ip6tables -A INPUT -p udp -s 1::42')
shell('ip6tables -A OUTPUT -s 1::50 -m comment --comment "010 output-1::50"')
end
it 'purges only the specified chain' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
}
EOS
apply_manifest(pp, :expect_changes => true)
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/010 output-1::50/)
expect(r.stdout).to_not match(/1::42/)
expect(r.stderr).to eq("")
end
end
it 'ignores managed rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'OUTPUT:filter:IPv6':
purge => true,
}
firewall { '010 output-1::50':
chain => 'OUTPUT',
proto => 'all',
source => '1::50',
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'ignores specified rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
ignore => [
'-s 1::42',
],
}
EOS
apply_manifest(pp, :catch_changes => do_catch_changes)
end
it 'adds managed rules with ignored rules' do
pp = <<-EOS
class { 'firewall': }
firewallchain { 'INPUT:filter:IPv6':
purge => true,
ignore => [
'-s 1::42',
],
}
firewall { '014 input-1::46':
chain => 'INPUT',
proto => 'all',
source => '1::46',
provider => 'ip6tables',
}
-> firewall { '013 input-1::45':
chain => 'INPUT',
proto => 'all',
source => '1::45',
provider => 'ip6tables',
}
-> firewall { '012 input-1::44':
chain => 'INPUT',
proto => 'all',
source => '1::44',
provider => 'ip6tables',
}
-> firewall { '011 input-1::43':
chain => 'INPUT',
proto => 'all',
source => '1::43',
provider => 'ip6tables',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(shell('ip6tables-save').stdout).to match(/-A INPUT -s 1::42(\/128)? -p tcp\s?\n-A INPUT -s 1::42(\/128)? -p udp/)
end
end
end
end

View File

@@ -0,0 +1,175 @@
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' do
before(:all) do
# In order to properly check stderr for anomalies we need to fix the deprecation warnings from puppet.conf.
config = shell('puppet config print config').stdout
shell("sed -i -e \'s/^templatedir.*$//\' #{config}")
end
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
r.stderr.should be_empty
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
r.stderr.should be_empty
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
r.stderr.should be_empty
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
r.stderr.should be_empty
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
r.stderr.should be_empty
end
end
end
if default['platform'] !~ /sles-10/
context 'accepts rules utilizing the statistic module' do
before :all do
iptables_flush_all_tables
# This command doesn't work with all versions/oses, so let it fail
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode nth --every 2 -j SNAT --to-source 2.3.4.5', :acceptable_exit_codes => [0,1,2] )
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode nth --every 1 --packet 0 -j SNAT --to-source 2.3.4.6')
shell('iptables -t nat -A POSTROUTING -d 1.2.3.4/32 -o eth0 -m statistic --mode random --probability 0.99 -j SNAT --to-source 2.3.4.7')
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
r.stderr.should be_empty
end
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 -m policy --dir out --pol ipsec -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.1.0/24 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.1.0/24 -o eth0 -m policy --dir out --pol ipsec --reqid 108 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.201.1/32 -d 192.168.122.0/24 -i eth0 -m policy --dir in --pol ipsec --reqid 107 --proto esp -j ACCEPT')
shell('iptables -t filter -A FORWARD -s 192.168.122.0/24 -d 192.168.201.1/32 -o eth0 -m policy --dir out --pol ipsec --reqid 107 --proto esp -j ACCEPT')
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
r.stderr.should be_empty
end
end
end
context 'accepts rules with -m (tcp|udp) without dport/sport' do
before :all do
iptables_flush_all_tables
shell('iptables -A INPUT -s 10.0.0.0/8 -p udp -m udp -j ACCEPT')
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
r.stderr.should be_empty
end
end
end
# version of iptables that ships with el5 doesn't work with the
# ip6tables provider
if default['platform'] !~ /el-5/ and default['platform'] !~ /sles-10/
context 'dport/sport with ip6tables' do
before :all do
if fact('osfamily') == 'Debian'
shell('echo "iptables-persistent iptables-persistent/autosave_v4 boolean false" | debconf-set-selections')
shell('echo "iptables-persistent iptables-persistent/autosave_v6 boolean false" | debconf-set-selections')
shell('apt-get install iptables-persistent -y')
end
ip6tables_flush_all_tables
shell('ip6tables -A INPUT -d fe80::/64 -p tcp -m tcp --dport 546 --sport 547 -j ACCEPT -m comment --comment 000-foobar')
end
it do
shell('puppet resource firewall \'000-foobar\' provider=ip6tables') do |r|
r.exit_code.should be_zero
# don't check stdout, testing preexisting rules, output is normal
r.stderr.should be_empty
end
end
end
end
end

View File

@@ -0,0 +1,280 @@
require 'spec_helper_acceptance'
describe 'complex ruleset 1' do
before :all do
iptables_flush_all_tables
ip6tables_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 { '001 ssh needed for beaker testing':
proto => 'tcp',
dport => '22',
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { '010 INPUT allow established and related':
proto => 'all',
state => ['ESTABLISHED', 'RELATED'],
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { "011 reject local traffic not on loopback interface":
iniface => '! lo',
proto => 'all',
destination => '127.0.0.1/8',
action => 'reject',
}
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 { '025 smtp':
outiface => '! eth0:2',
chain => 'OUTPUT',
proto => 'tcp',
dport => '25',
state => 'NEW',
action => 'accept',
}
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 { '443 ssl on aliased interface':
proto => 'tcp',
dport => '443',
state => 'NEW',
action => 'accept',
iniface => 'eth0:3',
}
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 => do_catch_changes)
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 -d 127.0.0.0\/(8|255\.0\.0\.0) (! -i|-i !) lo -m comment --comment \"011 reject local traffic not on loopback interface\" -j REJECT --reject-with icmp-port-unreachable/,
/-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 -p tcp -m multiport --dports 22 -m comment --comment \"001 ssh needed for beaker testing\" -j ACCEPT/,
/-A OUTPUT (! -o|-o !) eth0:2 -p tcp -m multiport --dports 25 -m comment --comment \"025 smtp\" -m state --state NEW -j ACCEPT/,
/-A INPUT -i eth0:3 -p tcp -m multiport --dports 443 -m comment --comment \"443 ssl on aliased interface\" -m state --state NEW -j ACCEPT/,
/-A INPUT -m comment --comment \"900 LOCAL_INPUT\" -j LOCAL_INPUT/,
/-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

View File

@@ -0,0 +1,98 @@
require 'spec_helper_acceptance'
# RHEL5 does not support -m socket
describe 'firewall socket property', :unless => default['platform'] =~ /el-5/ || fact('operatingsystem') == 'SLES' do
before :all do
iptables_flush_all_tables
ip6tables_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

View File

@@ -0,0 +1,60 @@
require 'spec_helper_acceptance'
# Some tests for the standard recommended usage
describe 'standard usage tests' 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 { "0002 reject local traffic not on loopback interface":
iniface => '! lo',
destination => '127.0.0.1/8',
action => 'reject',
}->
firewall { '003 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)
apply_manifest(pp, :catch_changes => do_catch_changes)
end
end

View File

@@ -0,0 +1,6 @@
--format
s
--colour
--loadby
mtime
--backtrace

View File

@@ -0,0 +1,7 @@
require 'puppetlabs_spec_helper/module_spec_helper'
# put local configuration and setup into spec_helper_local
begin
require 'spec_helper_local'
rescue LoadError
end

View File

@@ -0,0 +1,41 @@
require 'beaker-rspec'
require 'beaker/puppet_install_helper'
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', 'mangle'].each do |t|
expect(shell("ip6tables -t #{t} -F").stderr).to eq("")
end
end
def do_catch_changes
if default['platform'] =~ /el-5/
return false
else
return true
end
end
run_puppet_install_helper
RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
hosts.each do |host|
copy_module_to(host, :source => proj_root, :module_name => 'firewall')
on host, puppet('module install puppetlabs-stdlib --version 3.2.0')
# the ubuntu-14.04 docker image doesn't carry the iptables command
apply_manifest_on host, 'package { "iptables": ensure => installed }' if fact('osfamily') == 'Debian'
end
end
end

View File

@@ -0,0 +1,14 @@
RSpec.configure do |config|
config.mock_with :rspec
end
def with_debian_facts
let :facts do
{
:kernel => 'Linux',
:operatingsystem => 'Debian',
:operatingsystemrelease => '8.0',
:osfamily => 'Debian',
}
end
end

View File

@@ -0,0 +1,38 @@
require 'spec_helper'
describe 'firewall::linux::archlinux', :type => :class do
let(:facts) do
{
:osfamily => 'Archlinux',
:operatingsystem => 'Archlinux'
}
end
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

View File

@@ -0,0 +1,87 @@
require 'spec_helper'
describe 'firewall::linux::debian', :type => :class do
context "Debian 7" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '7.0'
}}
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('iptables-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
end
context 'deb7 enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '7.0'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables-persistent').with(
:enable => 'false'
)}
end
context "Debian 8" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => 'jessie/sid'
}}
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('netfilter-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
end
context 'deb8 enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => 'jessie/sid'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('netfilter-persistent').with(
:enable => 'false'
)}
end
context "Debian 8, alt operatingsystem" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '8.0'
}}
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('netfilter-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
end
context 'deb8, alt operatingsystem, enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '8.0'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('netfilter-persistent').with(
:enable => 'false'
)}
end
end

View File

@@ -0,0 +1,41 @@
require 'spec_helper'
describe 'firewall::linux::gentoo', :type => :class do
let(:facts) do
{
:osfamily => 'Gentoo',
:operatingsystem => 'Gentoo'
}
end
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
it { should contain_service('ip6tables').with(
:ensure => 'running',
:enable => 'true'
)}
it { should contain_package('net-firewall/iptables').with(
:ensure => 'present'
)}
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

View File

@@ -0,0 +1,71 @@
require 'spec_helper'
RSpec.shared_examples "ensures iptables service" do
context 'default' do
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
)}
end
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
describe 'firewall::linux::redhat', :type => :class do
%w{RedHat CentOS Fedora}.each do |os|
oldreleases = (os == 'Fedora' ? ['14'] : ['6.5'])
newreleases = (os == 'Fedora' ? ['15','Rawhide'] : ['7.0.1406'])
oldreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:operatingsystem => os,
:operatingsystemrelease => osrel,
:osfamily => 'RedHat',
:selinux => false,
}}
it { should_not contain_service('firewalld') }
it { should_not contain_package('iptables-services') }
it_behaves_like "ensures iptables service"
end
end
newreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:operatingsystem => os,
:operatingsystemrelease => osrel,
:osfamily => 'RedHat',
:selinux => false,
}}
it { should contain_service('firewalld').with(
:ensure => 'stopped',
:enable => false,
:before => 'Package[iptables-services]'
)}
it { should contain_package('iptables-services').with(
:ensure => 'present',
:before => 'Service[iptables]'
)}
it_behaves_like "ensures iptables service"
end
end
end
end

View File

@@ -0,0 +1,44 @@
require 'spec_helper'
describe 'firewall::linux', :type => :class do
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) {{
:kernel => 'Linux',
:operatingsystem => os,
:operatingsystemrelease => osrel,
:osfamily => 'RedHat',
:selinux => false,
}}
it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') }
it { should contain_package('iptables').with_ensure('present') }
end
end
end
end
end
context 'Debian like' do
%w{Debian Ubuntu}.each do |os|
context "operatingsystem => #{os}" do
releases = (os == 'Debian' ? ['6','7','8'] : ['10.04','12.04','14.04'])
releases.each do |osrel|
let(:facts) {{
:kernel => 'Linux',
:operatingsystem => os,
:operatingsystemrelease => osrel,
:osfamily => 'Debian',
:selinux => false,
}}
it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') }
it { should contain_package('iptables').with_ensure('present') }
end
end
end
end
end

View File

@@ -0,0 +1,35 @@
require 'spec_helper'
describe 'firewall', :type => :class do
context 'kernel => Linux' do
with_debian_facts
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 'kernel => SunOS' do
let(:facts) {{ :kernel => 'SunOS' }}
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
end
context 'kernel => Darwin' do
let(:facts) {{ :kernel => 'Darwin' }}
it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) }
end
context 'ensure => stopped' do
with_debian_facts
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

View File

@@ -0,0 +1,100 @@
require 'spec_helper'
describe "Facter::Util::Fact iptables_persistent_version" do
context "iptables-persistent applicable" 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|
if os == "Debian"
os_release = "7.0"
elsif os == "Ubuntu"
os_release = "14.04"
end
describe "#{os} package installed" do
before {
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return(os)
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return(os_release)
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.fact(:operatingsystemrelease)).to receive(:value).and_return('14.04')
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
context "netfilter-persistent applicable" do
before { Facter.clear }
let(:dpkg_cmd) { "dpkg-query -Wf '${Version}' netfilter-persistent 2>/dev/null" }
{
"Debian" => "0.0.20090701",
"Ubuntu" => "0.5.3ubuntu2",
}.each do |os, ver|
if os == "Debian"
os_release = "8.0"
elsif os == "Ubuntu"
os_release = "14.10"
end
describe "#{os} package installed" do
before {
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return(os)
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return(os_release)
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
os_release = "14.10"
before {
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return(os_release)
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
end

View 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

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env rspec
require 'spec_helper'
if Puppet.version < '3.4.0'
require 'puppet/provider/confine/exists'
else
require 'puppet/confine/exists'
end
provider_class = Puppet::Type.type(:firewall).provider(:ip6tables)
describe 'ip6tables' do
let(:params) { {:name => '000 test foo', :action => 'accept'} }
let(:provider) { provider_class }
let(:resource) { Puppet::Type.type(:firewall) }
let(:ip6tables_version) { '1.4.0' }
before :each do
end
def stub_iptables
allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return provider
# Stub confine facts
allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save"
allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian')
allow(Facter.fact('ip6tables_version')).to receive(:value).and_return(ip6tables_version)
allow(Puppet::Util::Execution).to receive(:execute).and_return ""
allow(Puppet::Util).to receive(:which).with("iptables-save").
and_return "/sbin/iptables-save"
end
shared_examples 'raise error' do
it {
stub_iptables
expect {
provider.new(resource.new(params))
}.to raise_error(Puppet::DevError, error_message)
}
end
shared_examples 'run' do
it {
stub_iptables
provider.new(resource.new(params))
}
end
context 'iptables 1.3' do
let(:params) { {:name => '000 test foo', :action => 'accept'} }
let(:error_message) { /The ip6tables provider is not supported on version 1\.3 of iptables/ }
let(:ip6tables_version) { '1.3.10' }
it_should_behave_like 'raise error'
end
context 'ip6tables nil' do
let(:params) { {:name => '000 test foo', :action => 'accept'} }
let(:error_message) { /The ip6tables provider is not supported on version 1\.3 of iptables/ }
let(:ip6tables_version) { nil }
it_should_behave_like 'run'
end
end

View File

@@ -0,0 +1,237 @@
#!/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
# 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 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) { |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',
'security:INPUT:IPv4',
'security:FORWARD:IPv4',
'security:OUTPUT: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',
'security:INPUT:IPv6',
'security:FORWARD:IPv6',
'security:OUTPUT: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

View File

@@ -0,0 +1,434 @@
#!/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
# 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 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) { |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_truthy
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 inverting rules' do
let(:resource) {
Puppet::Type.type(:firewall).new({
:name => '040 partial invert',
:table => 'filter',
:action => 'accept',
:chain => 'nova-compute-FORWARD',
:source => '0.0.0.0/32',
:destination => '255.255.255.255/32',
:sport => ['! 78','79','http'],
:dport => ['77','! 76'],
:proto => 'udp',
})
}
let(:instance) { provider.new(resource) }
it 'fails when not all array items are inverted' do
expect { instance.insert }.to raise_error Puppet::Error, /but '79', '80' are not prefixed/
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

View File

@@ -0,0 +1,688 @@
#!/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, :ipv4, :ipv6, :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
describe 'port deprecated' do
it "raises a warning" do
expect(Puppet).to receive(:warning).with /port to firewall is deprecated/
@resource[:port] = "22"
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
it "should accept a negated #{iface} value as a string" do
@resource[iface] = '! eth1'
@resource[iface].should == '! eth1'
end
it "should accept an interface alias for the #{iface} value as a string" do
@resource[iface] = 'eth1:2'
@resource[iface].should == 'eth1:2'
end
end
end
[:tosource, :todest, :to].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',
'2' => 'too-big',
'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, iptables-persistent, and iptables-services" do
@resource[:provider] = provider
@resource[:provider].should == provider
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent'),
Puppet::Type.type(:package).new(:name => 'iptables-services')
]
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
it 'is suitable' do
expect(@resource.suitable?).to be_truthy
end
end
describe 'firewall on unsupported platforms' do
it 'is not suitable' do
# Stub iptables version
allow(Facter.fact(:iptables_version)).to receive(:value).and_return(nil)
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return(nil)
# Stub confine facts
allow(Facter.fact(:kernel)).to receive(:value).and_return('Darwin')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Darwin')
resource = firewall.new(:name => "000 test foo", :ensure => :present)
# If our provider list is nil, then the Puppet::Transaction#evaluate will
# say 'Error: Could not find a suitable provider for firewall' but there
# isn't a unit testable way to get this.
expect(resource.suitable?).to be_falsey
end
end

View File

@@ -0,0 +1,217 @@
#!/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'],
'security' => ['INPUT','OUTPUT','FORWARD']
}.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 accept #{name} for Linux 3.7+" do
allow(Facter.fact(:kernelmajversion)).to receive(:value).and_return('3.7')
resource[:name] = name
resource[:name].should == name
end
it "should fail #{name} for Linux 2.6" do
allow(Facter.fact(:kernelmajversion)).to receive(:value).and_return('2.6')
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, iptables-persistent, and iptables-services" do
resource[:provider].should == :iptables_chain
packages = [
Puppet::Type.type(:package).new(:name => 'iptables'),
Puppet::Type.type(:package).new(:name => 'iptables-persistent'),
Puppet::Type.type(:package).new(:name => 'iptables-services')
]
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
stub_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
allow(Puppet::Type.type(:firewall).provider(:iptables)).to receive(:iptables_save).and_return(stub_return)
allow(Puppet::Type.type(:firewall).provider(:ip6tables)).to receive(:ip6tables_save).and_return(stub_return)
end
it 'should generate iptables resources' do
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return("1.4.21")
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
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return("1.4.21")
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
it 'is suitable' do
expect(resource.suitable?).to be_truthy
end
end
describe 'firewall on unsupported platforms' do
it 'is not suitable' do
# Stub iptables version
allow(Facter.fact(:iptables_version)).to receive(:value).and_return(nil)
allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return(nil)
# Stub confine facts
allow(Facter.fact(:kernel)).to receive(:value).and_return('Darwin')
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Darwin')
resource = firewallchain.new(:name => "INPUT:filter:IPv4", :ensure => :present)
# If our provider list is nil, then the Puppet::Transaction#evaluate will
# say 'Error: Could not find a suitable provider for firewall' but there
# isn't a unit testable way to get this.
expect(resource.suitable?).to be_falsey
end
end

View File

@@ -0,0 +1,208 @@
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('neighbour-solicitation', proto).should == '135' }
specify { subject.icmp_name_to_number('neighbour-advertisement', proto).should == '136' }
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 6 identified from operatingsystem and operatingsystemrelease' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6.5')
expect(subject).to receive(:execute).with(%w{/sbin/service iptables save})
subject.persist_iptables(proto)
end
it 'should exec for CentOS 7 identified from operatingsystem and operatingsystemrelease' do
allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil)
allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS')
allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7.0.1406')
expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init 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

Some files were not shown because too many files have changed in this diff Show More