puppetlabs-firewall-0.4.2/000755 000765 000024 00000000000 12213700171 016273 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/Changelog000644 000765 000024 00000027141 12213700171 020112 0ustar00apenneystaff000000 000000 ## puppetlabs-firewall changelog Release notes for puppetlabs-firewall module. --------------------------------------- #### 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 ... puppetlabs-firewall-0.4.2/CONTRIBUTING.md000644 000765 000024 00000030114 12213700171 020523 0ustar00apenneystaff000000 000000 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. - Associated the Redmine ticket in the message. The first line should include the ticket 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 suite passes after your commit (rake spec unit). * Submission: * Pre-requisites: - Make sure you have a [Redmine account](http://projects.puppetlabs.com) - Sign the [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign) - [Create a ticket](http://projects.puppetlabs.com/projects/modules/issues/new), or [watch the ticket](http://projects.puppetlabs.com/projects/modules/issues) 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 ================ 0. Decide what to base your work on. In general, you should always base your work on the oldest branch that your change is relevant to. - A bug fix should be based on the current stable series. If the bug is not present in the current stable release, then base it on `master`. - A new feature should be based on `master`. - Security fixes should be based on the current maintenance series (that is, the previous stable series). If the security issue was not present in the maintenance series, then it should be based on the current stable series if it was introduced there, or on `master` if it is not yet present in a stable release. 1. Make separate commits for logically separate changes. Please break your commits down into logically consistent units which include new or changed tests relevent 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're 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 isn't re-introduced, and that the feature isn't accidentally broken. Describe the technical detail of the change(s). If your description starts to get too long, that's 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 whitespaces or other "whitespace errors". You can do this by running "git diff --check" on your changes before you commit. 2. Sign the Contributor License Agreement Before we can accept your changes, we do need a signed Puppet Labs Contributor License Agreement (CLA). You can access the CLA via the [Contributor License Agreement link](https://projects.puppetlabs.com/contributor_licenses/sign) in the top menu bar of our Redmine instance. Once you've signed the CLA, a badge will show up next to your name on the [Puppet Project Overview Page](http://projects.puppetlabs.com/projects/modules?jump=welcome), and your name will be listed under "Contributor License Signers" section. If you have any questions about the CLA, please feel free to contact Puppet Labs via email at cla-submissions@puppetlabs.com. 3. Sending your patches We accept multiple ways of submitting your changes for inclusion. They are listed below in order of preference. Please keep in mind that any method that involves sending email to the mailing list directly requires you to be subscribed to the mailing list, and that your first post to the list will be held in a moderation queue. * GitHub Pull Requests 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" or one of the release, or RC branches. 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'll 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. You'll want to make sure that you have the appropriate destination branch in the repository under the puppetlabs organization. This should be the same branch that you based your changes off of. * Other pull requests If you already have a publicly accessible version of the repository hosted elsewhere, and don't wish to or cannot use GitHub, you can submit your change by requesting that we pull the changes from your repository by sending an email to the puppet-dev Google Groups mailing list. `git-request-pull(1)` provides a handy way to generate the text for the email requesting that we pull your changes (and does some helpful sanity checks in the process). * Mailing patches to the mailing list If neither of the previous methods works for you, then you can also mail the patches inline to the puppet-dev Google Group using either `rake mail_patches`, or by using `git-format-patch(1)`, and `git-send-email(1)` directly. `rake mail_patches` handles setting the appropriate flags to `git-format-patch(1)` and `git-send-email(1)` for you, but doesn't allow adding any commentary between the '---', and the diffstat in the resulting email. It also requires that you have created your topic branch in the form `//`. If you decide to use `git-format-patch(1)` and `git-send-email(1)` directly, please be sure to use the following flags for `git-format-patch(1)`: -C -M -s -n --subject-prefix='PATCH/puppet' * Attaching patches to Redmine As a method of last resort you can also directly attach the output of `git-format-patch(1)`, or `git-diff(1)` to a Redmine ticket. If you are generating the diff outside of Git, please be sure to generate a unified diff. 4. Update the related Redmine ticket. If there's a Redmine ticket associated with the change you submitted, then you should update the ticket to include the location of your branch, and change the status to "In Topic Branch Pending Merge", along with any other commentary you may wish to make. How to track the status of your change after it's been submitted ================================================================ Shortly after opening a pull request on GitHub, there should be an automatic message sent to the puppet-dev Google Groups mailing list notifying people of this. This notification is used to let the Puppet development community know about your requested change to give them a chance to review, test, and comment on the change(s). If you submitted your change via manually sending a pull request or mailing the patches, then we keep track of these using [patchwork](https://patchwork.puppetlabs.com). When code is merged into the project it is automatically removed from patchwork, and the Redmine ticket is manually updated with the commit SHA1. In addition, the ticket status must be updated by the person who merges the topic branch to a status of "Merged - Pending Release" We do our best to comment on or merge submitted changes within a week. However, if there hasn't been any commentary on the pull request or mailed patches, and it hasn't been merged in after a week, then feel free to ask for an update by replying on the mailing list to the automatic notification or mailed patches. It probably wasn't intentional, and probably just slipped through the cracks. Additional Resources ==================== * [Getting additional help](http://projects.puppetlabs.com/projects/puppet/wiki/Getting_Help) * [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests) * [Bug tracker (Redmine)](http://projects.puppetlabs.com/projects/modules) * [Patchwork](https://patchwork.puppetlabs.com) * [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign) * [General GitHub documentation](http://help.github.com/) * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) If you have commit access to the repository =========================================== Even if you have commit access to the repository, you'll 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 didn't 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. * Merging topic branches When merging code from a topic branch into the integration branch (Ex: master, 2.7.x, 1.6.x, etc.), there should always be a merge commit. You can accomplish this by always providing the `--no-ff` flag to `git merge`. git merge --no-ff --log ticket/1234-fix-something-broken The reason for always forcing this merge commit is that it provides a consistent way to look up what changes & commits were in a topic branch, whether that topic branch had one, or 500 commits. For example, if the merge commit had an abbreviated SHA-1 of `coffeebad`, then you could use the following `git log` invocation to show you which commits it brought in: git log coffeebad^1..coffeebad^2 The following would show you which changes were made on the topic branch: git diff coffeebad^1...coffeebad^2 Because we _always_ merge the topic branch into the integration branch the first parent (`^1`) of a merge commit will be the most recent commit on the integration branch from just before we merged in the topic, and the second parent (`^2`) will always be the most recent commit that was made in the topic branch. This also serves as the record of who performed the code review, as mentioned above. puppetlabs-firewall-0.4.2/Gemfile000644 000765 000024 00000000504 12213700171 017565 0ustar00apenneystaff000000 000000 source 'https://rubygems.org' group :development, :test do gem 'puppetlabs_spec_helper', :require => false gem 'rspec-system-puppet', '~>2.0' gem 'puppet-lint' end if puppetversion = ENV['PUPPET_GEM_VERSION'] gem 'puppet', puppetversion, :require => false else gem 'puppet', :require => false end # vim:ft=ruby puppetlabs-firewall-0.4.2/Gemfile.lock000644 000765 000024 00000002475 12213700171 020525 0ustar00apenneystaff000000 000000 GEM remote: https://rubygems.org/ specs: builder (3.2.2) diff-lcs (1.2.4) facter (1.7.2) hiera (1.2.1) json_pure json_pure (1.8.0) kwalify (0.7.2) metaclass (0.0.1) mocha (0.14.0) metaclass (~> 0.0.1) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.6.8) nokogiri (1.5.10) puppet (3.2.3) facter (~> 1.6) hiera (~> 1.0) rgen (~> 0.6.5) puppet-lint (0.3.2) puppetlabs_spec_helper (0.4.1) mocha (>= 0.10.5) rake rspec (>= 2.9.0) rspec-puppet (>= 0.1.1) rake (10.1.0) rbvmomi (1.6.0) builder nokogiri (>= 1.4.1) trollop rgen (0.6.5) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) rspec-core (2.14.5) rspec-expectations (2.14.1) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.14.3) rspec-puppet (0.1.6) rspec rspec-system (2.2.0) kwalify (~> 0.7.2) net-scp (~> 1.1) net-ssh (~> 2.6) nokogiri (~> 1.5.9) rbvmomi (~> 1.6) rspec (~> 2.13) systemu (~> 2.5) rspec-system-puppet (2.1.0) rspec-system (~> 2.0) systemu (2.5.2) trollop (2.0) PLATFORMS ruby DEPENDENCIES puppet puppet-lint puppetlabs_spec_helper rspec-system-puppet (~> 2.0) puppetlabs-firewall-0.4.2/lib/000755 000765 000024 00000000000 12213700171 017041 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/LICENSE000644 000765 000024 00000001651 12213700171 017303 0ustar00apenneystaff000000 000000 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. puppetlabs-firewall-0.4.2/manifests/000755 000765 000024 00000000000 12213700171 020264 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/metadata.json000644 000765 000024 00000045167 12213700171 020763 0ustar00apenneystaff000000 000000 { "name": "puppetlabs-firewall", "version": "0.4.2", "source": "git://github.com/puppetlabs/puppetlabs-firewall.git", "author": "puppetlabs", "license": "ASL 2.0", "summary": "Firewall Module", "description": "Manages Firewalls such as iptables", "project_page": "http://forge.puppetlabs.com/puppetlabs/firewall", "dependencies": [ ], "types": [ { "name": "firewall", "doc": " This type provides the capability to manage firewall rules within\n puppet.\n\n **Autorequires:**\n\n If Puppet is managing the iptables or ip6tables chains specified in the\n `chain` or `jump` parameters, the firewall resource will autorequire\n those firewallchain resources.\n\n If Puppet is managing the iptables or iptables-persistent packages, and\n the provider is iptables or ip6tables, the firewall resource will\n autorequire those packages to ensure that any required binaries are\n installed.\n", "properties": [ { "name": "ensure", "doc": " Manage the state of this rule. The default action is *present*.\n Valid values are `present`, `absent`." }, { "name": "action", "doc": " This is the action to perform on a match. Can be one of:\n\n * accept - the packet is accepted\n * reject - the packet is rejected with a suitable ICMP response\n * drop - the packet is dropped\n\n If you specify no value it will simply match the rule but perform no\n action unless you provide a provider specific parameter (such as *jump*).\n Valid values are `accept`, `reject`, `drop`." }, { "name": "source", "doc": " The source address. For example:\n\n source => '192.168.2.0/24'\n\n The source can also be an IPv6 address if your provider supports it.\n" }, { "name": "src_range", "doc": " The source IP range. For example:\n\n src_range => '192.168.1.1-192.168.1.10'\n\n The source IP range is must in 'IP1-IP2' format.\n Values can match `/^((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)-((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)/`. Requires features iprange." }, { "name": "destination", "doc": " The destination address to match. For example:\n\n destination => '192.168.1.0/24'\n\n The destination can also be an IPv6 address if your provider supports it.\n" }, { "name": "dst_range", "doc": " The destination IP range. For example:\n\n dst_range => '192.168.1.1-192.168.1.10'\n\n The destination IP range is must in 'IP1-IP2' format.\n Values can match `/^((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)-((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)/`. Requires features iprange." }, { "name": "sport", "doc": " The source port to match for this filter (if the protocol supports\n ports). Will accept a single element or an array.\n\n For some firewall providers you can pass a range of ports in the format:\n\n -\n\n For example:\n\n 1-1024\n\n This would cover ports 1 to 1024.\n" }, { "name": "dport", "doc": " The destination port to match for this filter (if the protocol supports\n ports). Will accept a single element or an array.\n\n For some firewall providers you can pass a range of ports in the format:\n\n -\n\n For example:\n\n 1-1024\n\n This would cover ports 1 to 1024.\n" }, { "name": "port", "doc": " The destination or source port to match for this filter (if the protocol\n supports ports). Will accept a single element or an array.\n\n For some firewall providers you can pass a range of ports in the format:\n\n -\n\n For example:\n\n 1-1024\n\n This would cover ports 1 to 1024.\n" }, { "name": "dst_type", "doc": " The destination address type. For example:\n\n dst_type => 'LOCAL'\n\n Can be one of:\n\n * UNSPEC - an unspecified address\n * UNICAST - a unicast address\n * LOCAL - a local address\n * BROADCAST - a broadcast address\n * ANYCAST - an anycast packet\n * MULTICAST - a multicast address\n * BLACKHOLE - a blackhole address\n * UNREACHABLE - an unreachable address\n * PROHIBIT - a prohibited address\n * THROW - undocumented\n * NAT - undocumented\n * XRESOLVE - undocumented\n Valid values are `UNSPEC`, `UNICAST`, `LOCAL`, `BROADCAST`, `ANYCAST`, `MULTICAST`, `BLACKHOLE`, `UNREACHABLE`, `PROHIBIT`, `THROW`, `NAT`, `XRESOLVE`. Requires features address_type." }, { "name": "src_type", "doc": " The source address type. For example:\n\n src_type => 'LOCAL'\n\n Can be one of:\n\n * UNSPEC - an unspecified address\n * UNICAST - a unicast address\n * LOCAL - a local address\n * BROADCAST - a broadcast address\n * ANYCAST - an anycast packet\n * MULTICAST - a multicast address\n * BLACKHOLE - a blackhole address\n * UNREACHABLE - an unreachable address\n * PROHIBIT - a prohibited address\n * THROW - undocumented\n * NAT - undocumented\n * XRESOLVE - undocumented\n Valid values are `UNSPEC`, `UNICAST`, `LOCAL`, `BROADCAST`, `ANYCAST`, `MULTICAST`, `BLACKHOLE`, `UNREACHABLE`, `PROHIBIT`, `THROW`, `NAT`, `XRESOLVE`. Requires features address_type." }, { "name": "proto", "doc": " The specific protocol to match for this rule. By default this is\n *tcp*.\n Valid values are `tcp`, `udp`, `icmp`, `ipv6-icmp`, `esp`, `ah`, `vrrp`, `igmp`, `ipencap`, `ospf`, `gre`, `all`." }, { "name": "tcp_flags", "doc": " Match when the TCP flags are as specified.\n Is a string with a list of comma-separated flag names for the mask,\n then a space, then a comma-separated list of flags that should be set.\n The flags are: SYN ACK FIN RST URG PSH ALL NONE\n Note that you specify them in the order that iptables --list-rules\n would list them to avoid having puppet think you changed the flags.\n Example: FIN,SYN,RST,ACK SYN matches packets with the SYN bit set and the\n\t ACK,RST and FIN bits cleared. Such packets are used to request\n TCP connection initiation.\n Requires features tcp_flags." }, { "name": "chain", "doc": " Name of the chain to use. Can be one of the built-ins:\n\n * INPUT\n * FORWARD\n * OUTPUT\n * PREROUTING\n * POSTROUTING\n\n Or you can provide a user-based chain.\n\n The default value is 'INPUT'.\n Values can match `/^[a-zA-Z0-9\\-_]+$/`. Requires features iptables." }, { "name": "table", "doc": " Table to use. Can be one of:\n\n * nat\n * mangle\n * filter\n * raw\n * rawpost\n\n By default the setting is 'filter'.\n Valid values are `nat`, `mangle`, `filter`, `raw`, `rawpost`. Requires features iptables." }, { "name": "jump", "doc": " The value for the iptables --jump parameter. Normal values are:\n\n * QUEUE\n * RETURN\n * DNAT\n * SNAT\n * LOG\n * MASQUERADE\n * REDIRECT\n * MARK\n\n But any valid chain name is allowed.\n\n For the values ACCEPT, DROP and REJECT you must use the generic\n 'action' parameter. This is to enfore the use of generic parameters where\n possible for maximum cross-platform modelling.\n\n If you set both 'accept' and 'jump' parameters, you will get an error as\n only one of the options should be set.\n Requires features iptables." }, { "name": "iniface", "doc": " Input interface to filter on.\n Values can match `/^[a-zA-Z0-9\\-\\._\\+]+$/`. Requires features interface_match." }, { "name": "outiface", "doc": " Output interface to filter on.\n Values can match `/^[a-zA-Z0-9\\-\\._\\+]+$/`. Requires features interface_match." }, { "name": "tosource", "doc": " When using jump => \"SNAT\" you can specify the new source address using\n this parameter.\n Requires features snat." }, { "name": "todest", "doc": " When using jump => \"DNAT\" you can specify the new destination address\n using this paramter.\n Requires features dnat." }, { "name": "toports", "doc": " For DNAT this is the port that will replace the destination port.\n Requires features dnat." }, { "name": "reject", "doc": " When combined with jump => \"REJECT\" you can specify a different icmp\n response to be sent back to the packet sender.\n Requires features reject_type." }, { "name": "log_level", "doc": " When combined with jump => \"LOG\" specifies the system log level to log\n to.\n Requires features log_level." }, { "name": "log_prefix", "doc": " When combined with jump => \"LOG\" specifies the log prefix to use when\n logging.\n Requires features log_prefix." }, { "name": "icmp", "doc": " When matching ICMP packets, this is the type of ICMP packet to match.\n\n A value of \"any\" is not supported. To achieve this behaviour the\n parameter should simply be omitted or undefined.\n Requires features icmp_match." }, { "name": "state", "doc": " Matches a packet based on its state in the firewall stateful inspection\n table. Values can be:\n\n * INVALID\n * ESTABLISHED\n * NEW\n * RELATED\n Valid values are `INVALID`, `ESTABLISHED`, `NEW`, `RELATED`. Requires features state_match." }, { "name": "limit", "doc": " Rate limiting value for matched packets. The format is:\n rate/[/second/|/minute|/hour|/day].\n\n Example values are: '50/sec', '40/min', '30/hour', '10/day'.\"\n Requires features rate_limiting." }, { "name": "burst", "doc": " Rate limiting burst value (per second) before limit checks apply.\n Values can match `/^\\d+$/`. Requires features rate_limiting." }, { "name": "uid", "doc": " UID or Username owner matching rule. Accepts a string argument\n only, as iptables does not accept multiple uid in a single\n statement.\n Requires features owner." }, { "name": "gid", "doc": " GID or Group owner matching rule. Accepts a string argument\n only, as iptables does not accept multiple gid in a single\n statement.\n Requires features owner." }, { "name": "set_mark", "doc": " Set the Netfilter mark value associated with the packet. Accepts either of:\n mark/mask or mark. These will be converted to hex if they are not already.\n Requires features mark." }, { "name": "pkttype", "doc": " Sets the packet type to match.\n Valid values are `unicast`, `broadcast`, `multicast`. Requires features pkttype." }, { "name": "isfragment", "doc": " Set to true to match tcp fragments (requires type to be set to tcp)\n Valid values are `true`, `false`. Requires features isfragment." }, { "name": "socket", "doc": " If true, matches if an open socket can be found by doing a coket lookup\n on the packet.\n Valid values are `true`, `false`. Requires features socket." } ], "parameters": [ { "name": "name", "doc": " The canonical name of the rule. This name is also used for ordering\n so make sure you prefix the rule with a number:\n\n 000 this runs first\n 999 this runs last\n\n Depending on the provider, the name of the rule can be stored using\n the comment feature of the underlying firewall subsystem.\n Values can match `/^\\d+[[:alpha:][:digit:][:punct:][:space:]]+$/`." }, { "name": "line", "doc": " Read-only property for caching the rule line.\n" } ], "providers": [ { "name": "ip6tables", "doc": "Ip6tables type provider\n\nRequired binaries: `ip6tables`, `ip6tables-save`. Supported features: `dnat`, `icmp_match`, `interface_match`, `iptables`, `log_level`, `log_prefix`, `mark`, `owner`, `pkttype`, `rate_limiting`, `reject_type`, `snat`, `state_match`, `tcp_flags`." }, { "name": "iptables", "doc": "Iptables type provider\n\nRequired binaries: `iptables`, `iptables-save`. Default for `kernel` == `linux`. Supported features: `address_type`, `dnat`, `icmp_match`, `interface_match`, `iprange`, `iptables`, `isfragment`, `log_level`, `log_prefix`, `mark`, `owner`, `pkttype`, `rate_limiting`, `reject_type`, `snat`, `socket`, `state_match`, `tcp_flags`." } ] }, { "name": "firewallchain", "doc": " This type provides the capability to manage rule chains for firewalls.\n\n Currently this supports only iptables, ip6tables and ebtables on Linux. And\n provides support for setting the default policy on chains and tables that\n allow it.\n\n **Autorequires:**\n If Puppet is managing the iptables or iptables-persistent packages, and\n the provider is iptables_chain, the firewall resource will autorequire\n those packages to ensure that any required binaries are installed.\n", "properties": [ { "name": "ensure", "doc": "The basic property that the resource should be in. Valid values are `present`, `absent`." }, { "name": "policy", "doc": " This is the action to when the end of the chain is reached.\n It can only be set on inbuilt chains (INPUT, FORWARD, OUTPUT,\n PREROUTING, POSTROUTING) and can be one of:\n\n * accept - the packet is accepted\n * drop - the packet is dropped\n * queue - the packet is passed userspace\n * return - the packet is returned to calling (jump) queue\n or the default of inbuilt chains\n Valid values are `accept`, `drop`, `queue`, `return`." } ], "parameters": [ { "name": "name", "doc": " The canonical name of the chain.\n\n For iptables the format must be {chain}:{table}:{protocol}.\n" } ], "providers": [ { "name": "iptables_chain", "doc": "Iptables chain provider\n\nRequired binaries: `iptables`, `iptables-save`, `ip6tables`, `ip6tables-save`, `ebtables`, `ebtables-save`. Default for `kernel` == `linux`. Supported features: `iptables_chain`, `policy`." } ] } ], "checksums": { "CONTRIBUTING.md": "346969b756bc432a2a2fab4307ebb93a", "Changelog": "1de1691b4ab10ee354f761a1f4c6f443", "Gemfile": "cbdce086f4dbabe5394121e2281b739f", "Gemfile.lock": "df949ce515d5c06d6ed31b9d7e5e3391", "LICENSE": "ade7f2bb88b5b4f034152822222ec314", "Modulefile": "5e06a785cd9bce7b53f95c23eba506d2", "README.markdown": "41df885b5286abc9ba27f054c5ff6dbf", "Rakefile": "35d0261289b65faa09bef45b888d40ae", "lib/facter/ip6tables_version.rb": "091123ad703f1706686bca4398c5b06f", "lib/facter/iptables_persistent_version.rb": "b7a47827cd3d3bb1acbd526a31da3acb", "lib/facter/iptables_version.rb": "facbd760223f236538b731c1d1f6cf8f", "lib/puppet/provider/firewall/ip6tables.rb": "e9579ae3afdf8b1392cbdc0335ef5464", "lib/puppet/provider/firewall/iptables.rb": "bb7ea2c54c60c1047e68745f3b370c6f", "lib/puppet/provider/firewall.rb": "32d2f5e5dcc082986b82ef26a119038b", "lib/puppet/provider/firewallchain/iptables_chain.rb": "e98592c22901792305e0d20376c9a281", "lib/puppet/type/firewall.rb": "2a591254b2df7528eafaa6dff5459ace", "lib/puppet/type/firewallchain.rb": "91ebccecff290a9ab2116867a74080c7", "lib/puppet/util/firewall.rb": "a9f0057c1b16a51a0bace5d4a8cc4ea4", "lib/puppet/util/ipcidr.rb": "e1160dfd6e73fc5ef2bb8abc291f6fd5", "manifests/init.pp": "ba3e697f00fc3d4e7e5b9c7fdbc6a89d", "manifests/linux/archlinux.pp": "1257fe335ecafa0629b285dc8621cf75", "manifests/linux/debian.pp": "626f0fd23f2f451ca14e2b7f690675fe", "manifests/linux/redhat.pp": "44ce25057ae8d814465260767b39c414", "manifests/linux.pp": "7380519131fa8daae0ef45f9a162aff7", "spec/fixtures/iptables/conversion_hash.rb": "012d92a358cc0c74304de14657bf9a23", "spec/spec_helper.rb": "faae8467928b93bd251a1a66e1eedbe5", "spec/spec_helper_system.rb": "4981e0b995c12996e628d004ffdcc9f4", "spec/system/basic_spec.rb": "34a22dedba01b8239024137bda8ab3f8", "spec/system/class_spec.rb": "04d89039312c3b9293dbb680878101c6", "spec/system/params_spec.rb": "f982f9eb6ecc8d6782b9267b59d321bf", "spec/system/purge_spec.rb": "a336e8a20d4c330606bf5955799a7e35", "spec/system/resource_cmd_spec.rb": "f991d2b7a3e2eb6d28471534cd38b0c8", "spec/system/standard_usage_spec.rb": "f80f86703843775ac14635464e9f7549", "spec/unit/classes/firewall_linux_archlinux_spec.rb": "1c600a9852ec328b14cb15b0630ed5ff", "spec/unit/classes/firewall_linux_debian_spec.rb": "6334936fb16223cf15f637083c67850e", "spec/unit/classes/firewall_linux_redhat_spec.rb": "f41b21caf6948f3ac08f42c1bc59ba1b", "spec/unit/classes/firewall_linux_spec.rb": "b934ab4e0a806f29bfdabd2369e41d0e", "spec/unit/classes/firewall_spec.rb": "14fc76eeb702913159661c01125baabb", "spec/unit/facter/iptables_persistent_version_spec.rb": "98aa337aae2ae8a2ac7f70586351e928", "spec/unit/facter/iptables_spec.rb": "ebb008f0e01530a49007228ca1a81097", "spec/unit/puppet/provider/iptables_chain_spec.rb": "6265dbb6be5af74f056d32c7e7236d0a", "spec/unit/puppet/provider/iptables_spec.rb": "b1e92084c8595b7e2ef21aa0800ea084", "spec/unit/puppet/type/firewall_spec.rb": "f229613c1bec34b6f84b544e021dc856", "spec/unit/puppet/type/firewallchain_spec.rb": "49157d8703daf8776e414ef9ea9e5cb3", "spec/unit/puppet/util/firewall_spec.rb": "3d7858f46ea3c97617311b7a5cebbae1", "spec/unit/puppet/util/ipcidr_spec.rb": "1a6eeb2dd7c9634fcfb60d8ead6e1d79" } }puppetlabs-firewall-0.4.2/Modulefile000644 000765 000024 00000000430 12213700171 020300 0ustar00apenneystaff000000 000000 name 'puppetlabs-firewall' version '0.4.2' source 'git://github.com/puppetlabs/puppetlabs-firewall.git' author 'puppetlabs' license 'ASL 2.0' summary 'Firewall Module' description 'Manages Firewalls such as iptables' project_page 'http://forge.puppetlabs.com/puppetlabs/firewall' puppetlabs-firewall-0.4.2/Rakefile000644 000765 000024 00000000610 12213700171 017735 0ustar00apenneystaff000000 000000 require 'rubygems' require 'bundler/setup' Bundler.require :default require 'rspec/core/rake_task' require 'puppetlabs_spec_helper/rake_tasks' require 'rspec-system/rake_task' require 'puppet-lint/tasks/puppet-lint' PuppetLint.configuration.ignore_paths = ['vendor/**/*.pp'] task :default do sh %{rake -T} end desc 'Run reasonably quick tests for CI' task :ci => [ :lint, :spec, ] puppetlabs-firewall-0.4.2/README.markdown000644 000765 000024 00000033745 12213700171 021010 0ustar00apenneystaff000000 000000 #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) * [Other Rules](#other-rules) 5. [Reference - An under-the-hood peek at what the module is doing](#reference) 6. [Limitations - OS compatibility, etc.](#limitations) 7. [Development - Guide for contributing to the module](#development) * [Tests - Testing your configuration](#tests) ##Overview The Firewall module lets you manage firewall rules with Puppet. ##Module Description PuppetLabs' Firewall introduces the resource `firewall`, which is used to manage and configure firewall rules from within the Puppet DSL. This module offers support for iptables, ip6tables, and ebtables. The module also introduces the resource `firewallchain`, which allows you to manage chains or firewall lists. At the moment, only iptables and ip6tables chains are supported. ##Setup ###What Firewall affects: * every node running a firewall * system's firewall settings * connection settings for managed nodes * unmanaged resources (get purged) * site.pp ###Setup Requirements Firewall uses Ruby-based providers, so you must have [pluginsync enabled](http://docs.puppetlabs.com/guides/plugins_in_modules.html#enabling-pluginsync). ###Beginning with Firewall To begin, you need to provide some initial top-scope configuration to ensure your firewall configurations are ordered properly and you do not lock yourself out of your box or lose any configuration. Persistence of rules between reboots is handled automatically, although there are known issues with ip6tables on older Debian/Ubuntu, as well as known issues with ebtables. In your `site.pp` (or some similarly top-scope file), set up a metatype to purge unmanaged firewall resources. This will clear any existing rules and make sure that only rules defined in Puppet exist on the machine. resources { "firewall": purge => true } Next, set up the default parameters for all of the firewall rules you will be establishing later. These defaults will ensure that the pre and post classes (you will be setting up in just a moment) are run in the correct order to avoid locking you out of your box during the first puppet run. Firewall { before => Class['my_fw::post'], require => Class['my_fw::pre'], } You also need to declare the `my_fw::pre` & `my_fw::post` classes so that dependencies are satisfied. This can be achieved using an External Node Classifier or the following class { ['my_fw::pre', 'my_fw::post']: } Finally, you should include the `firewall` class to ensure the correct packages are installed. class { 'firewall': } Now to create the `my_fw::pre` and `my_fw::post` classes. Firewall acts on your running firewall, making immediate changes as the catalog executes. Defining default pre and post rules allows you provide global defaults for your hosts before and after any custom rules; it is also required to avoid locking yourself out of your own boxes when Puppet runs. This approach employs a whitelist setup, so you can define what rules you want and everything else is ignored rather than removed. The `pre` class should be located in `my_fw/manifests/pre.pp` and should contain any default rules to be applied first. class my_fw::pre { Firewall { require => undef, } # Default firewall rules firewall { '000 accept all icmp': proto => 'icmp', action => 'accept', }-> firewall { '001 accept all to lo interface': proto => 'all', iniface => 'lo', action => 'accept', }-> firewall { '002 accept related established rules': proto => 'all', state => ['RELATED', 'ESTABLISHED'], action => 'accept', } } The rules in `pre` should allow basic networking (such as ICMP and TCP), as well as ensure that existing connections are not closed. The `post` class should be located in `my_fw/manifests/post.pp` and include any default rules to be applied last. class my_fw::post { firewall { '999 drop all': proto => 'all', action => 'drop', before => undef, } } To put it all together: the `before` parameter in `Firewall {}` ensures `my_fw::post` is run before any other rules and the the `require` parameter ensures `my_fw::pre` is run after any other rules. So the run order is: * run the rules in `my_fw::pre` * run your rules (defined in code) * run the rules in `my_fw::post` ###Upgrading ####Upgrading from version 0.2.0 and newer Upgrade the module with the puppet module tool as normal: puppet module upgrade puppetlabs/firewall ####Upgrading from version 0.1.1 and older Start by upgrading the module using the puppet module tool: puppet module upgrade puppetlabs/firewall Previously, you would have required the following in your `site.pp` (or some other global location): # Always persist firewall rules exec { 'persist-firewall': command => $operatingsystem ? { 'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4', /(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables', }, refreshonly => true, } Firewall { notify => Exec['persist-firewall'], before => Class['my_fw::post'], require => Class['my_fw::pre'], } Firewallchain { notify => Exec['persist-firewall'], } resources { "firewall": purge => true } With the latest version, we now have in-built persistence, so this is no longer needed. However, you will still need some basic setup to define pre & post rules. resources { "firewall": purge => true } Firewall { before => Class['my_fw::post'], require => Class['my_fw::pre'], } class { ['my_fw::pre', 'my_fw::post']: } class { 'firewall': } Consult the the documentation below for more details around the classes `my_fw::pre` and `my_fw::post`. ##Usage There are two kinds of firewall rules you can use with Firewall: default rules and application-specific rules. Default rules apply to general firewall settings, whereas application-specific rules manage firewall settings of a specific application, node, etc. All rules employ a numbering system in the resource's title that is used for ordering. When titling your rules, make sure you prefix the rule with a number. 000 this runs first 999 this runs last ###Default rules You can place default rules in either `my_fw::pre` or `my_fw::post`, depending on when you would like them to run. Rules placed in the `pre` class will run first, rules in the `post` class, last. Depending on the provider, the title of the rule can be stored using the comment feature of the underlying firewall subsystem. Values can match `/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/`. ####Examples of default rules Basic accept ICMP request example: firewall { "000 accept all icmp requests": proto => "icmp", action => "accept", } Drop all: firewall { "999 drop all other requests": action => "drop", } ###Application-specific rules Application-specific rules can live anywhere you declare the firewall resource. It is best to put your firewall rules close to the service that needs it, such as in the module that configures it. You should be able to add firewall rules to your application-specific classes so firewalling is performed at the same time when the class is invoked. For example, if you have an Apache module, you could declare the class as below class apache { firewall { '100 allow http and https access': port => [80, 443], proto => tcp, action => accept, } # ... the rest of your code ... } When someone uses the class, firewalling is provided automatically. class { 'apache': } ###Other rules You can also apply firewall rules to specific nodes. Usually, you will want to put the firewall rule in another class and apply that class to a node. But you can apply a rule to a node. node 'foo.bar.com' { firewall { '111 open port 111': dport => 111 } } You can also do more complex things with the `firewall` resource. Here we are doing some NAT configuration. firewall { '100 snat for network foo2': chain => 'POSTROUTING', jump => 'MASQUERADE', proto => 'all', outiface => "eth0", source => '10.1.2.0/24', table => 'nat', } In the below example, we are creating a new chain and forwarding any port 5000 access to it. firewall { '100 forward to MY_CHAIN': chain => 'INPUT', jump => 'MY_CHAIN', } # The namevar here is in the format chain_name:table:protocol firewallchain { 'MY_CHAIN:filter:IPv4': ensure => present, } firewall { '100 my rule': chain => 'MY_CHAIN', action => 'accept', proto => 'tcp', dport => 5000, } ###Additional Information You can access the inline documentation: puppet describe firewall Or puppet doc -r type (and search for firewall) ##Reference Classes: * [firewall](#class-firewall) Types: * [firewall](#type-firewall) * [firewallchain](#type-firewallchain) Facts: * [ip6tables_version](#fact-ip6tablesversion) * [iptables_version](#fact-iptablesversion) * [iptables_persistent_version](#fact-iptablespersistentversion) ###Class: firewall This class is provided to do the basic setup tasks required for using the firewall resources. At the moment this takes care of: * iptables-persistent package installation You should include the class for nodes that need to use the resources in this module. For example class { 'firewall': } ####`ensure` Indicates the state of `iptables` on your system, allowing you to disable `iptables` if desired. Can either be `running` or `stopped`. Default to `running`. ###Type: firewall This type provides the capability to manage firewall rules within puppet. For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge: ###Type:: firewallchain This type provides the capability to manage rule chains for firewalls. For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge: ###Fact: ip6tables_version The module provides a Facter fact that can be used to determine what the default version of ip6tables is for your operating system/distribution. ###Fact: iptables_version The module provides a Facter fact that can be used to determine what the default version of iptables is for your operating system/distribution. ###Fact: iptables_persistent_version Retrieves the version of iptables-persistent from your OS. This is a Debian/Ubuntu specific fact. ##Limitations While we aim to support as low as Puppet 2.6.x (for now), we recommend installing the latest Puppet version from the Puppetlabs official repos. Please note, we only aim support for the following distributions and versions - that is, we actually do ongoing system tests on these platforms: * Redhat 5.9 and 6.4 * Debian 6.0 and 7.0 * Ubuntu 10.04 and 12.04 If you want a new distribution supported feel free to raise a ticket and we'll consider it. If you want an older revision supported we'll also consider it, but don't get insulted if we reject it. Specifically, we will not consider Redhat 4.x support - its just too old. If you really want to get support for your OS we suggest writing any patch fix yourself, and for continual system testing if you can provide a sufficient trusted Veewee template we could consider adding such an OS to our ongoing continuous integration tests. Also, as this is a 0.x release the API is still in flux and may change. Make sure you read the release notes before upgrading. Bugs can be reported using Github Issues: ##Development Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) For this particular module, please also read CONTRIBUTING.md before contributing. Currently we support: * iptables * ip6tables * ebtables (chains only) But plans are to support lots of other firewall implementations: * FreeBSD (ipf) * Mac OS X (ipfw) * OpenBSD (pf) * Cisco (ASA and basic access lists) If you have knowledge in these technologies, know how to code, and wish to contribute to this project, we would welcome the help. ###Testing Make sure you have: * rake * bundler Install the necessary gems: bundle install And run the tests from the root of the source code: rake test If you have a copy of Vagrant 1.1.0 you can also run the system tests: RSPEC_SET=debian-606-x64 rake spec:system RSPEC_SET=centos-58-x64 rake spec:system *Note:* system testing is fairly alpha at this point, your mileage may vary. puppetlabs-firewall-0.4.2/spec/000755 000765 000024 00000000000 12213700171 017225 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/fixtures/000755 000765 000024 00000000000 12213700171 021076 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/spec_helper.rb000644 000765 000024 00000001353 12213700171 022045 0ustar00apenneystaff000000 000000 dir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift File.join(dir, 'lib') # Don't want puppet getting the command line arguments for rake or autotest ARGV.clear require 'rubygems' require 'bundler/setup' require 'rspec-puppet' Bundler.require :default, :test require 'pathname' require 'tmpdir' Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| require behaviour.relative_path_from(Pathname.new(dir)) end fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures')) RSpec.configure do |config| config.tty = true config.mock_with :rspec do |c| c.syntax = :expect end config.module_path = File.join(fixture_path, 'modules') config.manifest_dir = File.join(fixture_path, 'manifests') end puppetlabs-firewall-0.4.2/spec/spec_helper_system.rb000644 000765 000024 00000002555 12213700171 023456 0ustar00apenneystaff000000 000000 # This helper file is specific to the system tests for puppetlabs-firewall # and should be included by all tests under spec/system require 'rspec-system/spec_helper' require 'rspec-system-puppet/helpers' # Just some helpers specific to this module module LocalHelpers # This helper flushes all tables on the default machine. # # It checks that the flush command returns with no errors. # # @return [void] # @todo Need to optionally do the newer tables # @example # it 'should flush tables' do # iptables_flush_all_tables # end def iptables_flush_all_tables ['filter', 'nat', 'mangle', 'raw'].each do |t| shell "/sbin/iptables -t #{t} -F" do |r| r.stderr.should be_empty r.exit_code.should be_zero end end end end include RSpecSystemPuppet::Helpers RSpec.configure do |c| # Project root for the firewall code proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) # Enable colour in Jenkins c.tty = true # Import in our local helpers c.include ::LocalHelpers c.include RSpecSystemPuppet::Helpers # This is where we 'setup' the nodes before running our tests c.before :suite do # Install puppet puppet_install # Copy this module into the module path of the test node puppet_module_install(:source => proj_root, :module_name => 'firewall') end end puppetlabs-firewall-0.4.2/spec/system/000755 000765 000024 00000000000 12213700171 020551 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/000755 000765 000024 00000000000 12213700171 020204 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/classes/000755 000765 000024 00000000000 12213700171 021641 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/facter/000755 000765 000024 00000000000 12213700171 021450 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/puppet/000755 000765 000024 00000000000 12213700171 021521 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/puppet/provider/000755 000765 000024 00000000000 12213700171 023353 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/puppet/type/000755 000765 000024 00000000000 12213700171 022502 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/puppet/util/000755 000765 000024 00000000000 12213700171 022476 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/unit/puppet/util/firewall_spec.rb000644 000765 000024 00000017017 12213700171 025650 0ustar00apenneystaff000000 000000 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 '#icmp_name_to_number' do describe 'proto unsupported' do subject { resource } %w{inet5 inet8 foo}.each do |proto| it "should reject invalid proto #{proto}" do expect { subject.icmp_name_to_number('echo-reply', proto) }. to raise_error(ArgumentError, "unsupported protocol family '#{proto}'") end end end describe 'proto IPv4' do proto = 'inet' subject { resource } specify { subject.icmp_name_to_number('echo-reply', proto).should == '0' } specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '3' } specify { subject.icmp_name_to_number('source-quench', proto).should == '4' } specify { subject.icmp_name_to_number('redirect', proto).should == '6' } specify { subject.icmp_name_to_number('echo-request', proto).should == '8' } specify { subject.icmp_name_to_number('router-advertisement', proto).should == '9' } specify { subject.icmp_name_to_number('router-solicitation', proto).should == '10' } specify { subject.icmp_name_to_number('time-exceeded', proto).should == '11' } specify { subject.icmp_name_to_number('parameter-problem', proto).should == '12' } specify { subject.icmp_name_to_number('timestamp-request', proto).should == '13' } specify { subject.icmp_name_to_number('timestamp-reply', proto).should == '14' } specify { subject.icmp_name_to_number('address-mask-request', proto).should == '17' } specify { subject.icmp_name_to_number('address-mask-reply', proto).should == '18' } end describe 'proto IPv6' do proto = 'inet6' subject { resource } specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '1' } specify { subject.icmp_name_to_number('time-exceeded', proto).should == '3' } specify { subject.icmp_name_to_number('parameter-problem', proto).should == '4' } specify { subject.icmp_name_to_number('echo-request', proto).should == '128' } specify { subject.icmp_name_to_number('echo-reply', proto).should == '129' } specify { subject.icmp_name_to_number('router-solicitation', proto).should == '133' } specify { subject.icmp_name_to_number('router-advertisement', proto).should == '134' } specify { subject.icmp_name_to_number('redirect', proto).should == '137' } end end describe '#string_to_port' do subject { resource } specify { subject.string_to_port('80','tcp').should == '80' } specify { subject.string_to_port(80,'tcp').should == '80' } specify { subject.string_to_port('http','tcp').should == '80' } specify { subject.string_to_port('domain','udp').should == '53' } end describe '#to_hex32' do subject { resource } specify { subject.to_hex32('0').should == '0x0' } specify { subject.to_hex32('0x32').should == '0x32' } specify { subject.to_hex32('42').should == '0x2a' } specify { subject.to_hex32('4294967295').should == '0xffffffff' } specify { subject.to_hex32('4294967296').should == nil } specify { subject.to_hex32('-1').should == nil } specify { subject.to_hex32('bananas').should == nil } end describe '#persist_iptables' do before { Facter.clear } subject { resource } describe 'when proto is IPv4' do let(:proto) { 'IPv4' } it 'should exec for RedHat identified from osfamily' do allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat') expect(subject).to receive(:execute).with(%w{/sbin/service iptables 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.init save}) subject.persist_iptables(proto) end it 'should exec for CentOS identified from operatingsystem' do allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS') expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}) subject.persist_iptables(proto) end it 'should exec for Archlinux identified from osfamily' do allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux') expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules']) subject.persist_iptables(proto) end it 'should raise a warning when exec fails' do allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat') 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 puppetlabs-firewall-0.4.2/spec/unit/puppet/util/ipcidr_spec.rb000644 000765 000024 00000005074 12213700171 025315 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'Puppet::Util::IPCidr' do describe 'ipv4 address' do before { @ipaddr = Puppet::Util::IPCidr.new('96.126.112.51') } subject { @ipaddr } specify { subject.cidr.should == '96.126.112.51/32' } specify { subject.prefixlen.should == 32 } specify { subject.netmask.should == '255.255.255.255' } end describe 'single ipv4 address with cidr' do before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.51/32') } subject { @ipcidr } specify { subject.cidr.should == '96.126.112.51/32' } specify { subject.prefixlen.should == 32 } specify { subject.netmask.should == '255.255.255.255' } end describe 'ipv4 address range with cidr' do before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.0/24') } subject { @ipcidr } specify { subject.cidr.should == '96.126.112.0/24' } specify { subject.prefixlen.should == 24 } specify { subject.netmask.should == '255.255.255.0' } end describe 'ipv4 open range with cidr' do before { @ipcidr = Puppet::Util::IPCidr.new('0.0.0.0/0') } subject { @ipcidr } specify { subject.cidr.should == '0.0.0.0/0' } specify { subject.prefixlen.should == 0 } specify { subject.netmask.should == '0.0.0.0' } end describe 'ipv6 address' do before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334') } subject { @ipaddr } specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' } specify { subject.prefixlen.should == 128 } specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' } end describe 'single ipv6 addr with cidr' do before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334/128') } subject { @ipaddr } specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' } specify { subject.prefixlen.should == 128 } specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' } end describe 'ipv6 addr range with cidr' do before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:1234::/48') } subject { @ipaddr } specify { subject.cidr.should == '2001:db8:1234::/48' } specify { subject.prefixlen.should == 48 } specify { subject.netmask.should == 'ffff:ffff:ffff:0000:0000:0000:0000:0000' } end describe 'ipv6 open range with cidr' do before { @ipaddr = Puppet::Util::IPCidr.new('::/0') } subject { @ipaddr } specify { subject.cidr.should == '::/0' } specify { subject.prefixlen.should == 0 } specify { subject.netmask.should == '0000:0000:0000:0000:0000:0000:0000:0000' } end end puppetlabs-firewall-0.4.2/spec/unit/puppet/type/firewall_spec.rb000755 000765 000024 00000043352 12213700171 025660 0ustar00apenneystaff000000 000000 #!/usr/bin/env rspec require 'spec_helper' firewall = Puppet::Type.type(:firewall) describe firewall do before :each do @class = firewall @provider = double 'provider' @provider.stubs(:name).returns(:iptables) Puppet::Type::Firewall.stubs(:defaultprovider).returns @provider @resource = @class.new({:name => '000 test foo'}) # Stub iptables version Facter.fact(:iptables_version).stubs(:value).returns("1.4.2") Facter.fact(:ip6tables_version).stubs(:value).returns("1.4.2") # Stub confine facts Facter.fact(:kernel).stubs(:value).returns("Linux") Facter.fact(:operatingsystem).stubs(:value).returns("Debian") end it 'should have :name be its namevar' do @class.key_attributes.should == [:name] end describe ':name' do it 'should accept a name' do @resource[:name] = '000-test-foo' @resource[:name].should == '000-test-foo' end it 'should not accept a name with non-ASCII chars' do lambda { @resource[:name] = '%*#^(#$' }.should raise_error(Puppet::Error) end end describe ':action' do it "should have no default" do res = @class.new(:name => "000 test") res.parameters[:action].should == nil end [:accept, :drop, :reject].each do |action| it "should accept value #{action}" do @resource[:action] = action @resource[:action].should == action end end it 'should fail when value is not recognized' do lambda { @resource[:action] = 'not valid' }.should raise_error(Puppet::Error) end end describe ':chain' do [:INPUT, :FORWARD, :OUTPUT, :PREROUTING, :POSTROUTING].each do |chain| it "should accept chain value #{chain}" do @resource[:chain] = chain @resource[:chain].should == chain end end it 'should fail when the chain value is not recognized' do lambda { @resource[:chain] = 'not valid' }.should raise_error(Puppet::Error) end end describe ':table' do [:nat, :mangle, :filter, :raw].each do |table| it "should accept table value #{table}" do @resource[:table] = table @resource[:table].should == table end end it "should fail when table value is not recognized" do lambda { @resource[:table] = 'not valid' }.should raise_error(Puppet::Error) end end describe ':proto' do [:tcp, :udp, :icmp, :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all].each do |proto| it "should accept proto value #{proto}" do @resource[:proto] = proto @resource[:proto].should == proto end end it "should fail when proto value is not recognized" do lambda { @resource[:proto] = 'foo' }.should raise_error(Puppet::Error) end end describe ':jump' do it "should have no default" do res = @class.new(:name => "000 test") res.parameters[:jump].should == nil end ['QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK'].each do |jump| it "should accept jump value #{jump}" do @resource[:jump] = jump @resource[:jump].should == jump end end ['ACCEPT', 'DROP', 'REJECT'].each do |jump| it "should now fail when value #{jump}" do lambda { @resource[:jump] = jump }.should raise_error(Puppet::Error) end end it "should fail when jump value is not recognized" do lambda { @resource[:jump] = '%^&*' }.should raise_error(Puppet::Error) end end [:source, :destination].each do |addr| describe addr do it "should accept a #{addr} as a string" do @resource[addr] = '127.0.0.1' @resource[addr].should == '127.0.0.1/32' end ['0.0.0.0/0', '::/0'].each do |prefix| it "should be nil for zero prefix length address #{prefix}" do @resource[addr] = prefix @resource[addr].should == nil end end end end [:dport, :sport].each do |port| describe port do it "should accept a #{port} as string" do @resource[port] = '22' @resource[port].should == ['22'] end it "should accept a #{port} as an array" do @resource[port] = ['22','23'] @resource[port].should == ['22','23'] end it "should accept a #{port} as a number" do @resource[port] = 22 @resource[port].should == ['22'] end it "should accept a #{port} as a hyphen separated range" do @resource[port] = ['22-1000'] @resource[port].should == ['22-1000'] end it "should accept a #{port} as a combination of arrays of single and " \ "hyphen separated ranges" do @resource[port] = ['22-1000','33','3000-4000'] @resource[port].should == ['22-1000','33','3000-4000'] end it "should convert a port name for #{port} to its number" do @resource[port] = 'ssh' @resource[port].should == ['22'] end it "should not accept something invalid for #{port}" do expect { @resource[port] = 'something odd' }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/) end it "should not accept something invalid in an array for #{port}" do expect { @resource[port] = ['something odd','something even odder'] }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/) end end end [:dst_type, :src_type].each do |addrtype| describe addrtype do it "should have no default" do res = @class.new(:name => "000 test") res.parameters[addrtype].should == nil end end [:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].each do |type| it "should accept #{addrtype} value #{type}" do @resource[addrtype] = type @resource[addrtype].should == type end end it "should fail when #{addrtype} value is not recognized" do lambda { @resource[addrtype] = 'foo' }.should raise_error(Puppet::Error) end end [:iniface, :outiface].each do |iface| describe iface do it "should accept #{iface} value as a string" do @resource[iface] = 'eth1' @resource[iface].should == 'eth1' end end end [:tosource, :todest].each do |addr| describe addr do it "should accept #{addr} value as a string" do @resource[addr] = '127.0.0.1' end end end describe ':log_level' do values = { 'panic' => '0', 'alert' => '1', 'crit' => '2', 'err' => '3', 'warn' => '4', 'warning' => '4', 'not' => '5', 'notice' => '5', 'info' => '6', 'debug' => '7' } values.each do |k,v| it { @resource[:log_level] = k @resource[:log_level].should == v } it { @resource[:log_level] = 3 @resource[:log_level].should == 3 } it { lambda { @resource[:log_level] = 'foo' }.should raise_error(Puppet::Error) } end end describe ':icmp' do icmp_codes = { :iptables => { '0' => 'echo-reply', '3' => 'destination-unreachable', '4' => 'source-quench', '6' => 'redirect', '8' => 'echo-request', '9' => 'router-advertisement', '10' => 'router-solicitation', '11' => 'time-exceeded', '12' => 'parameter-problem', '13' => 'timestamp-request', '14' => 'timestamp-reply', '17' => 'address-mask-request', '18' => 'address-mask-reply' }, :ip6tables => { '1' => 'destination-unreachable', '3' => 'time-exceeded', '4' => 'parameter-problem', '128' => 'echo-request', '129' => 'echo-reply', '133' => 'router-solicitation', '134' => 'router-advertisement', '137' => 'redirect' } } icmp_codes.each do |provider, values| describe provider do values.each do |k,v| it 'should convert icmp string to number' do @resource[:provider] = provider @resource[:provider].should == provider @resource[:icmp] = v @resource[:icmp].should == k end end end end it 'should accept values as integers' do @resource[:icmp] = 9 @resource[:icmp].should == 9 end it 'should fail if icmp type is "any"' do lambda { @resource[:icmp] = 'any' }.should raise_error(Puppet::Error) end it 'should fail if icmp type cannot be mapped to a numeric' do lambda { @resource[:icmp] = 'foo' }.should raise_error(Puppet::Error) end end describe ':state' do it 'should accept value as a string' do @resource[:state] = :INVALID @resource[:state].should == [:INVALID] end it 'should accept value as an array' do @resource[:state] = [:INVALID, :NEW] @resource[:state].should == [:INVALID, :NEW] end it 'should sort values alphabetically' do @resource[:state] = [:NEW, :ESTABLISHED] @resource[:state].should == [:ESTABLISHED, :NEW] end end describe ':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 ':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 Facter.fact(:iptables_version).stubs(:value).returns(iptables_version) Facter.fact(:ip6tables_version).stubs(:value).returns(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 end end describe ":chain and :jump" do it 'should autorequire independent fwchains' do @resource[:chain] = 'FOO' @resource[:jump] = 'BAR' @resource[:table].should == :filter @resource[:provider].should == :iptables chain_foo = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4') chain_bar = Puppet::Type.type(:firewallchain).new(:name => 'BAR:filter:IPv4') catalog = Puppet::Resource::Catalog.new catalog.add_resource @resource catalog.add_resource chain_foo catalog.add_resource chain_bar rel = @resource.autorequire rel[0].source.ref.should == chain_foo.ref rel[0].target.ref.should == @resource.ref rel[1].source.ref.should == chain_bar.ref rel[1].target.ref.should == @resource.ref end end describe ':pkttype' do [:multicast, :broadcast, :unicast].each do |pkttype| it "should accept pkttype value #{pkttype}" do @resource[:pkttype] = pkttype @resource[:pkttype].should == pkttype end end it 'should fail when the pkttype value is not recognized' do lambda { @resource[:pkttype] = 'not valid' }.should raise_error(Puppet::Error) end end describe 'autorequire packages' do [:iptables, :ip6tables].each do |provider| it "provider #{provider} should autorequire package iptables" do @resource[:provider] = provider @resource[:provider].should == provider package = Puppet::Type.type(:package).new(:name => 'iptables') catalog = Puppet::Resource::Catalog.new catalog.add_resource @resource catalog.add_resource package rel = @resource.autorequire[0] rel.source.ref.should == package.ref rel.target.ref.should == @resource.ref end it "provider #{provider} should autorequire packages iptables and iptables-persistent" do @resource[:provider] = provider @resource[:provider].should == provider packages = [ Puppet::Type.type(:package).new(:name => 'iptables'), Puppet::Type.type(:package).new(:name => 'iptables-persistent') ] catalog = Puppet::Resource::Catalog.new catalog.add_resource @resource packages.each do |package| catalog.add_resource package end packages.zip(@resource.autorequire) do |package, rel| rel.source.ref.should == package.ref rel.target.ref.should == @resource.ref end end end end end puppetlabs-firewall-0.4.2/spec/unit/puppet/type/firewallchain_spec.rb000755 000765 000024 00000011217 12213700171 026656 0ustar00apenneystaff000000 000000 #!/usr/bin/env rspec require 'spec_helper' firewallchain = Puppet::Type.type(:firewallchain) describe firewallchain do before do # Stub confine facts Facter.fact(:kernel).stubs(:value).returns("Linux") Facter.fact(:operatingsystem).stubs(:value).returns("Debian") end let(:klass) { firewallchain } let(:provider) { prov = double 'provider' prov.stubs(:name).returns(:iptables_chain) prov } let(:resource) { Puppet::Type::Firewallchain.stubs(:defaultprovider).returns 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', 'OUTPUT'], 'mangle' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ], 'filter' => ['INPUT','OUTPUT','FORWARD'], 'raw' => [ 'PREROUTING', 'OUTPUT'], 'broute' => ['BROUTING'] }.each_pair do |table, allowedinternalchains| ['IPv4', 'IPv6', 'ethernet'].each do |protocol| [ 'test', '$5()*&%\'"^$09):' ].each do |chainname| name = "#{chainname}:#{table}:#{protocol}" if table == 'nat' && protocol == 'IPv6' it "should fail #{name}" do expect { resource[:name] = name }.to raise_error(Puppet::Error) end elsif protocol != 'ethernet' && table == 'broute' it "should fail #{name}" do expect { resource[:name] = name }.to raise_error(Puppet::Error) end else it "should accept name #{name}" do resource[:name] = name resource[:name].should == name end end end # chainname end # protocol [ 'PREROUTING', 'POSTROUTING', 'BROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ].each do |internalchain| name = internalchain + ':' + table + ':' if internalchain == 'BROUTING' name += 'ethernet' elsif table == 'nat' name += 'IPv4' else name += 'IPv4' end if allowedinternalchains.include? internalchain it "should allow #{name}" do resource[:name] = name resource[:name].should == name end else it "should fail #{name}" do expect { resource[:name] = name }.to raise_error(Puppet::Error) end end end # internalchain end # table, allowedinternalchainnames it 'should fail with invalid table names' do expect { resource[:name] = 'wrongtablename:test:IPv4' }.to raise_error(Puppet::Error) end it 'should fail with invalid protocols names' do expect { resource[:name] = 'test:filter:IPv5' }.to raise_error(Puppet::Error) end end describe ':policy' do [:accept, :drop, :queue, :return].each do |policy| it "should accept policy #{policy}" do resource[:policy] = policy resource[:policy].should == policy end end it 'should fail when value is not recognized' do expect { resource[:policy] = 'not valid' }.to raise_error(Puppet::Error) end [:accept, :drop, :queue, :return].each do |policy| it "non-inbuilt chains should not accept policy #{policy}" do expect { klass.new({:name => 'testchain:filter:IPv4', :policy => policy }) }.to raise_error(Puppet::Error) end it "non-inbuilt chains can accept policies on protocol = ethernet (policy #{policy})" do klass.new({:name => 'testchain:filter:ethernet', :policy => policy }) end end end describe 'autorequire packages' do it "provider iptables_chain should autorequire package iptables" do resource[:provider].should == :iptables_chain package = Puppet::Type.type(:package).new(:name => 'iptables') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource package rel = resource.autorequire[0] rel.source.ref.should == package.ref rel.target.ref.should == resource.ref end it "provider iptables_chain should autorequire packages iptables and iptables-persistent" do resource[:provider].should == :iptables_chain packages = [ Puppet::Type.type(:package).new(:name => 'iptables'), Puppet::Type.type(:package).new(:name => 'iptables-persistent') ] catalog = Puppet::Resource::Catalog.new catalog.add_resource resource packages.each do |package| catalog.add_resource package end packages.zip(resource.autorequire) do |package, rel| rel.source.ref.should == package.ref rel.target.ref.should == resource.ref end end end end puppetlabs-firewall-0.4.2/spec/unit/puppet/provider/iptables_chain_spec.rb000755 000765 000024 00000014477 12213700171 027677 0ustar00apenneystaff000000 000000 #!/usr/bin/env rspec require 'spec_helper' require 'puppet' describe 'iptables chain provider detection' do let(:exists) { Puppet::Provider::Confine::Exists } before :each do # Reset the default provider Puppet::Type.type(:firewallchain).defaultprovider = nil end it "should default to iptables provider if /sbin/(eb|ip|ip6)tables[-save] exists" do # Stub lookup for /sbin/iptables & /sbin/iptables-save allow(exists).to receive(:which).with("ebtables"). and_return "/sbin/ebtables" allow(exists).to receive(:which).with("ebtables-save"). and_return "/sbin/ebtables-save" allow(exists).to receive(:which).with("iptables"). and_return "/sbin/iptables" allow(exists).to receive(:which).with("iptables-save"). and_return "/sbin/iptables-save" allow(exists).to receive(:which).with("ip6tables"). and_return "/sbin/ip6tables" allow(exists).to receive(:which).with("ip6tables-save"). and_return "/sbin/ip6tables-save" # Every other command should return false so we don't pick up any # other providers allow(exists).to receive(:which).with() { |value| value !~ /(eb|ip|ip6)tables(-save)?$/ }.and_return false # Create a resource instance and make sure the provider is iptables resource = Puppet::Type.type(:firewallchain).new({ :name => 'test:filter:IPv4', }) expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewallchain::ProviderIptables_chain") end end describe 'iptables chain provider' do let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) } let(:resource) { Puppet::Type.type(:firewallchain).new({ :name => ':test:', }) } before :each do allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider allow(provider).to receive(:command).with(:ebtables_save).and_return "/sbin/ebtables-save" allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save" allow(provider).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save" end it 'should be able to get a list of existing rules' do # Pretend to return nil from iptables allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return("") allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return("") allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return("") provider.instances.each do |chain| expect(chain).to be_instance_of(provider) expect(chain.properties[:provider].to_s).to eq(provider.name.to_s) end end end describe 'iptables chain resource parsing' do let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) } before :each do ebtables = ['BROUTE:BROUTING:ethernet', 'BROUTE:broute:ethernet', ':INPUT:ethernet', ':FORWARD:ethernet', ':OUTPUT:ethernet', ':filter:ethernet', ':filterdrop:ethernet', ':filterreturn:ethernet', 'NAT:PREROUTING:ethernet', 'NAT:OUTPUT:ethernet', 'NAT:POSTROUTING:ethernet', ] allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return(' *broute :BROUTING ACCEPT :broute ACCEPT *filter :INPUT ACCEPT :FORWARD ACCEPT :OUTPUT ACCEPT :filter ACCEPT :filterdrop DROP :filterreturn RETURN *nat :PREROUTING ACCEPT :OUTPUT ACCEPT :POSTROUTING ACCEPT ') iptables = [ 'raw:PREROUTING:IPv4', 'raw:OUTPUT:IPv4', 'raw:raw:IPv4', 'mangle:PREROUTING:IPv4', 'mangle:INPUT:IPv4', 'mangle:FORWARD:IPv4', 'mangle:OUTPUT:IPv4', 'mangle:POSTROUTING:IPv4', 'mangle:mangle:IPv4', 'NAT:PREROUTING:IPv4', 'NAT:OUTPUT:IPv4', 'NAT:POSTROUTING:IPv4', 'NAT:mangle:IPv4', 'NAT:mangle:IPv4', 'NAT:mangle:IPv4', ':$5()*&%\'"^$): :IPv4', ] allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return(' # Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 *raw :PREROUTING ACCEPT [12:1780] :OUTPUT ACCEPT [19:1159] :raw - [0:0] COMMIT # Completed on Mon Jan 2 01:20:06 2012 # Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 *mangle :PREROUTING ACCEPT [12:1780] :INPUT ACCEPT [12:1780] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [19:1159] :POSTROUTING ACCEPT [19:1159] :mangle - [0:0] COMMIT # Completed on Mon Jan 2 01:20:06 2012 # Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 *nat :PREROUTING ACCEPT [2242:639750] :OUTPUT ACCEPT [5176:326206] :POSTROUTING ACCEPT [5162:325382] COMMIT # Completed on Mon Jan 2 01:20:06 2012 # Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 *filter :INPUT ACCEPT [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [5673:420879] :$5()*&%\'"^$): - [0:0] COMMIT # Completed on Mon Jan 2 01:20:06 2012 ') ip6tables = [ 'raw:PREROUTING:IPv6', 'raw:OUTPUT:IPv6', 'raw:ff:IPv6', 'mangle:PREROUTING:IPv6', 'mangle:INPUT:IPv6', 'mangle:FORWARD:IPv6', 'mangle:OUTPUT:IPv6', 'mangle:POSTROUTING:IPv6', 'mangle:ff:IPv6', ':INPUT:IPv6', ':FORWARD:IPv6', ':OUTPUT:IPv6', ':test:IPv6', ] allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return(' # Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 *raw :PREROUTING ACCEPT [2173:489241] :OUTPUT ACCEPT [0:0] :ff - [0:0] COMMIT # Completed on Mon Jan 2 01:31:39 2012 # Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 *mangle :PREROUTING ACCEPT [2301:518373] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] :ff - [0:0] COMMIT # Completed on Mon Jan 2 01:31:39 2012 # Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 *filter :INPUT ACCEPT [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [20:1292] :test - [0:0] COMMIT # Completed on Mon Jan 2 01:31:39 2012 ') @all = ebtables + iptables + ip6tables # IPv4 and IPv6 names also exist as resources {table}:{chain}:IP and {table}:{chain}: iptables.each { |name| @all += [ name[0..-3], name[0..-5] ] } ip6tables.each { |name| @all += [ name[0..-3], name[0..-5] ] } end it 'should have all in parsed resources' do provider.instances.each do |resource| @all.include?(resource.name) end end end puppetlabs-firewall-0.4.2/spec/unit/puppet/provider/iptables_spec.rb000644 000765 000024 00000012674 12213700171 026527 0ustar00apenneystaff000000 000000 #!/usr/bin/env rspec require 'spec_helper' require 'puppet/provider/confine/exists' describe 'iptables provider detection' do let(:exists) { Puppet::Provider::Confine::Exists } before :each do # Reset the default provider Puppet::Type.type(:firewall).defaultprovider = nil end it "should default to iptables provider if /sbin/iptables[-save] exists" do # Stub lookup for /sbin/iptables & /sbin/iptables-save allow(exists).to receive(:which).with("iptables"). and_return "/sbin/iptables" allow(exists).to receive(:which).with("iptables-save"). and_return "/sbin/iptables-save" # Every other command should return false so we don't pick up any # other providers allow(exists).to receive(:which).with() { |value| ! ["iptables","iptables-save"].include?(value) }.and_return false # Create a resource instance and make sure the provider is iptables resource = Puppet::Type.type(:firewall).new({ :name => '000 test foo', }) expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewall::ProviderIptables") end end describe 'iptables provider' do let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) } let(:resource) { Puppet::Type.type(:firewall).new({ :name => '000 test foo', :action => 'accept', }) } before :each do Puppet::Type::Firewall.stubs(:defaultprovider).returns 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 # Load in ruby hash for test fixtures. load 'spec/fixtures/iptables/conversion_hash.rb' describe 'when converting rules to resources' do ARGS_TO_HASH.each do |test_name,data| describe "for test data '#{test_name}'" do let(:resource) { provider.rule_to_hash(data[:line], data[:table], 0) } # If this option is enabled, make sure the parameters exactly match if data[:compare_all] then it "the parameter hash keys should be the same as returned by rules_to_hash" do expect(resource.keys).to match_array(data[:params].keys) end end # Iterate across each parameter, creating an example for comparison data[:params].each do |param_name, param_value| it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do # booleans get cludged to string "true" if param_value == true then expect(resource[param_name]).to be_true else expect(resource[param_name]).to eq(data[:params][param_name]) end end end end end end describe 'when working out general_args' do HASH_TO_ARGS.each do |test_name,data| describe "for test data '#{test_name}'" do let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) } let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) } let(:instance) { provider.new(resource) } it 'general_args should be valid' do expect(instance.general_args.flatten).to eq(data[:args]) end end end end describe 'when converting rules without comments to resources' do let(:sample_rule) { '-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT' } let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) } let(:instance) { provider.new(resource) } it 'rule name contains a MD5 sum of the line' do expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}") end 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 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 puppetlabs-firewall-0.4.2/spec/unit/facter/iptables_persistent_version_spec.rb000644 000765 000024 00000002042 12213700171 030635 0ustar00apenneystaff000000 000000 require 'spec_helper' describe "Facter::Util::Fact iptables_persistent_version" do before { Facter.clear } let(:dpkg_cmd) { "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" } { "Debian" => "0.0.20090701", "Ubuntu" => "0.5.3ubuntu2", }.each do |os, ver| describe "#{os} package installed" do before { Facter.fact(:operatingsystem).stubs(:value).returns(os) Facter::Util::Resolution.stubs(:exec).with(dpkg_cmd).returns(ver) } it { Facter.fact(:iptables_persistent_version).value.should == ver } end end describe 'Ubuntu package not installed' do before { Facter.fact(:operatingsystem).stubs(:value).returns("Ubuntu") Facter::Util::Resolution.stubs(:exec).with(dpkg_cmd).returns(nil) } it { Facter.fact(:iptables_persistent_version).value.should be_nil } end describe 'CentOS not supported' do before { Facter.fact(:operatingsystem).stubs(:value).returns("CentOS") } it { Facter.fact(:iptables_persistent_version).value.should be_nil } end end puppetlabs-firewall-0.4.2/spec/unit/facter/iptables_spec.rb000644 000765 000024 00000001200 12213700171 024603 0ustar00apenneystaff000000 000000 require 'spec_helper' describe "Facter::Util::Fact" do before { Facter.clear Facter.fact(:kernel).stubs(:value).returns("Linux") Facter.fact(:kernelrelease).stubs(:value).returns("2.6") } describe 'iptables_version' do it { Facter::Util::Resolution.stubs(:exec).with('iptables --version').returns('iptables v1.4.7') Facter.fact(:iptables_version).value.should == '1.4.7' } end describe 'ip6tables_version' do before { Facter::Util::Resolution.stubs(:exec).with('ip6tables --version').returns('ip6tables v1.4.7') } it { Facter.fact(:ip6tables_version).value.should == '1.4.7' } end end puppetlabs-firewall-0.4.2/spec/unit/classes/firewall_linux_archlinux_spec.rb000644 000765 000024 00000001442 12213700171 030302 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'firewall::linux::archlinux', :type => :class do it { should contain_service('iptables').with( :ensure => 'running', :enable => 'true' )} it { should contain_service('ip6tables').with( :ensure => 'running', :enable => 'true' )} context 'ensure => stopped' do let(:params) {{ :ensure => 'stopped' }} it { should contain_service('iptables').with( :ensure => 'stopped' )} it { should contain_service('ip6tables').with( :ensure => 'stopped' )} end context 'enable => false' do let(:params) {{ :enable => 'false' }} it { should contain_service('iptables').with( :enable => 'false' )} it { should contain_service('ip6tables').with( :enable => 'false' )} end end puppetlabs-firewall-0.4.2/spec/unit/classes/firewall_linux_debian_spec.rb000644 000765 000024 00000000771 12213700171 027533 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'firewall::linux::debian', :type => :class do it { should contain_package('iptables-persistent').with( :ensure => 'present' )} it { should contain_service('iptables-persistent').with( :ensure => nil, :enable => 'true', :require => 'Package[iptables-persistent]' )} context 'enable => false' do let(:params) {{ :enable => 'false' }} it { should contain_service('iptables-persistent').with( :enable => 'false' )} end end puppetlabs-firewall-0.4.2/spec/unit/classes/firewall_linux_redhat_spec.rb000644 000765 000024 00000001001 12213700171 027543 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'firewall::linux::redhat', :type => :class do it { should contain_service('iptables').with( :ensure => 'running', :enable => 'true' )} context 'ensure => stopped' do let(:params) {{ :ensure => 'stopped' }} it { should contain_service('iptables').with( :ensure => 'stopped' )} end context 'enable => false' do let(:params) {{ :enable => 'false' }} it { should contain_service('iptables').with( :enable => 'false' )} end end puppetlabs-firewall-0.4.2/spec/unit/classes/firewall_linux_spec.rb000644 000765 000024 00000001433 12213700171 026225 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'firewall::linux', :type => :class do let(:facts_default) {{ :kernel => 'Linux' }} it { should contain_package('iptables').with_ensure('present') } context 'RedHat like' do %w{RedHat CentOS Fedora}.each do |os| context "operatingsystem => #{os}" do let(:facts) { facts_default.merge({ :operatingsystem => os }) } it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') } end end end context 'Debian like' do %w{Debian Ubuntu}.each do |os| context "operatingsystem => #{os}" do let(:facts) { facts_default.merge({ :operatingsystem => os }) } it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') } end end end end puppetlabs-firewall-0.4.2/spec/unit/classes/firewall_spec.rb000644 000765 000024 00000001444 12213700171 025010 0ustar00apenneystaff000000 000000 require 'spec_helper' describe 'firewall', :type => :class do context 'kernel => Linux' do let(:facts) {{ :kernel => 'Linux' }} it { should contain_class('firewall::linux').with_ensure('running') } end context 'kernel => Windows' do let(:facts) {{ :kernel => 'Windows' }} it { expect { should include_class('firewall::linux') }.to raise_error(Puppet::Error) } end context 'ensure => stopped' do let(:facts) {{ :kernel => 'Linux' }} let(:params) {{ :ensure => 'stopped' }} it { should contain_class('firewall::linux').with_ensure('stopped') } end context 'ensure => test' do let(:facts) {{ :kernel => 'Linux' }} let(:params) {{ :ensure => 'test' }} it { expect { should include_class('firewall::linux') }.to raise_error(Puppet::Error) } end end puppetlabs-firewall-0.4.2/spec/system/basic_spec.rb000644 000765 000024 00000000707 12213700171 023175 0ustar00apenneystaff000000 000000 require 'spec_helper_system' # Here we put the more basic fundamental tests, ultra obvious stuff. describe "basic tests:" do context 'make sure we have copied the module across' do # No point diagnosing any more if the module wasn't copied properly context shell 'ls /etc/puppet/modules/firewall' do its(:stdout) { should =~ /Modulefile/ } its(:stderr) { should be_empty } its(:exit_code) { should be_zero } end end end puppetlabs-firewall-0.4.2/spec/system/class_spec.rb000644 000765 000024 00000002032 12213700171 023212 0ustar00apenneystaff000000 000000 require 'spec_helper_system' describe "firewall class:" do context 'should run successfully' do pp = "class { 'firewall': }" context puppet_apply(pp) do its(:stderr) { should be_empty } its(:exit_code) { should_not == 1 } its(:refresh) { should be_nil } its(:stderr) { should be_empty } its(:exit_code) { should be_zero } end end context 'ensure => stopped:' do pp = "class { 'firewall': ensure => stopped }" context puppet_apply(pp) do its(:stderr) { should be_empty } its(:exit_code) { should_not == 1 } its(:refresh) { should be_nil } its(:stderr) { should be_empty } its(:exit_code) { should be_zero } end end context 'ensure => running:' do pp = "class { 'firewall': ensure => running }" context puppet_apply(pp) do |r| its(:stderr) { should be_empty } its(:exit_code) { should_not == 1 } its(:refresh) { should be_nil } its(:stderr) { should be_empty } its(:exit_code) { should be_zero } end end end puppetlabs-firewall-0.4.2/spec/system/params_spec.rb000644 000765 000024 00000007000 12213700171 023370 0ustar00apenneystaff000000 000000 require 'spec_helper_system' describe "param based tests:" do # Takes a hash and converts it into a firewall resource def pp(params) name = params.delete('name') || '100 test' pm = <<-EOS firewall { '#{name}': EOS params.each do |k,v| pm += <<-EOS #{k} => #{v}, EOS end pm += <<-EOS } EOS pm end it 'test various params' do iptables_flush_all_tables facts = node.facts unless (facts['operatingsystem'] == 'CentOS') && \ facts['operatingsystemrelease'] =~ /^5\./ then ppm = pp({ 'table' => "'raw'", 'socket' => 'true', 'chain' => "'PREROUTING'", 'jump' => 'LOG', 'log_level' => 'debug', }) puppet_apply(ppm) do |r| r.exit_code.should == 2 r.stderr.should be_empty r.refresh r.stderr.should be_empty r.exit_code.should be_zero end end end it 'test log rule' do iptables_flush_all_tables ppm = pp({ 'name' => '998 log all', 'proto' => 'all', 'jump' => 'LOG', 'log_level' => 'debug', }) puppet_apply(ppm) do |r| r.exit_code.should == 2 r.stderr.should be_empty r.refresh r.stderr.should be_empty r.exit_code.should be_zero end end it 'test log rule - changing names' do iptables_flush_all_tables ppm1 = pp({ 'name' => '004 log all INVALID packets', 'chain' => 'INPUT', 'proto' => 'all', 'state' => 'INVALID', 'jump' => 'LOG', 'log_level' => '3', 'log_prefix' => '"IPTABLES dropped invalid: "', }) ppm2 = pp({ 'name' => '003 log all INVALID packets', 'chain' => 'INPUT', 'proto' => 'all', 'state' => 'INVALID', 'jump' => 'LOG', 'log_level' => '3', 'log_prefix' => '"IPTABLES dropped invalid: "', }) puppet_apply(ppm1) do |r| r.stderr.should be_empty r.exit_code.should == 2 end ppm = <<-EOS + "\n" + ppm2 resources { 'firewall': purge => true, } EOS puppet_apply(ppm) do |r| r.stderr.should be_empty r.exit_code.should == 2 end end it 'test log rule - idempotent' do iptables_flush_all_tables ppm1 = pp({ 'name' => '004 log all INVALID packets', 'chain' => 'INPUT', 'proto' => 'all', 'state' => 'INVALID', 'jump' => 'LOG', 'log_level' => '3', 'log_prefix' => '"IPTABLES dropped invalid: "', }) puppet_apply(ppm1) do |r| r.exit_code.should == 2 r.stderr.should be_empty r.refresh r.stderr.should be_empty r.exit_code.should be_zero end end it 'test src_range rule' do iptables_flush_all_tables ppm = pp({ 'name' => '997 block src ip range', 'chain' => 'INPUT', 'proto' => 'all', 'action' => 'drop', 'src_range' => '"10.0.0.1-10.0.0.10"', }) puppet_apply(ppm) do |r| r.exit_code.should == 2 r.stderr.should be_empty r.refresh r.stderr.should be_empty r.exit_code.should be_zero end end it 'test dst_range rule' do iptables_flush_all_tables ppm = pp({ 'name' => '998 block dst ip range', 'chain' => 'INPUT', 'proto' => 'all', 'action' => 'drop', 'dst_range' => '"10.0.0.2-10.0.0.20"', }) puppet_apply(ppm) do |r| r.exit_code.should == 2 r.stderr.should be_empty r.refresh r.stderr.should be_empty r.exit_code.should be_zero end end end puppetlabs-firewall-0.4.2/spec/system/purge_spec.rb000644 000765 000024 00000001176 12213700171 023237 0ustar00apenneystaff000000 000000 require 'spec_helper_system' describe "purge tests:" do context 'make sure duplicate existing rules get purged' do before :all do iptables_flush_all_tables shell('/sbin/iptables -A INPUT -s 1.2.1.2') shell('/sbin/iptables -A INPUT -s 1.2.1.2') end pp = <<-EOS class { 'firewall': } resources { 'firewall': purge => true, } EOS context puppet_apply(pp) do its(:stderr) { should be_empty } its(:exit_code) { should == 2 } end context shell('/sbin/iptables-save') do its(:stdout) { should_not =~ /1\.2\.1\.2/ } its(:stderr) { should be_empty } end end end puppetlabs-firewall-0.4.2/spec/system/resource_cmd_spec.rb000644 000765 000024 00000003276 12213700171 024572 0ustar00apenneystaff000000 000000 require 'spec_helper_system' # 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 context 'make sure it returns no errors when executed on a clean machine' do context puppet_resource('firewall') do its(:exit_code) { should be_zero } # don't check stdout, some boxes come with rules, that is normal its(:stderr) { should be_empty } 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. context puppet_resource('firewall') do its(:exit_code) { should be_zero } its(:stderr) { should be_empty } its(:stdout) { should == "\n" } end end context 'accepts rules without comments' do before :all do iptables_flush_all_tables shell('/sbin/iptables -A INPUT -j ACCEPT -p tcp --dport 80') end context puppet_resource('firewall') do |r| its(:exit_code) { should be_zero } # don't check stdout, testing preexisting rules, output is normal its(:stderr) { should be_empty } end end context 'accepts rules with invalid comments' do before :all do iptables_flush_all_tables shell('/sbin/iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http"') end context puppet_resource('firewall') do its(:exit_code) { should be_zero } # don't check stdout, testing preexisting rules, output is normal its(:stderr) { should be_empty } end end end puppetlabs-firewall-0.4.2/spec/system/standard_usage_spec.rb000644 000765 000024 00000002742 12213700171 025101 0ustar00apenneystaff000000 000000 require 'spec_helper_system' # Some tests for the standard recommended usage describe 'standard usage tests:' do context 'standard 1' do pp = <<-EOS class my_fw::pre { Firewall { require => undef, } # Default firewall rules firewall { '000 accept all icmp': proto => 'icmp', action => 'accept', }-> firewall { '001 accept all to lo interface': proto => 'all', iniface => 'lo', action => 'accept', }-> firewall { '002 accept related established rules': proto => 'all', state => ['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 context puppet_apply(pp) do its(:stderr) { should be_empty } its(:exit_code) { should_not == 1 } its(:refresh) { should be_nil } its(:stderr) { should be_empty } its(:exit_code) { should be_zero } end end end puppetlabs-firewall-0.4.2/spec/fixtures/iptables/000755 000765 000024 00000000000 12213700171 022701 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/fixtures/manifests/000755 000765 000024 00000000000 12213700171 023067 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/spec/fixtures/iptables/conversion_hash.rb000644 000765 000024 00000064201 12213700171 026421 0ustar00apenneystaff000000 000000 # These hashes allow us to iterate across a series of test data # creating rspec examples for each parameter to ensure the input :line # extrapolates to the desired value for the parameter in question. And # vice-versa # This hash is for testing a line conversion to a hash of parameters # which will be used to create a resource. ARGS_TO_HASH = { 'long_rule_1' => { :line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT', :table => 'filter', :compare_all => true, :params => { :action => "accept", :chain => "INPUT", :destination => "1.1.1.1/32", :dport => ["7061","7062"], :ensure => :present, :line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT', :name => "000 allow foo", :proto => "tcp", :provider => "iptables", :source => "1.1.1.1/32", :sport => ["7061","7062"], :table => "filter", }, }, 'action_drop_1' => { :line => '-A INPUT -m comment --comment "000 allow foo" -j DROP', :table => 'filter', :params => { :jump => nil, :action => "drop", }, }, 'action_reject_1' => { :line => '-A INPUT -m comment --comment "000 allow foo" -j REJECT', :table => 'filter', :params => { :jump => nil, :action => "reject", }, }, 'action_nil_1' => { :line => '-A INPUT -m comment --comment "000 allow foo"', :table => 'filter', :params => { :jump => nil, :action => nil, }, }, 'jump_custom_chain_1' => { :line => '-A INPUT -m comment --comment "000 allow foo" -j custom_chain', :table => 'filter', :params => { :jump => "custom_chain", :action => nil, }, }, 'source_destination_ipv4_no_cidr' => { :line => '-A INPUT -s 1.1.1.1 -d 2.2.2.2 -m comment --comment "000 source destination ipv4 no cidr"', :table => 'filter', :params => { :source => '1.1.1.1/32', :destination => '2.2.2.2/32', }, }, 'source_destination_ipv6_no_cidr' => { :line => '-A INPUT -s 2001:db8:85a3::8a2e:370:7334 -d 2001:db8:85a3::8a2e:370:7334 -m comment --comment "000 source destination ipv6 no cidr"', :table => 'filter', :params => { :source => '2001:db8:85a3::8a2e:370:7334/128', :destination => '2001:db8:85a3::8a2e:370:7334/128', }, }, 'source_destination_ipv4_netmask' => { :line => '-A INPUT -s 1.1.1.0/255.255.255.0 -d 2.2.0.0/255.255.0.0 -m comment --comment "000 source destination ipv4 netmask"', :table => 'filter', :params => { :source => '1.1.1.0/24', :destination => '2.2.0.0/16', }, }, 'source_destination_ipv6_netmask' => { :line => '-A INPUT -s 2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -d 2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -m comment --comment "000 source destination ipv6 netmask"', :table => 'filter', :params => { :source => '2001:db8:1234::/48', :destination => '2001:db8:4321::/48', }, }, 'dport_range_1' => { :line => '-A INPUT -m multiport --dports 1:1024 -m comment --comment "000 allow foo"', :table => 'filter', :params => { :dport => ["1-1024"], }, }, 'dport_range_2' => { :line => '-A INPUT -m multiport --dports 15,512:1024 -m comment --comment "000 allow foo"', :table => 'filter', :params => { :dport => ["15","512-1024"], }, }, 'sport_range_1' => { :line => '-A INPUT -m multiport --sports 1:1024 -m comment --comment "000 allow foo"', :table => 'filter', :params => { :sport => ["1-1024"], }, }, 'sport_range_2' => { :line => '-A INPUT -m multiport --sports 15,512:1024 -m comment --comment "000 allow foo"', :table => 'filter', :params => { :sport => ["15","512-1024"], }, }, 'dst_type_1' => { :line => '-A INPUT -m addrtype --dst-type LOCAL', :table => 'filter', :params => { :dst_type => 'LOCAL', }, }, 'src_type_1' => { :line => '-A INPUT -m addrtype --src-type LOCAL', :table => 'filter', :params => { :src_type => 'LOCAL', }, }, 'dst_range_1' => { :line => '-A INPUT -m iprange --dst-range 10.0.0.2-10.0.0.20', :table => 'filter', :params => { :dst_range => '10.0.0.2-10.0.0.20', }, }, 'src_range_1' => { :line => '-A INPUT -m iprange --src-range 10.0.0.2-10.0.0.20', :table => 'filter', :params => { :src_range => '10.0.0.2-10.0.0.20', }, }, 'tcp_flags_1' => { :line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"', :table => 'filter', :compare_all => true, :chain => 'INPUT', :proto => 'tcp', :params => { :chain => "INPUT", :ensure => :present, :line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"', :name => "000 initiation", :proto => "tcp", :provider => "iptables", :table => "filter", :tcp_flags => "SYN,RST,ACK,FIN SYN", }, }, 'state_returns_sorted_values' => { :line => '-A INPUT -m state --state INVALID,RELATED,ESTABLISHED', :table => 'filter', :params => { :state => ['ESTABLISHED', 'INVALID', 'RELATED'], :action => nil, }, }, 'comment_string_character_validation' => { :line => '-A INPUT -s 192.168.0.1/32 -m comment --comment "000 allow from 192.168.0.1, please"', :table => 'filter', :params => { :source => '192.168.0.1/32', }, }, 'log_level_debug' => { :line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG --log-level 7', :table => 'filter', :params => { :state => ['NEW'], :log_level => '7', :jump => 'LOG' }, }, 'log_level_warn' => { :line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG', :table => 'filter', :params => { :state => ['NEW'], :log_level => '4', :jump => 'LOG' }, }, 'load_limit_module_and_implicit_burst' => { :line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 15/hour', :table => 'filter', :params => { :dport => ['123'], :limit => '15/hour', :burst => '5' }, }, 'limit_with_explicit_burst' => { :line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 30/hour --limit-burst 10', :table => 'filter', :params => { :dport => ['123'], :limit => '30/hour', :burst => '10' }, }, 'proto_ipencap' => { :line => '-A INPUT -p ipencap -m comment --comment "0100 INPUT accept ipencap"', :table => 'filter', :params => { :proto => 'ipencap', } }, 'load_uid_owner_filter_module' => { :line => '-A OUTPUT -m owner --uid-owner root -m comment --comment "057 OUTPUT uid root only" -j ACCEPT', :table => 'filter', :params => { :action => 'accept', :uid => 'root', :chain => 'OUTPUT', }, }, 'load_uid_owner_postrouting_module' => { :line => '-t mangle -A POSTROUTING -m owner --uid-owner root -m comment --comment "057 POSTROUTING uid root only" -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'POSTROUTING', :uid => 'root', }, }, 'load_gid_owner_filter_module' => { :line => '-A OUTPUT -m owner --gid-owner root -m comment --comment "057 OUTPUT gid root only" -j ACCEPT', :table => 'filter', :params => { :action => 'accept', :chain => 'OUTPUT', :gid => 'root', }, }, 'load_gid_owner_postrouting_module' => { :line => '-t mangle -A POSTROUTING -m owner --gid-owner root -m comment --comment "057 POSTROUTING gid root only" -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'POSTROUTING', :gid => 'root', }, }, 'mark_set-mark' => { :line => '-t mangle -A PREROUTING -j MARK --set-xmark 0x3e8/0xffffffff', :table => 'mangle', :params => { :jump => 'MARK', :chain => 'PREROUTING', :set_mark => '0x3e8/0xffffffff', } }, 'iniface_1' => { :line => '-A INPUT -i eth0 -m comment --comment "060 iniface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'INPUT', :iniface => 'eth0', }, }, 'iniface_with_vlans_1' => { :line => '-A INPUT -i eth0.234 -m comment --comment "060 iniface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'INPUT', :iniface => 'eth0.234', }, }, 'iniface_with_plus_1' => { :line => '-A INPUT -i eth+ -m comment --comment "060 iniface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'INPUT', :iniface => 'eth+', }, }, 'outiface_1' => { :line => '-A OUTPUT -o eth0 -m comment --comment "060 outiface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth0', }, }, 'outiface_with_vlans_1' => { :line => '-A OUTPUT -o eth0.234 -m comment --comment "060 outiface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth0.234', }, }, 'outiface_with_plus_1' => { :line => '-A OUTPUT -o eth+ -m comment --comment "060 outiface" -j DROP', :table => 'filter', :params => { :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth+', }, }, 'pkttype multicast' => { :line => '-A INPUT -m pkttype --pkt-type multicast -j ACCEPT', :table => 'filter', :params => { :action => 'accept', :pkttype => 'multicast', }, }, 'socket_option' => { :line => '-A PREROUTING -m socket -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'PREROUTING', :socket => true, }, }, 'isfragment_option' => { :line => '-A INPUT -f -m comment --comment "010 a-f comment with dashf" -j ACCEPT', :table => 'filter', :params => { :name => '010 a-f comment with dashf', :action => 'accept', :isfragment => true, }, }, 'single_tcp_sport' => { :line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --sport 20443 -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'OUTPUT', :source => "10.94.100.46/32", :proto => "tcp", :sport => ["20443"], }, }, 'single_udp_sport' => { :line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --sport 20443 -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'OUTPUT', :source => "10.94.100.46/32", :proto => "udp", :sport => ["20443"], }, }, 'single_tcp_dport' => { :line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --dport 20443 -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'OUTPUT', :source => "10.94.100.46/32", :proto => "tcp", :dport => ["20443"], }, }, 'single_udp_dport' => { :line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --dport 20443 -j ACCEPT', :table => 'mangle', :params => { :action => 'accept', :chain => 'OUTPUT', :source => "10.94.100.46/32", :proto => "udp", :dport => ["20443"], }, }, } # This hash is for testing converting a hash to an argument line. HASH_TO_ARGS = { 'long_rule_1' => { :params => { :action => "accept", :chain => "INPUT", :destination => "1.1.1.1", :dport => ["7061","7062"], :ensure => :present, :name => "000 allow foo", :proto => "tcp", :source => "1.1.1.1", :sport => ["7061","7062"], :table => "filter", }, :args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "1.1.1.1/32", "-p", :tcp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061,7062", "-m", "comment", "--comment", "000 allow foo", "-j", "ACCEPT"], }, 'long_rule_2' => { :params => { :chain => "INPUT", :destination => "2.10.13.3/24", :dport => ["7061"], :ensure => :present, :jump => "my_custom_chain", :name => "700 allow bar", :proto => "udp", :source => "1.1.1.1", :sport => ["7061","7062"], :table => "filter", }, :args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "2.10.13.0/24", "-p", :udp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061", "-m", "comment", "--comment", "700 allow bar", "-j", "my_custom_chain"], }, 'no_action' => { :params => { :name => "100 no action", :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 no action"], }, 'zero_prefixlen_ipv4' => { :params => { :name => '100 zero prefix length ipv4', :table => 'filter', :source => '0.0.0.0/0', :destination => '0.0.0.0/0', }, :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv4'], }, 'zero_prefixlen_ipv6' => { :params => { :name => '100 zero prefix length ipv6', :table => 'filter', :source => '::/0', :destination => '::/0', }, :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv6'], }, 'source_destination_ipv4_no_cidr' => { :params => { :name => '000 source destination ipv4 no cidr', :table => 'filter', :source => '1.1.1.1', :destination => '2.2.2.2', }, :args => ['-t', :filter, '-s', '1.1.1.1/32', '-d', '2.2.2.2/32', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 no cidr'], }, 'source_destination_ipv6_no_cidr' => { :params => { :name => '000 source destination ipv6 no cidr', :table => 'filter', :source => '2001:db8:1234::', :destination => '2001:db8:4321::', }, :args => ['-t', :filter, '-s', '2001:db8:1234::/128', '-d', '2001:db8:4321::/128', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 no cidr'], }, 'source_destination_ipv4_netmask' => { :params => { :name => '000 source destination ipv4 netmask', :table => 'filter', :source => '1.1.1.0/255.255.255.0', :destination => '2.2.0.0/255.255.0.0', }, :args => ['-t', :filter, '-s', '1.1.1.0/24', '-d', '2.2.0.0/16', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 netmask'], }, 'source_destination_ipv6_netmask' => { :params => { :name => '000 source destination ipv6 netmask', :table => 'filter', :source => '2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000', :destination => '2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000', }, :args => ['-t', :filter, '-s', '2001:db8:1234::/48', '-d', '2001:db8:4321::/48', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 netmask'], }, 'sport_range_1' => { :params => { :name => "100 sport range", :sport => ["1-1024"], :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "1:1024", "-m", "comment", "--comment", "100 sport range"], }, 'sport_range_2' => { :params => { :name => "100 sport range", :sport => ["15","512-1024"], :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"], }, 'dport_range_1' => { :params => { :name => "100 sport range", :dport => ["1-1024"], :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "1:1024", "-m", "comment", "--comment", "100 sport range"], }, 'dport_range_2' => { :params => { :name => "100 sport range", :dport => ["15","512-1024"], :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"], }, 'dst_type_1' => { :params => { :name => '000 dst_type', :table => 'filter', :dst_type => 'LOCAL', }, :args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', :LOCAL, '-m', 'comment', '--comment', '000 dst_type'], }, 'src_type_1' => { :params => { :name => '000 src_type', :table => 'filter', :src_type => 'LOCAL', }, :args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', :LOCAL, '-m', 'comment', '--comment', '000 src_type'], }, 'dst_range_1' => { :params => { :name => '000 dst_range', :table => 'filter', :dst_range => '10.0.0.1-10.0.0.10', }, :args => ['-t', :filter, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-p', :tcp, '-m', 'comment', '--comment', '000 dst_range'], }, 'src_range_1' => { :params => { :name => '000 src_range', :table => 'filter', :dst_range => '10.0.0.1-10.0.0.10', }, :args => ['-t', :filter, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-p', :tcp, '-m', 'comment', '--comment', '000 src_range'], }, 'tcp_flags_1' => { :params => { :name => "000 initiation", :tcp_flags => "SYN,RST,ACK,FIN SYN", :table => "filter", }, :args => ["-t", :filter, "-p", :tcp, "-m", "tcp", "--tcp-flags", "SYN,RST,ACK,FIN", "SYN", "-m", "comment", "--comment", "000 initiation",] }, 'states_set_from_array' => { :params => { :name => "100 states_set_from_array", :table => "filter", :state => ['ESTABLISHED', 'INVALID'] }, :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 states_set_from_array", "-m", "state", "--state", "ESTABLISHED,INVALID"], }, 'comment_string_character_validation' => { :params => { :name => "000 allow from 192.168.0.1, please", :table => 'filter', :source => '192.168.0.1' }, :args => ['-t', :filter, '-s', '192.168.0.1/32', '-p', :tcp, '-m', 'comment', '--comment', '000 allow from 192.168.0.1, please'], }, 'port_property' => { :params => { :name => '001 port property', :table => 'filter', :port => '80', }, :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--ports', '80', '-m', 'comment', '--comment', '001 port property'], }, 'log_level_debug' => { :params => { :name => '956 INPUT log-level', :table => 'filter', :state => 'NEW', :jump => 'LOG', :log_level => 'debug' }, :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '7'], }, 'log_level_warn' => { :params => { :name => '956 INPUT log-level', :table => 'filter', :state => 'NEW', :jump => 'LOG', :log_level => 'warn' }, :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '4'], }, 'load_limit_module_and_implicit_burst' => { :params => { :name => '057 INPUT limit NTP', :table => 'filter', :dport => '123', :limit => '15/hour' }, :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '15/hour'], }, 'limit_with_explicit_burst' => { :params => { :name => '057 INPUT limit NTP', :table => 'filter', :dport => '123', :limit => '30/hour', :burst => '10' }, :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '30/hour', '--limit-burst', '10'], }, 'proto_ipencap' => { :params => { :name => '0100 INPUT accept ipencap', :table => 'filter', :proto => 'ipencap', }, :args => ['-t', :filter, '-p', :ipencap, '-m', 'comment', '--comment', '0100 INPUT accept ipencap'], }, 'load_uid_owner_filter_module' => { :params => { :name => '057 OUTPUT uid root only', :table => 'filter', :uid => 'root', :action => 'accept', :chain => 'OUTPUT', :proto => 'all', }, :args => ['-t', :filter, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT uid root only', '-j', 'ACCEPT'], }, 'load_uid_owner_postrouting_module' => { :params => { :name => '057 POSTROUTING uid root only', :table => 'mangle', :uid => 'root', :action => 'accept', :chain => 'POSTROUTING', :proto => 'all', }, :args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING uid root only', '-j', 'ACCEPT'], }, 'load_gid_owner_filter_module' => { :params => { :name => '057 OUTPUT gid root only', :table => 'filter', :chain => 'OUTPUT', :gid => 'root', :action => 'accept', :proto => 'all', }, :args => ['-t', :filter, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT gid root only', '-j', 'ACCEPT'], }, 'load_gid_owner_postrouting_module' => { :params => { :name => '057 POSTROUTING gid root only', :table => 'mangle', :gid => 'root', :action => 'accept', :chain => 'POSTROUTING', :proto => 'all', }, :args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING gid root only', '-j', 'ACCEPT'], }, 'mark_set-mark_int' => { :params => { :name => '058 set-mark 1000', :table => 'mangle', :jump => 'MARK', :chain => 'PREROUTING', :set_mark => '1000', }, :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 1000', '-j', 'MARK', '--set-xmark', '0x3e8/0xffffffff'], }, 'mark_set-mark_hex' => { :params => { :name => '058 set-mark 0x32', :table => 'mangle', :jump => 'MARK', :chain => 'PREROUTING', :set_mark => '0x32', }, :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'], }, 'mark_set-mark_hex_with_hex_mask' => { :params => { :name => '058 set-mark 0x32/0xffffffff', :table => 'mangle', :jump => 'MARK', :chain => 'PREROUTING', :set_mark => '0x32/0xffffffff', }, :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/0xffffffff', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'], }, 'mark_set-mark_hex_with_mask' => { :params => { :name => '058 set-mark 0x32/4', :table => 'mangle', :jump => 'MARK', :chain => 'PREROUTING', :set_mark => '0x32/4', }, :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/4', '-j', 'MARK', '--set-xmark', '0x32/0x4'], }, 'iniface_1' => { :params => { :name => '060 iniface', :table => 'filter', :action => 'drop', :chain => 'INPUT', :iniface => 'eth0', }, :args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], }, 'iniface_with_vlans_1' => { :params => { :name => '060 iniface', :table => 'filter', :action => 'drop', :chain => 'INPUT', :iniface => 'eth0.234', }, :args => ["-t", :filter, "-i", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], }, 'iniface_with_plus_1' => { :params => { :name => '060 iniface', :table => 'filter', :action => 'drop', :chain => 'INPUT', :iniface => 'eth+', }, :args => ["-t", :filter, "-i", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], }, 'outiface_1' => { :params => { :name => '060 outiface', :table => 'filter', :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth0', }, :args => ["-t", :filter, "-o", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], }, 'outiface_with_vlans_1' => { :params => { :name => '060 outiface', :table => 'filter', :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth0.234', }, :args => ["-t", :filter, "-o", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], }, 'outiface_with_plus_1' => { :params => { :name => '060 outiface', :table => 'filter', :action => 'drop', :chain => 'OUTPUT', :outiface => 'eth+', }, :args => ["-t", :filter, "-o", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], }, 'pkttype multicast' => { :params => { :name => '062 pkttype multicast', :table => "filter", :action => 'accept', :chain => 'INPUT', :iniface => 'eth0', :pkttype => 'multicast', }, :args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "pkttype", "--pkt-type", :multicast, "-m", "comment", "--comment", "062 pkttype multicast", "-j", "ACCEPT"], }, 'socket_option' => { :params => { :name => '050 socket option', :table => 'mangle', :action => 'accept', :chain => 'PREROUTING', :socket => true, }, :args => ['-t', :mangle, '-p', :tcp, '-m', 'socket', '-m', 'comment', '--comment', '050 socket option', '-j', 'ACCEPT'], }, 'isfragment_option' => { :params => { :name => '050 isfragment option', :table => 'filter', :proto => :all, :action => 'accept', :isfragment => true, }, :args => ['-t', :filter, '-p', :all, '-f', '-m', 'comment', '--comment', '050 isfragment option', '-j', 'ACCEPT'], }, 'isfragment_option not changing -f in comment' => { :params => { :name => '050 testcomment-with-fdashf', :table => 'filter', :proto => :all, :action => 'accept', }, :args => ['-t', :filter, '-p', :all, '-m', 'comment', '--comment', '050 testcomment-with-fdashf', '-j', 'ACCEPT'], }, } puppetlabs-firewall-0.4.2/manifests/init.pp000644 000765 000024 00000001311 12213700171 021564 0ustar00apenneystaff000000 000000 # = Class: firewall # # Manages packages and services required by the firewall type/provider. # # This class includes the appropriate sub-class for your operating system, # where supported. # # == Parameters: # # [*ensure*] # Ensure parameter passed onto Service[] resources. # Default: running # class firewall ( $ensure = running ) { case $ensure { /^(running|stopped)$/: { # Do nothing. } default: { fail("${title}: Ensure value '${ensure}' is not supported") } } case $::kernel { 'Linux': { class { "${title}::linux": ensure => $ensure, } } default: { fail("${title}: Kernel '${::kernel}' is not currently supported") } } } puppetlabs-firewall-0.4.2/manifests/linux/000755 000765 000024 00000000000 12213700171 021423 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/manifests/linux.pp000644 000765 000024 00000002340 12213700171 021763 0ustar00apenneystaff000000 000000 # = Class: firewall::linux # # Installs the `iptables` package for Linux operating systems and includes # the appropriate sub-class for any distribution specific services and # additional packages. # # == Parameters: # # [*ensure*] # Ensure parameter passed onto Service[] resources. When `running` the # service will be started on boot, and when `stopped` it will not. # Default: running # class firewall::linux ( $ensure = running ) { $enable = $ensure ? { running => true, stopped => false, } package { 'iptables': ensure => present, } case $::operatingsystem { 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': { class { "${title}::redhat": ensure => $ensure, enable => $enable, require => Package['iptables'], } } 'Debian', 'Ubuntu': { class { "${title}::debian": ensure => $ensure, enable => $enable, require => Package['iptables'], } } 'Archlinux': { class { "${title}::archlinux": ensure => $ensure, enable => $enable, require => Package['iptables'], } } default: {} } } puppetlabs-firewall-0.4.2/manifests/linux/archlinux.pp000644 000765 000024 00000001521 12213700171 023760 0ustar00apenneystaff000000 000000 # = Class: firewall::linux::archlinux # # Manages `iptables` and `ip6tables` services, and creates files used for # persistence, on Arch Linux systems. # # == Parameters: # # [*ensure*] # Ensure parameter passed onto Service[] resources. # Default: running # # [*enable*] # Enable parameter passed onto Service[] resources. # Default: true # class firewall::linux::archlinux ( $ensure = 'running', $enable = true ) { service { 'iptables': ensure => $ensure, enable => $enable, hasstatus => true, } service { 'ip6tables': ensure => $ensure, enable => $enable, hasstatus => true, } file { '/etc/iptables/iptables.rules': ensure => present, before => Service['iptables'], } file { '/etc/iptables/ip6tables.rules': ensure => present, before => Service['ip6tables'], } } puppetlabs-firewall-0.4.2/manifests/linux/debian.pp000644 000765 000024 00000002451 12213700171 023210 0ustar00apenneystaff000000 000000 # = Class: firewall::linux::debian # # Installs the `iptables-persistent` package for Debian-alike systems. This # allows rules to be stored to file and restored on boot. # # == Parameters: # # [*ensure*] # Ensure parameter passed onto Service[] resources. # Default: running # # [*enable*] # Enable parameter passed onto Service[] resources. # Default: true # class firewall::linux::debian ( $ensure = running, $enable = true ) { package { 'iptables-persistent': ensure => present, } if($::operatingsystemrelease =~ /^6\./ and $enable == true and versioncmp($::iptables_persistent_version, '0.5.0') < 0 ) { # This fixes a bug in the iptables-persistent LSB headers in 6.x, without it # we lose idempotency exec { 'iptables-persistent-enable': logoutput => on_failure, command => '/usr/sbin/update-rc.d iptables-persistent enable', unless => '/usr/bin/test -f /etc/rcS.d/S*iptables-persistent', require => Package['iptables-persistent'], } } else { # This isn't a real service/daemon. The start action loads rules, so just # needs to be called on system boot. service { 'iptables-persistent': ensure => undef, enable => $enable, hasstatus => true, require => Package['iptables-persistent'], } } } puppetlabs-firewall-0.4.2/manifests/linux/redhat.pp000644 000765 000024 00000000730 12213700171 023233 0ustar00apenneystaff000000 000000 # = 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 { 'iptables': ensure => $ensure, enable => $enable, hasstatus => true, } } puppetlabs-firewall-0.4.2/lib/facter/000755 000765 000024 00000000000 12213700171 020305 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/000755 000765 000024 00000000000 12213700171 020356 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/provider/000755 000765 000024 00000000000 12213700171 022210 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/type/000755 000765 000024 00000000000 12213700171 021337 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/util/000755 000765 000024 00000000000 12213700171 021333 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/util/firewall.rb000644 000765 000024 00000013536 12213700171 023475 0ustar00apenneystaff000000 000000 require 'socket' require 'resolv' require 'puppet/util/ipcidr' # Util module for puppetlabs-firewall module Puppet::Util::Firewall # Translate the symbolic names for icmp packet types to integers def icmp_name_to_number(value_icmp, protocol) if value_icmp =~ /\d{1,2}$/ value_icmp elsif protocol == 'inet' case value_icmp when "echo-reply" then "0" when "destination-unreachable" then "3" when "source-quench" then "4" when "redirect" then "6" when "echo-request" then "8" when "router-advertisement" then "9" when "router-solicitation" then "10" when "time-exceeded" then "11" when "parameter-problem" then "12" when "timestamp-request" then "13" when "timestamp-reply" then "14" when "address-mask-request" then "17" when "address-mask-reply" then "18" else nil end elsif protocol == 'inet6' case value_icmp when "destination-unreachable" then "1" when "time-exceeded" then "3" when "parameter-problem" then "4" when "echo-request" then "128" when "echo-reply" then "129" when "router-solicitation" then "133" when "router-advertisement" then "134" when "redirect" then "137" else nil end else raise ArgumentError, "unsupported protocol family '#{protocol}'" end end # Convert log_level names to their respective numbers def log_level_name_to_number(value) #TODO make this 0-7 only if value =~ /\d/ value else case value when "panic" then "0" when "alert" then "1" when "crit" then "2" when "err" then "3" when "error" then "3" when "warn" then "4" when "warning" then "4" when "not" then "5" when "notice" then "5" when "info" then "6" when "debug" then "7" else nil end end end # This method takes a string and a protocol and attempts to convert # it to a port number if valid. # # If the string already contains a port number or perhaps a range of ports # in the format 22:1000 for example, it simply returns the string and does # nothing. def string_to_port(value, proto) proto = proto.to_s unless proto =~ /^(tcp|udp)$/ proto = 'tcp' end if value.kind_of?(String) if value.match(/^\d+(-\d+)?$/) return value else return Socket.getservbyname(value, proto).to_s end else Socket.getservbyname(value.to_s, proto).to_s end end # Takes an address and returns it in CIDR notation. # # If the address is: # # - A hostname: # It will be resolved # - An IPv4 address: # It will be qualified with a /32 CIDR notation # - An IPv6 address: # It will be qualified with a /128 CIDR notation # - An IP address with a CIDR notation: # It will be normalised # - An IP address with a dotted-quad netmask: # It will be converted to CIDR notation # - Any address with a resulting prefix length of zero: # It will return nil which is equivilent to not specifying an address # def host_to_ip(value) begin value = Puppet::Util::IPCidr.new(value) rescue value = Puppet::Util::IPCidr.new(Resolv.getaddress(value)) end return nil if value.prefixlen == 0 value.cidr end # Validates the argument is int or hex, and returns valid hex # conversion of the value or nil otherwise. def to_hex32(value) begin value = Integer(value) if value.between?(0, 0xffffffff) return '0x' + value.to_s(16) end rescue ArgumentError # pass end return nil end def persist_iptables(proto) debug("[persist_iptables]") # Basic normalisation for older Facter os_key = Facter.value(:osfamily) os_key ||= case Facter.value(:operatingsystem) when 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer' 'RedHat' when 'Debian', 'Ubuntu' 'Debian' else Facter.value(:operatingsystem) end # Older iptables-persistent doesn't provide save action. if os_key == 'Debian' persist_ver = Facter.value(:iptables_persistent_version) if (persist_ver and Puppet::Util::Package.versioncmp(persist_ver, '0.5.0') < 0) os_key = 'Debian_manual' end end # Fedora 15 and newer use systemd for to persist iptable rules if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'Fedora' && Facter.value(:operatingsystemrelease).to_i >= 15 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.init save} when :IPv6 %w{/usr/libexec/ip6tables.init save} end when :Debian case proto.to_sym when :IPv4, :IPv6 %w{/usr/sbin/service iptables-persistent save} end when :Debian_manual case proto.to_sym when :IPv4 ["/bin/sh", "-c", "/sbin/iptables-save > /etc/iptables/rules"] end when :Archlinux case proto.to_sym when :IPv4 ["/bin/sh", "-c", "/usr/sbin/iptables-save > /etc/iptables/iptables.rules"] when :IPv6 ["/bin/sh", "-c", "/usr/sbin/ip6tables-save > /etc/iptables/ip6tables.rules"] end end # Catch unsupported OSs from the case statement above. if cmd.nil? debug('firewall: Rule persistence is not supported for this type/OS') return end begin execute(cmd) rescue Puppet::ExecutionFailure => detail warning("Unable to persist firewall rules: #{detail}") end end end puppetlabs-firewall-0.4.2/lib/puppet/util/ipcidr.rb000644 000765 000024 00000001646 12213700171 023141 0ustar00apenneystaff000000 000000 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 puppetlabs-firewall-0.4.2/lib/puppet/type/firewall.rb000644 000765 000024 00000054246 12213700171 023504 0ustar00apenneystaff000000 000000 # See: #10295 for more details. # # 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(:firewall) do include Puppet::Util::Firewall @doc = <<-EOS This type provides the capability to manage firewall rules within puppet. **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. EOS feature :rate_limiting, "Rate limiting features." feature :snat, "Source NATing" feature :dnat, "Destination NATing" feature :interface_match, "Interface matching" feature :icmp_match, "Matching ICMP types" feature :owner, "Matching owners" feature :state_match, "Matching stateful firewall states" feature :reject_type, "The ability to control reject messages" feature :log_level, "The ability to control the log level" feature :log_prefix, "The ability to add prefixes to log messages" feature :mark, "Set the netfilter mark value associated with the packet" feature :tcp_flags, "The ability to match on particular TCP flag settings" feature :pkttype, "Match a packet type" feature :socket, "Match open sockets" feature :isfragment, "Match fragments" feature :address_type, "The ability match on source or destination address type" feature :iprange, "The ability match on source or destination IP range " # provider specific features feature :iptables, "The provider provides iptables features." ensurable do desc <<-EOS Manage the state of this rule. The default action is *present*. EOS newvalue(:present) do provider.insert end newvalue(:absent) do provider.delete end defaultto :present end newparam(:name) do desc <<-EOS The canonical name of the rule. This name is also used for ordering so make sure you prefix the rule with a number: 000 this runs first 999 this runs last Depending on the provider, the name of the rule can be stored using the comment feature of the underlying firewall subsystem. EOS isnamevar # Keep rule names simple - they must start with a number newvalues(/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/) end newproperty(:action) do desc <<-EOS This is the action to perform on a match. Can be one of: * 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*). EOS newvalues(:accept, :reject, :drop) end # Generic matching properties newproperty(:source) do desc <<-EOS The source address. For example: source => '192.168.2.0/24' The source can also be an IPv6 address if your provider supports it. EOS munge do |value| begin @resource.host_to_ip(value) rescue Exception => e self.fail("host_to_ip failed for #{value}, exception #{e}") end end end # Source IP range newproperty(:src_range, :required_features => :iprange) do desc <<-EOS The source IP range. For example: src_range => '192.168.1.1-192.168.1.10' The source IP range is must in 'IP1-IP2' format. EOS newvalues(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)-((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/) end newproperty(:destination) do desc <<-EOS The destination address to match. For example: destination => '192.168.1.0/24' The destination can also be an IPv6 address if your provider supports it. EOS munge do |value| begin @resource.host_to_ip(value) rescue Exception => e self.fail("host_to_ip failed for #{value}, exception #{e}") end end end # Destination IP range newproperty(:dst_range, :required_features => :iprange) do desc <<-EOS 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. EOS newvalues(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)-((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/) end newproperty(:sport, :array_matching => :all) do desc <<-EOS 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: - For example: 1-1024 This would cover ports 1 to 1024. EOS munge do |value| @resource.string_to_port(value, :proto) end def is_to_s(value) should_to_s(value) end def should_to_s(value) value = [value] unless value.is_a?(Array) value.join(',') end end newproperty(:dport, :array_matching => :all) do desc <<-EOS 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: - For example: 1-1024 This would cover ports 1 to 1024. EOS munge do |value| @resource.string_to_port(value, :proto) end def is_to_s(value) should_to_s(value) end def should_to_s(value) value = [value] unless value.is_a?(Array) value.join(',') end end newproperty(:port, :array_matching => :all) do desc <<-EOS 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: - For example: 1-1024 This would cover ports 1 to 1024. EOS munge do |value| @resource.string_to_port(value, :proto) end def is_to_s(value) should_to_s(value) end def should_to_s(value) value = [value] unless value.is_a?(Array) value.join(',') end end newproperty(:dst_type, :required_features => :address_type) do desc <<-EOS The destination address type. For example: dst_type => 'LOCAL' Can be one of: * 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 - undocumented * NAT - undocumented * XRESOLVE - undocumented EOS newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE) end newproperty(:src_type, :required_features => :address_type) do desc <<-EOS The source address type. For example: src_type => 'LOCAL' Can be one of: * 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 - undocumented * NAT - undocumented * XRESOLVE - undocumented EOS newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE) end newproperty(:proto) do desc <<-EOS The specific protocol to match for this rule. By default this is *tcp*. EOS newvalues(:tcp, :udp, :icmp, :"ipv6-icmp", :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all) defaultto "tcp" end # tcp-specific newproperty(:tcp_flags, :required_features => :tcp_flags) do desc <<-EOS Match when the TCP flags are as specified. Is 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 them in the order that iptables --list-rules would list them to avoid having puppet think you changed the flags. 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. EOS end # Iptables specific newproperty(:chain, :required_features => :iptables) do desc <<-EOS Name of the chain to use. Can be one of the built-ins: * INPUT * FORWARD * OUTPUT * PREROUTING * POSTROUTING Or you can provide a user-based chain. The default value is 'INPUT'. EOS defaultto "INPUT" newvalue(/^[a-zA-Z0-9\-_]+$/) end newproperty(:table, :required_features => :iptables) do desc <<-EOS Table to use. Can be one of: * nat * mangle * filter * raw * rawpost By default the setting is 'filter'. EOS newvalues(:nat, :mangle, :filter, :raw, :rawpost) defaultto "filter" end newproperty(:jump, :required_features => :iptables) do desc <<-EOS The value for the iptables --jump parameter. Normal values are: * QUEUE * RETURN * DNAT * SNAT * LOG * MASQUERADE * REDIRECT * MARK But any valid chain name is allowed. For the values ACCEPT, DROP and REJECT you must use the generic 'action' parameter. This is to enfore the use of generic parameters where possible for maximum cross-platform modelling. If you set both 'accept' and 'jump' parameters, you will get an error as only one of the options should be set. EOS validate do |value| unless value =~ /^[a-zA-Z0-9\-_]+$/ raise ArgumentError, <<-EOS Jump destination must consist of alphanumeric characters, an underscore or a yphen. EOS end if ["accept","reject","drop"].include?(value.downcase) raise ArgumentError, <<-EOS Jump destination should not be one of ACCEPT, REJECT or DROP. Use the action property instead. EOS end end end # Interface specific matching properties newproperty(:iniface, :required_features => :interface_match) do desc <<-EOS Input interface to filter on. EOS newvalues(/^[a-zA-Z0-9\-\._\+]+$/) end newproperty(:outiface, :required_features => :interface_match) do desc <<-EOS Output interface to filter on. EOS newvalues(/^[a-zA-Z0-9\-\._\+]+$/) end # NAT specific properties newproperty(:tosource, :required_features => :snat) do desc <<-EOS When using jump => "SNAT" you can specify the new source address using this parameter. EOS end newproperty(:todest, :required_features => :dnat) do desc <<-EOS When using jump => "DNAT" you can specify the new destination address using this paramter. EOS end newproperty(:toports, :required_features => :dnat) do desc <<-EOS For DNAT this is the port that will replace the destination port. EOS end # Reject ICMP type newproperty(:reject, :required_features => :reject_type) do desc <<-EOS When combined with jump => "REJECT" you can specify a different icmp response to be sent back to the packet sender. EOS end # Logging properties newproperty(:log_level, :required_features => :log_level) do desc <<-EOS When combined with jump => "LOG" specifies the system log level to log to. EOS munge do |value| if value.kind_of?(String) value = @resource.log_level_name_to_number(value) else value end if value == nil && value != "" self.fail("Unable to determine log level") end value end end newproperty(:log_prefix, :required_features => :log_prefix) do desc <<-EOS When combined with jump => "LOG" specifies the log prefix to use when logging. EOS end # ICMP matching property newproperty(:icmp, :required_features => :icmp_match) do desc <<-EOS When matching ICMP packets, this is the type of ICMP packet to match. A value of "any" is not supported. To achieve this behaviour the parameter should simply be omitted or undefined. EOS validate do |value| if value == "any" raise ArgumentError, "Value 'any' is not valid. This behaviour should be achieved " \ "by omitting or undefining the ICMP parameter." end end munge do |value| if value.kind_of?(String) # ICMP codes differ between IPv4 and IPv6. case @resource[:provider] when :iptables protocol = 'inet' when :ip6tables protocol = 'inet6' else self.fail("cannot work out protocol family") end value = @resource.icmp_name_to_number(value, protocol) else value end if value == nil && value != "" self.fail("cannot work out icmp type") end value end end newproperty(:state, :array_matching => :all, :required_features => :state_match) do desc <<-EOS Matches a packet based on its state in the firewall stateful inspection table. Values can be: * INVALID * ESTABLISHED * NEW * RELATED EOS newvalues(:INVALID,:ESTABLISHED,:NEW,:RELATED) # States should always be sorted. This normalizes the resource states to # keep it consistent with the sorted result from iptables-save. def should=(values) @should = super(values).sort_by {|sym| sym.to_s} end def is_to_s(value) should_to_s(value) end def should_to_s(value) value = [value] unless value.is_a?(Array) value.join(',') end end # Rate limiting properties newproperty(:limit, :required_features => :rate_limiting) do desc <<-EOS 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'." EOS end newproperty(:burst, :required_features => :rate_limiting) do desc <<-EOS Rate limiting burst value (per second) before limit checks apply. EOS newvalue(/^\d+$/) end newproperty(:uid, :required_features => :owner) do desc <<-EOS UID or Username owner matching rule. Accepts a string argument only, as iptables does not accept multiple uid in a single statement. EOS end newproperty(:gid, :required_features => :owner) do desc <<-EOS GID or Group owner matching rule. Accepts a string argument only, as iptables does not accept multiple gid in a single statement. EOS end newproperty(:set_mark, :required_features => :mark) do desc <<-EOS Set 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. EOS munge do |value| int_or_hex = '[a-fA-F0-9x]' match = value.to_s.match("(#{int_or_hex}+)(/)?(#{int_or_hex}+)?") mark = @resource.to_hex32(match[1]) # Values that can't be converted to hex. # Or contain a trailing slash with no mask. if mark.nil? or (mark and match[2] and match[3].nil?) raise ArgumentError, "MARK value must be integer or hex between 0 and 0xffffffff" end # Old iptables does not support a mask. New iptables will expect one. iptables_version = Facter.fact('iptables_version').value mask_required = (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') >= 0) if mask_required if match[3].nil? value = "#{mark}/0xffffffff" else mask = @resource.to_hex32(match[3]) if mask.nil? raise ArgumentError, "MARK mask must be integer or hex between 0 and 0xffffffff" end value = "#{mark}/#{mask}" end else unless match[3].nil? raise ArgumentError, "iptables version #{iptables_version} does not support masks on MARK rules" end value = mark end value end end newproperty(:pkttype, :required_features => :pkttype) do desc <<-EOS Sets the packet type to match. EOS newvalues(:unicast, :broadcast, :multicast) end newproperty(:isfragment, :required_features => :isfragment) do desc <<-EOS Set to true to match tcp fragments (requires type to be set to tcp) EOS newvalues(:true, :false) end newproperty(:socket, :required_features => :socket) do desc <<-EOS If true, matches if an open socket can be found by doing a coket lookup on the packet. EOS newvalues(:true, :false) end newparam(:line) do desc <<-EOS Read-only property for caching the rule line. EOS end autorequire(:firewallchain) do reqs = [] protocol = nil case value(:provider) when :iptables protocol = "IPv4" when :ip6tables protocol = "IPv6" end unless protocol.nil? [value(:chain), value(:jump)].each do |chain| reqs << "#{chain}:#{value(:table)}:#{protocol}" unless chain.nil? end end reqs end # Classes would be a better abstraction, pending: # http://projects.puppetlabs.com/issues/19001 autorequire(:package) do case value(:provider) when :iptables, :ip6tables %w{iptables iptables-persistent} else [] end end validate do debug("[validate]") # TODO: this is put here to skip validation if ensure is not set. This # is because there is a revalidation stage called later where the values # are not set correctly. I tried tracing it - but have put in this # workaround instead to skip. Must get to the bottom of this. if ! value(:ensure) return end # First we make sure the chains and tables are valid combinations if value(:table).to_s == "filter" && value(:chain) =~ /PREROUTING|POSTROUTING/ self.fail "PREROUTING and POSTROUTING cannot be used in table 'filter'" end if value(:table).to_s == "nat" && value(:chain) =~ /INPUT|FORWARD/ self.fail "INPUT and FORWARD cannot be used in table 'nat'" end if value(:table).to_s == "raw" && value(:chain) =~ /INPUT|FORWARD|POSTROUTING/ self.fail "INPUT, FORWARD and POSTROUTING cannot be used in table raw" end # Now we analyse the individual properties to make sure they apply to # the correct combinations. if value(:iniface) unless value(:chain).to_s =~ /INPUT|FORWARD|PREROUTING/ self.fail "Parameter iniface only applies to chains " \ "INPUT,FORWARD,PREROUTING" end end if value(:outiface) unless value(:chain).to_s =~ /OUTPUT|FORWARD|POSTROUTING/ self.fail "Parameter outiface only applies to chains " \ "OUTPUT,FORWARD,POSTROUTING" end end if value(:uid) unless value(:chain).to_s =~ /OUTPUT|POSTROUTING/ self.fail "Parameter uid only applies to chains " \ "OUTPUT,POSTROUTING" end end if value(:gid) unless value(:chain).to_s =~ /OUTPUT|POSTROUTING/ self.fail "Parameter gid only applies to chains " \ "OUTPUT,POSTROUTING" end end if value(:set_mark) unless value(:jump).to_s =~ /MARK/ && value(:chain).to_s =~ /PREROUTING|OUTPUT/ && value(:table).to_s =~ /mangle/ self.fail "Parameter set_mark only applies to " \ "the PREROUTING or OUTPUT chain of the mangle table and when jump => MARK" end end if value(:dport) unless value(:proto).to_s =~ /tcp|udp|sctp/ self.fail "[%s] Parameter dport only applies to sctp, tcp and udp " \ "protocols. Current protocol is [%s] and dport is [%s]" % [value(:name), should(:proto), should(:dport)] end end if value(:jump).to_s == "DNAT" unless value(:table).to_s =~ /nat/ self.fail "Parameter jump => DNAT only applies to table => nat" end unless value(:todest) self.fail "Parameter jump => DNAT must have todest parameter" end end if value(:jump).to_s == "SNAT" unless value(:table).to_s =~ /nat/ self.fail "Parameter jump => SNAT only applies to table => nat" end unless value(:tosource) self.fail "Parameter jump => DNAT must have tosource parameter" end end if value(:jump).to_s == "REDIRECT" unless value(:toports) self.fail "Parameter jump => REDIRECT missing mandatory toports " \ "parameter" end end if value(:jump).to_s == "MASQUERADE" unless value(:table).to_s =~ /nat/ self.fail "Parameter jump => MASQUERADE only applies to table => nat" end end if value(:log_prefix) || value(:log_level) unless value(:jump).to_s == "LOG" self.fail "Parameter log_prefix and log_level require jump => LOG" end end if value(:burst) && ! value(:limit) self.fail "burst makes no sense without limit" end if value(:action) && value(:jump) self.fail "Only one of the parameters 'action' and 'jump' can be set" end end end puppetlabs-firewall-0.4.2/lib/puppet/type/firewallchain.rb000644 000765 000024 00000013001 12213700171 024467 0ustar00apenneystaff000000 000000 # This is a workaround for bug: #4248 whereby ruby files outside of the normal # provider/type path do not load until pluginsync has occured on the puppetmaster # # In this case I'm trying the relative path first, then falling back to normal # mechanisms. This should be fixed in future versions of puppet but it looks # like we'll need to maintain this for some time perhaps. $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..")) require 'puppet/util/firewall' Puppet::Type.newtype(:firewallchain) do include Puppet::Util::Firewall @doc = <<-EOS This type provides the capability to manage rule chains for firewalls. Currently this supports only iptables, ip6tables and ebtables on Linux. And provides support for setting the default policy on chains and tables that allow it. **Autorequires:** If Puppet is managing the iptables or iptables-persistent packages, and the provider is iptables_chain, the firewall resource will autorequire those packages to ensure that any required binaries are installed. EOS feature :iptables_chain, "The provider provides iptables chain features." feature :policy, "Default policy (inbuilt chains only)" ensurable do defaultvalues defaultto :present end newparam(:name) do desc <<-EOS The canonical name of the chain. For iptables the format must be {chain}:{table}:{protocol}. EOS isnamevar validate do |value| if value !~ Nameformat then raise ArgumentError, "Inbuilt chains must be in the form {chain}:{table}:{protocol} where {table} is one of FILTER, NAT, MANGLE, RAW, RAWPOST, BROUTE or empty (alias for filter), chain can be anything without colons or one of PREROUTING, POSTROUTING, BROUTING, INPUT, FORWARD, OUTPUT for the inbuilt chains, and {protocol} being IPv4, IPv6, ethernet (ethernet bridging) got '#{value}' table:'#{$1}' chain:'#{$2}' protocol:'#{$3}'" else chain = $1 table = $2 protocol = $3 case table when 'filter' if chain =~ /^(PREROUTING|POSTROUTING|BROUTING)$/ raise ArgumentError, "INPUT, OUTPUT and FORWARD are the only inbuilt chains that can be used in table 'filter'" end when 'mangle' if chain =~ InternalChains && chain == 'BROUTING' raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, FORWARD and OUTPUT are the only inbuilt chains that can be used in table 'mangle'" end when 'nat' if chain =~ /^(BROUTING|INPUT|FORWARD)$/ raise ArgumentError, "PREROUTING, POSTROUTING and OUTPUT are the only inbuilt chains that can be used in table 'nat'" end if protocol =~/^(IP(v6)?)?$/ raise ArgumentError, "table nat isn't valid in IPv6. You must specify ':IPv4' as the name suffix" end when 'raw' if chain =~ /^(POSTROUTING|BROUTING|INPUT|FORWARD)$/ raise ArgumentError,'PREROUTING and OUTPUT are the only inbuilt chains in the table \'raw\'' end when 'broute' if protocol != 'ethernet' raise ArgumentError,'BROUTE is only valid with protocol \'ethernet\'' end if chain =~ /^PREROUTING|POSTROUTING|INPUT|FORWARD|OUTPUT$/ raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'broute\'' end end if chain == 'BROUTING' && ( protocol != 'ethernet' || table!='broute') raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\' with protocol \'ethernet\' i.e. \'broute:BROUTING:enternet\'' end end end end newproperty(:policy) do desc <<-EOS This is the action to when the end of the chain is reached. It can only be set on inbuilt chains (INPUT, FORWARD, OUTPUT, PREROUTING, POSTROUTING) and can be one of: * accept - the packet is accepted * drop - the packet is dropped * queue - the packet is passed userspace * return - the packet is returned to calling (jump) queue or the default of inbuilt chains EOS newvalues(:accept, :drop, :queue, :return) defaultto do # ethernet chain have an ACCEPT default while other haven't got an # allowed value if @resource[:name] =~ /:ethernet$/ :accept else nil end end end # Classes would be a better abstraction, pending: # http://projects.puppetlabs.com/issues/19001 autorequire(:package) do case value(:provider) when :iptables_chain %w{iptables iptables-persistent} else [] end end validate do debug("[validate]") value(:name).match(Nameformat) chain = $1 table = $2 protocol = $3 # Check that we're not removing an internal chain if chain =~ InternalChains && value(:ensure) == :absent self.fail "Cannot remove in-built chains" end if value(:policy).nil? && protocol == 'ethernet' self.fail "you must set a non-empty policy on all ethernet table chains" end # Check that we're not setting a policy on a user chain if chain !~ InternalChains && !value(:policy).nil? && protocol != 'ethernet' self.fail "policy can only be set on in-built chains (with the exception of ethernet chains) (table:#{table} chain:#{chain} protocol:#{protocol})" end # no DROP policy on nat table if table == 'nat' && value(:policy) == :drop self.fail 'The "nat" table is not intended for filtering, the use of DROP is therefore inhibited' end end end puppetlabs-firewall-0.4.2/lib/puppet/provider/firewall/000755 000765 000024 00000000000 12213700171 024015 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/provider/firewall.rb000644 000765 000024 00000002010 12213700171 024333 0ustar00apenneystaff000000 000000 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 puppetlabs-firewall-0.4.2/lib/puppet/provider/firewallchain/000755 000765 000024 00000000000 12213700171 025020 5ustar00apenneystaff000000 000000 puppetlabs-firewall-0.4.2/lib/puppet/provider/firewallchain/iptables_chain.rb000644 000765 000024 00000010617 12213700171 030317 0ustar00apenneystaff000000 000000 Puppet::Type.type(:firewallchain).provide :iptables_chain do include Puppet::Util::Firewall @doc = "Iptables chain provider" has_feature :iptables_chain has_feature :policy optional_commands({ :iptables => 'iptables', :iptables_save => 'iptables-save', :ip6tables => 'ip6tables', :ip6tables_save => 'ip6tables-save', :ebtables => 'ebtables', :ebtables_save => 'ebtables-save', }) defaultfor :kernel => :linux # chain name is greedy so we anchor from the end. # [\d+:\d+] doesn't exist on ebtables Mapping = { :IPv4 => { :tables => method(:iptables), :save => method(:iptables_save), :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, }, :IPv6 => { :tables => method(:ip6tables), :save => method(:ip6tables_save), :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, }, :ethernet => { :tables => method(:ebtables), :save => method(:ebtables_save), :re => /^:(.+)\s(\S+)$/, } } InternalChains = /^(PREROUTING|POSTROUTING|BROUTING|INPUT|FORWARD|OUTPUT)$/ Tables = 'nat|mangle|filter|raw|rawpost|broute' Nameformat = /^(.+):(#{Tables}):(IP(v[46])?|ethernet)$/ def create # can't create internal chains if @resource[:name] =~ InternalChains self.warn "Attempting to create internal chain #{@resource[:name]}" end allvalidchains do |t, chain, table, protocol| 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 # can't delete internal chains if @resource[:name] =~ InternalChains self.warn "Attempting to destroy internal chain #{@resource[:name]}" end allvalidchains do |t, chain, table| debug "Deleting chain #{chain} on table #{table}" t.call ['-t',table,'-X',chain] end end def exists? 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 puppetlabs-firewall-0.4.2/lib/puppet/provider/firewall/ip6tables.rb000644 000765 000024 00000004511 12213700171 026234 0ustar00apenneystaff000000 000000 Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :iptables do @doc = "Ip6tables type provider" has_feature :iptables has_feature :rate_limiting has_feature :snat has_feature :dnat has_feature :interface_match has_feature :icmp_match has_feature :owner has_feature :state_match has_feature :reject_type has_feature :log_level has_feature :log_prefix has_feature :mark has_feature :tcp_flags has_feature :pkttype optional_commands({ :ip6tables => 'ip6tables', :ip6tables_save => 'ip6tables-save', }) def self.iptables(*args) ip6tables(*args) end def self.iptables_save(*args) ip6tables_save(*args) end @protocol = "IPv6" @resource_map = { :burst => "--limit-burst", :destination => "-d", :dport => "-m multiport --dports", :gid => "-m owner --gid-owner", :icmp => "-m icmp6 --icmpv6-type", :iniface => "-i", :jump => "-j", :limit => "-m limit --limit", :log_level => "--log-level", :log_prefix => "--log-prefix", :name => "-m comment --comment", :outiface => "-o", :port => '-m multiport --ports', :proto => "-p", :reject => "--reject-with", :source => "-s", :state => "-m state --state", :sport => "-m multiport --sports", :table => "-t", :todest => "--to-destination", :toports => "--to-ports", :tosource => "--to-source", :uid => "-m owner --uid-owner", :pkttype => "-m pkttype --pkt-type" } # Create property methods dynamically (@resource_map.keys << :chain << :table << :action).each do |property| define_method "#{property}" do @property_hash[property.to_sym] end define_method "#{property}=" do |value| @property_hash[:needs_change] = true end end # This is the order of resources as they appear in iptables-save output, # we need it to properly parse and apply rules, if the order of resource # changes between puppet runs, the changed rules will be re-applied again. # This order can be determined by going through iptables source code or just tweaking and trying manually @resource_list = [:table, :source, :destination, :iniface, :outiface, :proto, :gid, :uid, :sport, :dport, :port, :pkttype, :name, :state, :icmp, :limit, :burst, :jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject] end puppetlabs-firewall-0.4.2/lib/puppet/provider/firewall/iptables.rb000644 000765 000024 00000025731 12213700171 026155 0ustar00apenneystaff000000 000000 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 :rate_limiting has_feature :snat has_feature :dnat has_feature :interface_match has_feature :icmp_match has_feature :owner has_feature :state_match has_feature :reject_type has_feature :log_level has_feature :log_prefix has_feature :mark has_feature :tcp_flags has_feature :pkttype has_feature :isfragment has_feature :socket has_feature :address_type has_feature :iprange optional_commands({ :iptables => 'iptables', :iptables_save => 'iptables-save', }) defaultfor :kernel => :linux iptables_version = Facter.fact('iptables_version').value if (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') < 0) mark_flag = '--set-mark' else mark_flag = '--set-xmark' end @protocol = "IPv4" @resource_map = { :burst => "--limit-burst", :destination => "-d", :dst_type => "-m addrtype --dst-type", :dst_range => "-m iprange --dst-range", :dport => ["-m multiport --dports", "-m (udp|tcp) --dport"], :gid => "-m owner --gid-owner", :icmp => "-m icmp --icmp-type", :iniface => "-i", :jump => "-j", :limit => "-m limit --limit", :log_level => "--log-level", :log_prefix => "--log-prefix", :name => "-m comment --comment", :outiface => "-o", :port => '-m multiport --ports', :proto => "-p", :reject => "--reject-with", :set_mark => mark_flag, :socket => "-m socket", :source => "-s", :src_type => "-m addrtype --src-type", :src_range => "-m iprange --src-range", :sport => ["-m multiport --sports", "-m (udp|tcp) --sport"], :state => "-m state --state", :table => "-t", :tcp_flags => "-m tcp --tcp-flags", :todest => "--to-destination", :toports => "--to-ports", :tosource => "--to-source", :uid => "-m owner --uid-owner", :pkttype => "-m pkttype --pkt-type", :isfragment => "-f", } # Create property methods dynamically (@resource_map.keys << :chain << :table << :action).each do |property| define_method "#{property}" do @property_hash[property.to_sym] end define_method "#{property}=" do |value| @property_hash[:needs_change] = true 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, :src_range, :destination, :dst_range, :iniface, :outiface, :proto, :isfragment, :tcp_flags, :gid, :uid, :sport, :dport, :port, :dst_type, :src_type, :socket, :pkttype, :name, :state, :icmp, :limit, :burst, :jump, :todest, :tosource, :toports, :log_prefix, :log_level, :reject, :set_mark] def insert debug 'Inserting rule %s' % resource[:name] iptables insert_args end def update debug 'Updating rule %s' % resource[:name] iptables update_args end def delete debug 'Deleting rule %s' % resource[:name] iptables delete_args end def exists? properties[:ensure] != :absent end # Flush the property hash once done. def flush debug("[flush]") if @property_hash.delete(:needs_change) notice("Properties changed - updating rule") update end persist_iptables(self.class.instance_variable_get(:@protocol)) @property_hash.clear end def self.instances debug "[instances]" table = nil rules = [] counter = 1 # String#lines would be nice, but we need to support Ruby 1.8.5 iptables_save.split("\n").each do |line| unless line =~ /^\#\s+|^\:\S+|^COMMIT|^FATAL/ if line =~ /^\*/ table = line.sub(/\*/, "") else if hash = rule_to_hash(line, table, counter) rules << new(hash) counter += 1 end end end end rules end def self.rule_to_hash(line, table, counter) hash = {} keys = [] values = line.dup # These are known booleans that do not take a value, but we want to munge # to true if they exist. known_booleans = [:socket, :isfragment] #################### # PRE-PARSE CLUDGING #################### # --tcp-flags takes two values; we cheat by adding " around it # so it behaves like --comment values = values.sub(/--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1 \2"') # Trick the system for booleans known_booleans.each do |bool| if bool == :socket then values = values.sub(/#{@resource_map[bool]}/, '-m socket true') end if bool == :isfragment then # only replace those -f that are not followed by an l to # distinguish between -f and the '-f' inside of --tcp-flags. values = values.sub(/-f(?!l)(?=.*--comment)/, '-f true') end end ############ # MAIN PARSE ############ # Here we iterate across our values to generate an array of keys @resource_list.reverse.each do |k| resource_map_key = @resource_map[k] [resource_map_key].flatten.each do |opt| if values.slice!(/\s#{opt}/) keys << k break end end end # Manually remove chain values.slice!('-A') keys << :chain # Here we generate the main hash keys.zip(values.scan(/"[^"]*"|\S+/).reverse) { |f, v| hash[f] = v.gsub(/"/, '') } ##################### # POST PARSE CLUDGING ##################### # Normalise all rules to CIDR notation. [:source, :destination].each do |prop| hash[prop] = Puppet::Util::IPCidr.new(hash[prop]).cidr unless hash[prop].nil? end [:dport, :sport, :port, :state].each do |prop| hash[prop] = hash[prop].split(',') if ! hash[prop].nil? end # Convert booleans removing the previous cludge we did known_booleans.each do |bool| if hash[bool] != nil then unless hash[bool] == "true" then raise "Parser error: #{bool} was meant to be a boolean but received value: #{hash[bool]}." end end end # Our type prefers hyphens over colons for ranges so ... # Iterate across all ports replacing colons with hyphens so that ranges match # the types expectations. [:dport, :sport, :port].each do |prop| next unless hash[prop] hash[prop] = hash[prop].collect do |elem| elem.gsub(/:/,'-') end end # States should always be sorted. This ensures that the output from # iptables-save and user supplied resources is consistent. hash[:state] = hash[:state].sort unless hash[:state].nil? # This forces all existing, commentless rules or rules with invalid comments to be moved # to the bottom of the stack. # Puppet-firewall requires that all rules have comments (resource names) and match this # regex and will fail if a rule in iptables does not have a comment. We get around this # by appending a high level if ! hash[:name] num = 9000 + counter hash[:name] = "#{num} #{Digest::MD5.hexdigest(line)}" elsif not /^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/ =~ hash[:name] num = 9000 + counter hash[:name] = "#{num} #{/([[:alpha:][:digit:][:punct:][:space:]]+)/.match(hash[:name])[1]}" end # Iptables defaults to log_level '4', so it is omitted from the output of iptables-save. # If the :jump value is LOG and you don't have a log-level set, we assume it to be '4'. if hash[:jump] == 'LOG' && ! hash[:log_level] hash[:log_level] = '4' end # Iptables defaults to burst '5', so it is ommitted from the output of iptables-save. # If the :limit value is set and you don't have a burst set, we assume it to be '5'. if hash[:limit] && ! hash[:burst] hash[:burst] = '5' end hash[:line] = line hash[:provider] = self.name.to_s hash[:table] = table hash[:ensure] = :present # Munge some vars here ... # Proto should equal 'all' if undefined hash[:proto] = "all" if !hash.include?(:proto) # If the jump parameter is set to one of: ACCEPT, REJECT or DROP then # we should set the action parameter instead. if ['ACCEPT','REJECT','DROP'].include?(hash[:jump]) then hash[:action] = hash[:jump].downcase hash.delete(:jump) end hash end def insert_args args = [] args << ["-I", resource[:chain], insert_order] args << general_args args end def update_args args = [] args << ["-R", resource[:chain], insert_order] args << general_args args end def delete_args # Split into arguments line = properties[:line].gsub(/\-A/, '-D').split(/\s(?=(?:[^"]|"[^"]*")*$)/).map{|v| v.gsub(/"/, '')} line.unshift("-t", properties[:table]) end # This method takes the resource, and attempts to generate the command line # arguments for iptables. def general_args debug "Current resource: %s" % resource.class args = [] resource_list = self.class.instance_variable_get('@resource_list') resource_map = self.class.instance_variable_get('@resource_map') 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 res == :socket then resource_value = nil end if res == :isfragment then resource_value = nil 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(' ') # For sport and dport, convert hyphens to colons since the type # expects hyphens for ranges of ports. if [:sport, :dport, :port].include?(res) then resource_value = resource_value.collect do |elem| elem.gsub(/-/, ':') end end # our tcp_flags takes a single string with comma lists separated # by space # --tcp-flags expects two arguments if res == :tcp_flags one, two = resource_value.split(' ') args << one args << two elsif resource_value.is_a?(Array) args << resource_value.join(',') elsif !resource_value.nil? args << resource_value end end args end def insert_order debug("[insert_order]") rules = [] # Find list of current rules based on chain and table self.class.instances.each do |rule| if rule.chain == resource[:chain].to_s and rule.table == resource[:table].to_s rules << rule.name end end # No rules at all? Just bail now. return 1 if rules.empty? my_rule = resource[:name].to_s rules << my_rule rules.sort.index(my_rule) + 1 end end puppetlabs-firewall-0.4.2/lib/facter/ip6tables_version.rb000644 000765 000024 00000000354 12213700171 024272 0ustar00apenneystaff000000 000000 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 puppetlabs-firewall-0.4.2/lib/facter/iptables_persistent_version.rb000644 000765 000024 00000000674 12213700171 026471 0ustar00apenneystaff000000 000000 Facter.add(:iptables_persistent_version) do confine :operatingsystem => %w{Debian Ubuntu} setcode do # Throw away STDERR because dpkg >= 1.16.7 will make some noise if the # package isn't currently installed. cmd = "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" version = Facter::Util::Resolution.exec(cmd) if version.nil? or !version.match(/\d+\.\d+/) nil else version end end end puppetlabs-firewall-0.4.2/lib/facter/iptables_version.rb000644 000765 000024 00000000352 12213700171 024202 0ustar00apenneystaff000000 000000 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