Linux 網路工具中的瑞士軍刀 - socat & netcat

於清樂發表於2021-04-16

獨立部落格閱讀:https://ryan4yin.space/posts/socat-netcat/

個人筆記,不保證正確!

文中的命令均在 macOS Big Sur 和 Opensuse Tumbleweed 上測試通過

socat & netcat

netcat(network cat) 是一個歷史悠久的網路工具包,被稱作 TCP/IP 的瑞士軍刀,各大 Linux 發行版都有預設安裝 openbsd 版本的 netcat,它的命令列名稱為 nc.

而 socat(socket cat),官方文件描述它是 "netcat++" (extended design, new implementation),專案比較活躍,kubernetes-client(kubectl) 底層就是使用的它做各種流量轉發。

在不方便安裝 socat 的環境中,我們可以使用系統自帶的 netcat.
而在其他環境,可以考慮優先使用 socat.

一、簡介

socat 的基本命令格式:

socat [引數] 地址1 地址2

給 socat 提供兩個地址,socat 乾的活就是把兩個地址的流對接起來。左邊地址的輸出傳給右邊,同時又把右邊地址的輸出傳給左邊,也就是一個雙向的資料管道

聽起來好像沒啥特別的,但是實際上計算機網路乾的活也就是資料傳輸而已,卻影響了整個世界,不可小覷它的功能。

socat 支援非常多的地址型別:-/stdio,TCP, TCP-LISTEN, UDP, UDP-LISTEN, OPEN, EXEC, SOCKS, PROXY 等等,可用於埠監聽、連結,檔案和程式讀寫,代理橋接等等。

socat 的功能就是這麼簡單,命令列引數也很簡潔,唯一需要花點精力學習的就是它各種地址的定義和搭配寫法。

而 netcat 定義貌似沒這麼嚴謹,可以簡單的理解為網路版的 cat 命令 2333

二、安裝方法

各發行版都自帶 netcat,包名通常為 nc-openbsd,因此這裡只介紹 socat 的安裝方法:

# Debian/Ubuntu
sudo apt install socat

# CentOS/RedHat
sudo yum install socat

# macOS
brew install socat

其他發行版基本也都可以使用包管理器安裝 socat

三、常用命令

1. 網路除錯

1.1 檢測遠端埠的可連線性(確認防火牆沒問題)

以前你可能學過如何用 telnet 來做這項測試,不過現在很多發行版基本都不自帶 telnet 了,還需要額外安裝。
telnet 差不多已經快壽終正寢了,還是建議使用更專業的 socat/netcat

使用 socat/netcat 檢測遠端埠的可連線性:

# -d[ddd] 增加日誌詳細程度,-dd  Prints fatal, error, warning, and notice messages.
socat -dd - TCP:192.168.1.252:3306

# -v 顯示詳細資訊
# -z 不傳送資料,效果為立即關閉連線,快速得出結果
nc -vz 192.168.1.2 8080

# -vv 顯示更詳細的內容
# -w2 超時時間設為 2 秒
# 使用 nc 做簡單的埠掃描
nc -vv -w2 -z 192.168.1.2 20-500

1.2 測試本機埠是否能正常被外部訪問(檢測防火牆、路由)

在本機監聽一個 TCP 埠,接收到的內容傳到 stdout,同時將 stdin 的輸入傳給客戶端:

# 服務端啟動命令,socat/nc 二選一
socat TCP-LISTEN:7000 -
# -l --listening
nc -l 7000

# 客戶端連線命令,socat/nc 二選一
socat TCP:192.168.31.123:7000 -
nc 192.168.11.123 7000

UDP 協議的測試也非常類似,使用 netcat 的示例如下:

# 服務端,只監聽 ipv4
nc -u -l 8080

# 客戶端
nc -u 192.168.31.123 8080
# 客戶端本機測試,注意 localhost 會被優先解析為 ipv6! 這會導致服務端(ipv4)的 nc 接收不到資料!
nc -u localhost 8080

使用 socat 的 UDP 測試示例如下:

socat UDP-LISTEN:7000 -

socat UDP:192.168.31.123:7000 -

1.3 除錯 TLS 協議

參考 socat 官方文件:Securing Traffic Between two Socat Instances Using SSL

測試證照及私鑰的生成參見 [TLS 協議、TLS 證照、TLS 證照的配置方法、TLS 加密的破解手段]({{< ref "about-tls-cert/index.md" >}})

模擬一個 mTLS 伺服器,監聽 4433 埠,接收到的資料同樣輸出到 stdout:

# socat 需要使用同時包含證照和私鑰的 pem 檔案,生成方法如下
cat server.key server.crt > server.pem
cat client.key client.crt > client.pem

# 服務端啟動命令
socat openssl-listen:4433,reuseaddr,cert=server.pem,cafile=client.crt -

# 客戶端連線命令
socat - openssl-connect:192.168.31.123:4433,cert=client.pem,cafile=server.crt
# 或者使用 curl 連線(我們知道 ca.crt 和 server.crt 都能被用做 cacert/cafile)
curl -v --cacert ca.crt --cert client.crt --key client.key --tls-max 1.2 https://192.168.31.123:4433

上面的命令使用了 mTLS 雙向認證的協議,可通過設定 verify=0 來關掉客戶端認證,示例如下:


# socat 需要使用同時包含證照和私鑰的 pem 檔案,生成方法如下
cat server.key server.crt > server.pem

# 服務端啟動命令
socat openssl-listen:4433,reuseaddr,cert=server.pem,verify=0 -

# 客戶端連線命令,如果 ip/域名不受證照保護,就也需要新增 verify=0
socat - openssl-connect:192.168.31.123:4433,cafile=server.crt
# 或者使用 curl 連線,證照無效請新增 -k 跳過證照驗證
curl -v --cacert server.crt https://192.168.31.123:4433

2. 資料傳輸

通常傳輸檔案時,我都習慣使用 scp/ssh/rsync,但是 socat 其實也可以傳輸檔案。

以將 demo.tar.gz 從主機 A 傳送到主機 B 為例,
首先在資料傳送方 A 執行如下命令:

# -u 表示資料只從左邊的地址單向傳輸給右邊(socat 預設是一個雙向管道)
# -U 和 -u 相反,資料只從右邊單向傳輸給左邊
socat -u open:demo.tar.gz tcp-listen:2000,reuseaddr

然後在資料接收方 B 執行如下命令,就能把檔案接收到:

socat -u tcp:192.168.1.252:2000 open:demo.tar.gz,create
# 如果覺得太繁瑣,也可以直接通過 stdout 重定向
socat -u tcp:192.168.1.252:2000 - > demo.tar.gz

使用 netcat 也可以實現資料傳輸:

# 先在接收方啟動服務端
nc -l -p 8080 > demo.tar.gz
# 再在傳送方啟動客戶端傳送資料
nc 192.168.1.2 8080 < demo.tar.gz

3. 擔當臨時的 web 伺服器

使用 fork reuseaddr SYSTEM 三個命令,再用 systemd/supervisor 管理一下,就可以用幾行命令實現一個簡單的後臺伺服器。

下面的命令將監聽 8080 埠,並將資料流和 web.py 的 stdio 連線起來,可以直接使用瀏覽器訪問 http://<ip>:8080 來檢視效果。

socat TCP-LISTEN:8080,reuseaddr,fork SYSTEM:"python3 web.py"

假設 web.py 的內容為:

print("hello world")

curl localhost:8080 就應該會輸出 hello world

4. 埠轉發

監聽 8080 埠,建立該埠與 baidu.com:80 之間的雙向管道:

socat TCP-LISTEN:8080,fork,reuseaddr  TCP:baidu.com:80

拿 curl 命令測試一下,應該能正常訪問到百度:

# 注意指定 Host
curl -v -H 'Host: baidu.com' localhost:8080

參考

相關文章