logicmason
What's the best way to serve restricted ports (e.g. 80, 443) with Phoenix?
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 start it since it’s trying to access port 80 and linux restricts ports under 1024. Starting and running the server as root would work but I’m not going to do that.
In the past, working with other stacks the solutions I’ve used have been:
-
Use apache/nginx which do have elevated permissions to start but then drop them.
-
Use rails or node (or what ever I’m building with) on a higher port and run nginx in front as a proxy. (though Jose has said there’s usually no need to run Phoenix behind nginx)
-
Use IP tables (and break ipv6)
Based on what I’ve seen online, the best solution may be to use setcap to give the binary permission to use the port. However I’ve tried that on erl, mix, iex and elixir and still can’t get the server to bind to port 80.
$ sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/lib/erlan/bin/erl
$ sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/lib/elixir/bin/mix
$ sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/lib/elixir/bin/iex
$ sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/lib/elixir/bin/elixir
$ MIX_ENV=prod PORT=80 mix phoenix.server
06:10:14.161 [error] Failed to start Ranch listener Myapp.Endpoint.HTTP in :ranch_tcp:listen([port: 80]) for reason :eacces (permission denied)
Any suggestions?
Most Liked
voltone
The executable that’s actually opening the port is the BEAM VM. Start iex in one window, then run ps aux | grep beam in another to see the details of the process. In my case, the executable is /usr/lib/erlang/erts-7.2/bin/beam.smp. Try setting the capabilities on that file instead.
Update: confirmed on Ubuntu 14.04.
$ sudo setcap 'cap_net_bind_service=+ep' /usr/lib/erlang/erts-7.2/bin/beam.smp
$ iex
Erlang/OTP 18 [erts-7.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :gen_tcp.listen(443, [])
{:ok, #Port<0.1426>}
iex(2)>
logicmason
Got it! I had to use setcap on a file distillery created called run_erl. On my system it was:
sudo setcap CAP_NET_BIND_SERVICE=+eip /home/ubuntu/myapp/_build/prod/rel/myapp/erts-8.1/bin/run_erl
Now I can start the web server from its run script and with just sudo start myapp (since I’d already created the /etc/init/myapp.conf script)
Wow this was a bit of a rabbit but it should be easier for the next person searching on google. Thanks @voltone
I’ll definitely keep your idea in mind too, @bobbypriambodo. Nginx is always a possibility in the future if I need it for filtering spammy requests to “admin.php” and other non-existent URLs that seem to be popular for bots to hit.
voltone
jwarlander
…and probably after it’s been deployed
I suspect that tar doesn’t preserve capabilities on archived files.
idi527
On FreeBSD I just bind ports 80 and 443 to ports 8080 and 8443, respectively, with pf. And start the app without any need for sudo. I think it’s also possible on Linux, albeit with another firewall.







