檢視原始碼 在 TLS 上使用 Erlang 分散式系統

本節描述 Erlang 分散式系統如何使用 TLS 來獲得額外的驗證和安全性。

理論上,Erlang 分散式系統可以使用幾乎任何基於連線的協定作為承載。但是,需要一個模組來實作連線設定中特定於協定的部分。預設的分散式模組是 Kernel 應用程式中的 inet_tcp_dist。當啟動一個分散式的 Erlang 節點時,net_kernel 使用此模組來設定監聽埠和連線。

在 SSL 應用程式中,可以使用額外的分散式模組 inet_tls_dist 作為替代方案。所有分散式連線將使用 TLS,並且分散式系統中所有參與的 Erlang 節點都必須使用此分散式模組。

安全等級取決於提供給 TLS 連線設定的參數。但是,Erlang 節點的 Cookie 始終會被使用,因為它們可以用於區分兩個不同的 Erlang 網路。

要設定在 TLS 上使用 Erlang 分散式系統:

  • 步驟 1: 建置包含 SSL 應用程式的啟動腳本。
  • 步驟 2:net_kernel 指定分散式模組。
  • 步驟 3: 指定安全選項和其他 SSL 選項。
  • 步驟 4: 設定環境以始終使用 TLS。

以下章節將描述這些步驟。

建置包含 SSL 應用程式的啟動腳本

啟動腳本是使用 SASL 應用程式中的 systools 工具建置的。有關 systools 的更多資訊,請參閱 SASL 文件。這只是一個範例,說明可以做些什麼。

最簡單的啟動腳本只包含 Kernel 和 STDLIB 應用程式。這樣一個腳本位於 Erlang 發行版的 bin 目錄中。腳本的原始碼位於 Erlang 安裝頂層目錄下的 releases/<OTP version>/start_clean.rel 中。

執行下列步驟:

  • 將該腳本複製到另一個位置(最好是另一個名稱)。
  • 在 STDLIB 應用程式之後,新增 Crypto、Public Key 和 SSL 應用程式及其當前版本號。

以下顯示新增 TLS 的 .rel 檔案範例:

      {release, {"OTP  APN 181 01","R15A"}, {erts, "5.9"},
      [{kernel,"2.15"},
      {stdlib,"1.18"},
      {crypto, "2.0.3"},
      {public_key, "0.12"},
      {asn1, "4.0"},
      {ssl, "5.0"}
      ]}.

您的系統中的版本號碼會有所不同。每當腳本中包含的應用程式之一升級時,請更改腳本。

執行下列步驟:

  • 建置啟動腳本。

    假設 .rel 檔案儲存在目前目錄中的檔案 start_ssl.rel 中,則可以如下建置啟動腳本:

   1> systools:make_script("start_ssl",[]).

現在,目前目錄中會有一個 start_ssl.boot 檔案。

執行下列步驟:

  • 測試啟動腳本。為此,請使用 -boot 命令列參數啟動 Erlang,指定此啟動腳本(及其完整路徑,但不含 .boot 後綴)。在 UNIX 中,它看起來如下所示:
$ erl -boot /home/me/ssl/start_ssl
Erlang (BEAM) emulator version 5.0

Eshell V5.0  (abort with ^G)
1> whereis(ssl_manager).
<0.41.0>

whereis 函數呼叫會驗證 SSL 應用程式是否已啟動。

作為建置啟動腳本的替代方案,您可以明確地在命令列上新增 SSL ebin 目錄的路徑。這是使用命令列選項 -pa 完成的。這會起作用,因為 SSL 應用程式不需要啟動即可使分散式系統啟動,因為 SSL 應用程式的複製品會掛接到 Kernel 應用程式中。因此,只要可以存取 SSL 應用程式碼,分散式系統就會啟動。-pa 方法僅建議用於測試目的。

注意:

SSL 應用程式的複製品必須啟用在早期啟動階段中使用 SSL 程式碼,以設定分散式系統。但是,這使得軟升級 SSL 應用程式變得不可能。

為 net_kernel 指定分散式模組

TLS 的分散式模組名為 inet_tls_dist,並在命令列中使用選項 -proto_dist 指定。-proto_dist 的引數是模組名稱,不含後綴 _dist。因此,此分散式模組在命令列中使用 -proto_dist inet_tls 指定。

擴展命令列會得到以下結果:

$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls

為了啟動分散式系統,請為模擬器提供一個名稱:

$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls -sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]

Eshell V5.0  (abort with ^G)
(ssl_test@myhost)1>

但是,以這種方式啟動的節點會拒絕與其他節點通訊,因為沒有提供 TLS 參數(請參閱下一節)。

指定 TLS 選項

TLS 分散式系統選項可以寫入在節點啟動時查詢的檔案中。然後使用命令列引數 -ssl_dist_optfile 指定此檔案名。

任何可用的 TLS 選項都可以在選項檔案中指定。

注意:

使用 fun() 的選項必須使用語法 fun Mod:Func/Arity,因為在查詢檔案時無法編譯函數主體。檔案的編碼也可以按照模組 epp 的定義進行指定。

警告:

不要擅自更改 socket 選項 listbinaryactivepacketnodelaydeliver,因為它們由分散式協定處理常式本身使用。其他原始 socket 選項,例如 packet_size,可能會嚴重干擾,因此請注意!

為了讓 TLS 工作,至少必須為伺服器端指定一個公鑰和一個憑證,並且用戶端需要指定它信任的 CA(用戶端憑證是可選的,並且需要更多配置)。

在以下範例中(為了簡單起見),PEM 檔案 "/home/me/ssl/erlserver.pem" 同時包含伺服器憑證及其私鑰。

建立一個名為 "/home/me/ssl/ssl_test@myhost.conf" 的檔案。

[{server,
  [{certfile, "/home/me/ssl/erlserver.pem"}]},
 {client,
  [{cacertfile, "/home/me/ssl/client_trusted.pem"}]}].

然後像這樣啟動節點(命令中的換行符是為了方便閱讀,輸入時不應有):

$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
  -ssl_dist_optfile "/home/me/ssl/ssl_test@myhost.conf"
  -sname ssl_test

呼叫 ssl:handshake/3 時會使用 {server, Opts} 元組中的選項,而呼叫 ssl:connect/4 時會使用 {client, Opts} 元組中的選項。

對於用戶端,連線時會新增選項 {server_name_indication, atom_to_list(TargetNode)}。這使得可以使用用戶端選項 {verify, verify_peer},並且用戶端將驗證憑證是否與您要連線的節點名稱相符。只有當伺服器憑證是發給名稱 atom_to_list(TargetNode) 時,此選項才有效。

對於伺服器,也可以使用選項 {verify, verify_peer},並且伺服器只會接受具有伺服器知道的根憑證信任憑證的用戶端連線。如果用戶端出示不受信任的憑證,則會被拒絕。此選項最好與 {fail_if_no_peer_cert, true} 結合使用,否則如果用戶端未出示任何憑證,仍然會被接受。

以這種方式啟動的節點完全可以運作,並使用 TLS 作為分散式協定。

在 IPv6 上使用 TLS 分散式系統

可以使用在 IPv6 上使用 TLS 分散式系統,而不是 IPv4。為此,請在啟動 Erlang 時,在命令列或 ERL_FLAGS 環境變數中使用選項 -proto_dist inet6_tls,而不是 -proto_dist inet_tls

具有此選項的命令列範例看起來如下所示:

$ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
  -ssl_dist_optfile "/home/me/ssl/ssl_test@myhost.conf"
  -sname ssl_test

以這種方式啟動的節點只能與其他使用在 IPv6 上使用 TLS 分散式系統的節點通訊。

指定 TLS 選項(舊版)

注意:

以下章節描述 OTP 20.2 之前的 TLS 選項處理,並且只能處理實際可用選項的一小部分。這裡只是為了向後相容。

與上一節一樣,PEM 檔案 "/home/me/ssl/erlserver.pem" 同時包含伺服器憑證及其私鑰。

erl 命令列上,您可以指定 TLS 分散式系統在建立 socket 時新增的選項。

以下清單中最簡單的 TLS 選項可以透過將字首 server_client_ 新增至選項名稱來指定:

  • certfile
  • keyfile
  • password
  • cacertfile
  • verify
  • verify_fun(寫成 {Module, Function, InitialUserState}
  • crl_check
  • crl_cache(寫成 Erlang 詞彙)
  • reuse_sessions
  • secure_renegotiate
  • depth
  • hibernate_after
  • ciphers(使用舊的字串格式)

請注意,verify_fun 需要以與對應的 TLS 選項不同的形式寫入,因為命令列上不接受 funs。

伺服器還可以採用選項 dhfilefail_if_no_peer_cert(也帶有字首)。

client_ 字首的選項在分散式系統啟動與另一個節點的連線時使用。server_ 字首的選項在接受來自遠端節點的連線時使用。

原始 socket 選項(例如 packetsize)不得在命令列上指定。

用於指定 TLS 選項的命令列引數名為 -ssl_dist_opt,後面要跟隨成對的 SSL 選項及其值。引數 -ssl_dist_opt 可以重複任意次數。

現在,執行與上一節中範例相同的操作的命令列範例看起來如下所示(命令中的換行符是為了方便閱讀,輸入時不應有):

$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
  -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
  -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
  -sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]

Eshell V5.0  (abort with ^G)
(ssl_test@myhost)1>

設定環境以始終使用 TLS

指定 Erlang 引數的方便方法是使用環境變數 ERL_FLAGS。可以使用該變數指定使用 TLS 分散式系統所需的所有旗標,然後將其解譯為 Erlang 所有後續調用的命令列引數。

在 Unix (Bourne) shell 中,它看起來如下所示(換行符是為了方便閱讀,輸入時不應有):

$ ERL_FLAGS="-boot /home/me/ssl/start_ssl -proto_dist inet_tls
  -ssl_dist_opt server_certfile /home/me/ssl/erlserver.pem
  -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true"
$ export ERL_FLAGS
$ erl -sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]

Eshell V5.0  (abort with ^G)
(ssl_test@myhost)1> init:get_arguments().
[{root,["/usr/local/erlang"]},
 {progname,["erl "]},
 {sname,["ssl_test"]},
 {boot,["/home/me/ssl/start_ssl"]},
 {proto_dist,["inet_tls"]},
 {ssl_dist_opt,["server_certfile","/home/me/ssl/erlserver.pem"]},
 {ssl_dist_opt,["server_secure_renegotiate","true",
                "client_secure_renegotiate","true"]
 {home,["/home/me"]}]

init:get_arguments() 呼叫會驗證是否向模擬器提供了正確的引數。