Initial Chef repository
This commit is contained in:
121
cookbooks/firewall/CHANGELOG.md
Normal file
121
cookbooks/firewall/CHANGELOG.md
Normal file
@@ -0,0 +1,121 @@
|
||||
firewall Cookbook CHANGELOG
|
||||
=======================
|
||||
This file is used to list changes made in each version of the firewall cookbook.
|
||||
|
||||
v1.2.0 (2015-05-28)
|
||||
-------------------
|
||||
* #64 - Support the newer version of poise
|
||||
|
||||
v1.1.2 (2015-05-19)
|
||||
-------------------
|
||||
* #60 - Always add /32 or /128 to ipv4 or ipv6 addresses, respectively.
|
||||
- Make comment quoting optional; iptables on Ubuntu strips quotes on strings without any spaces
|
||||
|
||||
v1.1.1 (2015-05-11)
|
||||
-------------------
|
||||
* #57 - Suppress warning: already initialized constant XXX while Chefspec
|
||||
|
||||
v1.1.0 (2015-04-27)
|
||||
-------------------
|
||||
* #56 - Better ipv6 support for firewalld and iptables
|
||||
* #54 - Document raw parameter
|
||||
|
||||
v1.0.2 (2015-04-03)
|
||||
-------------------
|
||||
* #52 - Typo in :masquerade action name
|
||||
|
||||
v1.0.1 (2015-03-28)
|
||||
-------------------
|
||||
* #49 - Fix position attribute of firewall_rule providers to be correctly used as a string in commands
|
||||
|
||||
v1.0.0 (2015-03-25)
|
||||
-------------------
|
||||
* Major upgrade and rewrite as HWRP using poise
|
||||
* Adds support for iptables and firewalld
|
||||
* Modernize tests and other files
|
||||
* Fix many bugs from ufw defaults to multiport suppot
|
||||
|
||||
v0.11.8 (2014-05-20)
|
||||
--------------------
|
||||
* Corrects issue where on a secondary converge would not distinguish between inbound and outbound rules
|
||||
|
||||
|
||||
v0.11.6 (2014-02-28)
|
||||
--------------------
|
||||
[COOK-4385] - UFW provider is broken
|
||||
|
||||
|
||||
v0.11.4 (2014-02-25)
|
||||
--------------------
|
||||
[COOK-4140] Only notify when a rule is actually added
|
||||
|
||||
|
||||
v0.11.2
|
||||
-------
|
||||
### Bug
|
||||
- **[COOK-3615](https://tickets.opscode.com/browse/COOK-3615)** - Install required UFW package on Debian
|
||||
|
||||
v0.11.0
|
||||
-------
|
||||
### Improvement
|
||||
- [COOK-2932]: ufw providers work on debian but cannot be used
|
||||
|
||||
v0.10.2
|
||||
-------
|
||||
- [COOK-2250] - improve readme
|
||||
|
||||
v0.10.0
|
||||
------
|
||||
- [COOK-1234] - allow multiple ports per rule
|
||||
|
||||
v0.9.2
|
||||
------
|
||||
- [COOK-1615] - Firewall example docs have incorrect direction syntax
|
||||
|
||||
v0.9.0
|
||||
------
|
||||
The default action for firewall LWRP is now :enable, the default action for firewall_rule LWRP is now :reject. This is in line with a "default deny" policy.
|
||||
|
||||
- [COOK-1429] - resolve foodcritic warnings
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
- refactor all resources and providers into LWRPs
|
||||
- removed :reset action from firewall resource (couldn't find a good way to make it idempotent)
|
||||
- removed :logging action from firewall resource...just set desired level via the log_level attribute
|
||||
|
||||
v0.6.0
|
||||
------
|
||||
- [COOK-725] Firewall cookbook firewall_rule LWRP needs to support logging attribute.
|
||||
- Firewall cookbook firewall LWRP needs to support :logging
|
||||
|
||||
v0.5.7
|
||||
------
|
||||
- [COOK-696] Firewall cookbook firewall_rule LWRP needs to support interface
|
||||
- [COOK-697] Firewall cookbook firewall_rule LWRP needs to support the direction for the rules
|
||||
|
||||
v0.5.6
|
||||
------
|
||||
- [COOK-695] Firewall cookbook firewall_rule LWRP needs to support destination port
|
||||
|
||||
v0.5.5
|
||||
------
|
||||
- [COOK-709] fixed :nothing action for the 'firewall_rule' resource.
|
||||
|
||||
v0.5.4
|
||||
------
|
||||
- [COOK-694] added :reject action to the 'firewall_rule' resource.
|
||||
|
||||
v0.5.3
|
||||
------
|
||||
- [COOK-698] added :reset action to the 'firewall' resource.
|
||||
|
||||
v0.5.2
|
||||
------
|
||||
- Add missing 'requires' statements. fixes 'NameError: uninitialized constant' error.
|
||||
thanks to Ernad Husremović for the fix.
|
||||
|
||||
v0.5.0
|
||||
------
|
||||
- [COOK-686] create firewall and firewall_rule resources
|
||||
- [COOK-687] create UFW providers for all resources
|
||||
189
cookbooks/firewall/README.md
Normal file
189
cookbooks/firewall/README.md
Normal file
@@ -0,0 +1,189 @@
|
||||
firewall Cookbook
|
||||
=================
|
||||
[](http://travis-ci.org/opscode-cookbooks/firewall)
|
||||
|
||||
Provides a set of primitives for managing firewalls and associated rules.
|
||||
|
||||
PLEASE NOTE - The resource/providers in this cookbook are under heavy development. An attempt is being made to keep the resource simple/stupid by starting with less sophisticated firewall implementations first and refactor/vet the resource definition with each successive provider.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
### Platform
|
||||
* Ubuntu
|
||||
* Debian
|
||||
* Redhat
|
||||
* CentOS
|
||||
|
||||
Tested on:
|
||||
* Ubuntu 12.04
|
||||
* Ubuntu 14.04
|
||||
* Debian 7.8
|
||||
* CentOS 6.5
|
||||
* CentOS 7.0
|
||||
|
||||
|
||||
Recipes
|
||||
-------
|
||||
### default
|
||||
The default recipe creates a firewall resource with action install, and if `node['firewall']['allow_ssh']`, opens port 22 from the world.
|
||||
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
||||
* `default['firewall']['ufw']['defaults']` hash for template `/etc/default/ufw`
|
||||
|
||||
Resources/Providers
|
||||
-------------------
|
||||
- See `librariez/z_provider_mapping.rb` for a full list of providers for each platform and version.
|
||||
|
||||
### firewall
|
||||
#### Actions
|
||||
- `:enable`: *Default action* enable the firewall. this will make any rules that have been defined 'active'.
|
||||
- `:disable`: disable the firewall. drop any rules and put the node in an unprotected state.
|
||||
- `:flush`: Runs `iptables -F`. Only supported by the iptables firewall provider.
|
||||
- `:save`: Runs `service iptables save` under iptables, adds rules permanently under firewall. Not supported in ufw.
|
||||
|
||||
#### Attribute Parameters
|
||||
- name: name attribute. arbitrary name to uniquely identify this resource
|
||||
- log_level: level of verbosity the firewall should log at. valid values are: :low, :medium, :high, :full. default is :low.
|
||||
|
||||
#### Examples
|
||||
|
||||
```ruby
|
||||
# enable platform default firewall
|
||||
firewall 'ufw' do
|
||||
action :enable
|
||||
end
|
||||
|
||||
# increase logging past default of 'low'
|
||||
firewall 'debug firewalls' do
|
||||
log_level :high
|
||||
action :enable
|
||||
end
|
||||
```
|
||||
|
||||
### firewall_rule
|
||||
|
||||
#### Actions
|
||||
- `:allow`: the rule should allow incoming traffic.
|
||||
- `:deny`: the rule should deny incoming traffic.
|
||||
- `:reject`: *Default action: the rule should reject incoming traffic.
|
||||
- `:masqerade`: Add masqerade rule
|
||||
- `:redirect`: Add redirect-type rule
|
||||
- `:log`: Configure logging
|
||||
- `:remove`: Remove all rules
|
||||
|
||||
#### Attribute Parameters
|
||||
- name: name attribute. arbitrary name to uniquely identify this firewall rule
|
||||
- protocol: valid values are: :udp, :tcp. default is all protocols
|
||||
- port: incoming port number (ie. 22 to allow inbound SSH), or an array of incoming port numbers (ie. [80,443] to allow inbound HTTP & HTTPS). NOTE: `protocol` attribute is required with multiple ports, or a range of incoming port numbers (ie. 60000..61000 to allow inbound mobile-shell. NOTE: `protocol`, or an attribute is required with a range of ports.
|
||||
- source: ip address or subnet to filter on incoming traffic. default is `0.0.0.0/0` (ie Anywhere)
|
||||
- destination: ip address or subnet to filter on outgoing traffic.
|
||||
- dest_port: outgoing port number.
|
||||
- position: position to insert rule at. if not provided rule is inserted at the end of the rule list.
|
||||
- direction: direction of the rule. valid values are: :in, :out, default is :in
|
||||
- interface: interface to apply rule (ie. 'eth0').
|
||||
- logging: may be added to enable logging for a particular rule. valid values are: :connections, :packets. In the ufw provider, :connections logs new connections while :packets logs all packets.
|
||||
- raw: for passing a raw command to the provider (for use with custom modules, also used by zap provider to clean up non-chef managed rules)
|
||||
|
||||
#### Examples
|
||||
|
||||
```ruby
|
||||
# open standard ssh port, enable firewall
|
||||
firewall_rule 'ssh' do
|
||||
port 22
|
||||
action :allow
|
||||
notifies :enable, 'firewall[ufw]'
|
||||
end
|
||||
|
||||
# open standard http port to tcp traffic only; insert as first rule
|
||||
firewall_rule 'http' do
|
||||
port 80
|
||||
protocol :tcp
|
||||
position 1
|
||||
action :allow
|
||||
end
|
||||
|
||||
# restrict port 13579 to 10.0.111.0/24 on eth0
|
||||
firewall_rule 'myapplication' do
|
||||
port 13579
|
||||
source '10.0.111.0/24'
|
||||
direction :in
|
||||
interface 'eth0'
|
||||
action :allow
|
||||
end
|
||||
|
||||
# open UDP ports 60000..61000 for mobile shell (mosh.mit.edu), note
|
||||
# that the protocol attribute is required when using port_range
|
||||
firewall_rule 'mosh' do
|
||||
protocol :udp
|
||||
port 60000..61000
|
||||
action :allow
|
||||
end
|
||||
|
||||
# open multiple ports for http/https, note that the protocol
|
||||
# attribute is required when using ports
|
||||
firewall_rule 'http/https' do
|
||||
protocol :tcp
|
||||
port [80, 443]
|
||||
action :allow
|
||||
end
|
||||
|
||||
firewall 'ufw' do
|
||||
action :nothing
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
This section details "quick development" steps. For a detailed explanation, see [[Contributing.md]].
|
||||
|
||||
1. Clone this repository from GitHub:
|
||||
|
||||
$ git clone git@github.com:opscode-cookbooks/firewall.git
|
||||
|
||||
2. Create a git branch
|
||||
|
||||
$ git checkout -b my_bug_fix
|
||||
|
||||
3. Install dependencies:
|
||||
|
||||
$ bundle install
|
||||
|
||||
4. Make your changes/patches/fixes, committing appropiately
|
||||
5. **Write tests**
|
||||
6. Run the tests:
|
||||
- `bundle exec foodcritic -f any .`
|
||||
- `bundle exec rspec`
|
||||
- `bundle exec rubocop`
|
||||
- `bundle exec kitchen test`
|
||||
|
||||
In detail:
|
||||
- Foodcritic will catch any Chef-specific style errors
|
||||
- RSpec will run the unit tests
|
||||
- Rubocop will check for Ruby-specific style errors
|
||||
- Test Kitchen will run and converge the recipes
|
||||
|
||||
|
||||
License & Authors
|
||||
-----------------
|
||||
- Author:: Seth Chisamore (<schisamo@opscode.com>)
|
||||
|
||||
```text
|
||||
Copyright:: Copyright (c) 2011-2015 Opscode, Inc.
|
||||
|
||||
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.
|
||||
```
|
||||
1
cookbooks/firewall/attributes/default.rb
Normal file
1
cookbooks/firewall/attributes/default.rb
Normal file
@@ -0,0 +1 @@
|
||||
default['firewall']['allow_ssh'] = false
|
||||
12
cookbooks/firewall/attributes/ufw.rb
Normal file
12
cookbooks/firewall/attributes/ufw.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
default['firewall']['ufw']['defaults'] = {
|
||||
:ipv6 => 'yes',
|
||||
:manage_builtins => 'no',
|
||||
:ipt_sysctl => '/etc/ufw/sysctl.conf',
|
||||
:ipt_modules => 'nf_conntrack_ftp nf_nat_ftp nf_conntrack_netbios_ns',
|
||||
:policy => {
|
||||
:input => 'DROP',
|
||||
:output => 'ACCEPT',
|
||||
:forward => 'DROP',
|
||||
:application => 'SKIP'
|
||||
}
|
||||
}
|
||||
13
cookbooks/firewall/libraries/helpers.rb
Normal file
13
cookbooks/firewall/libraries/helpers.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module FirewallCookbook
|
||||
module Helpers
|
||||
def port_to_s(p)
|
||||
if p && p.is_a?(Integer)
|
||||
p.to_s
|
||||
elsif p && p.is_a?(Array)
|
||||
p.join(',')
|
||||
elsif p && p.is_a?(Range)
|
||||
"#{p.first}:#{p.last} "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
32
cookbooks/firewall/libraries/matchers.rb
Normal file
32
cookbooks/firewall/libraries/matchers.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
if defined?(ChefSpec)
|
||||
ChefSpec.define_matcher(:firewall)
|
||||
ChefSpec.define_matcher(:firewall_rule)
|
||||
|
||||
def enable_firewall(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall, :enable, resource)
|
||||
end
|
||||
|
||||
def disable_firewall(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall, :disable, resource)
|
||||
end
|
||||
|
||||
def allow_firewall_rule(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :allow, resource)
|
||||
end
|
||||
|
||||
def deny_firewall_rule(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :deny, resource)
|
||||
end
|
||||
|
||||
def reject_firewall_rule(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :reject, resource)
|
||||
end
|
||||
|
||||
def log_firewall_rule(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :log, resource)
|
||||
end
|
||||
|
||||
def remove_firewall_rule(resource)
|
||||
ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :remove, resource)
|
||||
end
|
||||
end
|
||||
92
cookbooks/firewall/libraries/provider_firewall_firewalld.rb
Normal file
92
cookbooks/firewall/libraries/provider_firewall_firewalld.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
#
|
||||
# Author:: Ronald Doorn (<rdoorn@schubergphilis.com>)
|
||||
# Cookbook Name:: firewall
|
||||
# Resource:: default
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallFirewalld < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def action_enable
|
||||
# prints all the firewall rules
|
||||
# pp @new_resource.subresources
|
||||
log_current_firewalld
|
||||
if active?
|
||||
Chef::Log.debug("#{@new_resource} already enabled.")
|
||||
else
|
||||
Chef::Log.debug("#{@new_resource} is about to be enabled")
|
||||
shell_out!('service', 'firewalld', 'start')
|
||||
shell_out!('firewall-cmd', '--set-default-zone=drop')
|
||||
Chef::Log.info("#{@new_resource} enabled.")
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
|
||||
def action_disable
|
||||
if active?
|
||||
shell_out!('firewall-cmd', '--set-default-zone=public')
|
||||
shell_out!('firewall-cmd', '--direct', '--remove-rules', 'ipv4', 'filter', 'INPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--remove-rules', 'ipv4', 'filter', 'OUTPUT')
|
||||
Chef::Log.info("#{@new_resource} disabled")
|
||||
new_resource.updated_by_last_action(true)
|
||||
else
|
||||
Chef::Log.debug("#{@new_resource} already disabled.")
|
||||
end
|
||||
end
|
||||
|
||||
def action_flush
|
||||
shell_out!('firewall-cmd', '--direct', '--remove-rules', 'ipv4', 'filter', 'INPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--remove-rules', 'ipv4', 'filter', 'OUTPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--permanent', '--remove-rules', 'ipv4', 'filter', 'INPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--permanent', '--remove-rules', 'ipv4', 'filter', 'OUTPUT')
|
||||
Chef::Log.info("#{@new_resource} flushed.")
|
||||
end
|
||||
|
||||
def action_save
|
||||
if shell_out!('firewall-cmd', '--direct', '--get-all-rules').stdout != shell_out!('firewall-cmd', '--direct', '--permanent', '--get-all-rules').stdout
|
||||
shell_out!('firewall-cmd', '--direct', '--permanent', '--remove-rules', 'ipv4', 'filter', 'INPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--permanent', '--remove-rules', 'ipv4', 'filter', 'OUTPUT')
|
||||
shell_out!('firewall-cmd', '--direct', '--get-all-rules').stdout.lines do |line|
|
||||
shell_out!("firewall-cmd --direct --permanent --add-rule #{line}")
|
||||
end
|
||||
Chef::Log.info("#{@new_resource} saved.")
|
||||
new_resource.updated_by_last_action(true)
|
||||
else
|
||||
Chef::Log.info("#{@new_resource} already up-to-date.")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active?
|
||||
@active ||= begin
|
||||
cmd = shell_out('firewall-cmd', '--state')
|
||||
cmd.stdout =~ /^running$/
|
||||
end
|
||||
end
|
||||
|
||||
def log_current_firewalld
|
||||
cmdstr = 'firewall-cmd --direct --get-all-rules'
|
||||
Chef::Log.info("#{@new_resource} log_current_firewalld (#{cmdstr}):")
|
||||
cmd = shell_out!(cmdstr)
|
||||
Chef::Log.info(cmd.inspect)
|
||||
rescue
|
||||
Chef::Log.info("#{@new_resource} log_current_firewalld failed!")
|
||||
end
|
||||
end
|
||||
end
|
||||
110
cookbooks/firewall/libraries/provider_firewall_iptables.rb
Normal file
110
cookbooks/firewall/libraries/provider_firewall_iptables.rb
Normal file
@@ -0,0 +1,110 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
||||
# Cookbook Name:: firewall
|
||||
# Resource:: default
|
||||
#
|
||||
# Copyright:: 2011, Opscode, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallIptables < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def action_enable
|
||||
converge_by('install package iptables and default DROP if no rules exist') do
|
||||
package 'iptables' do
|
||||
action :install
|
||||
end
|
||||
|
||||
# prints all the firewall rules
|
||||
# pp new_resource.subresources
|
||||
log_current_iptables
|
||||
if active?
|
||||
Chef::Log.info("#{new_resource} already enabled.")
|
||||
else
|
||||
Chef::Log.debug("#{new_resource} is about to be enabled")
|
||||
shell_out!('iptables -P INPUT DROP')
|
||||
shell_out!('iptables -P OUTPUT DROP')
|
||||
shell_out!('iptables -P FORWARD DROP')
|
||||
|
||||
shell_out!('ip6tables -P INPUT DROP')
|
||||
shell_out!('ip6tables -P OUTPUT DROP')
|
||||
shell_out!('ip6tables -P FORWARD DROP')
|
||||
Chef::Log.info("#{new_resource} enabled.")
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def action_disable
|
||||
if active?
|
||||
shell_out!('iptables -P INPUT ACCEPT')
|
||||
shell_out!('iptables -P OUTPUT ACCEPT')
|
||||
shell_out!('iptables -P FORWARD ACCEPT')
|
||||
shell_out!('iptables -F')
|
||||
|
||||
shell_out!('ip6tables -P INPUT ACCEPT')
|
||||
shell_out!('ip6tables -P OUTPUT ACCEPT')
|
||||
shell_out!('ip6tables -P FORWARD ACCEPT')
|
||||
shell_out!('ip6tables -F')
|
||||
Chef::Log.info("#{new_resource} disabled")
|
||||
new_resource.updated_by_last_action(true)
|
||||
else
|
||||
Chef::Log.debug("#{new_resource} already disabled.")
|
||||
end
|
||||
end
|
||||
|
||||
def action_flush
|
||||
shell_out!('iptables -F')
|
||||
shell_out!('ip6tables -F')
|
||||
Chef::Log.info("#{new_resource} flushed.")
|
||||
end
|
||||
|
||||
def action_save
|
||||
shell_out!('service iptables save')
|
||||
shell_out!('service ip6tables save')
|
||||
Chef::Log.info("#{new_resource} saved.")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active?
|
||||
@active ||= begin
|
||||
cmd = shell_out!('iptables-save')
|
||||
cmd.stdout =~ /INPUT ACCEPT/
|
||||
end
|
||||
@active_v6 ||= begin
|
||||
cmd = shell_out!('ip6tables-save')
|
||||
cmd.stdout =~ /INPUT ACCEPT/
|
||||
end
|
||||
@active && @active_v6
|
||||
end
|
||||
|
||||
def log_current_iptables
|
||||
cmdstr = 'iptables -L'
|
||||
Chef::Log.info("#{new_resource} log_current_iptables (#{cmdstr}):")
|
||||
cmd = shell_out!(cmdstr)
|
||||
Chef::Log.info(cmd.inspect)
|
||||
cmdstr = 'ip6tables -L'
|
||||
Chef::Log.info("#{new_resource} log_current_iptables (#{cmdstr}):")
|
||||
cmd = shell_out!(cmdstr)
|
||||
Chef::Log.info(cmd.inspect)
|
||||
rescue
|
||||
Chef::Log.info("#{new_resource} log_current_iptables failed!")
|
||||
end
|
||||
end
|
||||
end
|
||||
213
cookbooks/firewall/libraries/provider_firewall_rule_firewalld.rb
Normal file
213
cookbooks/firewall/libraries/provider_firewall_rule_firewalld.rb
Normal file
@@ -0,0 +1,213 @@
|
||||
#
|
||||
# Author:: Ronald Doorn (<rdoorn@schubergphilis.com>)
|
||||
# Cookbook Name:: firewall
|
||||
# Provider:: rule_iptables
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallRuleFirewalld < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
include FirewallCookbook::Helpers
|
||||
|
||||
def action_allow
|
||||
apply_rule(:allow)
|
||||
end
|
||||
|
||||
def action_deny
|
||||
apply_rule(:deny)
|
||||
end
|
||||
|
||||
def action_reject
|
||||
apply_rule(:reject)
|
||||
end
|
||||
|
||||
def action_redirect
|
||||
apply_rule(:redirect)
|
||||
end
|
||||
|
||||
def action_masquerade
|
||||
apply_rule(:masquerade)
|
||||
end
|
||||
|
||||
def action_log
|
||||
apply_rule(:log)
|
||||
end
|
||||
|
||||
def action_remove
|
||||
# TODO: specify which target to delete
|
||||
# for now this will remove raw + all targeted lines
|
||||
remove_rule(:allow)
|
||||
remove_rule(:deny)
|
||||
remove_rule(:reject)
|
||||
remove_rule(:redirect)
|
||||
remove_rule(:masquerade)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CHAIN = { :in => 'INPUT', :out => 'OUTPUT', :pre => 'PREROUTING', :post => 'POSTROUTING' } unless defined? CHAIN # , nil => "FORWARD"}
|
||||
TARGET = { :allow => 'ACCEPT', :reject => 'REJECT', :deny => 'DROP', :masquerade => 'MASQUERADE', :redirect => 'REDIRECT', :log => 'LOG --log-prefix \'iptables: \' --log-level 7' } unless defined? TARGET
|
||||
|
||||
def apply_rule(type = nil)
|
||||
ip_versions.each do |ip_version|
|
||||
firewall_command = 'firewall-cmd --direct --add-rule '
|
||||
|
||||
# TODO: implement logging for :connections :packets
|
||||
firewall_rule = build_firewall_rule(type, ip_version)
|
||||
|
||||
Chef::Log.debug("#{new_resource}: #{firewall_rule}")
|
||||
if rule_exists?(firewall_rule)
|
||||
Chef::Log.info("#{new_resource} #{type} rule exists... won't apply")
|
||||
else
|
||||
cmdstr = firewall_command + firewall_rule
|
||||
converge_by("firewall_rule[#{new_resource.name}] #{firewall_rule}") do
|
||||
notifying_block do
|
||||
shell_out!(cmdstr) # shell_out! is already logged
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_rule(type = nil)
|
||||
ip_versions.each do |_ip_version|
|
||||
firewall_command = 'firewall-cmd --direct --remove-rule '
|
||||
|
||||
# TODO: implement logging for :connections :packets
|
||||
firewall_rule = build_firewall_rule(type)
|
||||
|
||||
Chef::Log.debug("#{new_resource}: #{firewall_rule}")
|
||||
if rule_exists?(firewall_rule)
|
||||
cmdstr = firewall_command + firewall_rule
|
||||
converge_by("firewall_rule[#{new_resource.name}] #{firewall_rule}") do
|
||||
notifying_block do
|
||||
shell_out!(cmdstr) # shell_out! is already logged
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
else
|
||||
Chef::Log.info("#{new_resource} #{type} rule does not exists... won't remove")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ipv4_rule?
|
||||
if (new_resource.source && IPAddr.new(new_resource.source).ipv4?) ||
|
||||
(new_resource.destination && IPAddr.new(new_resource.destination).ipv4?)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ipv6_rule?
|
||||
if (new_resource.source && IPAddr.new(new_resource.source).ipv6?) ||
|
||||
(new_resource.destination && IPAddr.new(new_resource.destination).ipv6?)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ip_versions
|
||||
if ipv4_rule?
|
||||
versions = ['ipv4']
|
||||
elsif ipv6_rule?
|
||||
versions = ['ipv6']
|
||||
else # no source or destination address, add rules for both ipv4 and ipv6
|
||||
versions = %w(ipv4 ipv6)
|
||||
end
|
||||
versions
|
||||
end
|
||||
|
||||
def build_firewall_rule(type = nil, ip_version = 'ipv4')
|
||||
if new_resource.raw
|
||||
firewall_rule = new_resource.raw.strip
|
||||
else
|
||||
firewall_rule = "#{ip_version} filter "
|
||||
if new_resource.direction
|
||||
firewall_rule << "#{CHAIN[new_resource.direction.to_sym]} "
|
||||
else
|
||||
firewall_rule << 'FORWARD '
|
||||
end
|
||||
firewall_rule << "#{new_resource.position ? new_resource.position : 1} "
|
||||
|
||||
if [:pre, :post].include?(new_resource.direction)
|
||||
firewall_rule << '-t nat '
|
||||
end
|
||||
|
||||
# Firewalld order of prameters is important here see example output below:
|
||||
# ipv4 filter INPUT 1 -s 1.2.3.4/32 -d 5.6.7.8/32 -i lo -p tcp -m tcp -m state --state NEW -m comment --comment "hello" -j DROP
|
||||
firewall_rule << "-s #{ip_with_mask(new_resource.source)} " if new_resource.source && new_resource.source != '0.0.0.0/0'
|
||||
firewall_rule << "-d #{new_resource.destination} " if new_resource.destination
|
||||
|
||||
firewall_rule << "-i #{new_resource.interface} " if new_resource.interface
|
||||
firewall_rule << "-o #{new_resource.dest_interface} " if new_resource.dest_interface
|
||||
|
||||
firewall_rule << "-p #{new_resource.protocol} " if new_resource.protocol
|
||||
firewall_rule << '-m tcp ' if new_resource.protocol.to_sym == :tcp
|
||||
|
||||
# using multiport here allows us to simplify our greps and rule building
|
||||
firewall_rule << "-m multiport --sports #{port_to_s(new_resource.source_port)} " if new_resource.source_port
|
||||
firewall_rule << "-m multiport --dports #{port_to_s(dport_calc)} " if dport_calc
|
||||
|
||||
firewall_rule << "-m state --state #{new_resource.stateful.is_a?(Array) ? new_resource.stateful.join(',').upcase : new_resource.stateful.upcase} " if new_resource.stateful
|
||||
firewall_rule << "-m comment --comment '#{new_resource.description}' "
|
||||
firewall_rule << "-j #{TARGET[type]} "
|
||||
firewall_rule << "--to-ports #{new_resource.redirect_port} " if type == 'redirect'
|
||||
firewall_rule.strip!
|
||||
end
|
||||
firewall_rule
|
||||
end
|
||||
|
||||
def rule_exists?(rule)
|
||||
fail 'no rule supplied' unless rule
|
||||
|
||||
# match quotes generously
|
||||
detect_rule = rule.gsub(/'/, "'*")
|
||||
detect_rule = detect_rule.gsub(/"/, '"*')
|
||||
|
||||
match = shell_out!('firewall-cmd --direct --get-all-rules').stdout.lines.find do |line|
|
||||
# Chef::Log.debug("matching: [#{detect_rule}] to [#{line.chomp.rstrip}]")
|
||||
line =~ /#{detect_rule}/
|
||||
end
|
||||
|
||||
match
|
||||
rescue Mixlib::ShellOut::ShellCommandFailed
|
||||
Chef::Log.debug("#{new_resource} check fails with: " + match.inspect)
|
||||
Chef::Log.debug("#{new_resource} assuming #{rule} rule does not exist")
|
||||
false
|
||||
end
|
||||
|
||||
def dport_calc
|
||||
new_resource.dest_port || new_resource.port
|
||||
end
|
||||
|
||||
def ip_with_mask(ip)
|
||||
if ip.include?('/')
|
||||
ip
|
||||
elsif ipv4_rule?
|
||||
"#{ip}/32"
|
||||
elsif ipv6_rule?
|
||||
"#{ip}/128"
|
||||
else
|
||||
ip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
231
cookbooks/firewall/libraries/provider_firewall_rule_iptables.rb
Normal file
231
cookbooks/firewall/libraries/provider_firewall_rule_iptables.rb
Normal file
@@ -0,0 +1,231 @@
|
||||
#
|
||||
# Cookbook Name:: firewall
|
||||
# Provider:: rule_iptables
|
||||
#
|
||||
# Copyright 2012, computerlyrik
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallRuleIptables < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
include FirewallCookbook::Helpers
|
||||
|
||||
def action_allow
|
||||
apply_rule(:allow)
|
||||
end
|
||||
|
||||
def action_deny
|
||||
apply_rule(:deny)
|
||||
end
|
||||
|
||||
def action_reject
|
||||
apply_rule(:reject)
|
||||
end
|
||||
|
||||
def action_redirect
|
||||
apply_rule(:redirect)
|
||||
end
|
||||
|
||||
def action_masquerade
|
||||
apply_rule(:masquerade)
|
||||
end
|
||||
|
||||
def action_log
|
||||
apply_rule(:log)
|
||||
end
|
||||
|
||||
def action_remove
|
||||
# TODO: specify which target to delete
|
||||
# for now this will remove raw + all targeted lines
|
||||
remove_rule(:allow)
|
||||
remove_rule(:deny)
|
||||
remove_rule(:reject)
|
||||
remove_rule(:redirect)
|
||||
remove_rule(:masquerade)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CHAIN = { :in => 'INPUT', :out => 'OUTPUT', :pre => 'PREROUTING', :post => 'POSTROUTING' } unless defined? CHAIN # , nil => "FORWARD"}
|
||||
TARGET = { :allow => 'ACCEPT', :reject => 'REJECT', :deny => 'DROP', :masquerade => 'MASQUERADE', :redirect => 'REDIRECT', :log => 'LOG --log-prefix "iptables: " --log-level 7' } unless defined? TARGET
|
||||
|
||||
def apply_rule(type = nil)
|
||||
firewall_commands = determine_iptables_commands
|
||||
firewall_commands.each do |firewall_command|
|
||||
ipv6 = (firewall_command == 'ip6tables') ? true : false
|
||||
if new_resource.position
|
||||
firewall_command << ' -I '
|
||||
else
|
||||
firewall_command << ' -A '
|
||||
end
|
||||
|
||||
# TODO: implement logging for :connections :packets
|
||||
firewall_rule = build_firewall_rule(type)
|
||||
|
||||
Chef::Log.debug("#{new_resource}: #{firewall_rule}")
|
||||
if rule_exists?(firewall_rule, ipv6)
|
||||
Chef::Log.info("#{new_resource} #{type} rule exists... won't apply")
|
||||
else
|
||||
cmdstr = firewall_command + firewall_rule
|
||||
converge_by("firewall_rule[#{new_resource.name}] #{firewall_rule}") do
|
||||
notifying_block do
|
||||
shell_out!(cmdstr) # shell_out! is already logged
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_rule(type = nil)
|
||||
firewall_commands = determine_iptables_commands
|
||||
firewall_commands.each do |firewall_command|
|
||||
ipv6 = (firewall_command == 'ip6tables') ? true : false
|
||||
|
||||
# TODO: implement logging for :connections :packets
|
||||
firewall_rule = build_firewall_rule(type)
|
||||
|
||||
Chef::Log.debug("#{new_resource}: #{firewall_rule}")
|
||||
if rule_exists?(firewall_rule, ipv6)
|
||||
cmdstr = firewall_command + firewall_rule
|
||||
converge_by("firewall_rule[#{new_resource.name}] #{firewall_rule}") do
|
||||
notifying_block do
|
||||
shell_out!(cmdstr) # shell_out! is already logged
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
else
|
||||
Chef::Log.info("#{new_resource} #{type} rule does not exist... won't remove")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ipv4_rule?
|
||||
if (new_resource.source && IPAddr.new(new_resource.source).ipv4?) ||
|
||||
(new_resource.destination && IPAddr.new(new_resource.destination).ipv4?)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ipv6_rule?
|
||||
if (new_resource.source && IPAddr.new(new_resource.source).ipv6?) ||
|
||||
(new_resource.destination && IPAddr.new(new_resource.destination).ipv6?)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def determine_iptables_commands
|
||||
if ipv4_rule?
|
||||
commands = ['iptables']
|
||||
elsif ipv6_rule?
|
||||
commands = ['ip6tables']
|
||||
else # no source or destination address, add rules for both ipv4 and ipv6
|
||||
commands = %w(iptables ip6tables)
|
||||
end
|
||||
commands
|
||||
end
|
||||
|
||||
def build_firewall_rule(type = nil)
|
||||
if new_resource.raw
|
||||
firewall_rule = new_resource.raw.strip
|
||||
else
|
||||
firewall_rule = ''
|
||||
if new_resource.direction
|
||||
firewall_rule << "#{CHAIN[new_resource.direction.to_sym]} "
|
||||
else
|
||||
firewall_rule << 'FORWARD '
|
||||
end
|
||||
firewall_rule << "#{new_resource.position} " if new_resource.position
|
||||
|
||||
if [:pre, :post].include?(new_resource.direction)
|
||||
firewall_rule << '-t nat '
|
||||
end
|
||||
|
||||
# Iptables order of prameters is important here see example output below:
|
||||
# -A INPUT -s 1.2.3.4/32 -d 5.6.7.8/32 -i lo -p tcp -m tcp -m state --state NEW -m comment --comment "hello" -j DROP
|
||||
firewall_rule << "-s #{ip_with_mask(new_resource.source)} " if new_resource.source && new_resource.source != '0.0.0.0/0'
|
||||
firewall_rule << "-d #{new_resource.destination} " if new_resource.destination
|
||||
|
||||
firewall_rule << "-i #{new_resource.interface} " if new_resource.interface
|
||||
firewall_rule << "-o #{new_resource.dest_interface} " if new_resource.dest_interface
|
||||
|
||||
firewall_rule << "-p #{new_resource.protocol} " if new_resource.protocol
|
||||
firewall_rule << '-m tcp ' if new_resource.protocol.to_sym == :tcp
|
||||
|
||||
# using multiport here allows us to simplify our greps and rule building
|
||||
firewall_rule << "-m multiport --sports #{port_to_s(new_resource.source_port)} " if new_resource.source_port
|
||||
firewall_rule << "-m multiport --dports #{port_to_s(dport_calc)} " if dport_calc
|
||||
|
||||
firewall_rule << "-m state --state #{new_resource.stateful.is_a?(Array) ? new_resource.stateful.join(',').upcase : new_resource.stateful.upcase} " if new_resource.stateful
|
||||
firewall_rule << "-m comment --comment \"#{new_resource.description}\" "
|
||||
firewall_rule << "-j #{TARGET[type]} "
|
||||
firewall_rule << "--to-ports #{new_resource.redirect_port} " if type == 'redirect'
|
||||
firewall_rule.strip!
|
||||
end
|
||||
firewall_rule
|
||||
end
|
||||
|
||||
def rule_exists?(rule, ipv6 = false)
|
||||
fail 'no rule supplied' unless rule
|
||||
if new_resource.position
|
||||
detect_rule = rule.gsub(/#{CHAIN[new_resource.direction]}\s(\d+)/, '\1' + " -A #{CHAIN[new_resource.direction]}")
|
||||
else
|
||||
detect_rule = rule
|
||||
end
|
||||
|
||||
# match quotes generously
|
||||
detect_rule = detect_rule.gsub(/'/, "'*")
|
||||
detect_rule = detect_rule.gsub(/"/, '"*')
|
||||
|
||||
line_number = 0
|
||||
match = shell_out!(ipv6 ? 'ip6tables-save' : 'iptables-save').stdout.lines.find do |line|
|
||||
next if line !~ /#{CHAIN[new_resource.direction]}/
|
||||
next if line[0] != '-'
|
||||
line_number += 1
|
||||
line = "#{line_number} #{line}" if new_resource.position
|
||||
# Chef::Log.debug("matching: [#{detect_rule}] to [#{line.chomp.rstrip}]")
|
||||
line =~ /#{detect_rule}/
|
||||
end
|
||||
|
||||
match
|
||||
rescue Mixlib::ShellOut::ShellCommandFailed
|
||||
Chef::Log.debug("#{new_resource} check fails with: " + match.inspect)
|
||||
Chef::Log.debug("#{new_resource} assuming #{rule} rule does not exist")
|
||||
false
|
||||
end
|
||||
|
||||
def dport_calc
|
||||
new_resource.dest_port || new_resource.port
|
||||
end
|
||||
|
||||
def ip_with_mask(ip)
|
||||
if ip.include?('/')
|
||||
ip
|
||||
elsif ipv4_rule?
|
||||
"#{ip}/32"
|
||||
elsif ipv6_rule?
|
||||
"#{ip}/128"
|
||||
else
|
||||
ip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
241
cookbooks/firewall/libraries/provider_firewall_rule_ufw.rb
Normal file
241
cookbooks/firewall/libraries/provider_firewall_rule_ufw.rb
Normal file
@@ -0,0 +1,241 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
||||
# Cookbook Name:: firwall
|
||||
# Resource:: rule
|
||||
#
|
||||
# Copyright:: 2011, Opscode, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallRuleUfw < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
include FirewallCookbook::Helpers
|
||||
|
||||
def action_allow
|
||||
if rule_exists?
|
||||
Chef::Log.info("#{new_resource.name} already allowed, skipping")
|
||||
else
|
||||
apply_rule(:allow)
|
||||
end
|
||||
end
|
||||
|
||||
def action_deny
|
||||
if rule_exists?
|
||||
Chef::Log.info("#{new_resource.name} already denied, skipping")
|
||||
else
|
||||
apply_rule(:deny)
|
||||
end
|
||||
end
|
||||
|
||||
def action_reject
|
||||
if rule_exists?
|
||||
Chef::Log.info("#{new_resource.name} already rejected, skipping")
|
||||
else
|
||||
apply_rule(:reject)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_rule(type = nil) # rubocop:disable MethodLength
|
||||
Chef::Log.info("#{new_resource.name} apply_rule #{type}")
|
||||
# if we don't do this, we may see some bugs where traffic is opened on all ports to all hosts when only RELATED,ESTABLISHED was intended
|
||||
if new_resource.stateful
|
||||
msg = ''
|
||||
msg << "firewall_rule[#{new_resource.name}] was asked to "
|
||||
msg << "#{type} a stateful rule using #{new_resource.stateful} "
|
||||
msg << 'but ufw does not support this kind of rule. Consider guarding by platform_family.'
|
||||
fail msg
|
||||
end
|
||||
|
||||
# some examples:
|
||||
# ufw allow from 192.168.0.4 to any port 22
|
||||
# ufw deny proto tcp from 10.0.0.0/8 to 192.168.0.1 port 25
|
||||
# ufw insert 1 allow proto tcp from 0.0.0.0/0 to 192.168.0.1 port 25
|
||||
|
||||
ufw_command = ['ufw']
|
||||
if new_resource.position
|
||||
ufw_command << 'insert'
|
||||
ufw_command << new_resource.position.to_s
|
||||
end
|
||||
ufw_command << type.to_s
|
||||
ufw_command << rule.split
|
||||
|
||||
converge_by("firewall_rule[#{new_resource.name}] #{rule}") do
|
||||
notifying_block do
|
||||
# fail 'should be no actions'
|
||||
shell_out!(*ufw_command.flatten)
|
||||
shell_out!('ufw', 'status', 'verbose') # purely for the Chef::Log.debug output
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rule
|
||||
rule = ''
|
||||
rule << rule_interface
|
||||
rule << rule_logging
|
||||
rule << rule_proto
|
||||
rule << rule_dest_port
|
||||
rule << rule_source_port
|
||||
rule.strip
|
||||
end
|
||||
|
||||
def rule_interface
|
||||
rule = ''
|
||||
rule << "#{new_resource.direction} " if new_resource.direction
|
||||
if new_resource.interface
|
||||
if new_resource.direction
|
||||
rule << "on #{new_resource.interface} "
|
||||
else
|
||||
rule << "in on #{new_resource.interface} "
|
||||
end
|
||||
end
|
||||
rule
|
||||
end
|
||||
|
||||
def rule_proto
|
||||
rule = ''
|
||||
rule << "proto #{new_resource.protocol} " if new_resource.protocol
|
||||
rule
|
||||
end
|
||||
|
||||
def rule_dest_port
|
||||
rule = ''
|
||||
if new_resource.destination
|
||||
rule << "to #{new_resource.destination} "
|
||||
else
|
||||
rule << 'to any '
|
||||
end
|
||||
rule << "port #{port_to_s(dport_calc)} " if dport_calc
|
||||
rule
|
||||
end
|
||||
|
||||
def rule_source_port
|
||||
rule = ''
|
||||
|
||||
if new_resource.source
|
||||
rule << "from #{new_resource.source} "
|
||||
else
|
||||
rule << 'from any '
|
||||
end
|
||||
|
||||
if new_resource.source_port
|
||||
rule << "port #{port_to_s(new_resource.source_port)} "
|
||||
end
|
||||
rule
|
||||
end
|
||||
|
||||
def rule_logging
|
||||
case new_resource.logging && new_resource.logging.to_sym
|
||||
when :connections
|
||||
'log '
|
||||
when :packets
|
||||
'log-all '
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: currently only works when firewall is enabled
|
||||
def rule_exists?
|
||||
Chef::Log.info("#{new_resource.name} rule_exists?")
|
||||
# To Action From
|
||||
# -- ------ ----
|
||||
# 22 ALLOW Anywhere
|
||||
# 192.168.0.1 25/tcp DENY 10.0.0.0/8
|
||||
# 22 ALLOW Anywhere
|
||||
# 3309 on eth9 ALLOW Anywhere
|
||||
# Anywhere ALLOW Anywhere
|
||||
# 80 ALLOW Anywhere (log)
|
||||
# 8080 DENY 192.168.1.0/24
|
||||
# 1.2.3.5 5469/udp ALLOW 1.2.3.4 5469/udp
|
||||
# 3308 ALLOW OUT Anywhere on eth8
|
||||
|
||||
to = rule_exists_to? # col 1
|
||||
action = rule_exists_action? # col 2
|
||||
from = rule_exists_from? # col 3
|
||||
|
||||
# full regex from columns
|
||||
regex = rule_exists_regex?(to, action, from)
|
||||
|
||||
match = shell_out!('ufw', 'status').stdout.lines.find do |line|
|
||||
# TODO: support IPv6
|
||||
return false if line =~ /\(v6\)$/
|
||||
line =~ regex
|
||||
end
|
||||
|
||||
match
|
||||
end
|
||||
|
||||
def rule_exists_to?
|
||||
to = ''
|
||||
to << rule_exists_dest?
|
||||
|
||||
proto = rule_exists_proto?
|
||||
to << proto if proto
|
||||
|
||||
if to.empty?
|
||||
to << "Anywhere\s"
|
||||
else
|
||||
to
|
||||
end
|
||||
end
|
||||
|
||||
def rule_exists_action?
|
||||
action = new_resource.action
|
||||
action = action.first if action.is_a?(Enumerable)
|
||||
"#{Regexp.escape(action.to_s.upcase)}\s"
|
||||
end
|
||||
|
||||
def rule_exists_from?
|
||||
if new_resource.source && new_resource.source != '0.0.0.0/0'
|
||||
Regexp.escape(new_resource.source)
|
||||
elsif new_resource.source
|
||||
Regexp.escape('Anywhere')
|
||||
end
|
||||
end
|
||||
|
||||
def rule_exists_dest?
|
||||
if new_resource.destination
|
||||
"#{Regexp.escape(new_resource.destination)}\s"
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
def rule_exists_regex?(to, action, from)
|
||||
if to && new_resource.direction && new_resource.direction.to_sym == :out
|
||||
/^#{to}.*#{action}OUT\s.*#{from}$/
|
||||
elsif to
|
||||
/^#{to}.*#{action}.*#{from}$/
|
||||
end
|
||||
end
|
||||
|
||||
def rule_exists_proto?
|
||||
if new_resource.protocol && dport_calc
|
||||
"#{Regexp.escape(port_to_s(dport_calc))}/#{Regexp.escape(new_resource.protocol)}\s "
|
||||
elsif dport_calc
|
||||
"#{Regexp.escape(port_to_s(dport_calc))}\s "
|
||||
end
|
||||
end
|
||||
|
||||
def dport_calc
|
||||
new_resource.dest_port || new_resource.port
|
||||
end
|
||||
end
|
||||
end
|
||||
77
cookbooks/firewall/libraries/provider_firewall_ufw.rb
Normal file
77
cookbooks/firewall/libraries/provider_firewall_ufw.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
||||
# Cookbook Name:: firewall
|
||||
# Resource:: default
|
||||
#
|
||||
# Copyright:: 2011, Opscode, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Provider::FirewallUfw < Provider
|
||||
include Poise
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def action_enable
|
||||
converge_by('install ufw, template some defaults, and ufw enable') do
|
||||
package 'ufw' do
|
||||
action :nothing
|
||||
end.run_action(:install) # need this now if running in a provider
|
||||
|
||||
template '/etc/default/ufw' do
|
||||
action [:create]
|
||||
owner 'root'
|
||||
group 'root'
|
||||
mode '0644'
|
||||
source 'ufw/default.erb'
|
||||
cookbook 'firewall'
|
||||
action :nothing
|
||||
end.run_action(:create) # need this now if running in a provider
|
||||
|
||||
# new_resource.subresources contains all the firewall rules
|
||||
if active?
|
||||
Chef::Log.debug("#{new_resource} already enabled.")
|
||||
else
|
||||
shell_out!('ufw', 'enable', :input => 'yes')
|
||||
Chef::Log.info("#{new_resource} enabled")
|
||||
if new_resource.log_level
|
||||
shell_out!('ufw', 'logging', new_resource.log_level.to_s)
|
||||
Chef::Log.info("#{new_resource} logging enabled at '#{new_resource.log_level}' level")
|
||||
end
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def action_disable
|
||||
if active?
|
||||
shell_out!('ufw', 'disable')
|
||||
Chef::Log.info("#{new_resource} disabled")
|
||||
new_resource.updated_by_last_action(true)
|
||||
else
|
||||
Chef::Log.debug("#{new_resource} already disabled.")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active?
|
||||
@active ||= begin
|
||||
cmd = shell_out!('ufw', 'status')
|
||||
cmd.stdout =~ /^Status:\sactive/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
10
cookbooks/firewall/libraries/resource_firewall.rb
Normal file
10
cookbooks/firewall/libraries/resource_firewall.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Resource::Firewall < Resource
|
||||
include Poise(:container => true)
|
||||
|
||||
actions(:enable, :disable, :flush, :save)
|
||||
attribute(:log_level, :kind_of => [Symbol, String], :equal_to => [:low, :medium, :high, :full, 'low', 'medium', 'high', 'full'], :default => :low)
|
||||
end
|
||||
end
|
||||
36
cookbooks/firewall/libraries/resource_firewall_rule.rb
Normal file
36
cookbooks/firewall/libraries/resource_firewall_rule.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
require 'poise'
|
||||
|
||||
class Chef
|
||||
class Resource::FirewallRule < Resource
|
||||
include Poise(Chef::Resource::Firewall)
|
||||
|
||||
actions(:reject, :allow, :deny, :masquerade, :redirect, :log, :remove)
|
||||
|
||||
attribute(:protocol, :kind_of => [Symbol, String], :equal_to => [:udp, :tcp, :icmp, 'tcp', 'udp', 'icmp'], :default => :tcp)
|
||||
attribute(:direction, :kind_of => [Symbol, String], :equal_to => [:in, :out, :pre, :post, 'in', 'out', 'pre', 'post'], :default => :in)
|
||||
attribute(:logging, :kind_of => [Symbol, String], :equal_to => [:connections, :packets, 'connections', 'packets'])
|
||||
|
||||
attribute(:source, :callbacks => { 'must be a valid ip address' => ->(s) { valid_ip?(s) } })
|
||||
attribute(:source_port, :kind_of => [Integer, Array, Range]) # source port
|
||||
attribute(:interface, :kind_of => String)
|
||||
|
||||
attribute(:port, :kind_of => [Integer, Array, Range]) # shorthand for dest_port
|
||||
attribute(:destination, :callbacks => { 'must be a valid ip address' => ->(s) { valid_ip?(s) } })
|
||||
attribute(:dest_port, :kind_of => [Integer, Array, Range])
|
||||
attribute(:dest_interface, :kind_of => String)
|
||||
|
||||
attribute(:position, :kind_of => Integer)
|
||||
attribute(:stateful, :kind_of => [Symbol, String, Array])
|
||||
attribute(:redirect_port, :kind_of => Integer)
|
||||
attribute(:description, :kind_of => String, :name_attribute => true)
|
||||
|
||||
# for when you just want to pass a raw rule
|
||||
attribute(:raw, :kind_of => String)
|
||||
|
||||
def self.valid_ip?(ip)
|
||||
IPAddr.new(ip) ? true : false
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
22
cookbooks/firewall/libraries/z_provider_mapping.rb
Normal file
22
cookbooks/firewall/libraries/z_provider_mapping.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# provider mappings for Chef 11
|
||||
# https://www.chef.io/blog/2015/02/10/chef-12-provider-resolver/
|
||||
|
||||
#########
|
||||
# firewall
|
||||
#########
|
||||
Chef::Platform.set platform: :centos, version: '< 7.0', resource: :firewall, provider: Chef::Provider::FirewallIptables
|
||||
Chef::Platform.set platform: :centos, version: '>= 7.0', resource: :firewall, provider: Chef::Provider::FirewallFirewalld
|
||||
Chef::Platform.set platform: :redhat, version: '< 7.0', resource: :firewall, provider: Chef::Provider::FirewallIptables
|
||||
Chef::Platform.set platform: :redhat, version: '>= 7.0', resource: :firewall, provider: Chef::Provider::FirewallFirewalld
|
||||
Chef::Platform.set platform: :debian, resource: :firewall, provider: Chef::Provider::FirewallUfw
|
||||
Chef::Platform.set platform: :ubuntu, resource: :firewall, provider: Chef::Provider::FirewallUfw
|
||||
|
||||
#########
|
||||
# firewall_rule
|
||||
#########
|
||||
Chef::Platform.set platform: :centos, version: '< 7.0', resource: :firewall_rule, provider: Chef::Provider::FirewallRuleIptables
|
||||
Chef::Platform.set platform: :centos, version: '>= 7.0', resource: :firewall_rule, provider: Chef::Provider::FirewallRuleFirewalld
|
||||
Chef::Platform.set platform: :redhat, version: '< 7.0', resource: :firewall_rule, provider: Chef::Provider::FirewallRuleIptables
|
||||
Chef::Platform.set platform: :redhat, version: '>= 7.0', resource: :firewall_rule, provider: Chef::Provider::FirewallRuleFirewalld
|
||||
Chef::Platform.set platform: :debian, resource: :firewall_rule, provider: Chef::Provider::FirewallRuleUfw
|
||||
Chef::Platform.set platform: :ubuntu, resource: :firewall_rule, provider: Chef::Provider::FirewallRuleUfw
|
||||
1
cookbooks/firewall/metadata.json
Normal file
1
cookbooks/firewall/metadata.json
Normal file
File diff suppressed because one or more lines are too long
29
cookbooks/firewall/recipes/default.rb
Normal file
29
cookbooks/firewall/recipes/default.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Cookbook Name:: firewall
|
||||
# Recipe:: default
|
||||
#
|
||||
# Copyright 2011, Opscode, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
firewall 'default' do
|
||||
action :enable
|
||||
end
|
||||
|
||||
firewall_rule 'allow world to ssh' do
|
||||
port 22
|
||||
source '0.0.0.0/0'
|
||||
action [:allow]
|
||||
only_if { node['firewall']['allow_ssh'] }
|
||||
end
|
||||
13
cookbooks/firewall/templates/default/ufw/default.erb
Normal file
13
cookbooks/firewall/templates/default/ufw/default.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
# /etc/default/ufw
|
||||
# This file is managed by Chef. Do not edit.
|
||||
|
||||
IPV6=<%= node['firewall']['ufw']['defaults']['ipv6'] %>
|
||||
MANAGE_BUILTINS=<%= node['firewall']['ufw']['defaults']['manage_builtins'] %>
|
||||
|
||||
<% node['firewall']['ufw']['defaults']['policy'].each do |policy, value| -%>
|
||||
<%= "DEFAULT_#{policy.upcase}_POLICY=\"#{value}\"" %>
|
||||
<% end -%>
|
||||
|
||||
IPT_SYSCTL="<%= node['firewall']['ufw']['defaults']['ipt_sysctl'] %>"
|
||||
|
||||
IPT_MODULES="<%= node['firewall']['ufw']['defaults']['ipt_modules'] %>"
|
||||
Reference in New Issue
Block a user