2017年5月5日 星期五

理解 iptables 的使用


網路上的文章幾乎都使用舊版說明,雖然過時,但仍然可以學點常識.
iptables 資訊:  http://www.iptables.info/en/iptables-contents.html
各鏈條流程圖:  http://www.iptables.info/files/tables_traverse.jpg

列出 iptables 指令的使用方式:
        iptables   --help

        iptables   -h

常用的命令列格式:
      iptables   規範命令   鏈條名稱   規範選項
或     
      iptables   規範選項   規範命令   鏈條名稱

其中 規範選項  的各項順序只要處理得當,可以依照喜好自由調換位置.iptables 鏈條(chain)的規範命令是以"-大寫簡稱"或"--小寫全名"擇ㄧ使用:
   -A 或 --append  用於附加鏈條規則
   -C 或 --check  用於檢查鏈條規則是否存在
   -D 或 --delete  用於刪除鏈條規則, 以數字 1 為基準當第一條鏈條
   -I 或 --insert  用於插入鏈條規則, 若沒設定插入位置, 預設是插入當作第一條規則
   -L 或 --list  用於詳細列出鏈條規則
   -S 或 --list-rules  用於顯示鏈條簡表
   -N 或 --new   用於自訂鏈條規則
   -P 或 --policy  用於鏈條的預設政策,當鏈條的具體行動沒指定時將啟用(ACCEPT 或  DROP)
   -E 或 --rename-chain  用於改變鏈條名稱
   -R 或 --replace  用於改變鏈條的順序
   -F 或 --flush  用於刪除鏈條的規則
   -X 或 --delete-chain  用於刪除自訂鏈條
   -Z 或 --zero   用於鏈條計數器歸零

目前版本(version 1.6.0) iptables 內部共有五個表格(filter, mat, mangle,raw,security)可由規範選項 -t 來指定),每個表格各有不同的鏈條名稱,使用 -L 可以列出各表格的鏈條規則:
         $iptables    -t    filter    -L
由此來看 filter 有  INPUT,   FORWARDOUTPUT   3個鏈條,接著看:
         $iptables    -t   nat     -L
因此 nat 應該有  REROUTINGINPUTOUTPUTPOSTROUTING   4個鏈條,再來看:
         $iptables    -t   mangle    -L
所以 mangle 有  PREROUTINGINPUTFORWARDOUTPUTPOSTROUTING   5個鏈條
另外兩個 raw, security 似乎比較少用,詳細可上網找文章閱讀看看.

規範選項部份除了版本(-V--version)以外,都是以"-小寫簡稱"或"--小寫全名"擇ㄧ使用,有些選項可以使用驚嘆號 ! (反邏輯),將選項的邏輯允以反向(也就是在原本用意加上'非'這個字眼),例如:
  !  -p 或 --protocol  用於網路協定,像是  tcp 或 udp 或 icmp 或 all
  !  -s 或 --source  用於來源網址或網域, 像是  ipaddress 或 ipaddress/mask 之類的位址
  !  -d 或 --destination 用於目標網址或網域,像是 ipaddress 或 ipaddress/mask之 類的位址
  !  -i  或  --in-interface  用於來源網路界面, 像是 lo eth0 之類的名稱
  !  -o 或 --out-interface  用於目標網路界面, 像是 lo eth0 之類的名稱
  !  -f 或  --fragment  用於像是片狀的封包 ???
 只要不加驚嘆號, 就如字面上意義(正邏輯).

只能用正邏輯的規範選項有 :
   -j 或  --jump  指定目標(target), 可用 ACCEPT 或 REJECT 或 DROP
   -g 或 --goto  直接跳進指定的鏈條規範不返回
   -m 或 --match  使用指定模組來拓展匹配
   -t 或 --table   指定表格,可有 filter 或 nat 或 mangle 來過濾封包, 若未指定,則內定用 filter
   -w 或  --wait  等待表格鎖定時間,以秒為單位
   -v 或  --verbose  顯示冗餘資訊,像是目前計數器的數值... 等等

其它正邏輯選項還有:
   -4 或 --ipv4   用於 ipv4  的封包 ???
   -6 或 --ipv6  用於 ipv6 的封包 ???
   --modprobe=載入外部模組 ???
   -n 或 --numeric   網路位址(ip address/netmask)及端口(port)使用數字展開
   -x 或 --exact  展開數字 ???
   --line-numbers  當顯示列表時,前面附加行數的順序.

在 linux mint 系統上執行 iptables 必須要有 superuser 權限,自行定義一個變數(例如 iptables) 便於重複利用並簡化輸入:
    iptables='sudo iptables'
 
將鏈條名稱與規範選項合併就成為該鏈條的一個規則, iptables 如果定義規則時沒用選項 -t 來指定來表格,將會用 filter 當作內定表格.而且不同的表格將有不同的規範選項,因此底下說明可能只適用於 filter 表格(等同使用了 -t filter).

假設要設定 filter 表格內 OUTPUT 鏈條的規範,使用選項匹配模組(-m owner )去匹配是否等同指定的使用者(--uid-owner   $USER),如果選項匹配成功,執行指定目標(-j   REJECT ).將上述規則定義成一個變數 rule 方便後續使用:
    rule="OUTPUT   -m   owner   --uid-owner   $USER  -j   REJECT"

鏈條規範的執行流程會從所有設定規則的第一條開始一個個往下匹配,直到匹配成功去執行具體行動,如果在規則中沒有用 -j 設定目標,則會往下一條規則繼續匹配,如果全部規則都匹配不成,也就無具體行動,因此該鏈條的預設政策就會啟用(預設政策的規範選項僅可以是 ACCEPTDROP),要改變預設政策,使用規範命令 -P,例如:
     $iptables -P INPUT ACCEPT
     $iptables -P FORWARD  DROP
     $iptables -P OUTPUT  ACCEPT

使用  -A 添加規則到最後一條:
    $iptables   -A   $rule

如果認為規則很重要,可以使用 -I 插隊變成第一條規則:
    $iptables   -I   $rule

-L 可以詳細列出表格鏈條的規則,附加 --line-numbers 會將規則的順序一起列出:
    $iptables   -L   --line-numbers

或是用 -S 顯示表格鏈條的簡表:
    $iptables   -S

只要語法正確,就會被加入到鏈條的規則內, 因此相同的規則可能會重複,因此先檢查再添加可以防止重複添加,使用 -C 可以先檢查想啟用的規則是否存在,若存在只要輸出該規則的訊息:
    $iptables   -C   $rule   &&  echo Exist:$rule  ||  $iptables   -A   $rule

如果上述規則設定成功, 使用瀏覽器試試看,應該無法上網了.用 -D 可以刪除剛剛設定的規則:
    $iptables   -D   $rule

-D 只刪除第一個匹配到的規則,如果規則有重複,只會刪除第一個.如有需要可以執行 -D 多次直到刪除為止,或是用 -F 一次清除內部鏈條(但不含自訂名稱)所有的規則
    $iptables   -F  OUTPUT

-N 可以在表格自訂新鏈條,例如自訂一個有意義的鏈條名稱 OUTtcp:
     $iptables   -N   OUTtcp

一旦自訂鏈條名稱定義完成,就可以像正規鏈條一樣去定義規則:
     $iptables    -A   OUTtcp    -j   DROP

在相同表格內的自訂鏈條經由鏈條規範,使用選項 -j 當成目標(target)跳進去,例如:
     $iptables    -A   OUTPUT   -p   tcp   -j   OUTtcp

-X 可以清除自訂的鏈條(由 -N 指令去定義的鏈條),連同自訂鏈條名稱及規則都一併清除,如果 -X 後面不指定鏈條名稱,則清除全數自訂的鏈條:
    $iptables   -X   OUTtcp

用  -F  可以清除鏈條的規則,如果後面不指定表格,則只清除預設表格(-t filter)的鏈條規則(但不會清除自訂鏈條的名稱):
     $iptables   -F   OUTPUT

通常一開始設定 iptables 時,不僅要清除所有鏈條(包含自訂的)規則,連計數器也都一併清除為零,避免被無端的規則所干擾.
清除預設表自訂鏈條:
    $iptables   -X
清除預設表鏈條規則:
    $iptables   -F
清除預設表鏈條計數器:
    $iptables   -Z 

要注意的是 -P 所設定的預設政策(預設是全數鏈條 ACCEPT 開放)並不會因為清除而重設,只要之前有設定好,它就會持續有效.常用的伺服器端口,可以檢查 /etc/services 檔案內容,就可以略知一二,它包含有 tcp 及 udp 兩種通信協定:
    cat /etc/services | more

在 linux mint 系統,有些內部的 porcess 可能會使用 udp 端口相互溝通,如果全擋,怕會無法運作,可以用 ifconfig 查一下對外 ip 的界面,目前查到的是 enp2s0,因此管制該網路界面的輸出鍊,就能抵擋大部分的網路運作了.先清除所有規則,設定好預設政策,再分別設定 tcp 及 udp 的規則,把不符合規則的端口都丟棄:
    $iptables   -F
    $iptables   -P   INPUT   ACCEPT
    $iptables   -P   OUTPUT   ACCEPT
    $iptables   -A   OUTPUT   -o  enp2s0   -p    tcp     -j    DROP
    $iptables   -A   OUTPUT   -o  enp2s0   -p    udp    -j    DROP 

這樣子就阻擋了對外所有網路第三層的 tcp/udp 通信協定.執行 ping www.google.com.tw 看看,應該無法得到回應.但若直接用 ping 168.95.192.1 卻是可以的, 那是因為 ip 屬於第二層通信協定.只要插隊設定一條輸出鏈(-I   OUTPUT)的規則,管制網路界面(-o   enp2s0)的通信協定(-p   udp),比對端口(--dport   53),確認行動(-j   ACCEPT):
   $iptables   -I   OUTPUT -o   enp2s0   -p   udp   --dport   53   -j    ACCEPT

把 udp 端口 53 打開,主機就能向DNS(Domain Name Server)查詢到 ip 位址,接著就會得到 ip/icmp 回應,但此時仍無法用瀏覽器瀏覽網頁, 因為 http 是用 tcp 端口 80, 而 https 是用 tcp 端口 443.為了一次開放多個 tcp 端口,可以插隊設定一條輸出鏈(-I OUTPUT)的規則,管制通信協定(-p tcp),調用匹配模組(-m multiport),比對離散端口(--dport 80,443),確認行動(-j ACCEPT):
   $iptables   -I  OUTPUT  -o   enp2s0   -p   tcp -m multiport   --dport   80,443   -j  ACCEPT

此時用瀏覽器上網應該就能暢行無阻了,對於其它 tcp  端口,例如 ssh 端口 22, ftp 端口 21 等都可依樣畫葫蘆自行設定規則,總結這次瀏覽網頁所需設定的規則,使用預設表格(-t filter):
    iptables='sudo iptables'
    $iptables   -F
    $iptables   -P  INPUT   ACCEPT
    $iptables   -P  OUTPUT   ACCEPT
    $iptables   -A  OUTPUT   -o  enp2s0   -p  tcp     -j    DROP
    $iptables   -A  OUTPUT   -o  enp2s0   -p  udp    -j    DROP
    $iptables   -I   OUTPUT   -o  enp2s0   -p  udp   --dport   53   -j    ACCEPT
    $iptables   -I   OUTPUT   -o  enp2s0   -p  tcp -m  multiport   --dport   80,443   -j  ACCEPT

nat 表格的轉向(REDIRECT)的功能.由插入 nat 表格事前輸入鍊(-I PREROUTING  -t nat)當範例,當從外部(-i   enp2s0)接收到的封包時,原本要傳給 tcp 端口 80(-p  tcp  --dport 80)的,事先改成(REDIRECT)端口 3128(--to-port  3128),之後才進主機作後續鏈條的處理(可先參考網址有關鏈條的流程會比較清楚脈絡: http://www.iptables.info/files/tables_traverse.jpg)
    $iptables -I  PREROUTING   -t   nat  -i   enp2s0  -p  tcp   --dport  80   -j   REDIRECT   --to-port   3128 

送出去的封包更換端口也可以透過轉向的功能,由插入 nat 表格的輸出(-I OUTPUT -t nat)當範例,將原本要送出 tcp 端口 8888(-p tcp --dport 8888)的封包,換成(REDIRECT)端口 80(--to-port  80)後直接丟出去:
    $iptables -I  OUTPUT  -t   nat  -p   tcp  --dport   8888  -j   REDIRECT   --to-port   80

上述端口轉向並不一定會用到 ip 路由(Route)的功能,如果只是轉向特定端口,就不必將 /proc/sys/net/ipv4/ip_forward 打開.要將 linux 當作 ip 分享器,需利用超級使用者的權限開啟 ip_forward 的功能,再用 iptables 設定 nat 表格的事後鍊(POSTROUTING)把送出封包的來源位址(soure  ip)直接替換成主機位址,有兩種方式可以達成 ip 分享的目的:

第一個,當主機的位址是動態取得時,簡單設定 nat 表格的事後鍊(-A  POSTROUTING  -t  nat)讓 linux 內核把要送出去的封包(-o   enp2s0)自動改裝(-j MASQUERADE),變成 ip 分享器:
    sudo su
    echo 1 > /proc/sys/net/ipv4/ip_forward
    iptables  -A 
POSTROUTING  -t  nat    -o   enp2s0    -j   MASQUERADE


另一個是當主機固定位址時,把要送出去的封包(-o   enp2s0),直接用主機位址(假設 ip = 10.0.0.1)替換掉來源位址( -j SNAT --to-source),雖然麻煩一點,但速度較快:
    iptables  -A  POSTROUTING  -t  nat   -o   enp2s0   -j  SNAT --to-source 10.0.0.1 

如果要處理經 nat 改裝的封包,可以設定事前(-A PREROUTING -t nat),由網路界面收到的封包(-i   enp2s0),事先更換封包的目標位址(-j  DNAT --to-desination)由主機(10.0.0.1)來取代,藉此將它轉給主機再作後續處理:
    iptables  -A  PREROUTING  -t  nat  -i   enp2s0   -j  DNAT --to-desination 10.0.0.1
.

沒有留言: