Can I get Phoenix to not reply al all to certain http requests

For certain http requests, I would like to have no response. I know how to use send_resp return 500 or 404 or whatever, but is there a way to just disconnect with no response? ie I need a “send-no-resp”

How do you identify those requests? Is there some reverse proxy involved? If yes, let it handle that stuff, if no, it will act on that disconnect with a bad gateway or similar.

The software is for command and control of security devices. There are two scenarios where it would be desirable for “no response”. One is if the device thinks the request came from a hacker instead of a valid authenticated authorized controller. I.e. don’t give the hacker any info at all - let them time out not knowing why. The other example is when responding at machine speed to a massive DDoS attack. The controller of the network under attack would prefer to fire & forget to billions of devices. If some percentage don’t work, they can be cleaned up later. It would still be preferably to hit more devices faster without worrying about the responses. The command language has an option to request this as part of the command. Although the real reason why it needs to be done is because the requirements say so :wink: so I need to figure out how to do it.

2 Likes

I didn’t ask why you want to do so, but I asked if other software is involved that could do that.

Especially for the DDOS scenario, there are tools available that handle the connection drop at the kernel level and therefore could drop the connection faster than cowboy could even read the source IP.

2 Likes

I would handle these use cases from your firewall (iptables/pf) in cooperation with something like fail2ban. What nobbz said, you need to handle this as early as possible otherwise your can’t defend against the force of a DDoS (if you ever can). But in the generic use case of an attacker doing strange things on the TCP level you need firewalling (there’s so much more a firewall does for you like scrubbing/normalizing packets etc). If you detect stuff inside your app (so on the elixir level) you could write out log files that triggers fail2ban.

There are way more options to secure the device, should it really be completely open on the internet?
You could put it behind a wireguard vpn or on a zerotier private network (on top of the internet), you could secure it by running on TLS(https) but require the user to come in via mutal TLS (https where the client needs a certificate on the client side to connect).

edit: slight addition; not sure about the whole use case but if you’re afraid of DDoS then you could also go with services like cloudflare which do this as one of their core services, you just put them in front of your app(s).

1 Like

If you want to close the TCP connection without sending a reply, and you can be sure the Plug adapter is Plug.Cowboy, then you could use this hack:

def no_reply(conn) do
  {Plug.Cowboy.Conn, %{pid: ranch_pid}} = conn.adapter
  Process.exit(ranch_pid, :kill)
end

Letting the connection time out without sending a TCP FIN while still clearing resources on the server would require low-level access to the kernel TCP stack. Not even the new socket module in OTP 22 can do that.

Edit: this works with Cowboy 1, probably not without changes on Cowboy 2, due to the changes in the process model

5 Likes

Processes are cheap. Process.sleep(:infinity) :wink:

And you hold onto an ephemeral port on the gateway in front of you, it runs out of ports–mission accomplished for the DDOSer!

2 Likes

Hmmm, good point. Yeah don’t do that. :laughing:

Wrt to 'firewall (iptables/pf) ’ - This is the command coming in to tell you do exactly that. Ie it’s the theat intel coming in to tell you to not accept communications from this bad guy. And the threat intel box is telling billions of IOT devices, so doesn’t want to be bothered with replies. Probable answer is to either live with the replies or to use MQTT instead of HTTPS.

wonder if conn |> halt() works…

eg the example here https://hexdocs.pm/phoenix/Phoenix.Controller.html#module-plug-pipeline and then remove the redirect…

It works partially. Your server still sends a reply though – which is not the expected outcome when you want to deter a DDoS attack.

Others here are correct: you should enforce that with a really well-made router/switch configuration. Or offload the responsibility to the likes of Cloudflare.

You should offload this, as others said.
NGINX has a http status code for that, and your rule / CSP / Elixir response can reply that code.
https://httpstatuses.com/444

Though all the work that has to be done in the application to decide if a request should be “dropped” that way already costs.

Also the connection has already been accepted at this point in time.

A much more effective way against (D)DoS attacks is to just drop their packets as early in the network as possible.

If the package gets to your server and an ack/syn is sent, you already lost the battle

wrt ‘A much more effective way against (D)DoS attacks is to just drop their packets as early in the network as possible.’ - this is the actual use case we are trying to do. The first D in DDoS is distributed and the ‘as early in the network’ is the many billions of end devices or the billions of edge routers. Assuming the trends of the last 25 years continue, this will only get worse. OpenC2 is the standard to do the command & control you mention. The standard does, at the moment, call for this capability and I’d like to meet it if possible. Note I’m trying to build a proof of concet for the server to send the billions of messages wo hanging it waiting for replies (and more importantly not dealing with the few percent that either won’t reply or will reply with errors - if I block 90% that would be great). But if it works with replies, maybe I should lobby to change the OpenC2 standard instead.

It sounds like you are trying to build the Firewall layer everyone is talking about. Sounds like a cool project. Most likely you’re going to have to drop down to the Ranch layer, maybe lower.

I’d check out writing a custom Ranch Protocol, https://ninenines.eu/docs/en/ranch/2.0/guide/protocols/#_writing_a_protocol_handler

Specifically, you could probably return an :ignore from your init callback to just drop the connection.

1 Like