1. 程式人生 > >SSL/TLS深度解析--TLS性能優化

SSL/TLS深度解析--TLS性能優化

1.5 情況 out sha chang 連接 數據鏈路 優化 init

TCP 優化

Linux系統內核參數優化

[root@www ~]# cat /etc/redhat-release;uname -r
CentOS Linux release 7.5.1804 (Core) 
3.10.0-862.11.6.el7.x86_64

TLS 的下一層是 TCP 協議,所以對 TCP 的優化是可以直接影響到 TLS 的性能效率。

在對 TCP 的優化中,主要涉及以下幾個概念:

(1)擁塞控制(congestion control)機制:在一個 TCP 連接開始時,不知道對方的速度有多快。如果有足夠大的帶寬,服務器端可以用最快的速度傳送數據,但是如果對方的網絡很慢,服務器發送的數據太多的話,會壓跨連接,導致連接中斷。所以,每個 TCP 連接都有一個稱為擁塞窗口(cwnd = congestion window)的速度極限。這個窗口最初較小,在通信的過程中,如果雙方都能接受這個速度,那麽會加大這個擁塞窗口的值(初期增長很快,翻倍增長),這種機制被叫做慢啟動(slow start)。

擁塞控制機制對於 TLS 連接的影響比較大,TLS 握手消耗了寶貴的初始連接字節(當擁塞窗口較小時);如果擁塞窗口足夠大,那麽慢啟動不會有額外的延遲。但是,如果握手消息長度超過了擁塞窗口大小,發送方將必須把這個長信息拆分成兩塊,先發送一塊,等待確認(1個往返),增加擁塞窗口,然後再發送剩下的部分。這樣就增加了由 TLS 握手造成的延時。

(2) 慢啟動閾值 ssthresh(避免 cwnd 增長過快,網絡無法承擔,造成丟包)

如果 cwnd 小於 ssthresh,表示在慢啟動階段,cwnd是翻倍增長的;如果 cwnd 大於 ssthresh,那麽表示在擁塞避免階段,這時候 cwnd 不再像慢啟動階段那樣翻倍增長,而是線性增長,盡量避免網絡擁塞。

(3) 接收窗口(rwnd),用來表示最多能保存多少數據,實際中接收窗口rwnd的合理值取決於BDP的大小,也就是帶寬和延遲的乘積。如果帶寬是 80Mbps,延遲是 100ms,那麽計算過程如下:

BDP = 80Mbps 100ms = (80 / 8) (100 / 1000) = 1MB = 1024KB = 1048576B

TCP 用16位來記錄窗口大小,也就是說最大值是64KB,如果超過這個值,就需要 tcp_window_scaling 機制(默認是開啟)。配置內核參數中接收緩沖的大小,就可以控制接收窗口的大小:

net.ipv4.tcp_rmem = <MIN> <DEFAULT> <MAX>

Linux本身有一個緩沖大小自動調優的機制,窗口的實際大小會自動在最小值和最大值之間變化,找到性能和資源的平衡點。確認緩沖大小自動調優機制(0:關閉、1:開啟):sysctl -a | grep tcp_moderate_rcvbuf。如果緩沖大小自動調優機制設置成關閉狀態,那麽就把緩沖的 DEFAULT 值設置為 BDP;如果緩沖大小自動調優機制設置成開啟狀態,那麽就把緩沖的 MAX 設置為 BDP。

(4) 存儲 TCP 連接本身一些信息的額外開銷:net.ipv4.tcp_adv_win_scale 的值可能是 1 或者 2,如果是 1 的話,則表示二分之一的緩沖被用來做額外開銷,如果是 2 的話,則表示四分之一的緩沖被用來做額外開銷。按照這個邏輯,緩沖最終的合理值的具體計算方法如下:result=BDP / (1 – 1 / 2^tcp_adv_win_scale)。

(5) 空閑連接回到慢啟動:慢啟動在一段時間內沒有任何流量的連接上起作用,達到降低速度的效果,並且速度下降非常快。所謂的“一段時間”可以是非常小的,比如1秒鐘,但在實際場景中,每一個長連接(例如使用 HTTP 長連接)的速度都有可能被調到很慢!為了保持速度建議禁用這個功能。

在 Linux 上,可以在連接空閑時禁用慢啟動: 0表示否,1表示開啟慢啟動,默認是1

可以通過一下命令使其臨時生效,但重啟以後就失效了,查看:sysctl -a | gerp slow_start_after_idle

臨時:sysctl -w net.ipv4.tcp_slow_start_after_idle=0

永久生效:將 net.ipv4.tcp_slow_start_after_idle=0 設置添加到 /etc/sysctl.conf 配置文件中。

對擁塞窗口(cwnd)初始值調優:

啟動速度限制被稱為初始擁塞窗口(initial congestion window, initcwnd )。2013年4月發布的 RFC6928,google 建議默認情況下初始擁塞窗口設置為10個 MSS(約15 KB)。【Centos 7默認是10MSS】早期的建議是使用2或4個MSS(約3—6KB)。MSS 是 TCP 層上的概念,大小是 1460 字節。IP 層上是 MTU,1500字節。

[root@www ~]# sysctl -a |grep ssthresh
net.ipv4.tcp_max_ssthresh = 0   #在虛擬機中
[root@www ~]# sysctl -a |grep tcp_window_scaling
net.ipv4.tcp_window_scaling = 1
[root@www ~]# cat /proc/sys/net/ipv4/tcp_rmem    # rwnd值
4096    87380   6291456
[root@www ~]# sysctl -a |grep tcp_moderate_rcvbuf        
net.ipv4.tcp_moderate_rcvbuf = 1
[root@www ~]# sysctl -a |grep adv_win_scale
net.ipv4.tcp_adv_win_scale = 1
[root@www ~]# sysctl -a |grep start_after_idle
net.ipv4.tcp_slow_start_after_idle = 1

# 設置cwnd
[root@www ~]# ip route
default via 172.16.216.2 dev ens33 
169.254.0.0/16 dev ens33 scope link metric 1002 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 
[root@www ~]# ip route | while read p; do ip route change $p initcwnd 10; done
[root@www ~]# ip route
default via 172.16.216.2 dev ens33 initcwnd 10 
169.254.0.0/16 dev ens33 scope link metric 1002 initcwnd 10 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 initcwnd 10
# initcwnd 10:初始化cwnd
# 單方面提升發送端 cwnd 的大小並不一定有效,因為網絡中實際傳輸的未經確認的數據大小取決於  rwnd 和 cwnd 中的最小值,所以一旦接收方的 rwnd 比較小的話,會抑制 cwnd 的發揮。

# 設置initrwnd(linux kernel 2.6.33 and newer)
[root@www ~]# ip route
default via 172.16.216.2 dev ens33 
169.254.0.0/16 dev ens33 scope link metric 1002 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 
[root@www ~]# ip route | while read p; do ip route change $p initrwnd 10; done 
[root@www ~]# ip route
default via 172.16.216.2 dev ens33 initrwnd 10 
169.254.0.0/16 dev ens33 scope link metric 1002 initrwnd 10 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 initrwnd 10 

# 一些系統的rwnd值:
# Linux 2.6.32                                      3*MSS (usually 4380)
# Linux 3.0.0                                       10*MSS (usually 14600)
# Windows NT 6.1    (Windows 7 or Server 2008 R2)   8192 ^ 2
  • 優化 tcp time_wait ,減少time_wait 狀態的連接。主動關閉的一方會出現time_wait狀態。

    time_wait 狀態的連接要等待2個 MSL 的時間才會 close,會占用資源,盡量避免連接進入 time_wait 狀態。linux 裏 MSL一般是30秒,2 個MSL 是1分鐘,這個數值是硬編碼在內核中的,除非重新編譯內核,否則沒法修改。註:MSL最長報文生命周期:Maximum Segment Lifetime,MSL 。

    修改 fin_wait2 的值,減少 fin_wait2 的等待時間,超時以後會回收連接。

    開啟長連接:絕大多是瀏覽器在開啟長連接的情況下,接收到服務器斷開連接的fin以後,會恢復一個 ack;而不會不發送自己這一端的 fin ,這樣服務器一端就會等待 fin_timeout 時間後,回收連接。

    若不開啟長連接,服務器端關閉鏈接以後,鏈接的狀態會從 fin_wait2 轉換到 time_wait 。

    還可以考慮促使客戶端關閉鏈接,配置 keepalive_timeout 20s 10s; (nginx 配置),使客戶端的超時小於服務器端,瀏覽器會先關閉鏈接,這樣time_wait 狀態就會在客戶端,不過通過實驗看出只有火狐瀏覽器支持,狐火瀏覽器會識別 Keep-Alive: timeout=time 這個參數,而別的瀏覽器不會。

    不要設置回收 recycle=1 和 重用 reuse=1,NAT模式下會造成連接失敗( SYN 包不會被響應)

    time_wait 狀態的連接被重用(reuse)的條件是如下2個之一:

    1)初始序列號比time_wait狀態的老連接最末的序列號大。

    2)如果使用時間戳,那麽新到來的連接的時間戳比老連接的時間戳大。

    tcp_tw_reuse和tcp_tw_recycle要生效,必須 tcp_timestamps 是開啟的,默認也是開啟的。

  • 參數優化

    net.ipv4.tcp_max_syn_backlog = 1024 #SYN隊列的長度,默認是1024,加大隊列到8192或更大,可緩存更多等待的網絡連接。

    net.ipv4.tcp_max_tw_buckets = 180000 #保存 TIME_WAIT 狀態的套接字的最大數量,一旦超過這個數,TIME_WAIT套接字將立刻被清除,並發出警告。

    net.ipv4.ip_local_port_range = 1024 65535 # 向外連接的端口範圍。缺省值:32768到61000,可以擴大 1024 到 65535。

    net.ipv4.tcp_syncookies = 1 #開啟SYN Cookies,SYN等待隊列溢出時,使用cookies來處理,可防範少量SYN***。

    net.ipv4.tcp_retries2 = 15 #TCP失敗重傳的次數 ,默認15 ,可以調小一些,例如5。

    還可以配置用於 TCP/IP 鏈接所使用的內存,配置總內存的話,單位是“頁” ,具體的一個頁的大小可以通過 getconf PAGE_SIZE 這個命令獲取;讀寫所占用的內存單位是字節。

[root@www ~]# getconf PAGE_SIZE
4096

總內存

net.ipv4.tcp_mem = 93408 124544 186816

寫(緩沖)

net.ipv4.tcp_wmem = 4096 16384 3985408

讀(緩存)

net.ipv4.tcp_rmem = 4096 87380 3985408

[root@www ~]# cd /proc/sys/net/ipv4
[root@www ipv4]# cat tcp_fin_timeout 
60
[root@www ~]# sysctl -a |grep timestamps
net.ipv4.tcp_timestamps = 1

initcwnd

ip-sysctl

技術分享圖片

TLS 協議優化

  • 對TLS協議進行安全和速度調優

    1.密鑰交換

    使用TLS最大的成本除了延遲以外(多了2次往返),就是用於安全參數協商的CPU密集型操作,也就是密鑰交換(key exchange)。密鑰交換的CPU消耗很大程度上取決於服務器選擇的私鑰算法、密鑰長度和密鑰交換算法。

    破解密鑰的難度取決於密鑰的長度,密鑰越長越安全。但是也要考慮加密與解密所消耗的計算資源。目前有兩種私鑰算法可以使用:RSA和ECDSA。

    現在RSA算法的密鑰仍然大量存在,即使使用它進行密鑰交換的時候不支持前向加密。但是RSA還是可以用在身份認證上,當前RSA密鑰算法推薦最小長度2048位,並且考慮升級到3072位(雖然升級後效率下降較多),隨著RSA密鑰的增長它開始變得越來越慢。ECDSA會快很多,越來越多的站點支持ECDSA,中等長度256位的ECDSA提供與3072位RSA一樣的安全性,卻有更好的性能。

    推薦優先使用:ECDSA256_ECDHE256 與 RSA2048_ECDHE256 。

    技術分享圖片

    2. 證書

  • 證書鏈

    a、TLS握手的時候,服務器端會把證書鏈發送給客戶端進行驗證。

    b、證書鏈盡可能短。

    c、證書鏈要完整。

    d、盡量使用橢圓曲線證書鏈。

  • 證書吊銷檢查與OCSP服務

    雖然證書吊銷狀態在不斷變化,並且客戶端(瀏覽器)對如何檢查證書吊銷差異很大,但作為服務器端,要做到盡可能快的傳遞吊銷信息。

  • 使用帶OCSP信息的證書。

    OCSP被設計用於提供實時查詢,允許客戶端訪問吊銷信息。因此查詢簡短而快速(1個HTTP請求)。相比之下CRL是一個包含大量被吊銷證書的列表。一些客戶端只有當OCSP信息不可用的時候才下載CRL,在下載CRL的時候瀏覽器與服務器端的通信將暫停,直到CRL下載完成,所消耗的時間可能會有幾十秒。

  • 選擇具有快速且可靠的OCSP響應程序的CA

    不同CA之間的OCSP響應速度也不同。緩慢和錯誤的OCSP響應程序會潛在地導致性能下降。在決定使用OCSP響應之後,要考察CA對OCSP響應的性能與正確性。另一個選擇CA的標準是看更新OCSP響應的速度,最好自己的證書一經頒發就加入到OCSP響應程序中,一旦出了安全隱患被吊銷,OCSP響應也能迅速的更新。

[root@www ~]# openssl s_client -connect www.openssl.org:443 -status |grep -i ocsp
depth=1 C = US, O = Let‘s Encrypt, CN = Let‘s Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
OCSP response: 
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response

技術分享圖片

3. 會話恢復

TLS理解兩種類型的握手:完整握手和簡短握手。理論上完整握手只會在客戶端與服務器建立TLS會話(TLS session)的時候進行一次。後續的連接,雙方使用簡短握手恢復之前協商的會話。簡短握手因為不需要密鑰交換與密鑰生成等操作,所以會更快,並且少一次往返時間。

4.TLS的紀錄協議造成的在網絡傳輸中的額外開銷

TLS協議的最小傳輸單位是一個TLS記錄,它最多可以包含2^14=16384字節(16K)的數據。一條記錄在不加密的情況下只有很小的開銷;每個記錄以5字節的元數據開頭,即內容類型(1字節)、協議版本(2字節)和數據長度(2字節)。流加密、分組加密和已驗證密碼套件加密後的TLS記錄的額外開銷。

技術分享圖片

盡量避免發送小包數據。盡量緩沖應用層數據避免額外的網絡開銷。

5.對稱加密對CPU資源的消耗

加密操作有明顯的CPU成本,成本由加密算法、加密模式和完整性校驗算法三者決定。

6. TLS 記錄的緩存延遲

TLS記錄是TLS發送和接收數據的最小單位。TLS記錄的大小與下一層TCP包的大小並不匹配,一個全尺寸的TLS記錄16 KB需要被拆分成許多小的TCP包(大約12個),通常每個小於1.5 KB(1.3KB)。整個TLS記錄被分成小的TCP包後,各個小包會陸續到達,但在全部到齊之前是無法進行解密處理的。這是因為TLS記錄同樣是數據解密和完整性檢驗的最小單位。緩存延遲有時可能會比較大。

雖然通過TCP協議可以把丟失和延遲的數據包恢復,但仍然需要消耗一次往返。每一次額外的往返對於整個TLS記錄都意味著延遲。 初始擁塞窗口另一個觸發額外往返的延遲是在連接初期發送大量數據導致初始擁塞窗口溢出。一旦擁塞窗口滿了,發送端必須等待響應(1次往返),等到擁塞窗口增加再發送更多數據。

如果Web服務器支持TLS記錄調整,就應該考慮將默認值(16 KB這麽大的數值)改成更為合理的值,調整這個值由部署的密碼套件和相應的傳輸開銷決定,一般情況下設置成4 KB。如果將TLS記錄大小設置為與TCP/IP包準確匹配,那就設置成1400字節左右,然後通過觀察數據包逐步調整。IP報文理論上最大是65535個字節,是很大的,但是由於IP分片效果很不好,所以TCP在三次握手中互相得知對方的MSS(MTU減IP頭部),不給IP層很大塊的數據,避免IP數據報分片

例如,數據鏈路層最大傳輸單元(maximum transfer unit,MTU)是1500字節,那麽可以預見:

1,500 bytes MTU 去除額外開銷,所傳數據 1379 —1419 bytes 。

-20 bytes IPv4 herder | - 40 bytes IPv6 header

- 32 bytes TCP header TCP 頭部 最小是20字節可拓展的是40字節,最大為60字節

- 29 bytes TLS record | - 49 bytes TLS record


MSS 是1460 bytes :1460 - 32 - 29|49 = 1379 — 1399 bytes

首先MTU的值是變化的。雖然多數客戶端繼承以太網1500字節的限制,但也有一些協議支持更大的數據。比如,巨型幀(jumbo frame)允許多達9000字節。還有就是使用IPv4和IPv6(IPv4頭是20字節,IPv6頭是40字節)計算會略有不同,所密碼套件的變化也會影響這個數值。

另一個問題是減小TLS記錄的大小會增加傳輸開銷,也就是吞吐量會下降。如果將TLS記錄長度調大(最大16K),那麽由於是加密的數據,得要所有的數據(所有的IP包)都到齊了,才會順利的解密出明文,等待的時間會較長,吞吐率是上去了,響應的實時性就下降了。nginx上也有配置這個值的選項,只是不能動態調整。

SSL/TLS深度解析--TLS性能優化