README.md

# PhoenixDDoS

High performance application-layer DDoS protection for Elixir Phoenix.

<p align="center">
  <a href="https://hexdocs.pm/phoenix_ddos/PhoenixDDoS.html">
    <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>

# Table of contents

Usage
  Installation
  Configuration

How it works (you defined rules and list, it can put in jail or just block if it reach any threshold)

Features

Monitoring
  Telemetry events (@see module PhoenixDDoS.Telemetry)
  Notification to sentry

Local tests
  DDoS youself using siege (mix phoenix_ddos.attack_myself)

Benchmarks

Community
Next in roadmap

# Features

protection: ip safelist_ips
protection: ip blocklist_ips
protection: `PhoenixDDoS.IpRateLimit`
protection: `PhoenixDDoS.IpRateLimitPerRequestPath`
protection: log flooding

engine: jail system

monitoring: telemetry
monitoring: sentry

local tools: ddos youself testing


# Usage
<!-- MDOC -->

`phoenix_ddos` is a high perContact / Supportformance application-layer DDoS protection for Elixir Phoenix.

## Installation

1. 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
    {:remote_ip, "~> 1.1"}
  ]
end
```

2. Add the `PhoenixDDoS` plug to your app's Endpoint, after the excellent [RemoteIp][remote_ip_github] plug (optional but highly recommended !).

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

  # put as high in the order as possible
  plug RemoteIp
  plug PhoenixDDoS

  # ...

end
```

## Configuration

```elixir
config :phoenix_ddos,
  safelist_ips: ["1.2.3.4", "5.6.7.0"],
  blocklist_ips: ["11.12.13.0"],
  protections: [
    # ip rate limit
    {PhoenixDDoS.IpRateLimit, allowed: 500, period: {2, :minutes}},
    {PhoenixDDoS.IpRateLimit, allowed: 10_000, period: {1, :hour}},
    # ip rate limit on specific request_path
    {PhoenixDDoS.IpRateLimitPerRequestPath,
     request_paths: ["/graphql"], allowed: 20, period: {1, :minute}}
  ]
```

| Type | Option                    | Default | Description                                                                                     |
| :--- | :------------------------ | :------ | :---------------------------------------------------------------------------------------------- |
| bool | `enabled`                 | true    | set false to disable                                                                            |
| int  | `jail_time` (minutes)     | 15      | time an ip is fully blocked if caught by a protection. set nil to disable thus blocking instead |
| bool | `raise_on_reject`         | false   | raise when we reject a connexion instead of returning an http code error                        |
| int  | `http_code_on_reject`     | 429     | http code returned when we reject a connexion                                                   |
| list | `protections`             |         | @see Protections                                                                                |
| list | `safelist_ips`            |         | bypass all protections ips                                                                      |
| list | `blocklist_ips`           |         | always blocked ips                                                                              |
| bool | `on_jail_alert_to_sentry` | false   | notify slack when an ip get jailed                                                              |


### Examples with `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 with `PhoenixDDoS.IpRateLimitPerRequestPath`

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

2. you can also give a phoenix-like path
```elixir
    [{PhoenixDDoS.IpRateLimitPerRequestPath,
     request_paths: ["/admin/:id/dashboard"], allowed: 20, period: {1, :minute}}]
```

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

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

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

[remote_ip_github]: https://github.com/ajvondrak/remote_ip


# Community

Slack: join [elixir-lang](https://elixir-lang.slack.com/) and join channel `#phoenix_ddos`



## Next in roadmap
- ip blocklist/safelist with mask/subnet
- log central to avoid log spam and provide aggregated report
- more performances