Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 148 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,167 @@
# haproxy-lua-dnsbl

DNSBL (DNS blacklisting) module for HAProxy Lua. Dynamically block requests based on the response of the DNS query or use
any of the added extra request headers to make decissions down the line.
DNSBL (DNS blacklisting) module for HAProxy Lua. Dynamically block requests based on the response of the DNS query or use any of the added extra request headers to make decisions down the line.

# Installation
## Features

`haproxy-lua-dnsbl` is depending on [utils](https://github.com/dobrevit/haproxy-lua-utils) and `socket` library. Download
a copy of them in Lua accessible path, for example `/usr/share/lua/5.3/`.
- Query multiple DNSBL providers (Tor exit lists, Spamhaus, etc.)
- Cache results using HAProxy stick-tables for optimal performance
- Configurable track-sc index (sc0, sc1, sc2) for flexible stick-table usage
- Detailed HTTP headers for logging and downstream processing
- Fail-open design - errors don't block legitimate traffic

# Usage
## Supported DNSBL Providers

Please have a look at the [example](./example/) folder for inspiration how to use this library.
| Provider | Domain | Description |
|----------|--------|-------------|
| Dan.me.uk Tor List | `.torexit.dan.me.uk` | Tor exit node list |
| Tor Project | `.exitlist.torproject.org` | Official Tor exit list |
| Spamhaus SBL | `sbl.spamhaus.org` | Spam sources |
| Spamhaus XBL | `xbl.spamhaus.org` | Exploits/botnets |
| Spamhaus PBL | `pbl.spamhaus.org` | Policy block list |
| Spamhaus ZEN | `zen.spamhaus.org` | Combined list |

# How it works
## Quick Start

The library is using the `socket.dns` class to make DNS queries. The DNSBL domain is configured in the `dnsbl_domain`
variable. The DNSBL domain is expected to return a `NXDOMAIN` response if the IP address is not blacklisted. If the
response is `NXDOMAIN`, the request is allowed to pass through. If there is a response, the request is marked for denying.
Finally, based on the results, the client IP will flip a set of gpc counters that will act as a cache for the next
requests.
You can read more about the design of the library in the [docs](./docs/) section.
```haproxy
global
lua-load /usr/share/lua/5.3/dnsbl.lua

# Known limitations
backend st_dnsbl_cache
stick-table type ipv6 size 1m expire 30m store gpc0,gpc1

I'm unable to find a reliable way to call `do-resolve` from within Lua, so I decided to fall back to the Lua `socket.dns`
class and use it to make DNS queries. For this reason only A records are supported.
Next to it, although the code is making provisions for configuring the DNSBL domain, only `.torexit.dan.me.uk` is supported.
frontend http-in
bind *:80
http-request track-sc0 src table st_dnsbl_cache
http-request lua.dnsbl_query st_dnsbl_cache .torexit.dan.me.uk "" ""
http-request lua.dnsbl_block st_dnsbl_cache
default_backend servers
```

# TODO
## Installation

* [X] Add support for `.torexit.dan.me.uk` domain
* [ ] Add support for `.exitlist.torproject.org` domain
* [ ] Add support for `xbl.spamhaus.org` domain
* [ ] Make it possible to configure the cache and ban track-sc index (hard-coded for now)
`haproxy-lua-dnsbl` depends on [utils](https://github.com/dobrevit/haproxy-lua-utils) and `socket` library. Download copies to your Lua path:

# Contributing
```bash
# Install to Lua path (e.g., /usr/share/lua/5.3/)
cp src/dnsbl.lua /usr/share/lua/5.3/

# Install dependencies
wget -O /usr/share/lua/5.3/utils.lua \
https://raw.githubusercontent.com/dobrevit/haproxy-lua-utils/main/src/utils.lua
```

## Documentation

Full documentation is available in the [docs](./docs/) folder:

- [Documentation Index](./docs/README.md)
- [Architecture Overview](./docs/architecture.md) - How the module works
- [API Reference](./docs/api-reference.md) - Complete function documentation
- [Configuration Guide](./docs/configuration.md) - HAProxy setup instructions
- [Understanding DNSBL](./docs/dnsbl-explained.md) - How DNS blacklists work
- [HTTP Headers Reference](./docs/http-headers.md) - Headers set by the module
- [Troubleshooting](./docs/troubleshooting.md) - Common issues and solutions

## Examples

The [examples](./examples/) folder contains ready-to-use configurations:

| Example | Description |
|---------|-------------|
| [Basic](./examples/basic/) | Minimal working configuration |
| [Behind Proxy](./examples/behind-proxy/) | Using X-Forwarded-For header |
| [Multiple DNSBLs](./examples/multi-dnsbl/) | Querying multiple providers |
| [Docker](./examples/docker/) | Complete Docker test environment |
| [Logging](./examples/logging/) | Custom log formats with DNSBL headers |

## Usage

### Basic Usage

```haproxy
http-request lua.dnsbl_query <backend> <domain> <src_var> <src_header> [sc_index]
http-request lua.dnsbl_block <backend>
```

### Parameters

| Parameter | Description |
|-----------|-------------|
| `backend` | Backend name containing the stick-table |
| `domain` | DNSBL domain (e.g., `.torexit.dan.me.uk`) |
| `src_var` | HAProxy variable with client IP (optional) |
| `src_header` | HTTP header with client IP (optional) |
| `sc_index` | Track-sc index: 0, 1, or 2 (optional, default: 0) |

### Examples

```haproxy
# Basic - direct client IP
http-request lua.dnsbl_query st_cache .torexit.dan.me.uk "" ""

# Behind proxy - use X-Forwarded-For
http-request lua.dnsbl_query st_cache .torexit.dan.me.uk "" X-Forwarded-For

# Use sc1 instead of sc0
http-request lua.dnsbl_query st_cache .torexit.dan.me.uk "" "" 1

# Multiple providers with different track-sc indices
http-request track-sc0 src table st_tor
http-request track-sc1 src table st_spam
http-request lua.dnsbl_query st_tor .torexit.dan.me.uk "" "" 0
http-request lua.dnsbl_query st_spam xbl.spamhaus.org "" "" 1
```

## How it Works

The library uses the `socket.dns` class to make DNS queries. The DNSBL domain is expected to return:

- **NXDOMAIN** - IP is not blacklisted (request allowed)
- **A record response** - IP is blacklisted (request blocked)

Results are cached using HAProxy's stick-table with `gpc0` and `gpc1` counters:
- `gpc0 = 1` → IP was checked and allowed
- `gpc1 = 1` → IP was checked and blocked

## HTTP Headers

The module sets these headers on each request:

| Header | Description |
|--------|-------------|
| `X-DNSBL-Action` | Result: CACHE-ALLOW, LOOKUP-DENY, etc. |
| `X-DNSBL-Is-Allowed` | 1 if allowed, 0 if blocked |
| `X-DNSBL-Client-IP` | IP address that was checked |
| `X-DNSBL-Query` | DNS query that was made |
| `X-DNSBL-Zone` | Spamhaus zone (if applicable) |
| `X-DNSBL-Description` | Block reason (if applicable) |

## Changelog

### v0.4.0 (Current)

- Added support for `.exitlist.torproject.org` domain
- Added support for Spamhaus domains (sbl, xbl, pbl, zen)
- Added configurable track-sc index (sc0, sc1, sc2)
- Added `X-DNSBL-Zone` and `X-DNSBL-Description` headers
- Added comprehensive documentation and examples

### v0.3.0

- Initial public release
- Support for `.torexit.dan.me.uk` domain

## Known Limitations

- Only A record DNS queries are supported (not AAAA)
- Spamhaus may rate-limit queries from public DNS resolvers

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dobrevit/haproxy-lua-dnsbl/issues.

# License
## License

MIT License

Expand Down
50 changes: 50 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# HAProxy Lua DNSBL Documentation

Welcome to the HAProxy Lua DNSBL documentation. This module enables dynamic request blocking based on DNS blacklist lookups directly within HAProxy.

## Quick Start

1. [Installation & Configuration](configuration.md) - Get up and running quickly
2. [Basic Example](../examples/basic/haproxy.cfg) - Minimal working configuration

## Documentation

| Document | Description |
|----------|-------------|
| [Architecture Overview](architecture.md) | How the module works, request flow, and caching mechanism |
| [API Reference](api-reference.md) | Complete function documentation with parameters and examples |
| [Configuration Guide](configuration.md) | HAProxy setup, stick-tables, and module loading |
| [Understanding DNSBL](dnsbl-explained.md) | How DNS blacklists work and supported providers |
| [HTTP Headers Reference](http-headers.md) | All headers set by the module |
| [Troubleshooting](troubleshooting.md) | Common issues and debugging tips |

## Examples

| Example | Description |
|---------|-------------|
| [Basic Setup](../examples/basic/) | Minimal configuration using direct client IP |
| [Behind Proxy](../examples/behind-proxy/) | Using X-Forwarded-For when behind a reverse proxy |
| [Multiple DNSBLs](../examples/multi-dnsbl/) | Querying multiple blacklist providers |
| [Docker Environment](../examples/docker/) | Complete Docker-based test environment |
| [Logging](../examples/logging/) | Custom log formats with DNSBL headers |

## Supported DNSBL Providers

| Provider | Domain | Response Code | Description |
|----------|--------|---------------|-------------|
| Dan.me.uk Tor Exit | `.torexit.dan.me.uk` | `127.0.0.100` | Tor exit node list |
| Tor Project Exit List | `.exitlist.torproject.org` | `127.0.0.2` | Official Tor exit list |
| Spamhaus XBL | `xbl.spamhaus.org` | Various | Exploits Block List |
| Spamhaus ZEN | `zen.spamhaus.org` | Various | Combined Spamhaus list |

## Version

Current version: **0.4.0**

## License

MIT License - See [LICENSE](../LICENSE) for details.

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dobrevit/haproxy-lua-dnsbl/issues.
Loading
Loading