bartekupartek

bartekupartek

Is -ssl_dist_optfile the way to secure cluster of Beam nodes? How to secure connections of publicly exposed Beam nodes and epmd?

I’ve multiple api instances installed on the on premise servers that are sometimes offline. I’d like to setup one master admin node that would allow me to connect to all of them when they are online. The further concern is that it would be nice to connect tenants only to admin node but not to each other, so that only admin node should know about all tenants.

To achieve this goal I’ve setup admin project with Elixir 1.9 with Dockerfile that exposes following ports:

ENV APP_PORT=4000 BEAM_PORT=9000 ERL_EPMD_PORT=4369
EXPOSE $APP_PORT $BEAM_PORT $ERL_EPMD_PORT

and configured release with limited range of ephemeral ports of Beam node:
env.sh.eex

case $RELEASE_COMMAND in
  start*|daemon*)
    ELIXIR_ERL_OPTIONS="-kernel inet_dist_listen_min $BEAM_PORT inet_dist_listen_max $BEAM_PORT"
    export ELIXIR_ERL_OPTIONS
    ;;
  *)
    ;;
esac
export RELEASE_DISTRIBUTION=name
export RELEASE_NODE=<%= @release.name %>@my_brodcast_domain.com

The dockerimage is deployed in the Kubernetes cluster and this setup is valid until I’m using this ports only inside the k8s cluster. As I’ve mentioned my most of tenant applications are outside k8s cluster on the on premise servers, so I’ve setup my_brodcast_domain.com domain that resolves Kubernetes LoadBalnacer Service witch 4369, 9000, 4000 ports publicly:

apiVersion: v1
kind: Service
metadata:
  name: admin-api
  namespace: admin-api
  labels:
    app.kubernetes.io/name: admin-api
    app.kubernetes.io/part-of: admin-api
spec:
  selector:
    app.kubernetes.io/name: admin-api
    app.kubernetes.io/part-of: admin-api
  type: LoadBalancer
  ports:
  - port: 4369
    targetPort: 4369
    name: epmd
  - port: 9000
    targetPort: 9000
    name: erlang
  - port: 80
    targetPort: 4000
    protocol: TCP
    name: app

I’m able to connect to admin node from tenant servers instances:

iex --name "myapp2@my_brodcast_domain.com" --cookie "super_super_secret"

Also I’m able to login by remote console “remsh” to admin node and I’ve got access to all connected nodes, hurray I was happy for a while:

iex(admin_api@my_brodcast_domain.com)2> Node.list
[:"myapp2@my_brodcast_domain.com", :"myapp1@my_brodcast_domain.com",
 :"myapp3@my_brodcast_domain.com", :"myapp4@my_brodcast_domain.com"]

but now my concern is about security, I’ve read that I shouldn’t never expose Beam port and epmd port in the public network.

Since Erlang/OTP 20.2 release there is ssl_dist_optfile option with following description:

A new command line option -ssl_dist_optfile has been
added to facilitate specifying the many options needed
when using SSL as the distribution protocol.

I couldn’t find any interesting resource that would answer my question if is it possible to use it to secure my described cluster? If yes how to configure it with Elixir 1.9 release?
I need to exclude using of SSH port tunnels because clients are installing this apps by docker-compose and with just run command and they don’t have technical skills to configure SSH to pass communication over bastion host etc.
Are there some other options to achieve my goal?

Most Liked

dch

dch

A few things:

  1. never ever ever expose epmd & your node to the internet. Opinions vary about whether your phoenix/cowboy/… app can be directly accessible, but personally I leave TLS to haproxy or similar tools, do basic route and denial-of-service handling (max connections/second etc) there, and then spread that out across my OTP instances. I also use a firewall to handle blocking really bad actors where haproxy is not sufficient - I then distribute that blocklist across all nodes.

  2. I restrict the range of inter-node ports that the VM can use, and via firewall restrict that range to only permitted nodes. This may require that non-trusted nodes need to bridge via ssh to get in, but I consider direct node access something that users should never get, have, nor need.

  3. erlang cookies are not a particularly big space, and can be brute-forced in parallel - there is no ratelimiting nor any other significant protection. By default the auto-created erlang cookie is a 20 byte sequence of upper-case ASCII characters so choose your own, and make it longer with a wider key space. Leave your epmd listening only on a VPN or private network if you must.

  4. In my specific case, all BEAMs are connected to an internal IPv6 only mesh vpn and this means that any user has to auth to the vpn first. My haproxy instances can bridge the real world to the vpn world, across any physical node.

  5. This makes connecting via remsh like this from a permitted node viz #2:

$ ERL_ZFLAGS='-proto_dist inet6_tcp -kernel inet_dist_listen_min 4370 -kernel inet_dist_listen_max 4400' \
    iex  --cookie (ssh koans@i09 cat .erlang.cookie) \
    --name console@wintermute.zt.koans \
    --remsh zen@i09.zt.koans
  1. TLS only encrypts the traffic between nodes unless you verify certificates. In my experience, TLS has been a huge pain in the butt to manage. I don’t like it, and use mesh vpn or spiped to avoid it. https://www.erlang-solutions.com/blog/erlang-distribution-over-tls.html

  2. As a final point, there is no particular need to use the same cookie on all nodes if your app doesn’t require the mesh. You can manually initiate remsh connections from your control node, using Node.set_cookie/2 and then Node.connect/1 will use a per-cookie node.

Finally, I rarely need console access once apps are deployed to production, so I’m curious what drives this requirement. Do you use distributed erlang layer at all? If not, don’t configure it, and just use ssh port forwarding to deal with any nodes that need some remsh love.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

You can also solve this at the network layer by leveraging AWS VPCs, or the equivalent thereof in your cloud of choice. We don’t allow public internet traffic to hit our application servers at all. Public internet traffic hits load balancers, which route traffic on HTTP / HTTPs ports to the nodes.

@bartekupartek is using kubernetes so this is probably already how he’s doing things. For remsh look into kubectl exec.

NOTE: this does require that you trust your private network. If you’re a small to medium sized company this may be reasonable. If you’re large and doing multi-tenant stuff, you have to lose this assumption and do more complex stuff.

Where Next?

Popular in Questions Top

Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Other popular topics Top

belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement