尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Erlang/OTP SSH 配置完全指南:Option 入口、优先级层级与算法配置实战

Erlang/OTP SSH 配置完全指南:Option 入口、优先级层级与算法配置实战 编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载导读OTP 的 SSH 应用sshapplication提供了数量庞大的可配置选项Options覆盖客户端连接、服务端守护进程、算法协商、认证与密钥管理等多个层面。本文以 lib/ssh/doc/guides/configurations.md 为核心系统讲解这些选项的所有设置入口、四层优先级规则以及preferred_algorithms与modify_algorithms在不同层级间的协作机制。读完本文你将掌握在 Erlang 代码、erl 命令行、ssh.app与.config文件中分别配置 SSH 选项的正确姿势理解谁覆盖谁的优先级语义并能通过配置文件为遗留系统安全地启用已被默认集移除的算法。一、设置 Option 的两条主要途径OTP SSH 应用支持通过两种途径设置选项且二者可以混用Erlang 代码中的Options参数在调用ssh:daemon/3、ssh:connect/3及其变体时直接传入选项列表OTP 配置参数Configuration Parameters通过erl命令行、应用配置文件等 OTP 标准机制注入参见 OTP 配置参数说明。1.1 在函数调用中直接传入最直观的方式是把选项作为参数列表传给相关函数ssh:connect(22, [{user,foo}]).这里user被设置为foo客户端将以该用户名发起连接。同样的机制适用于ssh:connect/2,3,4、ssh:daemon/1,2,3等所有接受选项列表的公开 API。1.2 通过 OTP 配置参数注入配置参数有三种写入位置均会在应用启动时被读取。1erl 命令行erl -ssh user foo-ssh之后的key value对会被 OTP 写入ssh应用的环境变量application environment等效于设置application:set_env(ssh, user, foo)。2ssh.app文件的env部分在应用的.app资源文件的env元组中列出默认环境变量。以源码仓库中的 lib/ssh/src/ssh.app.src 为模板构建时%VSN%会被替换为实际版本号示例如下{application, ssh, [{description, SSH-2 for Erlang/OTP}, {vsn, 4.9}, {modules, [ssh, ... ssh_xfer]}, {registered, []}, {applications, [kernel, stdlib, crypto, public_key]}, {env, [{user, bar}]}, % HERE {mod, {ssh_app, []}}, ...注意applications列表声明了 ssh 对kernel、stdlib、crypto、public_key的依赖——其中crypto与public_key正是算法协商与密钥处理的基础。3.config文件把配置写入独立的 config 文件再通过erl -config加载erl -config ex1其中ex1.config内容为[{ssh, [{user, foo}]}].顶层元组的第一项ssh指明这是ssh应用的配置其值为一个选项属性列表。1.3 按角色隔离server_options 与 client_options如果某选项只针对服务端或只针对客户端可以把它放入server_options或client_options从而让同一份配置同时为两侧提供不同取值[{ssh, [{server_options, [{user, foo}]}, {client_options, [{user, bar}]}]}].此时**守护进程daemon使用用户名foo而客户端client**使用用户名bar。从源码看这一角色映射实现在 lib/ssh/src/ssh_options.erl 的cnf_key/1cnf_key(server) - server_options; cnf_key(client) - client_options.即服务端只从server_options读取角色化配置客户端只读取client_options。二、优先级四级覆盖规则当同一个选项通过多种方式被设置时遵循严格的优先级顺序。从低到高依次为级别来源说明Level 0OTP SSH 源码中的硬编码默认值由 lib/ssh/src/ssh_options.erl 的default/1及 lib/ssh/src/ssh_transport.erl 的default_algorithms1/1等定义Level 1OTP 配置参数即erl -ssh ...命令行、ssh.app的env、.config文件直接列出的选项Level 2配置参数中的server_options/client_options角色化包装的选项Level 3函数调用参数列表ssh:connect/3、ssh:daemon/3等调用中直接传入的选项同一选项若在多个层级被设置取最高层级的值。因此函数调用参数Level 3始终覆盖配置文件Level 1/2而配置文件又覆盖源码硬编码默认值Level 0。唯一的例外modify_algorithmsmodify_algorithms是一个累积型选项而非取高者选项。所有层级上的modify_algorithms会按层级从低到高的顺序依次应用到算法集合上Level 1 的修改先执行随后是 Level 2最后是 Level 3。如果某个层级出现了preferred_algorithms则该层的整个算法集会被替换为该选项指定的集合然后所有层级的modify_algorithms依旧按层级顺序施加在该结果之上。之所以这样设计是为了让用户无需修改任何代码仅通过在配置文件中添加一个选项就能把已被默认集移除的算法重新加回来以便与仍在使用旧算法的遗留系统互通。例如可以通过配置文件把ssh-dss这类已从默认集删除的公钥算法重新启用详见下文跨层级 modify_algorithms 示例。三、算法配置层级如何作用于算法集preferred_algorithms与modify_algorithms的协作细节有专门的章节说明参见 Configuring algorithms in SSH本节聚焦不同配置层级对它们的影响。3.1 ssh:start/0 前后的 default_algorithms 差异ssh:default_algorithms/0的返回值取决于 ssh 应用是否已启动应用未启动时返回硬编码默认算法列表Level 0但已经根据当前 cryptolibcrypto应用实际支持的能力做了裁剪应用已启动ssh:start/0后返回应用了 Level 0 与 Level 1 配置之后的算法列表。用文档中的完整示例验证。配置文件ex2.config$ cat ex2.config [{ssh, [{preferred_algorithms, [{cipher, [aes192-ctr]}, {public_key, [ssh-rsa]}, {kex, [ecdh-sha2-nistp384]}, {mac, [hmac-sha1]}]}]}].启动 Erlang 并加载该配置先不启动 ssh$ erl -config ex2 Erlang/OTP 29 [erts-16.3.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns] Eshell V16.3.1 (press CtrlG to abort, type help(). for help) 1 ssh:default_algorithms(). [{kex,[mlkem768x25519-sha256,curve25519-sha256, curve25519-sha256libssh.org,curve448-sha512, ecdh-sha2-nistp521,ecdh-sha2-nistp384, ecdh-sha2-nistp256, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256]}, {public_key,[ssh-ed25519,ssh-ed448,ecdsa-sha2-nistp521, ecdsa-sha2-nistp384,ecdsa-sha2-nistp256, rsa-sha2-512,rsa-sha2-256]}, {cipher,[{client2server,[aes256-gcmopenssh.com,aes256-ctr, aes192-ctr,aes128-gcmopenssh.com, aes128-ctr, chacha20-poly1305openssh.com]}, {server2client,[aes256-gcmopenssh.com,aes256-ctr, aes192-ctr,aes128-gcmopenssh.com, aes128-ctr, chacha20-poly1305openssh.com]}]}, {mac,[{client2server,[hmac-sha2-512-etmopenssh.com, hmac-sha2-256-etmopenssh.com, hmac-sha2-512,hmac-sha2-256, hmac-sha1-etmopenssh.com,hmac-sha1]}, {server2client,[hmac-sha2-512-etmopenssh.com, hmac-sha2-256-etmopenssh.com, hmac-sha2-512,hmac-sha2-256, hmac-sha1-etmopenssh.com,hmac-sha1]}]}, {compression,[{client2server,[none,zlibopenssh.com]}, {server2client,[none,zlibopenssh.com]}]}]注意此时ex2.config中的算法尚未生效——它们会在启动 ssh 时被应用2 ssh:start(). ok 3 ssh:default_algorithms(). [{kex,[ecdh-sha2-nistp384]}, {public_key,[ssh-rsa]}, {cipher,[{client2server,[aes192-ctr]}, {server2client,[aes192-ctr]}]}, {mac,[{client2server,[hmac-sha1]}, {server2client,[hmac-sha1]}]}, {compression,[{client2server,[none,zlibopenssh.com]}, {server2client,[none,zlibopenssh.com]}]}] 4可以看到算法集被替换成了ex2.config中指定的内容。由于配置中没有指定compression该项仍然保留硬编码默认值[none, zlibopenssh.com]——即 Level 1 只覆盖它显式列出的类别其余类别继续回落到 Level 0 默认值。从源码层面看这个启动前后差异是由 lib/ssh/src/ssh.erl 的start/1实现的start(Type) - case application:ensure_all_started(ssh, Type) of {ok, _} - %% Clear cached default_algorithms (if exists) ... ssh_transport:clear_default_algorithms_env(), %% ... and rebuild them taking configure options in account ssh_transport:default_algorithms(), ok; Other - Other end.启动成功后先清除缓存clear_default_algorithms_env/0再基于当前配置重建默认算法集。而 lib/ssh/src/ssh_transport.erl 中的default_algorithms/0带有缓存与 FIPS 模式感知逻辑default_algorithms() - FipsMode crypto:info_fips(), case application:get_env(ssh, ?DEFAULT_ALGS) of undefined - Algs build_cache(), application:set_env(ssh, ?DEFAULT_ALGS, {FipsMode,Algs}), Algs; {ok,{FipsMode,Algs}} - %% Cached, and the FIPS mode is the same now as when it was cached. Algs; {ok,{_OtherFipsMode,_Algs}} - %% Cached, but the FIPS mode has changed. Algs build_cache(), application:set_env(ssh, ?DEFAULT_ALGS, {FipsMode,Algs}), Algs end.算法结果缓存在内部应用环境键$def-algs$中且缓存与crypto:info_fips()的 FIPS 模式绑定——当 FIPS 模式发生变化时缓存会失效并重建。build_cache/0则负责真正合并配置build_cache() - Opts get_alg_conf(), Algs1 case proplists:get_value(preferred_algorithms, Opts) of undefined - [{K,default_algorithms1(K)} || K - algo_classes()]; Algs0 - {true,Algs01} ssh_options:check_preferred_algorithms(Algs0), Algs01 end, Algs case proplists:get_value(modify_algorithms, Opts) of undefined - Algs1; Modifications - ssh_options:initial_default_algorithms(Algs1, Modifications) end, Algs.其中get_alg_conf/0从应用环境读取preferred_algorithms与modify_algorithms对应 Level 1 配置default_algorithms1/1定义了每个算法类别的默认集合例如kex的默认集中明确排除了diffie-hellman-group1-sha1、diffie-hellman-group14-sha1、diffie-hellman-group-exchange-sha1分别在 OpenSSH 7.3.p1 与 8.2 中移除public_key的默认集排除了ssh-rsa与ssh-dss最终再经由supported_algorithms/1的select_crypto_supported/1按当前 cryptolib 的实际能力裁剪。算法类别algo_classes()固定为[kex, public_key, cipher, mac, compression]。3.2 ssh:connect 与 ssh:daemon 中的算法选项客户端通过ssh:connect/2,3,4等建立连接时、服务端通过ssh:daemon/1,2,3启动时函数调用参数列表中的选项同样参与算法协商客户端连接时使用环境变量client_optionsLevel 2 的客户端侧守护进程启动时使用server_optionsLevel 2 的服务端侧。当存在多个层级的preferred_algorithms时取最高层级的那一个——即函数调用参数列表Level 3优先级最高随后从 Level 0 开始按顺序应用所有层级的modify_algorithms。继续上面的例子。连接一台服务器通过modify_algorithms删除当前唯一的kex算法ecdh-sha2-nistp384并把curve25519-sha256libssh.org追加到删除后空列表中4 {ok,C} ssh:connect(loopback, 22, [{modify_algorithms, [{rm, [{kex, [ecdh-sha2-nistp384]}]}, {append, [{kex, [curve25519-sha256libssh.org]}]}]}]). {ok,0.118.0}通过ssh:connection_info/2检查客户端与服务端实际协商出的算法可以看到唯一的kex算法curve25519-sha256libssh.org被选中5 ssh:connection_info(C, algorithms). {algorithms,[{kex,curve25519-sha256libssh.org}, {hkey,ssh-rsa}, {send_mac,hmac-sha1}, {recv_mac,hmac-sha1}, {encrypt,aes192-ctr}, {decrypt,aes192-ctr}, {compress,none}, {decompress,none}, {send_ext_info,false}, {recv_ext_info,true}]}3.3 跨层级 modify_algorithms 示例用配置文件启用 ssh-dss下面验证一个关键机制低层级的modify_algorithms是否作用于高层级的preferred_algorithms。做法是通过配置文件启用虽然受支持、但不在默认集中的ssh-dss算法。配置文件ex3.config[{ssh, [{modify_algorithms, [{prepend, [{public_key, [ssh-dss]}]}]}]}].新建的 Erlang shell 中可以看到public_key项里并没有ssh-dss1 proplists:get_value(public_key, ssh:default_algorithms()). [ssh-ed25519,ssh-ed448,ecdsa-sha2-nistp521, ecdsa-sha2-nistp384,ecdsa-sha2-nistp256, rsa-sha2-512,rsa-sha2-256] 2随后调用ssh:connect/3在参数列表中用preferred_algorithms把每类算法削减到只剩一个2 ssh:start(). ok 3 {ok,C} ssh:connect(loopback, 22, [{preferred_algorithms, [{public_key, [ecdsa-sha2-nistp256]}, {kex, [ecdh-sha2-nistp256]}, {cipher, [chacha20-poly1305openssh.com]}, {mac, [hmac-sha2-256]}, {compression, [none]}]}]). {ok,0.101.0} 4 ssh:connection_info(C,algorithms). {algorithms,[{kex,ecdh-sha2-nistp256}, {hkey,ssh-dss}, {send_mac,chacha20-poly1305openssh.com}, {recv_mac,chacha20-poly1305openssh.com}, {encrypt,chacha20-poly1305openssh.com}, {decrypt,chacha20-poly1305openssh.com}, {compress,none}, {decompress,none}, {send_ext_info,false}, {recv_ext_info,true}]} 5调用虽然只允许了ecdsa-sha2-nistp256但最终选中的公钥算法却是ssh-dss——来自ex3.configLevel 1中modify_algorithms的prepend操作被应用到了函数调用Level 3的preferred_algorithms之上。完整公钥算法列表实际为[ssh-dss,ecdsa-sha2-nistp256]prepend把ssh-dss放在最前因此协商优先选择了ssh-dss。这个例子证明了仅靠修改配置文件就能在不改动任何调用代码的前提下扩充算法集。需要说明的是示例故意使用prepend而非append来强制协商选中ssh-dss实际生产环境中更稳妥的做法是用append把非默认算法追加到列表末尾从而保持默认算法的优先级。3.4 修改操作的底层语义从源码可以确认modify_algorithms三类操作的精确语义。在 lib/ssh/src/ssh_options.erl 中eval_op(rm, Opt, Pref, []) when is_list(Opt), is_list(Pref) - Pref -- Opt; eval_op(append, Opt, Pref, []) when is_list(Opt), is_list(Pref) - (Pref--Opt) Opt; eval_op(prepend, Opt, Pref, []) when is_list(Opt), is_list(Pref) - Opt (Pref--Opt).rm从偏好列表Pref中删除Opt列出的算法append先删除再追加即把Opt中的算法移到列表末尾避免重复prepend把Opt中的算法置于列表最前先删除原位置再置顶。所有modify_algorithms输入还会经过check_modify_algorithms/1校验只接受append、prepend、rm三种二元组操作且算法列表不允许重复、只允许原子再经rm_non_supported/2与 lib/ssh/src/ssh_transport.erl 的supported_algorithms/1比对剔除当前 cryptolib 不支持的算法。preferred_algorithms与modify_algorithms的最终合并发生在final_preferred_algorithms/1它把modify_algorithms施加到preferred_algorithms上——这正是上一节跨层级示例能够生效的代码级原因。四、配置选项的合并顺序与校验结合 lib/ssh/src/ssh_options.erl 的config_val/3可以看到每个选项的取值来源按如下顺序合并config_val(Key, RoleCnfs, Opts) - case lists:keysearch(Key, 1, Opts) of {value, {_,V}} - {ok,V}; false - case lists:keysearch(Key, 1, RoleCnfs) of {value, {_,V}} - {ok,V}; false - application:get_env(ssh, Key) % returns {ok,V} | undefined end end.即优先级函数调用参数Level 3 →server_options/client_optionsLevel 2 → 应用环境/配置文件Level 1最终落到default/1定义的硬编码默认值Level 0。而modify_algorithms走的是专门的config_val/3分支把三层来源**全部串接**起来构成按层级顺序全部应用的累积语义config_val(modify_algorithmsKey, RoleCnfs, Opts) - V case application:get_env(ssh, Key) of {ok,V0} - V0; _ - [] end proplists:get_value(Key, RoleCnfs, []) proplists:get_value(Key, Opts, []), case V of [] - undefined; _ - {append,V} end;每个选项在保存前还会经过OptionDefinitionsdefault/1返回的表中声明的chk校验函数例如preferred_algorithms对应check_preferred_algorithms/1、modify_algorithms对应check_modify_algorithms/1非法取值会抛出{error,{EO,KV,Reason}}形式的错误其中EO为eoptions选项列表错误或eerl_enverl 环境错误便于定位问题来源。五、相关阅读安全加固相关的选项配置参见 Hardening SSHpreferred_algorithms与modify_algorithms的协作细则及更多示例参见 Configuring algorithms in SSHOTP 配置参数机制的通用说明erl -config、应用环境等参见 OTP 配置参数全部选项的定义与校验逻辑位于 lib/ssh/src/ssh_options.erl算法默认集与 cryptolib 能力裁剪位于 lib/ssh/src/ssh_transport.erlssh:default_algorithms/0、ssh:connect/3、ssh:daemon/3等公开接口定义于 lib/ssh/src/ssh.erl应用资源文件模板为 lib/ssh/src/ssh.app.src。赞分享编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载相关推荐Erlang/OTP SSH 算法配置完全指南从协商机制到 preferred_algorithms 与 modify_algorithms 实战Erlang/OTP SSH 算法配置完全指南从协商机制到 preferred_algorithms 与 modify_algorithms 实战 本文以 O编程语言语言运行时标准库编译器并发编程**Erlang/OTP 安装与配置完全指南**Erlang/OTP 安装与配置完全指南 项目基础介绍及主要编程语言 Erlang/OTP 是一个专为构建高度可扩展的软实时系统设计的编程语言及其运行时环境。它编程语言语言运行时标准库编译器并发编程Erlang/OTP Logger 配置实战指南从 Cookbook 示例到源码级原理Erlang/OTP Logger 配置实战指南从 Cookbook 示例到源码级原理 导读 Erlang/OTP 自 21.0 起由 Kernel 应用内置编程语言语言运行时标准库编译器并发编程上一篇ComfyUI自定义脚本如何通过UI增强工具提升AI绘画工作流效率下一篇如何系统排查SillyTavern故障从诊断到修复的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表