本章將和大家分享Linux中的許可權控制。廢話不多說,下麵我們直接進入主題。 一、基礎知識 Linux作為一種多用戶的操作系統(伺服器系統),允許多個用戶同時登陸到系統上,並響應每個用戶的請求。 任何需要使用操作系統的用戶,都需要一個系統賬號,賬號分為:管理員賬號與普通用戶賬號。 在Linux中,操作 ...
10 網路應用程式和服務
本章探討基本的網路應用--在用戶空間運行的客戶端和伺服器,它們位於應用層。由於這一層位於堆棧的頂層,離最終用戶很近,因此你可能會發現這部分內容比第 9 章的內容更容易理解。事實上,你每天都在與網路瀏覽器等網路客戶端應用程式交互。
為了完成工作,網路客戶端需要連接到相應的網路伺服器。Unix 網路伺服器有多種形式。伺服器程式可以自己監聽埠,也可以通過輔助伺服器進行監聽。我們將介紹一些常見的伺服器,以及有助於瞭解和調試伺服器運行的工具。
網路客戶端使用操作系統的傳輸層協議和介面,因此瞭解 TCP 和 UDP 傳輸層的基礎知識非常重要。讓我們從使用 TCP 的網路客戶端開始瞭解網路應用。
10.1 服務基礎
TCP服務最容易理解,因為它們建立在簡單、不間斷的雙向數據流基礎之上。瞭解它們如何工作的最佳方法也許是直接與 TCP 80 埠上的未加密網路伺服器通信,以瞭解數據是如何在連接中移動的。例如,運行以下命令連接到 IANA 文檔示例網路伺服器:
$ telnet localhost 80
您應該會得到如下響應,表明與伺服器的連接成功:
$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
...
$ telnet localhost 81 # 沒有開放的埠
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
現在輸入這兩行
GET / HTTP/1.1
# 按兩次ENTER鍵。伺服器會發送一堆 HTML 文本作為回應。要終止連接,請按 CTRL-D。兩次ENTER鍵也會退出。
HTTP/1.1 408 Request Timeout
Date: Fri, 26 Jul 2024 03:00:44 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 296
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>408 Request Timeout</title>
</head><body>
<h1>Request Timeout</h1>
<p>Server timeout waiting for the HTTP request from the client.</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at 127.0.1.1 Port 80</address>
</body></html>
Connection closed by foreign host.
註意:HTTP 1.1 和它的前身 HTTP 1.0 一樣,已經過時;現在使用的是 HTTP/2、QUIC 和新興的 HTTP/3 等更新的協議。
在最後一行後按兩次 ENTER 鍵。伺服器會發送一堆 HTML 文本作為回應。要終止連接,請按 CTRL-D。
本練習演示了
- 本地網路伺服器進程在監聽 TCP 80 埠。
- telnet 是發起連接的客戶端。
必須按 CTRL-D 鍵終止連接的原因是,由於大多數網頁需要多次請求才能載入,因此保持連接開放是合理的。如果你在協議層面探索網路伺服器,你可能會發現這種行為各不相同。例如,許多伺服器在連接打開後,如果沒有很快收到請求,就會迅速斷開連接。
telnet 最初是用來登錄遠程主機的。客戶端程式可能沒有預設安裝在你的發行版上,但很容易作為一個額外的軟體包安裝。儘管 telnet 遠程登錄伺服器完全不安全(稍後將瞭解到),但 telnet 客戶端在調試遠程服務時非常有用。如果你正在尋找一個通用的網路客戶端,可以考慮第 10.5.3 節中介紹的 netcat。
10.2 近距離觀察
在上一個示例中,你通過 telnet 使用 HTTP 應用層協議與網路上的網路伺服器進行了手動交互。雖然你通常會使用網路瀏覽器來進行這種連接,但讓我們在 telnet 的基礎上更進一步,使用一個知道如何與 HTTP 應用層對話的命令行程式。我們將使用帶有特殊選項的 curl 工具來記錄通信細節:
$ curl --trace-ascii trace_file http://www.example.org/
你會得到大量 HTML 輸出。忽略它(或將其重定向到 /dev/null),轉而查看新創建的文件 trace_file。如果連接成功,在curl嘗試與伺服器建立TCP連接時,文件的第一部分應該如下所示:
== Info: Trying 93.184.216.34...
== Info: TCP_NODELAY set
== Info: Connected to www.example.org (93.184.216.34) port 80 (#0)
到目前為止,你所看到的一切都發生在傳輸層或以下。然而,如果連接成功,curl 會嘗試發送請求(“頭”);這是應用層開始的地方:
=> Send header, 79 bytes (0x4f)
2 0000: GET / HTTP/1.1
0010: Host: www.example.org
0027: User-Agent: curl/7.58.0
0040: Accept: */*
004d:
第1行是curl的調試輸出,告訴你接下來要做什麼。其餘幾行顯示了curl發送給伺服器的內容。粗體文字是發送給伺服器的內容;開頭的十六進位數字是curl添加的調試偏移量,幫助你跟蹤發送或接收了多少數據。
在第2行,你可以看到curl首先向伺服器發送了一條GET命令(就像使用telnet一樣),然後是一些額外的伺服器信息和一行空行。接下來,伺服器會發送一個回覆,首先是它自己的報頭:
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 22 bytes (0x16)
0000: Accept-Ranges: bytes
<= Recv header, 12 bytes (0xc)
0000: Age: 17629
--snip--
和前面的輸出很像,<=行是調試輸出,0000:在輸出行之前,用來告訴你偏移量(在curl中,頭不計入偏移量,這就是為什麼所有這些行都以0開頭)。
伺服器回覆中的標頭可能相當長,但在某些時候,伺服器會從發送標頭過渡到發送實際請求的文件,就像下麵這樣:
<= Recv header, 22 bytes (0x16)
0000: Content-Length: 1256
<= Recv header, 2 bytes (0x2)
1 0000:
<= Recv data, 1256 bytes (0x4e8)
0000: <!doctype html>.<html>.<head>. <title>Example Domain</title>.
0040: . <meta charset="utf-8" />. <meta http-equiv="Content-type
--snip--
該輸出還說明瞭應用層的一個重要屬性。儘管調試輸出顯示的是Recv header和Recv data,暗示這是來自伺服器的兩種不同的信息,但curl如何與操作系統對話以獲取這兩種信息、操作系統如何處理它們,以及網路如何處理下麵的數據包,都沒有任何區別。curl知道在此之前它一直在獲取報文頭,但當它接收到HTTP報文頭結束的空行1時,它就知道要把後面的內容理解為請求的文檔。
發送這些數據的伺服器也是如此。在發送回覆時,伺服器的操作系統並不區分報頭和文檔數據;這種區分發生在用戶空間伺服器程式中。
10.3 網路伺服器
大多數網路伺服器與系統中的其他伺服器守護進程(如 cron)一樣,只是它們與網路埠交互。事實上,第 7 章討論的 syslogd 在使用 -r 選項啟動時,就會接受 514 埠的 UDP 數據包。
以下是其他一些常見的網路伺服器,你可能會發現它們正在你的系統上運行:
- httpd、apache、apache2、nginx 網路伺服器
- sshd 安全 shell 守護進程
- postfix、qmail、sendmail 郵件伺服器
- cupsd 列印伺服器
- nfsd、mountd 網路文件系統(文件共用)守護進程
- smbd、nmbd Windows 文件共用守護進程(參見第 12 章)
- rpcbind 遠程過程調用(RPC)埠映射服務守護進程
大多數網路伺服器的一個共同特點是,它們通常作為多個進程運行。至少有一個進程在網路埠上監聽,當收到新的傳入連接時,監聽進程會使用 fork() 創建一個子進程,然後由子進程負責新的連接。子進程(通常稱為工作進程)會在連接關閉時終止。與此同時,原始監聽進程繼續監聽網路埠。通過這種進程,伺服器可以輕鬆地處理許多連接,而不會有太多麻煩。
不過,這種模式也有一些例外。調用 fork() 會增加大量系統開銷。為了避免這種情況,高性能 TCP 伺服器(如 Apache 網路伺服器)可能會在啟動時創建多個工作進程,以便在需要時處理連接。接受 UDP 數據包的伺服器根本不需要分叉,因為它們不需要監聽連接;它們只需接收數據並作出反應。
10.3.1 Secure Shell
每個網路伺服器程式的工作方式都有些不同。為了親身體驗伺服器的配置和運行,讓我們仔細看看獨立的安全外殼(SSH)伺服器。作為最常見的網路服務應用程式之一,SSH 是遠程訪問 Unix 機器的事實標準。SSH 的設計目的是實現安全的 shell 登錄、遠程程式執行、簡單的文件共用等功能--它取代了舊式的、不安全的 telnet 和 rlogin 遠程訪問系統,使用公鑰加密技術進行身份驗證,並使用更簡單的密碼來處理會話數據。大多數互聯網服務提供商和雲服務提供商都要求使用 SSH 通過 shell 訪問其服務,許多基於 Linux 的網路設備(如網路附加存儲設備或 NAS 設備)也通過 SSH 提供訪問。OpenSSH (http://www.openssh.com/) 是一種流行的 Unix 免費 SSH 實現,幾乎所有 Linux 發行版都預裝了它。OpenSSH 的客戶端程式是 ssh,伺服器程式是 sshd。SSH 協議主要有兩個版本: 1 和 2。OpenSSH 只支持版本 2,由於存在漏洞和缺乏使用,已經放棄了對版本 1 的支持。
SSH 有許多有用的功能和特性,其中包括以下功能:
- 加密你的密碼和所有其他會話數據,保護你不被窺探。
- 隧道其他網路連接,包括來自 X 視窗系統客戶端的網路連接。(你將在第 14 章學到更多關於 X 的知識)。
- 為幾乎所有操作系統提供客戶端。
- 使用密鑰進行主機身份驗證。
註意:隧道傳輸是將一個網路連接打包並傳輸到另一個網路連接中的過程。使用 SSH 對 X 視窗系統連接進行隧道傳輸的優點是,SSH 會為你設置顯示環境,並對隧道內的 X 數據進行加密。
SSH 也有一些缺點。首先,為了建立 SSH 連接,你需要遠程主機的公鑰,而且不一定能以安全的方式獲得(不過你可以手動檢查以確保你沒有被欺騙)。要瞭解幾種密碼學方法的工作原理,請閱讀《嚴肅密碼學》(Serious Cryptography)一書: 現代加密實用入門》(No Starch Press,2017 年),作者 Jean-Philippe Aumasson。關於 SSH 的兩本深度書籍是 SSH Mastery: 第 2 版,Michael W. Lucas 著(Tilted Windmill Press,2018 年)和 SSH,The Secure Shell: 第 2 版,作者 Daniel J. Barrett、Richard E. Silverman 和 Robert G. Byrnes(O'Reilly,2005 年)。
公鑰加密:我們一直在使用公鑰這個術語,但並沒有太多的上下文,所以讓我們回過頭來簡單討論一下,以防你對它不熟悉。直到 20 世紀 70 年代,加密演算法都是對稱的,要求信息的發送方和接收方擁有相同的密鑰。破解密碼需要竊取密鑰,而擁有密鑰的人越多,密鑰被泄露的機會就越多。但使用公開密鑰加密技術時,有兩個密鑰:公開密鑰和私人密鑰。公開密鑰可以加密信息,但不能解密;因此,誰能獲得這把密鑰並不重要。只有私人密鑰才能解密來自公開密鑰的信息。在大多數情況下,保護私人密鑰更容易,因為只需一份副本,而且不必傳輸。
加密之外的另一個應用是身份驗證;有一些方法可以在不傳輸任何密鑰的情況下驗證某人是否持有給定公鑰的私鑰。
10.3.2 sshd服務
運行sshd服務允許遠程連接到系統需要配置文件和主機密鑰。大多數發行版會將配置文件保存在 /etc/ssh 配置目錄中,併在您安裝其 sshd 軟體包時為您正確配置一切。(伺服器配置文件名為 sshd_config,很容易與客戶端的 ssh_config 設置文件混淆,所以要小心)。
你應該不需要更改 sshd_config 文件中的任何內容,但檢查一下也無妨。該文件由鍵值對組成,如本片段所示。
Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
以 # 開頭的行是註釋,sshd_config 中的許多註釋表示各種參數的預設值,如本節選所示。sshd_config(5) 手冊頁麵包含參數和可能值的說明,但這些是最重要的:
- HostKey 文件 將文件用作主機密鑰。(接下來將介紹主機密鑰)。
- PermitRootLogin 值 如果值設置為 yes,則允許超級用戶使用 SSH 登錄。將值設為否則無法登錄。
- LogLevel 級別 以 syslog 級別(預設為 INFO)記錄信息。
- SyslogFacility name 使用 syslog 設施名稱(預設為 AUTH)記錄信息。
- X11Forwarding 值 如果值設置為 yes,則啟用 X 視窗系統客戶端隧道。
- XAuthLocation path 指定系統中 xauth 實用程式的位置。沒有該路徑,X 隧道將無法運行。如果 xauth 不在 /usr/bin,則將路徑設為 xauth 的完整路徑名。
OpenSSH 有多個主機密鑰集。每個密鑰集都有一個公鑰(擴展名為 .pub 文件)和一個私鑰(沒有擴展名)。不要讓任何人看到私鑰,即使是在你自己的系統上,因為如果有人獲取了私鑰,你就會面臨被入侵的風險。
SSH 版本 2 有 RSA 和 DSA 密鑰。RSA 和 DSA 是公鑰加密演算法。密鑰文件名如表 10-1 所示。
表 10-1: OpenSSH 密鑰文件
創建密鑰需要進行數字計算,生成公鑰和私鑰。通常情況下,你不需要創建密鑰,因為 OpenSSH 安裝程式或發行版的安裝腳本會為你創建密鑰,但如果你計劃使用 ssh-agent 等程式,提供無密碼的身份驗證服務,就需要知道如何創建密鑰。要創建 SSH 協議版本 2 密鑰,請使用 OpenSSH 自帶的 ssh-keygen 程式:
# ssh-keygen -t rsa -N '' -f /etc/ssh/ssh_host_rsa_key
# ssh-keygen -t dsa -N '' -f /etc/ssh/ssh_host_dsa_key
SSH 伺服器和客戶端還使用名為 ssh_known_hosts 的密鑰文件來存儲來自其他主機的公鑰。如果要使用基於遠程客戶端身份的身份驗證,伺服器的 ssh_known_hosts 文件必須包含所有受信任客戶端的主機公鑰。如果要更換機器,瞭解密鑰文件會很方便。從頭開始安裝新機器時,可以從舊機器導入密鑰文件,確保用戶在連接新機器時不會出現密鑰不匹配的情況。
10.3.2.1 啟動 SSH 伺服器
雖然大多數發行版都自帶 SSH,但它們通常不會預設啟動 sshd 伺服器。在 Ubuntu 和 Debian 上,新系統不會安裝 SSH 伺服器(openssh-server);安裝其軟體包會創建密鑰、啟動伺服器,並將伺服器啟動添加到啟動配置中。
Fedora 預設安裝了 sshd,但已關閉。要在啟動時啟動 sshd,請像這樣使用 systemctl:
# systemctl enable sshd
如果想立即啟動伺服器而無需重啟,請使用
# systemctl start sshd
Fedora 通常會在第一次啟動 sshd 時創建任何缺失的主機密鑰文件。
如果您運行的是其他發行版,可能不需要手動配置 sshd 啟動。不過,你應該知道有兩種啟動模式:獨立模式和按需模式。獨立伺服器要常見得多,只需以 root 身份運行 sshd 即可。sshd 伺服器進程會將其 PID 寫入 /var/run/sshd.pid(當然,當由 systemd 運行時,它也會被其 cgroup 跟蹤,這在第 6 章中已有介紹)。
作為替代方案,systemd 可以通過套接字單元按需啟動 sshd。這通常不是個好主意,因為伺服器偶爾需要生成密鑰文件,而這個過程可能需要很長時間。
10.3.3 fail2ban
如果在機器上設置 SSH 伺服器並將其開放到互聯網上,很快就會發現不斷有人試圖入侵。如果你的系統配置正確,而且你沒有選擇愚蠢的密碼,這些暴力破解攻擊是不會成功的。不過,它們會很煩人,消耗 CPU 時間,還會不必要地擾亂日誌。
為了防止這種情況,您需要建立一種機制來阻止重覆登錄嘗試。到目前為止,fail2ban 軟體包是最常用的方法;它只是一個監視日誌信息的腳本。當看到某台主機在一定時間內發出了一定數量的失敗請求時,fail2ban 就會使用 iptables 創建一條規則,拒絕來自該主機的流量。經過一段指定時間後(在此期間,主機可能已經放棄了連接),fail2ban 會刪除該規則。
大多數 Linux 發行版都提供了預設 SSH 預設值的 fail2ban 軟體包。
10.3.4 SSH 客戶端
要登錄遠程主機,請運行
$ ssh remote_username@remote_host
如果本地用戶名與遠程主機上的用戶名相同,則可以省略 remote_username@。您還可以在 ssh 命令之間運行管道,如下麵的示例所示,將目錄 dir 複製到另一臺主機:
$ tar zcvf - dir | ssh remote_host tar zxvf -
全局 SSH 客戶端配置文件 ssh_config 應位於 /etc/ssh,與 sshd_config 文件位於同一位置。與伺服器配置文件一樣,客戶端配置文件也有鍵值對,但不需要更改。
使用 SSH 客戶端最常見的問題是本地 ssh_known_hosts 或 .ssh/known_hosts 文件中的 SSH 公鑰與遠程主機上的密鑰不匹配。錯誤的密鑰會導致類似的錯誤或警告:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
38:c2:f6:0d:0d:49:d4:05:55:68:54:2a:2f:83:06:11.
Please contact your system administrator.
Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
1 Offending key in /home/user/.ssh/known_hosts:12
RSA host key for host has changed and you have requested
strict checking.
Host key verification failed.
這通常意味著遠程主機的管理員更改了密鑰(硬體或雲伺服器升級時經常會發生這種情況),但如果不確定,向管理員確認一下也無妨。無論如何,前面的信息告訴你,壞密鑰位於用戶已知主機文件 1 的第 12 行。
如果你不懷疑有詐,只需刪除違規行或用正確的公鑰替換即可。
10.3.4.1 SSH文件傳輸客戶端
OpenSSH 包含文件傳輸程式 scp 和 sftp,這兩個程式可替代舊式的不安全程式 rcp 和 ftp。你可以使用 scp 將文件傳輸到遠程機器或從遠程機器傳輸到你的機器,或從一臺主機傳輸到另一臺主機。它的工作原理與 cp 命令類似。下麵是幾個例子。
從遠程主機複製文件到當前目錄:
$ scp user@host:file .
將文件從本地機器複製到遠程主機:
$ scp file user@host:dir
將文件從一臺遠程主機複製到另一臺遠程主機:
$ scp user1@host1:file user2@host2:dir
sftp 程式與過時的命令行 ftp 客戶端類似,使用 get 和 put 命令。遠程主機必須安裝有 sftp-server 程式,如果遠程主機也使用 OpenSSH,則可以使用該程式。
如果你需要比 scp 和 sftp 更多的功能和靈活性(例如,如果你經常傳輸大量文件),請參考第 12 章中介紹的 rsync。
10.3.4.2 非Unix平臺的SSH客戶端
有適用於所有流行操作系統的 SSH 客戶端。你應該選擇哪個?PuTTY 是一款優秀的基本 Windows 客戶端,包含一個安全的文件拷貝程式。MacOS 基於 Unix,包含 OpenSSH。另外Windows下麵的MobaXterm用戶甚至更多。
10.3.4.3 免密登錄實例
# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): /root/.ssh/my_id
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/my_id.
Your public key has been saved in /root/.ssh/my_id.pub.
The key fingerprint is:
1c:ee:bb:73:b2:42:34:02:e2:85:bf:c9:97:01:d1:f7 [email protected]
The key's randomart image is:
+--[ RSA 2048]----+
| ..o |
|...o . . |
|..o.. . o |
| . ...oo E |
| . ooo.S |
| + o.. |
| .. . |
| . o.. |
| .+* |
+-----------------+
You have new mail in /var/spool/mail/root
# ssh-copy-id root@tf2
# ssh tf2
註意sshd_config中PubkeyAuthentication要設置為yes。參考:https://www.ibm.com/support/pages/configuring-ssh-login-without-password
10.4 systemd之前的網路連接伺服器:inetd/xinetd
在 systemd 和第 6.3.7 節中介紹的套接字單元得到廣泛使用之前,有一些伺服器提供了構建網路服務的標準方法。許多次要網路服務對連接的要求非常相似,因此為每項服務安裝獨立伺服器的效率會很低。每個伺服器都必須單獨配置,以處理埠監聽、訪問控制和埠配置。對於大多數服務來說,這些操作都是以相同的方式執行的;只有當伺服器接受連接時,才會以不同的方式處理通信。
簡化伺服器使用的一種傳統方法是使用 inetd 守護進程,它是一種超級伺服器,旨在規範網路埠訪問以及伺服器程式和網路埠之間的介面。啟動 inetd 後,它會讀取配置文件,然後監聽文件中定義的網路埠。當有新的網路連接進入時,inetd 會將一個新啟動的進程連接到該連接上。
inetd 的更新版本 xinetd 提供了更簡便的配置和更好的訪問控制,但 xinetd 幾乎已被 systemd 完全取代。不過,在舊系統或不使用 systemd 的系統上,你可能會看到它。
TCP WRAPPERS: TCPPD、/etc/hosts.allow 和 /etc/hosts.deny
在 iptables 等低級防火牆流行之前,許多管理員使用 TCP 封裝庫和守護進程來控制對網路服務的訪問。在這些實現中,inetd 運行 tcpd 程式,首先查看傳入連接以及 /etc/hosts.allow 和 /etc/hosts.deny 文件中的訪問控制列表。tcpd 程式會記錄連接,如果它認為傳入連接沒有問題,就會將其交給最終服務程式。你可能會遇到仍在使用 TCP 封裝系統的系統,但我們不會詳細介紹,因為它在很大程度上已不再使用。
10.5 診斷工具
讓我們來看看一些對探查應用層有用的診斷工具。有些工具會深入到傳輸層和網路層,因為應用層中的所有內容最終都會映射到這些較低層中的內容。
如第 9 章所述,netstat 是一種基本的網路服務調試工具,可以顯示大量傳輸層和網路層統計數據。表 10-2 列出了一些用於查看連接的有用選項。
表 10-2:netstat 的有用連接報告選項
10.5.1 lsof
在第 8 章中,你瞭解到 lsof 不僅可以跟蹤打開的文件,還可以列出當前使用或監聽埠的程式。要獲取此類程式的完整列表,請運行
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rpcbind 700 root 6u IPv4 10492 0t0 UDP *:sunrpc 1
rpcbind 700 root 8u IPv4 10508 0t0 TCP *:sunrpc (LISTEN)
avahi-dae 872 avahi 13u IPv4 21736375 0t0 UDP *:mdns 2
cupsd 1010 root 9u IPv6 42321174 0t0 TCP ip6-localhost:ipp (LISTEN) 3
ssh 14366 juser 3u IPv4 38995911 0t0 TCP thishost.local:55457-> 4
somehost.example.com:ssh (ESTABLISHED)
chromium- 26534 juser 8r IPv4 42525253 0t0 TCP thishost.local:41551-> 5
anotherhost.example.com:https (ESTABLISHED)
以普通用戶身份運行該命令時,只會顯示該用戶的進程。以 root 用戶身份運行該命令時,輸出結果應如下所示,顯示各種進程和用戶:
此示例輸出顯示了伺服器和客戶端程式的用戶和進程 ID,從最上面的老式 RPC 服務 1,到 avahi 提供的多播 DNS 服務 2,甚至到 IPv6 就緒的印表機服務 cupsd 3。最後兩個條目顯示的是客戶端連接:SSH 連接 4 和來自 Chromium 網路瀏覽器的安全網路連接 5。由於輸出內容可能非常廣泛,通常最好使用過濾器(如下節所述)。
lsof 程式與 netstat 程式一樣,會嘗試將找到的每個 IP 地址反向解析為主機名,從而減慢輸出速度。使用 -n 選項可禁用名稱解析:
# lsof -n -i
還可以指定 -P 來禁用 /etc/services 埠名稱查找。
如果要查找某個埠(例如,知道某個進程正在使用某個埠,並想知道該進程是什麼),請使用此命令:
# lsof -i:port
完整語法如下
# lsof -iprotocol@host:port
協議、@host 和 :port 參數都是可選的,它們將相應地過濾 lsof 的輸出。與大多數網路工具一樣,host 和 port 可以是名稱或數字。例如,如果只想查看 TCP 埠 443(HTTPS 埠)上的連接,請使用
# lsof -iTCP:443
要根據 IP 版本進行過濾,請使用 -i4 (IPv4) 或 -i6 (IPv6)。您可以將其作為單獨選項添加,也可以在更複雜的過濾器中直接添加數字(例如,-i6TCP:443)。
你可以從 /etc/services 中指定服務名稱(如 -iTCP:ssh),而不是數字。
連接狀態是一個特別方便的 lsof 過濾器。例如,要只顯示監聽 TCP 埠的進程,請輸入
# lsof -iTCP -sTCP:LISTEN
通過該命令,您可以很好地概覽系統上當前運行的網路伺服器進程。不過,由於 UDP 伺服器不監聽,也沒有連接,因此必須使用 -iUDP 才能查看正在運行的客戶端和伺服器。這通常不是問題,因為系統中可能不會有很多 UDP 伺服器。
10.5.2 tcpdump
通常情況下,您的系統不會去理會那些不屬於其 MAC 地址的網路流量。如果您需要查看穿越網路的確切信息,tcpdump 會將您的網路介面卡設置為混雜模式,並報告穿越網路的每個數據包。輸入不帶參數的 tcpdump 會產生如下輸出,其中包括一個 ARP 請求和網路連接:
# tcpdump
tcpdump: listening on eth0
20:36:25.771304 arp who-has mikado.example.com tell duplex.example.com
20:36:25.774729 arp reply mikado.example.com is-at 0:2:2d:b:ee:4e
20:36:25.774796 duplex.example.com.48455 > mikado.example.com.www: S 3200063165:3200063165(0) win 5840 <mss 1460,sackOK,timestamp 38815804[|tcp]> (DF)
20:36:25.779283 mikado.example.com.www > duplex.example.com.48455: S 3494716463:3494716463(0) ack 3200063166 win 5792 <mss 1460,sackOK,timestamp 4620[|tcp]> (DF)
20:36:25.779409 duplex.example.com.48455 > mikado.example.com.www: . ack 1 win 5840 <nop,nop,timestamp 38815805 4620> (DF)
20:36:25.779787 duplex.example.com.48455 > mikado.example.com.www: P 1:427(426) ack 1 win 5840 <nop,nop,timestamp 38815805 4620> (DF)
20:36:25.784012 mikado.example.com.www > duplex.example.com.48455: . ack 427 win 6432 <nop,nop,timestamp 4620 38815805> (DF)
20:36:25.845645 mikado.example.com.www > duplex.example.com.48455: P 1:773(772) ack 427 win 6432 <nop,nop,timestamp 4626 38815805> (DF)
20:36:25.845732 duplex.example.com.48455 > mikado.example.com.www: . ack 773 win 6948 <nop,nop,timestamp 38815812 4626> (DF)
9 packets received by filter
0 packets dropped by kernel
你可以通過添加過濾器讓 tcpdump 更具針對性。您可以根據源主機和目標主機、網路、乙太網地址、網路模型中許多不同層的協議等進行過濾。tcpdump 可識別的許多數據包協議包括 ARP、RARP、ICMP、TCP、UDP、IP、IPv6、AppleTalk 和 IPX 數據包。例如,要讓 tcpdump 只輸出 TCP 數據包,請運行
# tcpdump tcp
要查看網路數據包和 UDP 數據包,請輸入
# tcpdump udp or port 80 or port 443
關鍵字 or 指定左邊或右邊的條件必須為真才能通過過濾器。同樣,和關鍵字要求兩個條件都為真。
如果需要進行大量的數據包嗅探,可以考慮使用圖形用戶界面來替代 tcpdump,如 Wireshark。
在前面的示例中,tcp、udp 和埠 80 是被稱為基元的過濾器的基本元素。表 10-3 列出了最重要的基元。
tcpdump 可以使用多個運算符(如 and 和 !),還可以用括弧將運算符分組。如果你打算認真使用 tcpdump,請務必閱讀 pcap-filter(7) 手冊,尤其是描述基元的部分。
註意:使用 tcpdump 時要小心。本節前面顯示的 tcpdump 輸出只包括數據包 TCP(傳輸層)和 IP(互聯網層)報頭信息,但你也可以讓 tcpdump 列印整個數據包內容。儘管現在大多數重要的網路流量都已通過 TLS 加密,但除非你擁有這些網路或得到許可,否則不應隨意窺探。
參考資料
- 軟體測試精品書籍文檔下載持續更新 https://github.com/china-testing/python-testing-examples 請點贊,謝謝!
- 本文涉及的python測試開發庫 謝謝點贊! https://github.com/china-testing/python_cn_resouce
- python精品書籍下載 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品書籍下載 https://www.cnblogs.com/testing-/p/17438558.html
10.5.3 netcat
netcat 可以連接遠程 TCP/UDP 埠、指定本地埠、監聽埠、掃描埠、將標準 I/O 重定向到網路連接或從網路連接重定向到標準 I/O 等。要使用 netcat 打開一個埠的 TCP 連接,請運行
$ netcat host port
如果將標準輸入重定向到 netcat,netcat 會在對方結束連接時終止,這可能會讓人感到困惑,因為在發送數據後,你可能無法得到提示(與幾乎所有其他命令管道不同)。你可以隨時按下 CTRL-C 結束連接。(如果你希望程式和網路連接根據標準輸入流終止,請嘗試使用 sock 程式)。
要監聽某個埠,請運行
$ netcat -l port_number
如果 netcat 成功監聽到埠,它就會等待連接,一旦建立連接,就會列印該連接的輸出,並將任何標準輸入發送到該連接。
下麵是關於 netcat 的一些補充說明:
- 預設情況下沒有太多調試輸出。如果出現故障,netcat 會無聲地失敗,但會設置一個適當的退出代碼。如果需要更多信息,可添加 -v (“verbose”)選項。
- 預設情況下,netcat 客戶端會嘗試連接 IPv4 和 IPv6。不過,在伺服器模式下,netcat 預設使用 IPv4。要強制使用協議,IPv4 使用-4,IPv6 使用-6。
- -u 選項指定 UDP 而不是 TCP。
10.5.4 埠掃描
有時,你甚至不知道網路上的機器在提供什麼服務,甚至不知道哪些 IP 地址在使用。Network Mapper (Nmap) 程式會掃描機器或機器網路上的所有埠,尋找開放的埠,併列出找到的埠。大多數發行版都有 Nmap 軟體包,也可以從 http://www.insecure.org/ 獲取。(有關 Nmap 的所有功能,請參閱 Nmap 手冊頁面和線上資源)。
在列出自己機器上的埠時,通常至少從兩點運行 Nmap 掃描:從自己的機器和另一臺機器(可能在本地網路之外)。這樣做可以讓您大致瞭解防火牆阻止了哪些埠。
如果你想用 Nmap 掃描的網路是由其他人控制的,請先徵得允許。網路管理員會監視埠掃描,通常會禁止訪問運行埠掃描的機器。
運行 nmap host 在主機上運行一般掃描。例如
$ nmap 10.1.2.2
Starting Nmap 5.21 ( http://nmap.org ) at 2015-09-21 16:51 PST
Nmap scan report for 10.1.2.2
Host is up (0.00027s latency).
Not shown: 993 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
80/tcp open http
111/tcp open rpcbind
8800/tcp open unknown
9000/tcp open cslistener
9090/tcp open zeus-admin
Nmap done: 1 IP address (1 host up) scanned in 0.12 seconds
正如你在這裡看到的,許多服務都是開放的,其中許多在大多數發行版中都不是預設開啟的。事實上,這裡唯一預設開啟的是 111 埠,即 rpcbind 埠。
如果添加 -6 選項,Nmap 還能掃描 IPv6 埠。這可以方便地識別不支持 IPv6 的服務。
10.6 遠程過程調用
上一節中掃描的 rpcbind 服務怎麼樣?RPC 是遠程過程調用(remote procedure call,簡稱 RPC)的縮寫,是一個位於應用層底層的系統。它的設計目的是讓程式員更容易構建客戶端/伺服器網路應用程式,其中客戶端程式調用在遠程伺服器上執行的函數。每種遠程伺服器程式都有一個指定的程式編號。
RPC 實現使用 TCP 和 UDP 等傳輸協議,需要一個特殊的中介服務將程式編號映射到 TCP 和 UDP 埠。該伺服器稱為 rpcbind,它必須運行在任何想要使用 RPC 服務的電腦上。
要查看電腦上有哪些 RPC 服務,請運行
$ rpcinfo -p localhost
RPC 是一種不死的協議。網路文件系統(NFS)和網路信息服務(NIS)系統都使用 RPC,但它們在單機上完全沒有必要。但每當你認為已經完全不需要 rpcbind 時,就會有其他東西出現,例如 GNOME 中的文件訪問監視器 (FAM) 支持。
10.7 網路安全
由於Linux 是 PC 平臺上非常流行的 Unix,尤其是它被廣泛用於網路伺服器,因此吸引了許多不懷好意的人試圖入侵電腦系統。第 9.25 節討論了防火牆,但這並不是安全問題的全部。
網路安全會吸引極端分子--那些非常喜歡入侵系統的人(無論是為了好玩還是賺錢),以及那些精心設計保護方案並非常喜歡將試圖入侵其系統的人趕走的人。(幸運的是,要確保系統安全,你並不需要知道太多。以下是幾條基本經驗法則:
運行儘可能少的服務 入侵者無法侵入系統中不存在的服務。如果你知道某項服務是什麼,但你並沒有使用它,那麼就不要僅僅因為 “以後可能會用到 ”而打開它。
用防火牆儘可能多地阻止 Unix 系統有許多內部服務,但你可能並不知道(如 RPC 埠映射伺服器的 TCP 111 埠),世界上任何其他系統都不應該知道這些服務。由於許多不同類型的程式會監聽不同的埠,因此要跟蹤和監管系統中的服務非常困難。為了防止入侵者發現系統中的內部服務,請使用有效的防火牆規則,併在路由器上安裝防火牆。
跟蹤您向互聯網提供的服務 如果您運行 SSH 伺服器、Postfix 或類似服務,請及時更新軟體並獲取相應的安全警報。(有關線上資源,請參見第 10.7.2 節)。
伺服器使用 “長期支持 ”發行版 安全團隊的工作通常集中在穩定、受支持的發行版上。開發和測試版本(如 Debian Unstable 和 Fedora Rawhide)受到的關註要少得多。
不要給不需要的人提供系統賬戶 從本地賬戶獲得超級用戶訪問許可權要比遠程入侵容易得多。事實上,由於大多數系統上都有大量的軟體(以及由此產生的錯誤和設計缺陷),因此在進入 shell 提示符後,很容易就能獲得系統的超級用戶訪問許可權。不要以為你的朋友知道如何保護自己的密碼(或者一開始就選擇了好的密碼)。
避免安裝可疑的二進位軟體包 它們可能包含木馬。
這就是保護自己的實用方法。但為什麼要這樣做呢?針對 Linux 機器的網路攻擊有三種基本類型:
- 完全入侵 這意味著獲得機器的超級用戶許可權(完全控制)。入侵者可以通過嘗試服務攻擊(如緩衝區溢出漏洞),或接管保護不力的用戶賬戶,然後嘗試利用編寫不佳的 setuid 程式來實現這一目的。
- 拒絕服務(DoS)攻擊 無需使用任何特殊訪問許可權,就能阻止電腦執行網路服務,或迫使電腦以其他方式出現故障。通常,DoS 攻擊只是網路請求的泛濫,但也可能是利用伺服器程式中的漏洞導致崩潰。這類攻擊較難防範,但較容易應對。
- 惡意軟體 Linux 用戶對電子郵件蠕蟲和病毒等惡意軟體大多免疫,原因很簡單,因為他們的電子郵件客戶端還沒蠢到會實際運行郵件附件中發送的程式。但 Linux 惡意軟體確實存在。避免從你從未聽說過的地方下載和安裝可執行文件。
10.7.1 典型漏洞
有兩種基本類型的漏洞需要擔心:直接攻擊和明文密碼嗅探。直接攻擊試圖接管機器,但並不十分隱蔽。最常見的攻擊方式之一是在系統中找到未受保護或易受攻擊的服務。這可以是簡單的預設未驗證服務,例如沒有密碼的管理員賬戶。一旦入侵者可以訪問系統上的某項服務,他們就可以利用它試圖入侵整個系統。過去,常見的直接攻擊是緩衝區溢出漏洞,即粗心的程式員沒有檢查緩衝區數組的邊界。內核中的地址空間佈局隨機化(ASLR)技術和其他地方的保護措施在一定程度上緩解了這種情況。
明文密碼嗅探攻擊會捕獲以明文形式通過網路發送的密碼,或使用從眾多數據泄露事件中提取的密碼資料庫。一旦攻擊者獲取了你的密碼,游戲就結束了。從這裡開始,攻擊者將不可避免地試圖在本地獲得超級用戶訪問許可權(如前所述,這比遠程攻擊要容易得多),或試圖將機器用作攻擊其他主機的中介,或兩者兼而有之。
註意:如果需要運行不提供本機加密支持的服務,可以試試 Stunnel (http://www.stunnel.org/),這是一個加密封裝包,與 TCP 封裝包很相似。Stunnel 尤其擅長封裝通常使用 systemd 套接字單元或 inetd 激活的服務。
由於實施和設計不當,有些服務長期成為攻擊目標。你應該始終停用以下服務(這些服務目前都已過時,而且在大多數系統中很少被預設激活):
- ftpd 不管出於什麼原因,所有 FTP 伺服器似乎都存在漏洞。此外,大多數 FTP 伺服器使用明文密碼。如果需要將文件從一臺機器轉移到另一臺機器,請考慮使用基於 SSH 的解決方案或 rsync 伺服器。
- telnetd、rlogind、rexecd 所有這些服務都以明文形式傳遞遠程會話數據(包括密碼)。除非使用啟用 Kerberos 的版本,否則應避免使用這些服務。
10.7.2 安全資源
這裡有三個很好的安全資源:
- SANS Institute (http://www.sans.org/) 提供培訓、服務、列出當前頂級漏洞的免費每周通訊、安全策略示例等。
- 卡內基梅隆大學軟體工程研究所的 CERT 部門 (http://www.cert.org/) 是查找最嚴重問題的好地方。
- 黑客和 Nmap 創建者 Gordon “Fyodor” Lyon (http://www.insecure.org/) 的項目 Insecure.org 是查找 Nmap 和各種網路漏洞測試工具的好去處。與許多其他網站相比,該網站在漏洞利用方面更加開放和具體。
如果你對網路安全感興趣,就應該瞭解一下傳輸層安全(TLS)及其前身安全套接字層(SSL)。這些用戶空間網路級別通常被添加到網路客戶端和伺服器中,通過使用公鑰加密和證書來支持網路交易。Davies 的 Implementing SSL/TLS Using Cryptography and PKI(Wiley,2011 年)或 Jean-Philippe Aumasson 的 Serious Cryptography(《嚴肅密碼學》)是一本很好的指南: 現代加密實用入門》(No Starch Press,2017 年)。
10.8 展望未來
如果你有興趣接觸一些複雜的網路伺服器,一些非常常見的伺服器包括 Apache 或 nginx 網路伺服器和 Postfix 電子郵件伺服器。網路伺服器尤其易於安裝,大多數發行版都提供了安裝包。如果你的機器位於防火牆或支持 NAT 的路由器後面,你可以隨意嘗試配置,而不必擔心安全問題。
在過去的幾章中,我們逐漸從內核空間進入了用戶空間。本章討論的實用程式中,只有 tcpdump 等少數幾個與內核交互。本章其餘部分將介紹套接字如何在內核傳輸層和用戶空間應用層之間架起橋梁。這是程式員特別感興趣的高級內容,如果你願意,可以跳到下一章。
10.9 網路套接字
現在我們將換個角度,看看進程是如何完成從網路讀取數據和向網路寫入數據的工作的。對於進程來說,從已經建立的網路連接中讀取數據和向網路連接中寫入數據都非常簡單:只需要一些系統調用,你可以在 recv(2) 和 send(2) 手冊中找到相關信息。從進程的角度來看,最需要知道的也許是在使用這些系統調用時如何訪問網路。在 Unix 系統中,進程使用套接字來標識何時以及如何與網路對話。套接字是進程通過內核訪問網路的介面,代表了用戶空間和內核空間的邊界。它們通常也用於進程間通信(IPC)。
由於進程需要以不同方式訪問網路,因此有不同類型的套接字。例如,TCP 連接由流套接字(SOCK_STREAM,從程式員的角度來看)表示,UDP 連接由數據報套接字(SOCK_DGRAM)表示。
網路套接字的設置可能有些複雜,因為你需要在特定的時候考慮套接字類型、IP 地址、埠和傳輸協議。不過,在理清所有初始細節後,伺服器會使用某些標準方法來處理從網路傳入的流量。圖 10-1 中的流程圖顯示了許多伺服器如何處理傳入流套接字的連接。
請註意,這種伺服器涉及兩種套接字:一種用於監聽,一種用於讀寫。主進程使用監聽套接字查找來自網路的連接。當有新連接進入時,主進程會使用 accept() 系統調用來接受連接,併為該連接創建專用的讀寫套接字。接著,主進程使用 fork() 創建一個新的子進程來處理該連接。最後,原始套接字仍作為監聽器,代表主進程繼續尋找更多連接。
當一個進程設置了一個特定類型的套接字後,它就能以適合套接字類型的方式與之交互。這就是套接字的靈活性所在:如果你需要改變底層傳輸層,你不必重寫所有發送和接收數據的部分;你主要需要修改初始化代碼。
如果你是一名程式員,並想學習如何使用套接字介面,W. Richard Stevens、Bill Fenner 和 Andrew M. Rudoff 合著的《Unix 網路編程》第 1 捲第 3 版(Addison-Wesley Professional,2003 年)是一本經典指南。第 2 捲也涉及進程間通信。
10.10 Unix 域套接字
使用網路設施的應用程式不必涉及兩個獨立的主機。許多應用程式都是作為客戶機-伺服器或點對點機制構建的,其中在同一臺機器上運行的進程使用進程間通信來協商需要完成哪些工作以及由誰來完成。例如,systemd 和 NetworkManager 等守護進程使用 D-Bus 監控系統事件並做出反應。
進程之間可以通過本地主機(127.0.0.1 或 ::1 )使用常規 IP 網路進行通信,但它們通常使用一種稱為 Unix 域套接字的特殊套接字作為替代。當進程連接到 Unix 域套接字時,其行為幾乎與網路套接字完全相同:可以監聽和接受套接字上的連接,甚至可以選擇不同的套接字類型,使其行為與 TCP 或 UDP 相似。
註意:請記住,Unix 域套接字不是網路套接字,其背後也沒有網路。使用域套接字甚至不需要配置網路。Unix 域套接字也不必綁定到套接字文件。一個進程可以創建一個未命名的 Unix 域套接字,並與另一個進程共用地址。
開發人員喜歡將 Unix 域套接字用於 IPC 有兩個原因。首先,它們允許在文件系統中使用特殊的套接字文件來控制訪問許可權,因此任何無法訪問套接字文件的進程都無法使用它。另外,由於不與網路交互,所以更簡單,也不容易受到傳統網路入侵。例如,你通常可以在 /var/run/dbus 中找到 D-Bus 的套接字文件:
$ ls -l /var/run/dbus/system_bus_socket
srwxrwxrwx 1 root root 0 Nov 9 08:52 /var/run/dbus/system_bus_socket
其次,由於 Linux 內核在處理 Unix 域套接字時無需經過網路子系統的許多層,因此性能往往要好得多。
為 Unix 域套接字編寫代碼與支持普通網路套接字並無太大區別。由於可以帶來很大的好處,一些網路伺服器同時通過網路和 Unix 域套接字進行通信。例如,MySQL 資料庫伺服器 mysqld 可以接受來自遠程主機的客戶端連接,但通常也會在 /var/run/mysqld/mysqld.sock 提供一個 Unix 域套接字。
您可以使用 lsof -U 查看系統上當前使用的 Unix 域套接字列表:
# lsof -U
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 19701 mysql 12u unix 0xe4defcc0 0t0 35201227 /var/run/mysqld/mysqld.sock
chromium- 26534 juser 5u unix 0xeeac9b00 0t0 42445141 socket
tlsmgr 30480 postfix 5u unix 0xc3384240 0t0 17009106 socket
tlsmgr 30480 postfix 6u unix 0xe20161c0 0t0 10965 private/tlsmgr
--snip--
由於許多應用程式大量使用未命名的套接字,因此列表會很長,這些套接字在 NAME 輸出列中用 socket 表示。