puppet-firewall模块

  1. 先睹为快
  2. 代码讲解
  3. 推荐阅读
  4. 动手练习

iptables是一个配置Linux内核防火墙的命令行工具,通过设定一些特殊的规则,以允许或拒绝数据包通过。

puppet-firewall模块是由Puppet公司维护的官方模块, 用于管理防火墙和其规则。该模块通过扩展自定义资源类型来管理firewall规则和
iptables chains, 当前支持iptables和ip6tables。

puppet-firewall项目地址:’https://github.com/puppetlabs/puppetlabs-firewall

1.先睹为快

不想看下面大段的代码解析,已经跃跃欲试了?

先别激动,这个模块的使用是有风险的,操作不慎会把自己也拒之门外,请确保可以通过除ssh外的方式登陆。

创建learn_firewall.pp文件并编辑:

  1. class my_fw::pre {
  2. Firewall {
  3. require => undef,
  4. }
  5. # Default firewall rules
  6. firewall { '000 accept all icmp':
  7. proto => 'icmp',
  8. action => 'accept',
  9. }->
  10. firewall { '001 accept all to lo interface':
  11. proto => 'all',
  12. iniface => 'lo',
  13. action => 'accept',
  14. }->
  15. firewall { '002 reject local traffic not on loopback interface':
  16. iniface => '! lo',
  17. proto => 'all',
  18. destination => '127.0.0.1/8',
  19. action => 'reject',
  20. }->
  21. firewall { '003 accept related established rules':
  22. proto => 'all',
  23. state => ['RELATED', 'ESTABLISHED'],
  24. action => 'accept',
  25. }
  26. }
  27. class my_fw::post {
  28. firewall { '999 drop all':
  29. proto => 'all',
  30. action => 'drop',
  31. before => undef,
  32. }
  33. }
  34. class my_fw {
  35. firewall { '004 Allow inbound SSH':
  36. dport => 22,
  37. proto => tcp,
  38. action => accept,
  39. provider => 'iptables',
  40. }
  41. firewall { '005 Allow inbound HTTP':
  42. dport => 80,
  43. proto => tcp,
  44. action => accept,
  45. provider => 'iptables',
  46. }
  47. }
  48. Firewall {
  49. before => Class['my_fw::post'],
  50. require => Class['my_fw::pre'],
  51. }
  52. class { ['my_fw::pre', 'my_fw::post','my_fw']: }
  53. class { 'firewall': }

在终端下输入以下命令:

$ puppet apply -v learn_firewall.pp

在执行该命令前,操作系统的防火墙规则为空:

  1. $ iptables -L
  2. Chain INPUT (policy ACCEPT)
  3. target prot opt source destination
  4. Chain FORWARD (policy ACCEPT)
  5. target prot opt source destination
  6. Chain OUTPUT (policy ACCEPT)
  7. target prot opt source destination

在执行完成该命令后,防火墙规则发生了以下变化:

  1. $ iptables -L
  2. Chain INPUT (policy ACCEPT)
  3. target prot opt source destination
  4. ACCEPT icmp -- anywhere anywhere /* 000 accept all icmp */
  5. ACCEPT all -- anywhere anywhere /* 001 accept all to lo interface */
  6. REJECT all -- anywhere loopback/8 /* 002 reject local traffic not on loopback interface */ reject-with icmp-port-unreachable
  7. ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED /* 003 accept related established rules */
  8. ACCEPT tcp -- anywhere anywhere multiport dports ssh /* 004 Allow inbound SSH */
  9. ACCEPT tcp -- anywhere anywhere multiport dports http /* 005 Allow inbound HTTP */
  10. ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
  11. ACCEPT icmp -- anywhere anywhere
  12. ACCEPT all -- anywhere anywhere
  13. ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
  14. REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
  15. DROP all -- anywhere anywhere /* 999 drop all */
  16. Chain FORWARD (policy ACCEPT)
  17. target prot opt source destination
  18. REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
  19. Chain OUTPUT (policy ACCEPT)
  20. target prot opt source destination

2.代码讲解

2.1 class firewall

firewall类用于管理Iptables软件包和服务,会根据内核类型申明不同的类进行管理。

  1. class firewall (
  2. $ensure = running,
  3. $pkg_ensure = present,
  4. $service_name = $::firewall::params::service_name,
  5. $package_name = $::firewall::params::package_name,
  6. ) inherits ::firewall::params {
  7. case $ensure {
  8. /^(running|stopped)$/: {
  9. # Do nothing.
  10. }
  11. default: {
  12. fail("${title}: Ensure value '${ensure}' is not supported")
  13. }
  14. }
  15. case $::kernel {
  16. 'Linux': {
  17. class { "${title}::linux":
  18. ensure => $ensure,
  19. pkg_ensure => $pkg_ensure,
  20. service_name => $service_name,
  21. package_name => $package_name,
  22. }
  23. }
  24. 'FreeBSD': {
  25. }
  26. default: {
  27. fail("${title}: Kernel '${::kernel}' is not currently supported")
  28. }
  29. }
  30. }

以Linux为例,firewall::linux会根据操作系统的不同调用对应的firewall::linux::xxx类:

  1. case $::operatingsystem {
  2. 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos',
  3. 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': {
  4. class { "${title}::redhat":
  5. ensure => $ensure,
  6. enable => $enable,
  7. package_name => $package_name,
  8. service_name => $service_name,
  9. require => Package['iptables'],
  10. }
  11. }
  12. 'Debian', 'Ubuntu': {
  13. class { "${title}::debian":
  14. ensure => $ensure,
  15. enable => $enable,
  16. package_name => $package_name,
  17. service_name => $service_name,
  18. require => Package['iptables'],
  19. }
  20. }
  21. ...
  22. }

以RedHat为例,firewall::linux::redhat会根据操作系统和版本的不同跳转到相应的逻辑。通过这个模块可以发现,firewall类仅完成了安装软件包
和管理服务状态,但要维护一个支持多平台和版本的模块并非易事,需要投入大量的精力进去,这也是社区模式可以得到众多公司认可的原因。

  1. # RHEL 7 and later and Fedora 15 and later require the iptables-services
  2. # package, which provides the /usr/libexec/iptables/iptables.init used by
  3. # lib/puppet/util/firewall.rb.
  4. if ($::operatingsystem != 'Amazon')
  5. and (($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
  6. or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0)) {
  7. service { 'firewalld':
  8. ensure => stopped,
  9. enable => false,
  10. before => Package[$package_name],
  11. }
  12. }
  13. if $package_name {
  14. package { $package_name:
  15. ensure => $package_ensure,
  16. before => Service[$service_name],
  17. }
  18. }
  19. if ($::operatingsystem != 'Amazon')
  20. and (($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
  21. or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0)) {
  22. ...
  23. # Redhat 7 selinux user context for /etc/sysconfig/iptables is set to unconfined_u
  24. case $::selinux {
  25. #lint:ignore:quoted_booleans
  26. 'true',true: {
  27. case $::operatingsystemrelease {
  28. /^(6|7)\..*/: { $seluser = 'unconfined_u' }
  29. default: { $seluser = 'system_u' }
  30. }
  31. }
  32. #lint:endignore
  33. default: { $seluser = undef }
  34. }
  35. file { "/etc/sysconfig/${service_name}":
  36. ensure => present,
  37. owner => 'root',
  38. group => 'root',
  39. mode => '0600',
  40. seluser => $seluser,
  41. }
  42. }

在上述代码中,需要理解以下新知识点:

第一点,versioncmp函数用于比较两个版本号并返回比较结果,例如:

$result = versioncmp(a, b)

  • a大于b,返回1
  • a等于b,返回0
  • a小于b,返回-1

第二点,要理解运算的优先级顺序,在上述代码出现了一段比较复杂的条件语句:

  1. if ($::operatingsystem != 'Amazon')
  2. and (($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
  3. or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0))

首先()的优先级最高,因此以下表达式会优先进行计算:

  • ($::operatingsystem != ‘Amazon’)
  • (($::operatingsystem != ‘Fedora’ and versioncmp($::operatingsystemrelease, ‘7.0’) >= 0))
  • ($::operatingsystem == ‘Fedora’ and versioncmp($::operatingsystemrelease, ‘15’) >= 0))

其次,==的优先级等于!=高于>=高于and
最后是最外层的and/or运算if statement1 and statement2 or statement 3,那么其运算顺序是哪一种?

  • if (statement1 and statement2) or (statement 3)
  • if statement1 and (statement2 or statement 3)

答案是前一种,因为and的优先级高于or

第三点,掌握case条件语句的语法。

case条件语句和if条件语句类似,均是选择其中的一个Puppet代码块进行执行,但其更适合用于字符串和数值的匹配。

  1. case $facts['name'] {
  2. 'A': { include role::case1 }
  3. 'B', 'C': { include role::case2 }
  4. /^(D|E)$/: { include role::case3 }
  5. default: { include role::default_case }
  6. }

2.2 type firewall

资源类型firewall用于管理防火墙规则,以下举例说明如何在真实环境中使用该类型:

2.2.1 为apache开启80和443端口

  1. firewall { '100 allow http and https access':
  2. dport => [80, 443],
  3. proto => tcp,
  4. action => accept,
  5. }

2.2.2 丢弃FIN/RST/ACK包如果没有对应的SYN包

  1. firewall { '002 drop NEW external website packets with FIN/RST/ACK set and SYN unset':
  2. chain => 'INPUT',
  3. state => 'NEW',
  4. action => 'drop',
  5. proto => 'tcp',
  6. sport => ['! http', '! 443'],
  7. source => '! 10.0.0.0/8',
  8. tcp_flags => '! FIN,SYN,RST,ACK SYN',
  9. }
2.2.3 SNAT 10.1.2.0/24
  1. firewall { '100 snat for network foo2':
  2. chain => 'POSTROUTING',
  3. jump => 'MASQUERADE',
  4. proto => 'all',
  5. outiface => 'eth0',
  6. source => '10.1.2.0/24',
  7. table => 'nat',
  8. }

2.3 type firewallchain

资源类型firewallchain用于管理管理防火墙的规则链,以下举例说明如何在真实环境中使用该类型:

2.3.1 默认丢弃INPUT链上的包
  1. firewallchain { 'INPUT:filter:IPv4':
  2. ensure => present,
  3. policy => drop,
  4. before => undef,
  5. }

需要说明的是,这个模块有一定的 局限性,如只支持管理iptable和ip6tables。此外,在和Neutron同时使用时会遇到iptable规则的冲突问题。

3.扩展阅读

4.动手练习

  1. 本章给出的第一个示例learn_firewall.pp存在一些问题,当修改防火墙规则时,旧的规则不会被删除,请修复这个问题
  2. 为OpenStack Nova服务编写firewall规则,开放相应的服务端口