2017年2月9日 星期四

fwmp 懶人的通訊埠轉發指令

通訊埠轉發 貴哥不太想學 iptables 指令。 但是身為 IP 不夠用的網管, 經常有 ip forwarding 通訊埠轉發 的需求。 每次弱弱地抄指令都很怕抄錯。 於是寫了 fwmp 跟 fwmp.lxc 兩個小程式, 可以用一個指令幫區網內的一部機器 (可以是實體機、 kvm 虛擬機或 lxc 容器) 同時轉發好幾個通訊埠。 kvm/lxc 的部分, 我主要在 proxmox 4.2-2/725d76f0 的環境底下測試。 如果你在其他版本測試成功或失敗, 請留言分享一下。

先說, 我的程式很不專業。 如果你只需要幫 lxc 容器轉發, 建議採用比較專業的 lxc-expose-port。 我的語法是學他的; 但網路規則只套用最基本的功能。 如果你需要幫實體機或 proxmox 的虛擬機做 ip forwarding, 才考慮用我的程式。

一、 臨時/一次性的轉發

這一節跟 proxmox 無關。 假設你管理一個區網, 門口有一部負責做 NAT 的門神機 (例如 請 raspberry pi 當網路門神), 對外的網卡叫做 eth7; 區網內有一部 192.168.43.56。 不論它是實體機、 kvm 或 lxc 容器, 如果是臨時/一次性的轉發, 本節的指令都適用。

現在你想從外界透過門神機的 port 43562 用 ssh (port 22) 登入區網內的一部機器 192.168.43.56 並且想透過門神機的 port 43568 查看同樣這部機器的網頁 (port 80)。 請把 fwmp.perl 放到門神機的 /usr/sbin 底下, 改名為 fwmp 。 然後這樣下: fwmp -s eth7 add 192.168.43.56 43562:22 43568:80 就一次新增了兩條規則。 另外還有以下的用法:

  1. fwmp -s eth7 list 192.168.43.56 查看這部區網機有哪些埠透過這個方式連到外網。
  2. fwmp -s eth7 clear 192.168.43.56 清除這部區網機的所有 (透過 fwmp 所設定的) port forwarding。
  3. fwmp -s eth7 set 192.168.43.56 43561:21 先清除 (clear) 所有 port forwarding, 再新增 (add) 一條規則。

如果要查看 「透過 fwmp 所設定的所有規則」 (而不僅限於一部區網機), 可以這樣下: iptables-save | grep fwmp。 「在 iptables 規則上面加註解」 這招是從 這裡 學來的。

你可以把程式開頭的 s => 'vmbr0', 改成 s => 'eth7', 讓它預設轉發 eth7 上面的網路流量, 這樣就可省略每一句的 -s eth7

二、 proxmox 下的 kvm 設定

如果 192.168.43.56 是 proxmox 裡面的一部 kvm 虛擬機, 而且每次開機就是要轉發固定的那幾個 ports, 那麼可以把設定寫入 proxmox host 的 /etc/network/interfaces 裡面。 假設你的 proxmox 已設定好 NAT ( 教學1 教學2)。 我的設定稍微複雜一點, 有兩個 bridges: vmbr0 對外, 有固定 IP; vmbr43 負責 192.168.43.0/24 的區網。 其中 vmbr43 的段落長得類似這樣:

auto vmbr43
iface vmbr43 inet static
        address 192.168.43.1
        netmask 255.255.255.0
        bridge_ports none
        bridge_stp off
        bridge_fd 0
        post-up echo 1 > /proc/sys/net/ipv4/ip_forward
        post-up iptables -t nat -A POSTROUTING -s '192.168.43.0/24' -o vmbr0 -j MASQUERADE
        post-down iptables -t nat -D POSTROUTING -s '192.168.43.0/24' -o vmbr0 -j MASQUERADE
        post-up fwmp set 192.168.43.56 43562:22 43568:80
        post-down fwmp clear 192.168.43.56
        post-up fwmp set 192.168.43.57 43572:22 43578:80
        post-down fwmp clear 192.168.43.57

一直到 MASQUERADE 那兩句為止, 都是從其他地方 (例如前面的兩個教學文連結) 抄來的。 跟轉發相關的, 就是 fwmp 那四句, 幫兩部機器轉發共四個 ports。 如果像 一般的教學文直接下 iptables 指令, 就需要八句, 而且比較容易寫錯。

改完之後, 要在門神機 (proxmox host) 上面執行 service networking restart 才會生效。

把 guests 的設定寫在 host 的設定檔裡面, 不是很漂亮。 如果這些 fwmp 指令能夠放在 guest 各自的設定檔裡面會更好。 但是目前 proxmox 並沒有這樣的功能。 根據 1 2, 另一個方法是去 hack proxmox 的 script; 不過小的沒練過, 不敢在家裡亂學。

三、 proxmox 下的 lxc 設定

至於 proxmox 底下的 lxc guests, 則可以把轉發指令寫在個別的設定檔裡面。 假設我們想讓 271 號 lxc 借用門神機的 port 43712 擺攤, 提供 ssh 服務 (port 22)、 借用門神機的 port 43718 擺攤, 提供 http 服務 (port 80)。 又假設它的 IP 是 192.168.43.71。

  1. 把我的 fwmp.lxc.perl 抓到 /etc/lxc 底下、 改名為 /etc/lxc/fwmp.lxc 。
  2. 在 /etc/pve/lxc/271.conf 最後面加上兩句:
    lxc.network.script.up = /etc/lxc/fwmp.lxc set 192.168.43.71 43712:22 43718:80 > /root/error.log 2>&1
    lxc.network.script.down = /etc/lxc/fwmp.lxc clear 192.168.43.71 > /root/error.log 2>&1
    
  3. 在 /etc/pve/lxc/271.conf 裡面, 確認 net0: ...,name=eth0 這一句裡的 net0 跟 eth0 兩個數字必須一致! 如果不一致, 改任何一個皆可。

所以我的設定檔長得類似這樣:

... 
# 以上省略
net0: bridge=vmbr43,hwaddr=01:23:45:67:89:ab,ip=192.168.43.71/24,name=eth0,type=veth
net1: bridge=vmbr0,gw=12.34.56.254,hwaddr=01:23:45:67:89:cd,ip=12.34.56.78/24,name=eth1,type=veth
ostype: debian
rootfs: local-zfs:subvol-271-disk-1,size=8G
swap: 512

lxc.network.script.up = /etc/lxc/fwmp.lxc set 192.168.43.71 43712:22 43718:80 > /root/error.log 2>&1
lxc.network.script.down = /etc/lxc/fwmp.lxc clear 192.168.43.71 > /root/error.log 2>&1

以後每次啟動容器 pct start 271 或停止容器 pct stop 271 時, 就會自動設或清除定上述轉發規則。 如果失敗, 請查看 /root/error.log 內的錯誤訊息。 如果 /root/error.log 裡面只有 271 的 IP, 沒有錯誤訊息, 那就成功了 -- 可以刪除設定檔中 > /root/error.log 2>&1 那兩串, 並且略過以下的解釋。

四、 proxmox 下的 lxc 設定檔的進一步解釋

如果是 「不在 proxmox 環境底下的陽春 lxc」, 它的設定檔放在 /var/lib/lxc/271/config 。 但是在 proxmox 底下, 應該去改 /etc/pve/lxc/271.conf 才對, 因為 proxmox 會拿它來產生 (覆蓋掉) /var/lib/lxc/271/config 。 根據 手冊, 這個 (proxmox 專屬的) 設定檔裡也可以放入 陽春的 lxc 設定指令。 (所以用等號或冒號都可以。)

透過 lxc.network.script.up 啟動時, fwmp.lxc 會在最後面多接收到 (proxmox 系統幫我們補上的) $cname net down veth $iface 共五個額外的參數, 其中 $cname 是容器代號; $iface 是容器的網卡代號。 所以 fwmp.lxc 只是整理一下命令列; 最後還是呼叫 fwmp 來設定規則。

其實我在 fwmp 裡面還用 lxc-info -n $cname -iH 指令來查詢 lxc 的 $IP, 目的是想讓用戶可以用 $cname 取代 IP。 不過這只對執行中的 lxc 有效。 所以在 /etc/pve/lxc/$cname.conf 裡面還是直接寫 $IP 好了。

不知為什麼, 一旦加了 lxc.network.script.up = ... 這一句, proxmox 裡面的 lxc 反而就不會把容器的網卡加入它原屬的 bridge 裡面。 也就是說, 在 proxmox host 上面用 brctl show vmbr0 查詢, 會看不見 271 的網卡。 所以 fwmp.lxc 裡面還多加了一段 hack, 再把 271 的網卡加回去它所屬的 bridge 去。 問題是 lxc 容器可能不只一張網卡, 那到底從 host 這頭看見的網卡名稱, 跟從 guest 那頭看見的網卡名稱, 該如何對應呢? 只查到 複雜的解法 觀念教學文2; 查不到簡單的指令。 所以用 「假設 net0 跟 eth0 的數字一致」 的偷吃步來矇混過去。

五、 結論

感覺設計得醜醜的, 很多地方都是 hacks。 請留言幫忙改進吧。

沒有留言:

張貼留言