【译】使用TLS进行Erlang分发
本节介绍Erlang发行版如何使用TLS来获得额外的验证和安全性。
理论上,Erlang发行版可以使用几乎所有基于连接的协议作为承载。但是,需要一个用于实现连接设置的协议特定部分的模块。内核应用程序中的默认分发模块是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。
以下各节描述了这些步骤。
4.1 构建包括SSL应用程序在内的启动脚本
使用SASL应用程序中的systools实用程序来构建引导脚本。 有关systools的更多信息,请参见SASL文档。 这仅仅是可以做的一个例子。
最简单的启动脚本可能仅包括内核和STDLIB应用程序。 这样的脚本位于Erlang发行版的bin目录中。 该脚本的源代码位于发行版/ <OTP版本> /start_clean.rel下的Erlang安装目录下。
请执行下列操作:
- 将该脚本复制到另一个位置(最好是另一个名称)。
- 在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命令行参数指定此启动脚本(具有其完整路径,但不带.boot后缀)来启动Erlang。 在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应用程序的克隆已挂接到内核应用程序中。 因此,只要可以访问SSL应用程序代码,就开始分发。 仅建议将-pa方法用于测试目的。
注意:SSL应用程序的克隆必须允许在建立发行版所需的早期引导阶段使用SSL代码。 但是,这使得无法软升级SSL应用程序。
4.2 指定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参数(请参阅下一节)。
4.3 指定TLS选项
可以将TLS分发选项写入启动节点时要查询的文件。 然后,使用命令行参数-ssl_dist_optfile指定此文件名。
可以在选项文件中指定任何可用的TLS选项,但请注意,采用fun()的选项必须使用语法fun Mod:Func/Arity,因为在查阅文件时无法编译功能主体。
不要篡改套接字选项列表,二进制文件,活动文件,数据包,nodelay和交付,因为它们由分发协议处理程序本身使用。 其他原始套接字选项(例如packet_size)可能会严重干扰,因此请注意!
为了使TLS起作用,必须为服务器端至少指定一个公钥和一个证书。 在以下示例中,PEM文件“ /home/me/ssl/erlserver.pem”包含服务器证书及其私钥。
创建一个名为“ /home/me/ssl/ssl_test@myhost.conf”的文件:
[{server,
[{certfile, "/home/me/ssl/erlserver.pem"},
{secure_renegotiate, true}]},
{client,
[{secure_renegotiate, true}]}].
然后像这样启动节点(命令中的换行符是为了提高可读性,键入时不应出现在换行符中):
$ 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:ssl_accept/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作为分发协议,以此方式启动的节点可以正常运行。
4.4 指定TLS选项(旧版)
与上一节一样,PEM文件“ /home/me/ssl/erlserver.pem”包含服务器证书及其私钥。
在erl命令行上,您可以指定创建套接字时TLS分发添加的选项。
可以通过在选项名称中添加前缀server_或client_来指定以下列表中最简单的TLS选项:
- certfile
- keyfile
- password
- cacertfile
- verify
- verify_fun (write as {Module, Function, InitialUserState})
- crl_check
- crl_cache (write as Erlang term)
- reuse_sessions
- secure_renegotiate
- depth
- hibernate_after
- ciphers (use old string format)
注意,因为在命令行上不接受funs,所以verify_fun需要以与相应的TLS选项不同的形式编写。
服务器还可以采用选项dhfile和fail_if_no_peer_cert(也带有前缀)。
当发行版启动到另一个节点的连接时,将使用client_-prefixed选项。 接受来自远程节点的连接时,将使用server_前缀选项。
原始套接字选项(例如数据包和大小)不能在命令行上指定。
用于指定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>
4.5 设置环境以始终使用TLS(旧版)
指定Erlang参数的简便方法是使用环境变量ERL_FLAGS。 可以在该变量中指定使用TLS分发所需的所有标志,然后将其解释为所有后续Erlang调用的命令行参数。
在Unix(Bourne)外壳程序中,它看起来可能如下所示(换行符是为了提高可读性,键入时不要出现换行符):
$ 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()调用可验证是否向模拟器提供了正确的参数。
4.6 在IPv6上使用TLS分发
可以在IPv6而非IPv4上使用TLS分发。 为此,在命令行或ERL_FLAGS环境变量中启动Erlang时,传递选项-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分发与其他节点通信。