### 使用 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
- 使用公共模塊
- 使用外部節點分類器
- 創建自定義的資源類型
- 創建自定義的提供者