### 使用 iptables 管理防火墙
> Programming can be fun, so can cryptography; however they should not be combined.
>
> — Kreitzberg and Shneiderman
C 编程语言被形容为 “只写” 的语言;它是如此的简洁、高效, 甚至你自己读自己写过的代码都可能很难理解。 同样地,Linux 内核内置的包过滤防火墙的 iptables 的配置也是如此。 一条原始的 iptables 命令规则看上去像这样:
```
iptables -A INPUT -d 10.0.2.15/32 -p tcp -m tcp --dport 80 -j ACCEPT
```
除非你会因为掌握了命令行的这些似乎毫无意义的字符串而获得男子气概 (诚然这是 UNIX 系统管理员的职业病), 否则,能够以更加象征性和可读性的方式来表达防火墙规则是更好的选择。 Puppet 在这方面可以为我们提供帮助,因为我们可以用它对 iptables 的实现细节进行抽象, 并通过参考管理员所控制的服务角色来定义防火墙规则,例如:
```
iptables::role { "web-server": }
```
#### 准备工作
你需要我们在第 5 章 [为配置文件添加配置行](#ch05sec01) 一节中创建的 append_if_no_such_line 函数。
#### 操作步骤
1. 创建一个 iptables 模块:
```
# mkdir /etc/puppet/modules/iptables
# mkdir /etc/puppet/modules/iptables/manifests
# mkdir /etc/puppet/modules/iptables/files
```
2. 使用如下内容创建 /etc/puppet/modules/iptables/manifests/init.pp 文件:
```
class iptables {
file { [ "/root/iptables",
"/root/iptables/hosts",
"/root/iptables/roles" ]:
ensure => directory,
}
file { "/root/iptables/roles/common":
source => "puppet:///modules/iptables/common.role",
notify => Exec["run-iptables"],
}
file { "/root/iptables/names":
source => "puppet:///modules/iptables/names",
notify => Exec["run-iptables"],
}
file { "/root/iptables/iptables.sh":
source => "puppet:///modules/iptables/iptables.sh",
mode => "755",
notify => Exec["run-iptables"],
}
file { "/root/iptables/hosts/${hostname}":
content => "export MAIN_IP=${ipaddress}\n",
replace => false,
require => File["/root/iptables/hosts"],
notify => Exec["run-iptables"],
}
exec { "run-iptables":
cwd => "/root/iptables",
command => "/usr/bin/test -f hosts/${hostname} &&
/root/iptables/iptables.sh && /sbin/iptables-save >
/etc/iptables.rules",
refreshonly => true,
}
append_if_no_such_line { "restore iptables rules":
file => "/etc/network/interfaces",
line => "pre-up iptables-restore < /etc/iptables.rules",
}
define role() {
include iptables
file { "/root/iptables/roles/${name}":
source => "puppet:///modules/iptables/${name}.role",
replace => false,
require => File["/root/iptables/roles"],
notify => Exec["run-iptables"],
}
append_if_no_such_line { "${name} role":
file => "/root/iptables/hosts/${hostname}",
line => ". `dirname \$0`/roles/${name}",
require => File["/root/iptables/hosts/${hostname}"],
notify => Exec["run-iptables"],
}
}
}
```
3. 使用如下内容创建 /etc/puppet/modules/iptables/files/iptables.sh 文件:
```
# Server names and ports
. `dirname $0`/names
# Interfaces (override in host-specific file if necessary)
export EXT_INTERFACE=eth0
# Flush and remove all chains
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
iptables -X
# Allow all traffic on loopback interface
iptables -I INPUT 1 -i lo -j ACCEPT
iptables -I OUTPUT 1 -o lo -j ACCEPT
# Allow established and related connections
iptables -I INPUT 2 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -I OUTPUT 2 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Include machine specific settings
HOST_RULES=`dirname $0`/hosts/`hostname -s`
[ -f ${HOST_RULES} ] && . ${HOST_RULES}
[ "${MAIN_IP}" == "" ] && ( echo No MAIN_IP was set, \
please set the primary IP address in ${HOST_RULES}. ; exit 1 )
# Include common settings
. `dirname $0`/roles/common
# Drop all non-matching packets
iptables -A INPUT -j LOG --log-prefix "INPUT: "
iptables -A INPUT -j DROP
iptables -A OUTPUT -j LOG --log-prefix "OUTPUT: "
iptables -A OUTPUT -j DROP
echo -e "Test remote login and then:\n iptables-save \
>/etc/iptables.rules"
```
4. 使用如下内容创建 /etc/puppet/modules/iptables/files/names 文件:
```
# Servers
export PUPPETMASTER=10.0.2.15
# Well-known ports
export DNS=53
export FTP=21
export GIT=9418
export HEARTBEAT=694
export IMAPS=993
export IRC=6667
export MONIT=2828
export MYSQL=3306
export MYSQL_MASTER=3307
export NRPE=5666
export NTP=123
export POSTGRES=5432
export PUPPET=8140
export RSYNCD=873
export SMTP=25
export SPHINX=3312
export SSH=22
export STARLING=3307
export SYSLOG=514
export WEB=80
export WEB_SSL=443
export ZABBIX=10051
```
5. 使用如下内容创建 /etc/puppet/modules/iptables/files/common.role 文件:
```
# Common rules for all hosts
iptables -A INPUT -p tcp -m tcp -d ${MAIN_IP} --dport ${SSH} -j ACCEPT
iptables -A INPUT -p ICMP --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p ICMP --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${SSH} -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${SMTP} -j ACCEPT
iptables -A OUTPUT -p udp --dport ${NTP} -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${NTP} -j ACCEPT
iptables -A OUTPUT -p udp --dport ${DNS} -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${WEB} -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${WEB_SSL} -j ACCEPT
iptables -A OUTPUT -p tcp -d ${PUPPETMASTER} --dport ${PUPPET} -j ACCEPT
iptables -A OUTPUT -p tcp --dport ${MYSQL} -j ACCEPT
# Drop some commonly probed ports
iptables -A INPUT -p tcp --dport 23 -j DROP # telnet
iptables -A INPUT -p tcp --dport 135 -j DROP # epmap
iptables -A INPUT -p tcp --dport 139 -j DROP # netbios
iptables -A INPUT -p tcp --dport 445 -j DROP # Microsoft DS
iptables -A INPUT -p udp --dport 1433 -j DROP # SQL server
iptables -A INPUT -p tcp --dport 1433 -j DROP # SQL server
iptables -A INPUT -p udp --dport 1434 -j DROP # SQL server
iptables -A INPUT -p tcp --dport 1434 -j DROP # SQL server
iptables -A INPUT -p tcp --dport 2967 -j DROP # SSC-agent
```
6. 使用如下内容创建 /etc/puppet/modules/iptables/files/web-server.role 文件:
```
# Access to web
iptables -A INPUT -p tcp -d ${MAIN_IP} --dport ${WEB} -j ACCEPT
# Send mail from web applications
iptables -A OUTPUT -p tcp --dport ${SMTP} -j ACCEPT
```
7. 使用如下内容创建 /etc/puppet/modules/iptables/files/puppet-server.role 文件:
```
# Access to puppet
iptables -A INPUT -p tcp -d ${MAIN_IP} --dport ${PUPPET} -j ACCEPT
```
8. 在你的 Puppetmaster 节点上包含如下内容:
```
iptables::role { "web-server": }
iptables::role { "puppet-server": }
```
9. 运行 Puppet:
```
# puppet agent --test
info: Retrieving plugin
info: Caching catalog for cookbook.bitfieldconsulting.com
info: Applying configuration version '1311682880'
notice: /Stage[main]/Iptables/File[/root/iptables]/ensure: created
notice: /Stage[main]/Iptables/File[/root/iptables/names]/ensure:
defined content as '{md5}9bb004a7d2c6d70616b149d044c22669'
info: /Stage[main]/Iptables/File[/root/iptables/names]: Scheduling
refresh of Exec[run-iptables]
notice: /Stage[main]/Iptables/File[/root/iptables/hosts]/ensure:
created
notice: /Stage[main]/Iptables/File[/root/iptables/hosts/cookbook]/
ensure: defined content as '{md5}d00bc730514bbb74cdef3dad70058a81'
info: /Stage[main]/Iptables/File[/root/iptables/hosts/cookbook]:
Scheduling refresh of Exec[run-iptables]
notice: /Stage[main]//Node[cookbook]/Iptables::Role[web-server]/
Append_if_no_such_line[web-server role]/Exec[/bin/echo '. `dirname
$0`/roles/web-server' >> '/root/iptables/hosts/cookbook']/returns:
executed successfully
info: /Stage[main]//Node[cookbook]/Iptables::Role[web-server]/
Append_if_no_such_line[web-server role]/Exec[/bin/echo '. `dirname
$0`/roles/web-server' >> '/root/iptables/hosts/cookbook']:
Scheduling refresh of Exec[run-iptables]
notice: /Stage[main]//Node[cookbook]/Iptables::Role[puppetserver]/
Append_if_no_such_line[puppet-server role]/Exec[/bin/echo
'. `dirname $0`/roles/puppet-server' >> '/root/iptables/hosts/
cookbook']/returns: executed successfully
info: /Stage[main]//Node[cookbook]/Iptables::Role[puppet-server]/
Append_if_no_such_line[puppet-server role]/Exec[/bin/echo '.
`dirname $0`/roles/puppet-server' >> '/root/iptables/hosts/
cookbook']: Scheduling refresh of Exec[run-iptables]
notice: /Stage[main]/Iptables/File[/root/iptables/roles]/ensure:
created
notice: /Stage[main]//Node[cookbook]/Iptables::Role[puppetserver]/
File[/root/iptables/roles/puppet-server]/ensure: defined
content as '{md5}c30a13f7792525c181e14e78c9a510cd'
info: /Stage[main]//Node[cookbook]/Iptables::Role[puppet-server]/
File[/root/iptables/roles/puppet-server]: Scheduling refresh of
Exec[run-iptables]
notice: /Stage[main]//Node[cookbook]/Iptables::Role[web-server]/
File[/root/iptables/roles/web-server]/ensure: defined content as
'{md5}11e5747cb2737903ffc34133f5fe2452'
info: /Stage[main]//Node[cookbook]/Iptables::Role[web-server]/
File[/root/iptables/roles/web-server]: Scheduling refresh of
Exec[run-iptables]
notice: /Stage[main]/Iptables/File[/root/iptables/roles/common]/
ensure: defined content as '{md5}116f57d4e31f3e0b351da6679dca15e3'
info: /Stage[main]/Iptables/File[/root/iptables/roles/common]:
Scheduling refresh of Exec[run-iptables]
notice: /Stage[main]/Iptables/File[/root/iptables/iptables.sh]/
ensure: defined content as '{md5}340ff9fb5945e9fc7dd78b21f45dd823'
info: /Stage[main]/Iptables/File[/root/iptables/iptables.sh]:
Scheduling refresh of Exec[run-iptables]
notice: /Stage[main]/Iptables/Exec[run-iptables]: Triggered
'refresh' from 8 events
notice: /Stage[main]/Iptables/Append_if_no_such_line[restore
iptables rules]/Exec[/bin/echo 'pre-up iptables-restore < /etc/
iptables.rules' >> '/etc/network/interfaces']/returns: executed
successfully
notice: Finished catalog run in 4.86 seconds
```
10. 检查要求的规则是否已被安装:
```
# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT tcp -- 0.0.0.0/0 10.0.2.15 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 10.0.2.15 tcp dpt:8140
ACCEPT tcp -- 0.0.0.0/0 10.0.2.15 tcp dpt:22
ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmp type 8
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:23
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:135
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:139
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:445
DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:1433
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:1433
DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:1434
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:1434
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:2967
LOG all -- 0.0.0.0/0 0.0.0.0/0 LOG
flags 0 level 4 prefix `INPUT: '
DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmp type 8
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:123
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:123
ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
ACCEPT tcp -- 0.0.0.0/0 10.0.2.15 tcp dpt:8140
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306
LOG all -- 0.0.0.0/0 0.0.0.0/0 LOG
flags 0 level 4 prefix `OUTPUT: '
DROP all -- 0.0.0.0/0 0.0.0.0/0
```
#### 工作原理
为了创建一套合适的防火墙规则,我们需要知道节点的主 IP 地址以及其运行了哪些服务。 我们还需要添加一些所有的机器都要设置的共同规则(例如,允许 SSH),并运行一系列的 iptables 命令以激活我们已经生成的规则。 之后我们还要保存这些规则,以便使这些规则可以在开机时恢复执行。 下面介绍所有的这一切是如何完成的。
首先,我们创建一个 names 文件为常用的端口定义 shell 变量。 这意味着,当我们定义防火墙规则时,可以引用变量,例如对于 MySQL 服务可以使用变量 ${MYSQL} 取代数值端口号 3306。
common.role 文件包含了一些对所有机器都有用的规则。编辑这个文件以适应你自己的所有机器 (例如,你可能仅允许从指定的 IP 范围访问 SSH)。
web-server.role 和 puppet-server.role 文件包含了两个特定角色的规则。 你可以添加更多的文件用于定义你的网络中所需的众多角色:例如,数据库服务器、应用服务器、 DNS 服务器,等等。文件中所有的规则都具有如下的格式:
```
iptables -A INPUT -p tcp -d ${MAIN_IP} --dport ${WEB} -j ACCEPT
```
通常,你只需要修改 ${WEB} 这部分:将其替换为另一个端口名(定义在 names 文件中) 的变量引用(例如 ${POSTGRES})。如果你需要更多的端口名,在 names 文件中添加相应的定义。
iptables.sh 脚本读取其他的所有文件并执行规则要求的 iptables 命令。 每当相关文件有任何改变,Puppet 就执行这个脚本,因此要想刷新防火墙, 你需要做的工作就是改变相关的配置并运行 Puppet。
Puppet 还会将当前的规则集保存在 /etc/iptables.rules 文件中。 为了让机器重启后加载规则集文件,Puppet 在 /etc/network/interfaces 文件中添加了如下一行:
```
pre-up iptables-restore < /etc/iptables.rules
```
所有这一切意味着,在相关的模块中(例如 apache 模块), 你只要简单地包含如下一行即可创建相应的防火墙:
```
iptables::role { "web-server": }
```
一旦防火墙被激活,任何不符合规则的数据包将被阻止并记录到 /var/log/messages 日志文件。 检查这个文件,以帮助解决防火墙的任何问题。
#### 更多用法
如果在你的规则中引用了某些特定的机器(例如,你的监控服务器), 可以在 names 文件中添加如下的定义:
```
MONITOR=10.0.2.15
```
然后在适当的位置(例如 common.role 文件中),你可以允许来自这台机器的访问, 例如,允许来自监控服务器对指定主机 NRPE 端口的访问规则如下:
```
iptables -A INPUT -p tcp -m tcp -d ${MAIN_IP} -s ${MONITOR} --dport ${NRPE} -j ACCEPT
```
你也可以用这种方法指定数据库服务器,以及在 .role 文件中需要引用特定的 IP 地址、 网络地址和地址范围等情况。
像这样动态生成防火墙规则集对于云基础设施是非常有用的, 云中的服务器列表会因为节点的创建和销毁而不断地变化。 对于需要触发防火墙重建的任何资源,你只要在此资源中添加如下代码即可:
```
notify => Exec["run-iptables"],
```
你可能有一个由版本控制系统维护的或通过 **cloud API** (例如,Rackspace 或 Amazon EC2) 自动更新的 “主服务器列表(master server list)”。 可以在 Puppet 中将这个列表定义成一个 file 资源,通过在此资源中使用 notify 参数即可触发防火墙的重建,所以每次当你检入(check in)主服务器列表的变化, 每台机器上运行的 Puppet 将相应地更新其防火墙。
当然,这种高度的自动化意味着你需要对你检入的内容格外小心, 因为任何错误都可能会导致整个基础设施离线。
测试变更的一种好方法是对用于测试的 Puppet 配置清单使用一个单独的 Git 分支, 在分支中仅将变更应用到一到两台服务器。一旦你验证了变更的正确性, 就可以将其合并到主分支并回滚到主分支。
- Puppet 2.7 Cookbook 中文版
- 中文翻译版
- 译者序
- 项目缘起
- 翻译方法
- 社区链接
- 社区建议
- 贡献者
- 原书版权页
- 关于作者
- 前言
- 本书内容
- 阅读前提
- 适用读者
- 格式约定
- 读者反馈
- 客户支持
- 下载案例代码
- 勘误表
- Puppet 基础设施
- 使用版本控制
- 使用提交钩子
- 使用 Rake 部署变更
- 配置 Puppet 的文件服务器
- 从 cron 运行 Puppet
- 使用自动签名
- 预签名证书
- 从 Puppet 的 filebucket 检索文件
- 使用 Passenger 扩展 Puppet 的部署规模
- 创建去中心化的分布式 Puppet 架构
- 监控、报告和排错
- 生成报告
- 通过 Email 发送包含特定标签的日志信息
- 创建图形化报告
- 自动生成 HTML 文档
- 绘制依赖关系图
- 测试你的 Puppet 配置清单
- 执行模拟运行
- 检测编译错误
- 理解 Puppet 的错误信息
- 显示命令的输出结果
- 输出调试信息
- 检查配置设置
- 使用标签
- 使用运行阶段
- 使用不同的环境
- Puppet 语言及其写作风格
- 使用 Puppet 社区规范
- 使用模块
- 使用标准的命名规范
- 使用嵌入式 Ruby 代码
- 使用纯 Ruby 代码书写配置清单
- 遍历多个项目
- 书写强大的条件语句
- 在 if 语句中使用正则表达式
- 使用选择器和 case 语句
- 检测字符串中是否包含指定的值
- 使用正则表达式替换
- 书写更优质的配置清单
- 使用资源的数组
- 使用 define 资源
- 指定资源的依赖关系
- 使用节点继承
- 使用类的继承和重载
- 给类传递参数
- 书写可重用的跨平台配置清单
- 获得系统的环境信息
- 导入动态信息
- 从 CSV 文件导入数据
- 给 Shell 命令传递参数
- 使用文件和软件包
- 为配置文件添加配置行
- 使用 Augeas 自动修改配置文件
- 使用配置片段构建配置文件
- 使用 ERB 模板
- 在模板中遍历数组
- 从第三方仓库安装软件包
- 配置 APT 软件仓库
- 配置 GEM 仓库
- 从源码包自动构建软件
- 比较软件包的版本
- 用户和虚拟资源
- 使用虚拟资源
- 使用虚拟资源管理用户
- 管理用户基于密钥的 SSH 访问
- 管理用户的自定义文件
- 有效地分发 cron 任务
- 当文件更新时运行命令
- 使用主机资源
- 为文件资源指定多个源
- 使用文件资源递归地分发整个目录树
- 清理过期的旧文件
- 使用日程表资源
- 资源的审计
- 临时禁用资源
- 管理时区
- 应用程序
- 管理 Apache 服务
- 创建 Apache 虚拟主机
- 创建 Nginx 虚拟主机
- 创建 MySQL 数据库及用户
- 管理 Drupal 站点
- 管理 Rails 应用程序
- 服务器和云基础设施
- 部署 Nagios 监控服务器
- 使用 Heartbeat 构建高可用服务
- 管理 NFS 服务和文件共享
- 使用 HAProxy 为多个 web 服务器实现负载均衡
- 使用 iptables 管理防火墙
- 管理 Amazon 的 EC2 实例
- 使用 Vagrant 管理虚拟机
- 外部工具和 Puppet 生态环境
- 创建 Facter 的自定义 fact
- 在运行 Puppet 之前和之后执行命令
- 从 Shell 会话生成 Puppet 配置清单
- 从运行的系统上生成 Puppet 配置清单
- 使用 Puppet Dashboard
- 使用 Foreman
- 使用 MCollective
- 使用公共模块
- 使用外部节点分类器
- 创建自定义的资源类型
- 创建自定义的提供者