Elixir 分布式安全和白名单允许节点

Elixir distributed security and whitelisting allowed nodes

TL;DR

设置

我已经开始尝试在(目前)两个不同的服务器上分发 Elixir。

例如,假设服务器的两个IP地址是:

  1. 198.51.100.0
  2. 203.0.113.0

首先,我向 iptables firewall on both servers, opening up port 4369 (EPMD) 添加了新规则,并为节点添加了 9000-9010 之间的 10 个端口范围。我也只允许来自其他服务器的确切 IP 地址的传入连接。

198.51.100.0 的配置示例:

-A INPUT -p tcp -m state --state NEW --dport 4369 -s 203.0.113.0 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 9000:9010 -s 203.0.113.0 -j ACCEPT

203.0.113.0 的配置示例:

-A INPUT -p tcp -m state --state NEW --dport 4369 -s 198.51.100.0 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 9000:9010 -s 198.51.100.0 -j ACCEPT

现在我可以在每台机器上打开 iex 个外壳:

198.51.100.0:

$ iex --name one@198.51.100.0 --cookie secret --erl '-kernel inet_dist_listen_min 9000' --erl '-kernel inedist_listen_max 9010'

203.0.113.0:

$ iex --name two@203.0.113.0 --cookie secret --erl '-kernel inet_dist_listen_min 9000' --erl '-kernel inedist_listen_max 9010'

我可以从节点一成功连接到节点二:

iex(one@198.51.100.0)> Node.connect(:'two@203.0.113.0')
true

并列出来自节点二的节点:

iex(two@203.0.113.0)> Node.list
[:"one@198.51.100.0"]

我的问题:

我读到 :net_kernel.allow/1 可用于将允许连接的确切列表列入白名单。但我似乎无法让它工作:

iex(one@198.51.100.0)> :net_kernel.allow([])
:ok
iex(one@198.51.100.0)> Node.connect(:'two@203.0.113.0')
true

我希望,因为我允许 none 的列表,所以不允许连接。有什么建议吗?

更新:

我发现如果我将至少一个值传递给 :net_kernel.allow,它似乎有效:

iex(one@198.51.100.0)> :net_kernel.allow([:'127.0.0.0'])
:ok
iex(one@198.51.100.0)> Node.connect(:'two@203.0.113.0')
false
23:38:27.702 [error] ** Connection attempt with disallowed node :"two@203.0.113.0" **
iex(one@198.51.100.0)> :net_kernel.allow([:'two@203.0.113.0'])
:ok
iex(one@198.51.100.0)> Node.connect(:'two@203.0.113.0')
true

这是技巧吗?

白名单基于 VM Cookie,~/.erlang.cookie。 然后只有授权的节点才能拥有良好的 cookie 才能 connect.
对于安全性部分,我在我的服务器和我的笔记本电脑之间设置了一个 Tinc mesh VPN,这就是我需要的所有安全性,同时提供了极大的灵活性。

net_kernel 是一个创建 gen_server 进程的模块。在该进程状态下,它有一些参数,例如 allowed,它包含一个允许的节点列表,并且在启动时由一个空列表启动。

有一个未记录的功能,如果给定的连接节点不是允许节点的成员但该列表为空,它允许节点连接。来自 net_kernel.erl 模块的这段代码说明了这个事实:

setup(Node,Type,From,State) ->
    Allowed = State#state.allowed,
    case lists:member(Node, Allowed) of
        false when Allowed =/= [] ->
            error_msg("** Connection attempt with "
                      "disallowed node ~w ** ~n", [Node]),
            {error, bad_node};
        _ ->
            %% set up connection to given node
    end.

另一个重要的注意事项是关于 net_kernel:allow/1 函数,它是一个 append-only 函数。当使用 ++ 运算符将新节点添加到以前的节点时,您可以在其源代码中检查这一事实:

handle_call({allow, Nodes}, From, State) ->
    case all_atoms(Nodes) of
        true ->
            Allowed = State#state.allowed,
            async_reply({reply,ok,State#state{allowed = Allowed ++ Nodes}},
                        From);
        false ->
            async_reply({reply,error,State}, From)
    end;