README.md

# PhoenixDDOS

Application-layer DDOS protection for phoenix.

<p align="center">
  <a href="https://hexdocs.pm/phoenix_ddos">
    <img alt="Hex Docs" src="http://img.shields.io/badge/hex.pm-docs-green.svg?style=flat">
  </a>

  <a href="https://github.com/xward/phoenix_ddos/actions/workflows/ci.yml">
    <img alt="CI Status" src="https://github.com/xward/phoenix_ddos/actions/workflows/ci.yml/badge.svg">
  </a>

  <a href="https://hex.pm/packages/phoenix_ddos">
    <img alt="Hex Version" src="https://img.shields.io/hexpm/v/phoenix_ddos.svg">
  </a>

  <a href="https://opensource.org/licenses/Apache-2.0">
    <img alt="Apache 2 License" src="https://img.shields.io/hexpm/l/phoenix_ddos">
  </a>
</p>

# Installation

Add `:phoenix_ddos` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:phoenix_ddos, "~> 0.7"},
    # Highly recommended, this will makes sure we get the correct remote_ip in Conn
    {:remote_ip, "~> 1.1"}
  ]
end
```

# Usage

Add the `PhoenixDDOS` plug to your app's plug pipeline, along with the excellent `RemoteIp` (optional but highly recommended !).


```elixir
defmodule MyApp.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  # ...

  plug RemoteIp
  plug PhoenixDDOS

  # ...

end
```

# Configuration

```elixir
config :phoenix_ddos,
  protections: [
    # ip rate limit
    {PhoenixDDOS.IpRateLimit, allowed: 500, period: {1, :minute}},
    # ip rate limit on specific request_path
    {PhoenixDDOS.IpRateLimitPerRequestPath,
     request_paths: ["/graphql"], allowed: 20, period: {1, :minute}}
  ]
```

| Option                 | Default                | Description                                                               |
| :--------------------- | :--------------------- | :------------------------------------------------------------------------ |
| `enabled`              |      true   (@compil)  | set to false to disable                                                   |
| `jail_time`            |     15      (@compil)  | time an ip is fully blocked if caught by a protection. set nil to disable |
| `raise_on_reject`      |     false              | raise when we reject a connexion instead of returning an http code error  |
| `http_code_on_reject`  |       429              | http code returned when we reject a connexion                             |
| `protections`          | mandatory              | @see protections configuration                                            |


# Motivation

Add layer of protection within your phoenix application. Multi-layered DDoS protection is the best protection !

you don't always have access to a ddos protection in between internet and your phoenix application
You want advance ddos feature you can't have outside an applicative environment



# Protections configuration


## Ip jail

All protections that trigger a deny of an ip will push said ip into jail.

Jail default duration is 5min, configurable.

You can also configure this time per protection, set `jail_time` to nil to disable.


## Examples `PhoenixDDOS.IpRateLimit`

1. 500 per minute max, if triggered ip will be in jail for 15 minutes
```elixir
  [{PhoenixDDOS.IpRateLimit, allowed: 500, period: {1, :minute}}]
```

2. disable jail, ip will only be throttle to 500 per minute
```elixir
  [{PhoenixDDOS.IpRateLimit, allowed: 500, period: {1, :minute}, jail_time: nil}]
```

## Examples `PhoenixDDOS.IpRateLimitPerRequestPath`

1. single route
```elixir
    [{PhoenixDDOS.IpRateLimitPerRequestPath,
     request_paths: ["/graphql"], allowed: 20, period: {1, :minute}}]
```

2. multiple route consumming same quota
```elixir
    [{PhoenixDDOS.IpRateLimitPerRequestPath,
     request_paths: ["/graphql", "/graphiql"], allowed: 20, shared: true, period: {1, :minute}}]
```

3. multiple route consumming independant quota
```elixir
    [{PhoenixDDOS.IpRateLimitPerRequestPath,
     request_paths: ["/graphql", "/graphiql"], allowed: 20, period: {1, :minute}}]
```

4. is equivalant to:
```elixir
  [
   {PhoenixDDOS.IpRateLimitPerRequestPath,
    request_paths: ["/graphql"], allowed: 20, period: {1, :minute}},
   {PhoenixDDOS.IpRateLimitPerRequestPath,
    request_paths: ["/graphiql"], allowed: 20, period: {1, :minute}}
  ]
```

## period syntax example

* period: {30, :second}
* period: {1, :minute}
* period: {2, :minute}
* period: {1, :hour}
* period: {1, :day}