Added automatic firewall configuation 38/138/2
authorJosh Farwell <jfarwell@linuxfoundation.org>
Wed, 10 Jun 2015 00:47:26 +0000 (17:47 -0700)
committerJosh Farwell <jfarwell@linuxfoundation.org>
Wed, 10 Jun 2015 16:08:23 +0000 (09:08 -0700)
Added puppetlabs/firewall as an optional dependency, ::core::config now
calls a subclass that parses the config file values for relevant
information and creates firewall rules based on the config. Since
the program's default settings bind all services to 127.0.0.1, by
default it does nothing. Opens ports to all addresses by default
if non-localhost address is specified, takes an options hash that
defines allowed_hosts.

Change-Id: If9122950ae218ca2b1cf3bbb96997b3c9ad2818a
Signed-off-by: Josh Farwell <jfarwell@linuxfoundation.org>
.fixtures.yml
README.md
manifests/core.pp
manifests/core/config.pp
manifests/core/config/firewall.pp [new file with mode: 0644]
manifests/params.pp
spec/classes/core__config__firewall_spec.rb [new file with mode: 0644]
spec/classes/core__config_spec.rb

index 0ca2750..077a5e0 100644 (file)
@@ -1,7 +1,9 @@
 fixtures:
   # use the forge modules so we can easily pin to a specific version
-  forge_modules:
-    stdlib: "puppetlabs/stdlib"
-
+  #forge_modules:
+  #  stdlib: "puppetlabs/stdlib"
+  repositories:
+    firewall: "git://github.com/puppetlabs/puppetlabs-firewall.git"
+    stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git"
   symlinks:
     mailman3: "#{source_dir}"
index 734d085..a415149 100644 (file)
--- a/README.md
+++ b/README.md
@@ -40,6 +40,8 @@ management, etc.) this is the time to mention it.
 
 ### Setup Requirements **OPTIONAL**
 
+Mention optional dependency of puppetlabs/firewall here.
+
 If your module requires anything extra before setting up (pluginsync enabled,
 etc.), mention it here.
 
index ec75976..3204d6f 100644 (file)
@@ -41,6 +41,7 @@ class mailman3::core (
   $core_manage_database        = $mailman3::params::core_manage_database,
   $core_manage_firewall        = $mailman3::params::core_manage_firewall,
   $core_override_options       = {},
+  $core_override_allowed_hosts = {},
 ) inherits mailman3::params {
 
   # Make sure parameters are properly formatted
@@ -49,8 +50,14 @@ class mailman3::core (
   validate_bool($core_manage_database)
   validate_bool($core_manage_firewall)
   validate_hash($core_override_options)
+  validate_hash($core_override_allowed_hosts)
 
-  $core_options = merge($mailman3::params::core_default_options, $core_override_options)
+  # Merge together options with defaults from ::params, right most wins
+  $core_options = merge($mailman3::params::core_default_options,
+    $core_override_options)
+
+  $core_allowed_hosts = merge($mailman3::params::core_default_allowed_hosts,
+    $core_override_allowed_hosts)
 
   anchor { 'mailman3::core::begin': }
   anchor { 'mailman3::core::end': }
@@ -64,6 +71,7 @@ class mailman3::core (
     core_manage_database => $core_manage_database,
     core_manage_firewall => $core_manage_firewall,
     core_options         => $core_options,
+    core_allowed_hosts   => $core_allowed_hosts,
   }
 
   Anchor['mailman3::core::begin'] ->
index ee25686..5634882 100644 (file)
@@ -39,10 +39,12 @@ class mailman3::core::config (
   $core_manage_database,
   $core_manage_firewall,
   $core_options,
+  $core_allowed_hosts,
 ) {
   validate_bool($core_manage_database)
   validate_bool($core_manage_firewall)
   validate_hash($core_options)
+  validate_hash($core_allowed_hosts)
 
   anchor{ 'mailman3::core::config::begin': }
   anchor{ 'mailman3::core::config::end': }
@@ -56,4 +58,11 @@ class mailman3::core::config (
     content => template('mailman3/mailman.cfg.erb')
   }
 
+  if ($core_manage_firewall) {
+    class { 'mailman3::core::config::firewall':
+      core_manage_firewall => $core_manage_firewall,
+      core_allowed_hosts   => $core_allowed_hosts,
+      core_options         => $core_options,
+    }
+  }
 }
diff --git a/manifests/core/config/firewall.pp b/manifests/core/config/firewall.pp
new file mode 100644 (file)
index 0000000..a615f85
--- /dev/null
@@ -0,0 +1,118 @@
+# == Class: mailman3::core::config::firewall
+#
+# Full description of class mailman3 here.
+#
+# === Parameters
+#
+# Document parameters here.
+#
+# [*sample_parameter*]
+#   Explanation of what this parameter affects and what it defaults to.
+#   e.g. "Specify one or more upstream ntp servers as an array."
+#
+# === Variables
+#
+# Here you should define a list of variables that this module would require.
+#
+# [*sample_variable*]
+#   Explanation of how this variable affects the function of this class and if
+#   it has a default. e.g. "The parameter enc_ntp_servers must be set by the
+#   External Node Classifier as a comma separated list of hostnames." (Note,
+#   global variables should be avoided in favor of class parameters as
+#   of Puppet 2.6.)
+#
+# === Examples
+#
+#  class { 'mailman3':
+#    servers => [ 'pool.ntp.org', 'ntp.local.company.com' ],
+#  }
+#
+# === Authors
+#
+# Josh Farwell <jfarwell@linuxfoundation.org>
+#
+# === Copyright
+#
+# Copyright 2015 Josh Farwell, unless otherwise noted.
+#
+class mailman3::core::config::firewall (
+  $core_allowed_hosts,
+  $core_manage_firewall,
+  $core_options,
+){
+  validate_hash($core_allowed_hosts)
+  validate_bool($core_manage_firewall)
+  validate_hash($core_options)
+
+  if ($core_manage_firewall) {
+    if ( has_key($core_options, 'webservice') ) {
+
+      if ( has_key($core_options['webservice'], 'hostname') ) {
+        validate_string($core_options['webservice']['hostname'])
+        $rest_hostname = $core_options['webservice']['hostname']
+      }
+      else {
+        $rest_hostname = 'localhost'
+      }
+
+      if ( has_key($core_options['webservice'], 'port') ) {
+        validate_string($core_options['webservice']['port'])
+        $rest_port = $core_options['webservice']['port']
+      }
+      else {
+        $rest_port = '8001'
+      }
+
+    }
+    else {
+      $rest_port     = '8001'
+      $rest_hostname = 'localhost'
+    }
+
+    unless ($rest_hostname =~ '^localhost$|^127\.0\.0\.1$') {
+      $core_allowed_hosts['rest'].each |String $host| {
+        firewall { "060 mailman3 REST allow ${host}":
+          proto  => 'tcp',
+          state  => ['NEW'],
+          action => 'accept',
+          port   => $rest_port,
+          source => $host,
+        }
+      }
+    }
+
+    if ( has_key($core_options, 'mta') ) {
+      if (has_key($core_options['mta'], 'lmtp_host') ) {
+        validate_string($core_options['mta']['lmtp_host'])
+        $lmtp_host = $core_options['mta']['lmtp_host']
+      }
+      else {
+        $lmtp_host = '127.0.0.1'
+      }
+
+      if (has_key($core_options['mta'], 'lmtp_port') ) {
+        validate_string($core_options['mta']['lmtp_port'])
+        $lmtp_port = $core_options['mta']['lmtp_port']
+      }
+      else {
+        $lmtp_port = '8024'
+      }
+    }
+    else {
+      $lmtp_host = '127.0.0.1'
+      $lmtp_port = '8024'
+    }
+
+    unless ($lmtp_host =~ '^localhost$|^127\.0\.0\.1$') {
+      $core_allowed_hosts['lmtp'].each |String $host| {
+        firewall { "060 mailman3 LMTP allow ${host}":
+          proto  => 'tcp',
+          state  => ['NEW'],
+          action => 'accept',
+          port   => $lmtp_port,
+          source => $host,
+        }
+      }
+    }
+  }
+}
index e97a485..df6757f 100644 (file)
@@ -49,18 +49,22 @@ class mailman3::params {
   $rest_admin_pass = 'restpass'
 
   # Mailman Core values
-  $core_layout     = 'fhs'
-  $core_bin_dir    = '/usr/libexec/mailman3'
-  $core_var_dir    = '/var/lib/mailman3'
-  $core_queue_dir  = '/var/spool/mailman3'
-  $core_log_dir    = '/var/log/mailman3'
-  $core_lock_dir   = '/run/lock/mailman3'
-  $core_ext_dir    = '/etc/mailman3.d'
-  $core_pid_file   = '/run/mailman3/master.pid'
-  $core_site_owner = 'root@localhost'
-  $core_version    = 'installed' # could also be 3.0.0
+  $core_layout        = 'fhs'
+  $core_bin_dir       = '/usr/libexec/mailman3'
+  $core_var_dir       = '/var/lib/mailman3'
+  $core_queue_dir     = '/var/spool/mailman3'
+  $core_log_dir       = '/var/log/mailman3'
+  $core_lock_dir      = '/run/lock/mailman3'
+  $core_ext_dir       = '/etc/mailman3.d'
+  $core_pid_file      = '/run/mailman3/master.pid'
+  $core_site_owner    = 'root@localhost'
+  $core_version       = 'installed' # could also be 3.0.0
 
-  # Mailman Core default options hashes
+  # Mailman Core default options
+  $core_default_allowed_hosts = {
+    'rest' => ['0.0.0.0'],
+    'lmtp' => ['0.0.0.0'],
+  }
   $core_default_options = {
     'mailman'      => {
       'site_owner' => $mailman3::params::core_site_owner,
@@ -81,7 +85,6 @@ class mailman3::params {
       'admin_user'  => $mailman3::params::rest_admin_user,
       'admin_pass'  => $mailman3::params::rest_admin_pass,
     }
-
   }
 
 }
diff --git a/spec/classes/core__config__firewall_spec.rb b/spec/classes/core__config__firewall_spec.rb
new file mode 100644 (file)
index 0000000..cb8c1c2
--- /dev/null
@@ -0,0 +1,160 @@
+require 'spec_helper'
+
+describe 'mailman3::core::config::firewall', :type => :class do
+  # we do not have default values so the class should fail to compile
+  context 'with defaults for all parameters' do
+    let (:params) {{}}
+
+    it do
+      expect {
+        should compile
+      }.to raise_error(RSpec::Expectations::ExpectationNotMetError,
+        /Must pass /)
+    end
+  end
+
+  context 'with assumed default parameters' do
+    let (:params) { {
+        'core_manage_firewall' => true,
+        'core_options'         => {},
+        'core_allowed_hosts'   => {
+          'rest' => ['0.0.0.0'],
+          'lmtp' => ['0.0.0.0'],
+          },
+    } }
+
+    # because default settings allow localhost only, no firewall changes are needed
+    it { is_expected.to_not contain_firewall('060 mailman3 REST allow 0.0.0.0') }
+    it { is_expected.to_not contain_firewall('060 mailman3 LMTP allow 0.0.0.0') }
+
+  end
+
+  # even if relevant settings are present, if the flag is false, no rules should be created
+  context 'with manage_firewall flag set to false' do
+    let (:params) { {
+        'core_manage_firewall' => false,
+        'core_options'         => {
+          'webservice'         => {
+            'hostname'         => 'example.com',
+            'port'             => '4242',
+          },
+          'mta'                => {
+            'lmtp_host'        => '10.1.10.1',
+            'lmtp_port'        => '9999',
+          },
+        },
+        'core_allowed_hosts'   => {
+          'rest' => ['0.0.0.0'],
+          'lmtp' => ['0.0.0.0'],
+          },
+    } }
+
+    it { is_expected.to_not contain_firewall('060 mailman3 REST allow 0.0.0.0') }
+    it { is_expected.to_not contain_firewall('060 mailman3 LMTP allow 0.0.0.0') }
+
+  end
+
+  context 'with user defined hostname and port' do
+    let (:params) { {
+        'core_allowed_hosts'   => {
+          'rest'  => ['0.0.0.0'],
+          'lmtp'  => ['0.0.0.0'],
+          },
+        'core_manage_firewall' => true,
+        'core_options'         => {
+          'webservice'         => {
+            'hostname'         => 'example.com',
+            'port'             => '4242',
+          },
+          'mta'                => {
+            'lmtp_host'        => '10.1.10.1',
+            'lmtp_port'        => '9999',
+          },
+        },
+    } }
+
+    it { is_expected.to contain_firewall('060 mailman3 REST allow 0.0.0.0').with(
+      'proto'  => 'tcp',
+      'state'  => ['NEW'],
+      'action' => 'accept',
+      'port'   => '4242',
+      'source' => '0.0.0.0',
+    ) }
+
+    it { is_expected.to contain_firewall('060 mailman3 LMTP allow 0.0.0.0').with(
+      'proto'  => 'tcp',
+      'state'  => ['NEW'],
+      'action' => 'accept',
+      'port'   => '9999',
+      'source' => '0.0.0.0',
+    ) }
+
+  end
+
+  context 'with user defined allowed_hosts' do
+    let (:params) { {
+        'core_allowed_hosts'   => {
+          'rest'               => ['10.0.0.0/16','10.1.10.2','example.com'],
+          'lmtp'               => ['10.0.0.0/16','10.1.10.2','example.com'],
+        },
+        'core_manage_firewall' => true,
+        'core_options'         => {
+          'webservice'         => {
+            'hostname'         => 'mail.example.com',
+          },
+          'mta'                => {
+            'lmtp_host'        => '192.168.1.1',
+          },
+        },
+    } }
+
+    it { is_expected.to contain_firewall("060 mailman3 REST allow 10.0.0.0/16").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8001',
+      'source' => '10.0.0.0/16',
+    ) }
+
+    it { is_expected.to contain_firewall("060 mailman3 REST allow 10.1.10.2").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8001',
+      'source' => '10.1.10.2',
+    ) }
+
+    it { is_expected.to contain_firewall("060 mailman3 REST allow example.com").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8001',
+      'source' => 'example.com',
+    ) }
+
+    it { is_expected.to contain_firewall("060 mailman3 LMTP allow 10.0.0.0/16").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8024',
+      'source' => '10.0.0.0/16',
+    ) }
+
+    it { is_expected.to contain_firewall("060 mailman3 LMTP allow 10.1.10.2").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8024',
+      'source' => '10.1.10.2',
+    ) }
+
+    it { is_expected.to contain_firewall("060 mailman3 LMTP allow example.com").with(
+      'proto' => 'tcp',
+      'state' => ['NEW'],
+      'action' => 'accept',
+      'port'   => '8024',
+      'source' => 'example.com',
+    ) }
+  end
+end
+
index 7f377c1..f142b23 100644 (file)
@@ -13,8 +13,12 @@ describe 'mailman3::core::config', :type => :class do
   context 'with assumed default parameters' do
     let (:params) {
       {
+        'core_allowed_hosts'   => {
+          'rest'               => ['0.0.0.0'],
+          'lmtp'               => ['0.0.0.0'],
+        },
         'core_manage_database' => false,
-        'core_manage_firewall' => false,
+        'core_manage_firewall' => true,
         'core_options'              => {
           'mailman'            => {
             'site_owner'       => 'root@localhost',
@@ -36,21 +40,32 @@ describe 'mailman3::core::config', :type => :class do
         },
       } }
 
-  it { is_expected.to contain_file('/etc/mailman.cfg').with(
-    'ensure'  => 'file',
-    'owner'   => 'mailman',
-    'group'   => 'mailman',
-    'mode'    => '0640',
-    'content' => "; MANAGED BY PUPPET\n\n[mailman]\n  layout = fhs\n  site_owner = root@localhost\n\n[paths.fhs]\n  bin_dir = /usr/libexec/mailman3\n  ext_dir = /etc/mailman3.d\n  lock_dir = /run/lock/mailman3\n  log_dir = /var/log/mailman3\n  pid_file = /run/mailman3/master.pid\n  queue_dir = /var/spool/mailman3\n  var_dir = /var/lib/mailman3\n\n[webservice]\n  admin_pass = GENERATE\n  admin_user = restadmin\n\n",
-  ) }
+    it { is_expected.to contain_file('/etc/mailman.cfg').with(
+      'ensure'  => 'file',
+      'owner'   => 'mailman',
+      'group'   => 'mailman',
+      'mode'    => '0640',
+      'content' => "; MANAGED BY PUPPET\n\n[mailman]\n  layout = fhs\n  site_owner = root@localhost\n\n[paths.fhs]\n  bin_dir = /usr/libexec/mailman3\n  ext_dir = /etc/mailman3.d\n  lock_dir = /run/lock/mailman3\n  log_dir = /var/log/mailman3\n  pid_file = /run/mailman3/master.pid\n  queue_dir = /var/spool/mailman3\n  var_dir = /var/lib/mailman3\n\n[webservice]\n  admin_pass = GENERATE\n  admin_user = restadmin\n\n",
+    ) }
+
+    it { is_expected.to contain_class('mailman3::core::config::firewall').with(
+      'core_allowed_hosts' => {
+        'rest'             => ['0.0.0.0'],
+        'lmtp'             => ['0.0.0.0'],
+      },
+    ) }
 
   end
 
   context 'with user defined parameters' do
     let (:params) {
       {
+        'core_allowed_hosts'   => {
+          'rest'               => ['10.0.0.1'],
+          'lmtp'               => ['10.0.0.1'],
+        },
         'core_manage_database' => false,
-        'core_manage_firewall' => false,
+        'core_manage_firewall' => true,
         'core_options'              => {
           'mailman'            => {
             'site_owner'       => 'admin@example.com',
@@ -75,13 +90,31 @@ describe 'mailman3::core::config', :type => :class do
         },
       } }
 
-  it { is_expected.to contain_file('/etc/mailman.cfg').with(
-    'ensure'  => 'file',
-    'owner'   => 'mailman',
-    'group'   => 'mailman',
-    'mode'    => '0640',
-    'content' => "; MANAGED BY PUPPET\n\n[mailman]\n  layout = fhs\n  site_owner = admin@example.com\n\n[paths.fhs]\n  bin_dir = /foo/libexec/mailman3\n  ext_dir = /foo/mailman3.d\n  lock_dir = /foo/lock/mailman3\n  log_dir = /foo/log/mailman3\n  pid_file = /foo/mailman3/master.pid\n  queue_dir = /foo/spool/mailman3\n  var_dir = /foo/lib/mailman3\n\n[testsection]\n  test_value = foobar\n\n[webservice]\n  admin_pass = bar\n  admin_user = fooadmin\n\n",
-  ) }
+    it { is_expected.to contain_file('/etc/mailman.cfg').with(
+      'ensure'  => 'file',
+      'owner'   => 'mailman',
+      'group'   => 'mailman',
+      'mode'    => '0640',
+      'content' => "; MANAGED BY PUPPET\n\n[mailman]\n  layout = fhs\n  site_owner = admin@example.com\n\n[paths.fhs]\n  bin_dir = /foo/libexec/mailman3\n  ext_dir = /foo/mailman3.d\n  lock_dir = /foo/lock/mailman3\n  log_dir = /foo/log/mailman3\n  pid_file = /foo/mailman3/master.pid\n  queue_dir = /foo/spool/mailman3\n  var_dir = /foo/lib/mailman3\n\n[testsection]\n  test_value = foobar\n\n[webservice]\n  admin_pass = bar\n  admin_user = fooadmin\n\n",
+    ) }
+
+    it { is_expected.to contain_class('mailman3::core::config::firewall').with(
+      'core_allowed_hosts' => { 'rest' => ['10.0.0.1'], 'lmtp' => ['10.0.0.1'] },
+    ) }
+
+  end
+
+  context 'with manage_firewall flag set to false' do
+
+    let (:params) {
+      {
+        'core_manage_database' => false,
+        'core_manage_firewall' => false,
+        'core_allowed_hosts'   => {},
+        'core_options'         => {},
+      } }
+
+    it { is_expected.to_not contain_class('mailman3::core::config::firewall') }
 
   end