MIX_ENV=prod on a Phoenix server crashes on starting with PORT=80
Asked Answered
S

3

12

I am having some issues with mix, and MIX_ENV=prod with mix phoenix.server, where it fails on starting. Running all latest (Elixir 1.0.5, Phoenix 0.14.0) except Erlang (17.x, 17.3 I think) on Linode's Ubuntu 14.04.

$ MIX_ENV=prod PORT=80 mix phoenix.server
{"Kernel pid terminated",application_controller,"{application_start_failure,elirc_site,{{shutdown,{failed_to_start_child,'Elixir.ElircSite.Endpoint',{shutdown,{failed_to_start_child,'Elixir.Phoenix.Endpoint.Server',{shutdown,{failed_to_start_child,{ranch_listener_sup,'Elixir.ElircSite.Endpoint.HTTP'},{shutdown,{failed_to_start_child,ranch_acceptors_sup,{{badmatch,{error,eacces}},[{ranch_acceptors_sup,init,1,[{file,\"src/ranch_acceptors_sup.erl\"},{line,30}]},{supervisor,init,1,[{file,\"supervisor.erl\"},{line,243}]},{gen_server,init_it,6,[{file,\"gen_server.erl\"},{line,306}]},{proc_lib,init_p_do_apply,3,[{file,\"proc_lib.erl\"},{line,239}]}]}}}}}}}}},{'Elixir.ElircSite',start,[normal,[]]}}}"}

Specifically I think its this part of the trace.

{{badmatch,{error,eacces}},[{ranch_acceptors_sup,init,1,[{file,\"src/ranch_acceptors_sup.erl\"},{line,30}]}
Saxena answered 30/6, 2015 at 19:24 Comment(0)
S
5

It looks like your server is attempting to bind to a restricted port (less than 1024) without root privileges. Try using a higher port, like Phoenix's default of 4000. If it needs to be on port 80, either run it as root or (better) proxy it with nginx.

Subacute answered 30/6, 2015 at 19:31 Comment(3)
There is usually no need to run Phoenix behind nginx though. :)Carroty
Right. That's only better if you don't want to run your application level code as root but absolutely need port 80/443 (which I thought might have been the case here).Subacute
You could also do a NAT to the port using iptables (or firewall of preference). Ex: iptables -t nat -A OUTPUT -d myhostname -p tcp --dport 80 -j REDIRECT --to-ports 8080 && iptables -t nat -A PREROUTING -d myhostname -p tcp --dport 80 -j REDIRECT --to-ports 8080Lucretialucretius
D
5

As Nick Meharry pointed out you are trying to run your Phoenix server process on a port that traditionally on Unix, only root can bind to (low ports (<1024)).

Running your process as root is not recommended because of security reasons - an attacker who would take over the process could get root access to the whole operating system.

It's more secure to work around this by running your server on a high port (for instance, 4000) and use a simple iptables rule to forward the connections from port 80 to port 4000. Note that any user on your machine can bind to port 4000 - so you lose the extra protection from the low ports.

Another solution is to allow certain programs (mix, elixir) to bind to ports below 1024 using CAP_NET_BIND_SERVICE Linux kernels capability (available since 2.6.24) which can be set using setcap. But then still any user can use this executables unless they are available only to a certain user by using proper file access rights.

Digestant answered 1/7, 2015 at 9:14 Comment(0)
K
5

I see the following options:

  1. Use apache/nginx in front as a proxy for phoenix running on a higher port behind it—The creator of Elixir, has said there's usually no need to do that in this very question thread. If you already need nginx for something else (like running WP or serving static images), then just do this.
  2. Run as root—simple but very bad idea from a security perspective.
  3. Use IP tables—simple and effective, but breaks ipv6
  4. Use setcap to give specific binaries the power to open lower ports.

I like option 4.

In order to run mix phoenix.server and similar commands, you need to run setcap on your BEAM binary. You can find it by briefly starting the server as root and doing ps -ef | grep beam. On my system, it's /usr/lib/erlang/erts-8.1/bin/beam.smp, so I ran:

sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/lib/erlang/erts-8.1/bin/beam.smp

Now the server can be started on a normal port with mix commands. Next, presumably you're going to want to deploy an actual elixir release and run it as a service. The same strategy works. Use setcap on the run_erl binary your release created. For my app it was:

sudo setcap CAP_NET_BIND_SERVICE=+eip /home/ubuntu/myapp/_build/prod/rel/myapp/erts-8.1/bin/run_erl

Now an upstart or systemd script can launch the server, too.

Keele answered 13/12, 2016 at 11:55 Comment(1)
For me I had to use setcap on /beam not /beam.smp . This helps with testing if it worked: run iex, then :gen_tcp.listen(80, []). Should result something like: {:ok, #Port<0.1426>}Shortcake

© 2022 - 2024 — McMap. All rights reserved.