feat: added CLI to manage ddos protection system

This commit is contained in:
Eugene Prodan
2021-06-11 22:14:43 +03:00
parent fc978cbca6
commit a4b4e84544
5 changed files with 239 additions and 33 deletions

View File

@ -1,19 +1,53 @@
## HaProxy DDoS protection system PoC
If there is an unusual HTTP requests flow to a specific domain, the system detects it and triggers DDoS protection mode.
Each new client will be first forced to complete hCaptcha, before proceeding to the website.
The system provides functionality to protect certain (or all) resources on HaProxy from L7 DDoS attacks.
It works by requiring a user to have a specific cookie issued after successful captcha completion. If a user does not have the cookie, he gets redirected to a special captcha page.
##### How to test
It is by no means a cure for all ills, but should help you mitigate a moderate DDoS attack without disrupting the service.
#### How it works
![alternative text](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/mora9715/haproxy_ddos_protector/master/docs/interaction_diagram.txt)
#### How to test
- export hcaptcha sitekey and secret:
```bash
export HCAPTCHA_SITEKEY=xxxXXxxx
export HCAPTCHA_SECRET=xxxXXxxx
```
They can be obtained after creating a free account on https://www.hcaptcha.com/
- run docker compose:
```bash
docker compose up
```
- visit *http://127.0.0.1/captcha*
- visit *http://127.0.0.1*
For demostration purposes DDoS-protection mode was enabled by default.
#### CLI
The system comes with CLI. It can be used to manage global and per-domain protection:
```bash
Usage: ./ddos-cli <command> [options]
Command line interface to manage per-domain and global DDoS protection.
optional arguments:
-h, --help Show this help message and exit.
Commands:
Global management:
./ddos-cli global status Show status of global server ddos mode.
./ddos-cli global enable Enable global ddos mode.
./ddos-cli global disable Disable global ddos mode.
Domain management:
./ddos-cli domain list List all domains with ddos mode on.
./ddos-cli domain status <domain> Get ddos mode status for a domain.
./ddos-cli domain add <domain> Enable ddos mode for a domain.
./ddos-cli domain del <domain> Disable ddos mode for a domain.
```

View File

@ -0,0 +1,2 @@
poge.com
domain.com

View File

@ -19,8 +19,8 @@ frontend http-in
acl captcha_passed var(txn.captcha_passed) -m bool
acl on_captcha_url path -m beg /captcha
http-request lua.hcaptcha-redirect if !{ path -m beg /captcha }
http-request use-service lua.hcaptcha-view if { path /captcha }
http-request use-service lua.hcaptcha-view if on_captcha_url
http-request lua.hcaptcha-redirect if !on_captcha_url ddos_mode_enabled OR domain_under_ddos
http-request redirect location /captcha?%[capture.req.uri] code 301 if !captcha_passed !on_captcha_url ddos_mode_enabled OR domain_under_ddos
default_backend servers

197
src/cli/ddos-cli Executable file
View File

@ -0,0 +1,197 @@
#!/usr/bin/env bash
HAPROXY_DDOS_DOMAINS_FILE="/usr/local/etc/haproxy/domains_under_ddos.txt"
HAPROXY_GLOBAL_ACL="hdr_cnt"
HAPROXY_SOCKET="/var/run/haproxy.sock"
SOCAT="$(which socat)"
DOMAIN_REGEX='(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)'
_h_show_acl() {
if [[ ${1} ]]; then
local cmd="show acl #${1}"
else
local cmd="show acl"
fi
echo ${cmd} | ${SOCAT} ${HAPROXY_SOCKET} stdio
}
_h_add_acl() {
echo "add acl #${1} ${2}" | ${SOCAT} ${HAPROXY_SOCKET} stdio
}
_h_del_acl() {
echo "del acl #${1} ${2}" | ${SOCAT} ${HAPROXY_SOCKET} stdio
}
_help() {
/bin/cat <<EOF
Usage: $0 <command> [options]
Command line interface to manage per-domain and global DDoS protection.
optional arguments:
-h, --help Show this help message and exit.
Commands:
Global management:
$0 global status Show status of global server ddos mode.
$0 global enable Enable global ddos mode.
$0 global disable Disable global ddos mode.
Domain management:
$0 domain list List all domains with ddos mode on.
$0 domain status <domain> Get ddos mode status for a domain.
$0 domain add <domain> Enable ddos mode for a domain.
$0 domain del <domain> Disable ddos mode for a domain.
EOF
}
_ensure_domain_passed() {
if ! [[ ${1} ]]; then
echo "Error: 'domain' argument is required for this action"
_help
exit 1
elif ! echo ${1} | grep -qP ${DOMAIN_REGEX}; then
echo "Error: '${1}' is not a valid domain"
_help
exit 1
fi
}
_domain_list() {
local domain_acl_id=$(_h_show_acl | grep ${HAPROXY_DDOS_DOMAINS_FILE} | cut -d' ' -f1)
_h_show_acl ${domain_acl_id} | cut -d' ' -f2
}
_domain_status() {
local ddos_domains="$(_domain_list)"
local global_ddos_acl_id=$(_h_show_acl | grep ${HAPROXY_GLOBAL_ACL} | cut -d' ' -f1)
local global_ddos_status=$(_h_show_acl ${global_ddos_acl_id} | cut -d' ' -f2)
if echo "${ddos_domains}" | grep -q "^${1}$"; then
echo "DDoS-protection mode is enabled for ${1}"
else
echo "DDoS-protection mode is disabled for ${1}"
if [[ ${global_ddos_status} -eq 0 ]]; then
echo "ATTENTION: DDoS-protection mode is enabled globally"
fi
fi
}
_domain_add() {
local ddos_domains="$(_domain_list)"
if echo "${ddos_domains}" | grep -q "^${1}$"; then
echo "DDoS-protection mode is already enabled for ${1}"
exit 0
fi
local domain_acl_id=$(_h_show_acl | grep ${HAPROXY_DDOS_DOMAINS_FILE} | cut -d' ' -f1)
_h_add_acl ${domain_acl_id} ${1} &>/dev/null
if ! grep -q "^${1}$" ${HAPROXY_DDOS_DOMAINS_FILE}; then
echo ${1} >> ${HAPROXY_DDOS_DOMAINS_FILE}
fi
echo "DDoS-protection mode was enabled for ${1}"
}
_domain_del() {
local ddos_domains="$(_domain_list)"
if ! echo "${ddos_domains}" | grep -q "^${1}$"; then
echo "DDoS-protection mode is already disabled for ${1}"
exit 0
fi
local domain_acl_id=$(_h_show_acl | grep ${HAPROXY_DDOS_DOMAINS_FILE} | cut -d' ' -f1)
_h_del_acl ${domain_acl_id} ${1} &>/dev/null
if grep -q "^${1}$" ${HAPROXY_DDOS_DOMAINS_FILE}; then
sed -i "/^${1}$/d" ${HAPROXY_DDOS_DOMAINS_FILE}
fi
echo "DDoS-protection mode was disabled for ${1}"
}
_global_status() {
local global_ddos_acl_id=$(_h_show_acl | grep ${HAPROXY_GLOBAL_ACL} | cut -d' ' -f1)
local global_ddos_status=$(_h_show_acl ${global_ddos_acl_id} | cut -d' ' -f2)
if [[ ${global_ddos_status} -eq 0 ]]; then
echo "DDoS-protection mode is enabled globally"
else
echo "DDoS-protection mode is disabled globally"
fi
}
_global_enable() {
local global_ddos_acl_id=$(_h_show_acl | grep ${HAPROXY_GLOBAL_ACL} | cut -d' ' -f1)
local global_ddos_status=$(_h_show_acl ${global_ddos_acl_id} | cut -d' ' -f2)
if [[ ${global_ddos_status} -eq 0 ]]; then
echo "DDoS-protection mode is already enabled globally"
exit 0
fi
_h_add_acl ${global_ddos_acl_id} 0 &>/dev/null
_h_del_acl ${global_ddos_acl_id} 1 &>/dev/null
echo "DDoS-protection mode was enabled globally"
}
_global_disable() {
local global_ddos_acl_id=$(_h_show_acl | grep ${HAPROXY_GLOBAL_ACL} | cut -d' ' -f1)
local global_ddos_status=$(_h_show_acl ${global_ddos_acl_id} | cut -d' ' -f2)
if [[ ${global_ddos_status} -eq 1 ]]; then
echo "DDoS-protection mode is already disabled globally"
exit 0
fi
_h_add_acl ${global_ddos_acl_id} 1 &>/dev/null
_h_del_acl ${global_ddos_acl_id} 0 &>/dev/null
echo "DDoS-protection mode was disabled globally"
}
_handle_global_management() {
case ${1} in
status) _global_status;;
enable) _global_enable;;
disable) _global_disable;;
*) _help; exit 1;;
esac
}
_handle_domain_management() {
case ${1} in
list) _domain_list;;
status)
_ensure_domain_passed ${2}
_domain_status $2;;
add)
_ensure_domain_passed ${2}
_domain_add $2;;
del)
_ensure_domain_passed ${2}
_domain_del $2;;
*) _help; exit 1;;
esac
}
if ! [[ "${@}" ]]; then
_help
exit 1
fi
for i in "${@}"; do
case ${i} in
-h|--help) _help; exit 0;;
domain) MODE=DOMAIN; shift; break;;
global) MODE=GLOBAL; shift; break;;
*) _help; exit 1;;
esac
done
case ${MODE} in
DOMAIN) _handle_domain_management ${@};;
GLOBAL) _handle_global_management ${@};;
esac

View File

@ -1,27 +0,0 @@
#!/usr/bin/env bash
_help() {
/bin/cat <<EOF
Usage: $0 <command> [options]
Show help screen and exit.
optional arguments:
-h, --help show this help message and exit
Commands:
Global management:
$0 global status Show status of global server ddos mode.
$0 global enable Enable global ddos mode.
$0 global disable Disable global ddos mode.
Domain management:
$0 domain list List all domains with ddos mode on.
$0 domain status <domain> Get ddos mode status for a domain.
$0 domain add <domain> Enable ddos mode for a domain.
$0 domain del <domain> Disable ddos mode for a domain.
EOF
}
if ! [[ ${@} ]]; then
_help
fi