Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elixir distributed security and whitelisting allowed nodes

TL;DR

  • How to whitelist nodes that can Node.connect to Elixir?
  • Any other security recommendations?

Setup

I've begun tinkering with distributing Elixir across (for now) two different servers.

For example, let's say the server's two IP addresses are:

  1. 198.51.100.0
  2. 203.0.113.0

First, I added new rules to the iptables firewall on both servers, opening up port 4369 (EPMD) and a range of 10 ports between 9000-9010 for nodes. I'm also only allowing incoming connections from the other server's exact IP address.

Example config for 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

Example config for 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

Now I can open up iex shells on each machine:

198.51.100.0:

$ iex --name [email protected] --cookie secret --erl '-kernel inet_dist_listen_min 9000' --erl '-kernel inedist_listen_max 9010'

203.0.113.0:

$ iex --name [email protected] --cookie secret --erl '-kernel inet_dist_listen_min 9000' --erl '-kernel inedist_listen_max 9010'

I can successfully connect to node two from node one:

iex([email protected])> Node.connect(:'[email protected]')
true

And list nodes from node two:

iex([email protected])> Node.list
[:"[email protected]"]

My Question:

I've read that :net_kernel.allow/1 can be used to whitelist an exact list of allowed connections. But I can't seem to get it working:

iex([email protected])> :net_kernel.allow([])
:ok
iex([email protected])> Node.connect(:'[email protected]')
true

I would expect that since I've allowed a list of none, no connection would be allowed. Any tips?

Update:

I discovered that if I pass at least one value to :net_kernel.allow, it seems to work:

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

Is that the trick?

like image 310
seanomlor Avatar asked Dec 25 '22 10:12

seanomlor


2 Answers

Whitelisting is based on the VM Cookie, ~/.erlang.cookie. Then only authorized Nodes will be in possession of the good cookie to be able to connect.
For the security part, I've set up a Tinc mesh VPN between my servers and my laptop, and it's all the security I need while providing great flexibility.

like image 158
Hécate Avatar answered Dec 28 '22 08:12

Hécate


net_kernel is a module that creates a gen_server process. In that process state it has some parameters such as allowed which holds a list of allowed nodes and at startup was initiated by an empty list.

There is an undocumented feature that if the given node for connection is not a member of allowed nodes but that list is empty, it lets the node to connect. This code snippet from net_kernel.erl module says this fact:

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.

Another important note is about net_kernel:allow/1 function which is an append-only function. You can check this fact in its source code when the new nodes are added to previous ones with ++ operator:

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;
like image 32
Hamidreza Soleimani Avatar answered Dec 28 '22 10:12

Hamidreza Soleimani