erlware / relx

Sane, simple release creation for Erlang
http://erlware.github.io/relx
Apache License 2.0
697 stars 232 forks source link

Don't use '-setcookie' if cookie file exists #803

Open weiss opened 4 years ago

weiss commented 4 years ago

The extended start script's RPC commands (such as remote_console) use -setcookie $COOKIE even in the case where a $HOME/.erlang.cookie file exists. The $COOKIE is read from that file and added to the command line, which (usually) allows other local users to read out the cookie from the ps(1) output, for example. Therefore, I think it would be nice if this could be changed.

ferd commented 4 years ago

I'm pretty sure you want to allow -setcookie $COOKIE to take over reading the file, because otherwise you can't override what the file says? The cookie setting is usually done from the vm.args file more than anything else iirc.

weiss commented 4 years ago

I'm all for allowing to override. This is just about the case where no cookie is specified in the vm.args file. Commands such as daemon and foreground then fall back to $HOME/.erlang.cookie as you'd expect. So do the RPC commands, but for those, the script implements the fallback by doing COOKIE=$(cat "$HOME/.erlang.cookie") and then -setcookie $COOKIE. My issue is about changing this implementation, not about changing behavior.

My rationale is that adding the $COOKIE to the process argument list might be seen as a security issue Relx users might want to avoid by omitting the cookie from the vm.args file. They might not expect the extended start script to add it "behind their back" to RPC command lines.

tsloughter commented 4 years ago

Cookies aren't used for security.

That said, I'd be happy to have a change that kept it out of the ps output anyway. Preferably by changing the name of the process, but I think that could only be done for stuff like rpc's by patching Erlang itself.

The other option may turn into a bit of a mess but maybe not. We already check all the different ways the cookie could be set, it may work by simply changing to setting an variable COOKIE_ARG instead of COOKIE that is created like COOKIE_ARG="-setcookie ${COOKIE}". Except in the case that the cookie found in ~/.erlang.cookie in which case COOKIE_ARG="".

I think if that turned out to not be a mess of a patch we'd definitely accept it.

weiss commented 4 years ago

Cookies aren't used for security.

If they're not used for preventing access from unauthorized users, what's their purpose? And doesn't this imply Erlang/OTP releases cannot safely be run on a multi-user/multi-service system because there's no way to prevent other users from gaining unlimited access to the Erlang/OTP process? (Except in non-distributed mode, which would render the extended start script pretty much useless.)

I think if that turned out to not be a mess of a patch we'd definitely accept it.

Okay, I might have a go at it then. Thanks a lot for your feedback!

tsloughter commented 4 years ago

Cookies prevent nodes from different clusters from joining each other. Sort of like a cluster name that each node has to make sure they are all on the same page on what cluster they are apart of.

In a multi-tenant environment the inet_tls_dist should be used.

If not using TLS, or well, even if using TLS, depending on the environment you have additional options for restricting access. For example with Kubernetes there are network policies and you can restrict connections to nodes that way.

weiss commented 4 years ago

Well. I've chosen Erlang/OTP for writing a piece of (non-clustered) server software, and I figured it makes sense to use your extended start script rather than reinventing wheels. I was hoping to publish a release without having to tell my users that unlike with typical non-Erlang daemons, they must go to great lenghts to lock down / containerize the software and/or set up TLS just to be able to control the local node.

From what I can tell, the cookie mechanism basically offers the properties of shared secret authentication mechanisms as long as you can assume the attacker can't sniff the traffic. Which may or may not be good enough depending on the attack vectors you'd like to protect against, but not equivalent to exposing the cookie on the process argument list.

tsloughter commented 4 years ago

I tend to just bind to localhost and then any attack requires gaining access to the node first.

You can lock down further as you mention with containers and only binding within the container network, at that point an attacker would have to gain access to the node and then have permissions to run a command inside the container that is running the release.

weiss commented 4 years ago

I tend to just bind to localhost and then any attack requires gaining access to the node first.

Yes, my point was entirely about local attackers. If admins run my Erlang/OTP deamon and Nginx on the same system, and the attacker exploits some PHP script to get local access, I'd like to avoid giving him full control over my Erlang/OTP deamon by means of running ps(1). I think admins outside our OTP bubble won't assume us bypassing Unix user privilege seperation that way, and I can see them screaming "CVE!!!" once they notice.

That said, I'd be happy to have a change that kept it out of the ps output anyway. Preferably by changing the name of the process, but I think that could only be done for stuff like rpc's by patching Erlang itself.

BTW, if Erlang itself was patched, I think the more straightfoward solution would be to have it support an ERL_COOKIE environment variable.

tsloughter commented 4 years ago

Yea, Erlang supporting an env variable is certainly an option. I was simply thinking about how a process can change its name so it shows up different in the process status.

ferd commented 4 years ago

Erlang nodes can also call erlang:set_cookie/1 to change their global cookie, overriding whatever is in the command line, and set erlang:set_cookie(Node, Cookie) to do per-node settings (useful when setting up nodes that are to bridge gaps between sub-clusters), so this can let you pick a distinct cookie than what is in the command line for your software.

Do note that an attacker can likely still (particularly for non-TLS setups) do a DoS by just initiating distributed Erlang connections with bad atom names and exhausting all the memory on the node since atoms are not garbage-collected.

tsloughter commented 4 years ago

@ferd true, but then the extended start script won't work.

Also the attacker can snoop the cookie from the connection if they are on the network and you aren't using TLS.

So from an attacker who really wants to do something removing the cookie from ps doesn't help.

It certainly doesn't hurt, but like Fred mentioned with the DoS attack, best to assume that if someone is able to compromise the machine Erlang is on and it is exposing a listen port (as in, not restricting it to within a docker container so that the attacker would first have to gain permission to exec into the container) that the Erlang node is compromised.

So still happy to have a patch that removes it from ps somehow but worth keeping in mind the reality of the "security" it provides.

weiss commented 4 years ago

Snooping localhost traffic usually requires root privileges.

worth keeping in mind the reality of the "security" it provides.

Absolutely :smile: I tried to describe my view of that reality above. I totally agree that hiding the cookie only protects against some but not all attack vectors.

tsloughter commented 4 years ago

@weiss are you able to work on this?

I think it is going to require some tricky scripting. I may take a look at implementing it tomorrow if another feature I'm trying to get in to relx before releasing 4.0.0 is quick enough.

weiss commented 4 years ago

Sorry, I had no time last week-end, but I still have this on my list. Congrats to the release!