"ddos challenge" style script #4
5 changed files with 35 additions and 62 deletions
|
|
@ -10,8 +10,10 @@ aproxy is an Activity Pub Reverse Proxy Framework built on OpenResty (NGINX + Lu
|
|||
|
||||
### Testing
|
||||
```sh
|
||||
# Install test dependencies (only needed once)
|
||||
# Install test dependencies (only needed once for project setup)
|
||||
make testdeps
|
||||
|
||||
# run this to setup the PATH so that it works
|
||||
eval (luarocks-5.1 path --bin)
|
||||
|
||||
# Run test suite
|
||||
|
|
|
|||
|
|
@ -252,23 +252,8 @@ protected_paths = {}
|
|||
|
||||
4. **Use PCRE regex**: Patterns are PCRE regular expressions, so you can use advanced patterns like `^/api/v[0-9]+/search$` for complex matching.
|
||||
|
||||
## Testing
|
||||
|
||||
The module includes comprehensive tests. To run them:
|
||||
|
||||
```bash
|
||||
# Install test dependencies (once)
|
||||
make testdeps
|
||||
eval $(luarocks-5.1 path --bin)
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Token Generation**: The module uses Lua's `math.random` for token generation. For production use with high security requirements, consider integrating a cryptographically secure random source.
|
||||
|
||||
2. **Cookie Security**: Cookies are set with `HttpOnly` and `SameSite=Lax` flags for security. Consider adding `Secure` flag if you're running HTTPS only.
|
||||
|
||||
3. **Shared Dictionary Size**: Size the shared dictionaries appropriately:
|
||||
|
|
@ -281,37 +266,3 @@ make test
|
|||
set_real_ip_from 10.0.0.0/8; # Your proxy IP range
|
||||
real_ip_header X-Forwarded-For;
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Shared State**: Uses nginx shared memory, so it's per-server. In a multi-server setup, bans and tokens aren't shared across servers.
|
||||
- **Memory Limits**: Shared dictionaries have fixed sizes. Old entries are evicted when full (LRU).
|
||||
- **Not a Complete Solution**: This helps with volumetric attacks and simple bots, but sophisticated attackers can bypass it. Use in combination with other security measures.
|
||||
|
||||
## Combining With Other Scripts
|
||||
|
||||
This module can be combined with other aproxy scripts for defense in depth:
|
||||
|
||||
```lua
|
||||
return {
|
||||
version = 1,
|
||||
wantedScripts = {
|
||||
-- First layer: Challenge-response for DDoS/bot protection
|
||||
['ddos_protection_challenge'] = {
|
||||
ban_duration = 3600,
|
||||
token_duration = 86400,
|
||||
-- ... config
|
||||
},
|
||||
|
||||
-- Second layer: Restrict specific expensive endpoints
|
||||
['pleroma_restrict_unauthenticated_search'] = {},
|
||||
|
||||
-- Third layer: Allowlist for webfinger
|
||||
['webfinger_allowlist'] = {
|
||||
accounts = {'user@domain.com'}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scripts execute in order, so the challenge runs first, filtering out bots before they hit your more specific rules.
|
||||
|
|
|
|||
|
|
@ -2,18 +2,16 @@
|
|||
-- Similar to Cloudflare's "Under Attack" mode
|
||||
-- Presents a challenge page with a honeypot link
|
||||
|
||||
local resty_random = require "resty.random"
|
||||
local resty_string = require "resty.string"
|
||||
|
||||
local function generateToken()
|
||||
-- Generate a random token for validation
|
||||
-- Using ngx.time() and math.random for simplicity
|
||||
-- In production, consider using a more secure method
|
||||
math.randomseed(ngx.time() * 1000 + math.random(1, 1000))
|
||||
local chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
local token = {}
|
||||
for i = 1, 32 do
|
||||
local idx = math.random(1, #chars)
|
||||
token[i] = chars:sub(idx, idx)
|
||||
-- Generate a cryptographically strong random token
|
||||
local strong_random = resty_random.bytes(16, true)
|
||||
while strong_random == nil do
|
||||
strong_random = resty_random.bytes(16, true)
|
||||
end
|
||||
return table.concat(token)
|
||||
return resty_string.to_hex(strong_random)
|
||||
end
|
||||
|
||||
local function getCookieValue(cookie_header, cookie_name)
|
||||
|
|
@ -161,8 +159,11 @@ end
|
|||
|
||||
local function serveQuestionChallenge(original_uri, state)
|
||||
-- Select a random question
|
||||
math.randomseed(ngx.time() * 1000 + math.random(1, 1000))
|
||||
local q_idx = math.random(1, #QUESTIONS)
|
||||
local random_byte = resty_random.bytes(1, true)
|
||||
while random_byte == nil do
|
||||
random_byte = resty_random.bytes(1, true)
|
||||
end
|
||||
local q_idx = (string.byte(random_byte) % #QUESTIONS) + 1
|
||||
local question = QUESTIONS[q_idx]
|
||||
|
||||
-- Generate a challenge ID to store the correct answer
|
||||
|
|
|
|||
3
test.lua
3
test.lua
|
|
@ -9,6 +9,9 @@ end
|
|||
package.preload['resty.string'] = function()
|
||||
return require('tests.mock_resty_string')
|
||||
end
|
||||
package.preload['resty.random'] = function()
|
||||
return require('tests.mock_resty_random')
|
||||
end
|
||||
|
||||
-- Create a mock shared dictionary
|
||||
local function createMockSharedDict()
|
||||
|
|
|
|||
16
tests/mock_resty_random.lua
Normal file
16
tests/mock_resty_random.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
-- Mock implementation of resty.random for testing
|
||||
|
||||
local random = {}
|
||||
|
||||
function random.bytes(len, strong)
|
||||
-- For testing, generate predictable random bytes using math.random
|
||||
-- In real OpenResty, this would use cryptographic random sources
|
||||
local bytes = {}
|
||||
for i = 1, len do
|
||||
-- Use math.random to generate bytes (0-255)
|
||||
table.insert(bytes, string.char(math.random(0, 255)))
|
||||
end
|
||||
return table.concat(bytes)
|
||||
end
|
||||
|
||||
return random
|
||||
Loading…
Add table
Add a link
Reference in a new issue