478 lines
16 KiB
Markdown
478 lines
16 KiB
Markdown
# iauthd-ts
|
|
|
|
A TypeScript implementation of an IAuth daemon for Nefarious IRCd. This daemon performs real-time DNS-based blacklist (DNSBL) lookups on connecting clients and can block, mark, or whitelist users based on the results.
|
|
|
|
This is a port of the original Perl `iauthd.pl` to TypeScript/Node.js.
|
|
|
|
## Features
|
|
|
|
- **DNSBL Lookups**: Check connecting clients against multiple DNS blacklists
|
|
- **Flexible Matching**: Match by index value or bitmask
|
|
- **Caching**: DNS results are cached to reduce lookup overhead
|
|
- **SASL Authentication**: Handle SASL PLAIN authentication directly in IAuth (no services required)
|
|
- **SASL/LOC Support**: Exempt authenticated users from blocks
|
|
- **WEBIRC Support**: Re-check real IP for trusted proxy connections
|
|
- **Marking**: Tag users with marks visible to operators
|
|
- **Class Assignment**: Assign users to connection classes based on DNSBL results
|
|
- **Whitelisting**: Exempt users matching certain DNSBLs from all blocks
|
|
|
|
## How It Works
|
|
|
|
```
|
|
┌─────────────┐ stdin/stdout ┌─────────────┐
|
|
│ Nefarious │ ◄──────────────────► │ iauthd-ts │
|
|
│ IRCd │ IAuth Protocol │ │
|
|
└─────────────┘ └──────┬──────┘
|
|
│
|
|
│ DNS queries
|
|
▼
|
|
┌─────────────┐
|
|
│ DNSBL │
|
|
│ Servers │
|
|
└─────────────┘
|
|
```
|
|
|
|
1. **Client Connects**: IRCd sends client info to iauthd-ts via the IAuth protocol
|
|
2. **DNSBL Lookup**: iauthd-ts reverses the client IP and queries configured DNSBLs
|
|
3. **Result Processing**: Responses are checked against configured index/bitmask rules
|
|
4. **Decision**: Client is accepted (with optional marks/class) or rejected
|
|
5. **Response**: iauthd-ts sends the decision back to IRCd
|
|
|
|
### IAuth Protocol
|
|
|
|
iauthd-ts communicates with Nefarious via stdin/stdout using the IAuth protocol:
|
|
|
|
| Direction | Message | Description |
|
|
|-----------|---------|-------------|
|
|
| IRCd → iauthd | `C <id> <ip> <port> <serverip> <serverport>` | Client introduction |
|
|
| IRCd → iauthd | `H <id> <class>` | Hurry up (registration timeout approaching) |
|
|
| IRCd → iauthd | `R <id> <account>` | Client authenticated via SASL/LOC |
|
|
| IRCd → iauthd | `D <id>` | Client disconnected |
|
|
| iauthd → IRCd | `D <id> <ip> <port> [class]` | Accept client |
|
|
| iauthd → IRCd | `k <id> <ip> <port> :<reason>` | Reject client |
|
|
| iauthd → IRCd | `m <id> <ip> <port> MARK <data>` | Mark client |
|
|
| IRCd → iauthd | `A <id> <ip> <port> S <mechanism> [:<certfp>]` | SASL auth start |
|
|
| IRCd → iauthd | `A <id> <ip> <port> H :<user@host:ip>` | SASL host info |
|
|
| IRCd → iauthd | `a <id> <ip> <port> :<data>` | SASL auth data |
|
|
| iauthd → IRCd | `c <id> <ip> <port> :<challenge>` | SASL challenge |
|
|
| iauthd → IRCd | `L <id> <ip> <port> <account>` | SASL login success |
|
|
| iauthd → IRCd | `f <id> <ip> <port>` | SASL auth failed |
|
|
| iauthd → IRCd | `l <id> <ip> <port> :<mechanisms>` | SASL mechanism list |
|
|
| iauthd → IRCd | `Z <id> <ip> <port>` | SASL auth complete |
|
|
|
|
## Requirements
|
|
|
|
- Node.js 18 or later
|
|
- npm
|
|
|
|
## Installation
|
|
|
|
### From Source
|
|
|
|
```bash
|
|
cd tools/iauthd-ts
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
### Docker
|
|
|
|
iauthd-ts is automatically built as part of the Nefarious Docker image. The compiled files are located at `/home/nefarious/ircd/iauthd-ts/`.
|
|
|
|
## Configuration
|
|
|
|
iauthd-ts reads configuration from `#IAUTH` directives in the config file. These can be embedded in your `ircd.conf` since IRCd ignores lines starting with `#`.
|
|
|
|
### Directives
|
|
|
|
#### POLICY
|
|
```
|
|
#IAUTH POLICY RTAWUwFr
|
|
```
|
|
Sets the IAuth policy flags. See `doc/readme.iauth` for details.
|
|
|
|
#### DNSTIMEOUT
|
|
```
|
|
#IAUTH DNSTIMEOUT 5
|
|
```
|
|
Seconds to wait for DNSBL lookups. Default: 5
|
|
|
|
#### CACHETIME
|
|
```
|
|
#IAUTH CACHETIME 86400
|
|
```
|
|
Seconds to cache DNSBL results. Default: 86400 (24 hours)
|
|
|
|
#### BLOCKMSG
|
|
```
|
|
#IAUTH BLOCKMSG Sorry! Your connection has been rejected due to poor reputation.
|
|
```
|
|
Message shown to blocked users.
|
|
|
|
#### DEBUG
|
|
```
|
|
#IAUTH DEBUG
|
|
```
|
|
Enable debug output.
|
|
|
|
#### SASLDB
|
|
```
|
|
#IAUTH SASLDB /path/to/users
|
|
```
|
|
Path to the SASL users file. When set, iauthd-ts will handle SASL PLAIN authentication directly instead of routing to services. The policy must include 'S' for this to work.
|
|
|
|
#### SASLFAILMSG
|
|
```
|
|
#IAUTH SASLFAILMSG Authentication failed
|
|
```
|
|
Message shown when SASL authentication fails.
|
|
|
|
#### DNSBL
|
|
```
|
|
#IAUTH DNSBL server=<server> [options...]
|
|
```
|
|
|
|
Options:
|
|
| Option | Description |
|
|
|--------|-------------|
|
|
| `server=<host>` | DNSBL server hostname (required) |
|
|
| `index=<n,n,...>` | Match if response equals any of these values |
|
|
| `bitmask=<n>` | Match if response AND bitmask is non-zero |
|
|
| `mark=<tag>` | Apply this mark to matching clients |
|
|
| `block=all` | Block all matching clients |
|
|
| `block=anonymous` | Block matching clients unless SASL authenticated |
|
|
| `class=<name>` | Assign matching clients to this connection class |
|
|
| `whitelist` | Matching clients are exempt from all blocks |
|
|
| `cachetime=<n>` | Override cache time for this DNSBL |
|
|
|
|
### Example Configuration
|
|
|
|
```
|
|
# IAuth Configuration
|
|
#IAUTH POLICY RTAWUwFr
|
|
#IAUTH CACHETIME 86400
|
|
#IAUTH DNSTIMEOUT 5
|
|
#IAUTH BLOCKMSG Sorry! Your connection has been rejected due to poor reputation.
|
|
|
|
# Block open proxies and drones (unless authenticated)
|
|
#IAUTH DNSBL server=dnsbl.dronebl.org index=2,3,5,6,7,8,9,10,13,14,15 mark=dronebl block=anonymous
|
|
|
|
# Mark Tor exit nodes but don't block
|
|
#IAUTH DNSBL server=rbl.efnetrbl.org index=4 mark=tor
|
|
|
|
# Block other bad actors
|
|
#IAUTH DNSBL server=rbl.efnetrbl.org index=1,2,3,5 mark=efnetrbl block=anonymous
|
|
|
|
# Whitelist from a private DNSBL
|
|
#IAUTH DNSBL server=whitelist.example.com whitelist cachetime=3600
|
|
```
|
|
|
|
### SASL Authentication Configuration
|
|
|
|
To enable SASL authentication handling in iauthd-ts (instead of routing to services):
|
|
|
|
1. **Create a users file** (`users` or any path you prefer):
|
|
```
|
|
# Format: username:passwordhash
|
|
# Use genhash.ts to create hashes
|
|
admin:$5$salt$hashedpassword
|
|
user1:$6$salt$hashedpassword
|
|
```
|
|
|
|
2. **Generate password hashes**:
|
|
```bash
|
|
npx tsx src/genhash.ts mypassword sha256
|
|
# Output: $5$randomsalt$hashedpasswordvalue
|
|
```
|
|
|
|
3. **Configure iauthd-ts**:
|
|
```
|
|
#IAUTH POLICY RTAWUwFrS
|
|
#IAUTH SASLDB /path/to/users
|
|
#IAUTH SASLFAILMSG Invalid username or password
|
|
```
|
|
|
|
The `S` in the policy enables SASL handling.
|
|
|
|
4. **Supported hash formats**:
|
|
- `$5$salt$hash` - SHA-256 (recommended)
|
|
- `$6$salt$hash` - SHA-512
|
|
- `$1$salt$hash` - MD5 (legacy)
|
|
- Plain text (for testing only)
|
|
|
|
The users file is automatically reloaded when modified (checked on each SASL attempt).
|
|
|
|
### Modular Authentication Providers
|
|
|
|
iauthd-ts supports a modular authentication system with multiple backends. Providers are tried in priority order (lower number = tried first) until one succeeds.
|
|
|
|
#### AUTH Directive
|
|
```
|
|
#IAUTH AUTH provider=<type> [options...]
|
|
```
|
|
|
|
#### File Provider
|
|
Uses a static users file (same as SASLDB but with explicit provider syntax):
|
|
```
|
|
#IAUTH AUTH provider=file path=/path/to/users priority=50
|
|
```
|
|
|
|
#### LDAP Provider
|
|
|
|
> **Note**: LDAP support has not yet been tested with real LDAP servers. Please report any issues.
|
|
|
|
Two modes are supported:
|
|
|
|
**Direct Bind Mode** - Binds directly as the user:
|
|
```
|
|
#IAUTH AUTH provider=ldap uri=ldap://ldap.example.com:389 mode=direct userdn=uid=%s,ou=users,dc=example,dc=com
|
|
```
|
|
|
|
**Search Mode** - Admin bind, search for user, then bind as user:
|
|
```
|
|
#IAUTH AUTH provider=ldap uri=ldaps://ldap.example.com:636 mode=search basedn=ou=users,dc=example,dc=com binddn=cn=admin,dc=example,dc=com bindpass=secret userfilter=(uid=%s) groupdn=cn=ircusers,ou=groups,dc=example,dc=com
|
|
```
|
|
|
|
LDAP Options:
|
|
| Option | Required | Mode | Description |
|
|
|--------|----------|------|-------------|
|
|
| `uri` | Yes | Both | LDAP server URI (ldap:// or ldaps://) |
|
|
| `mode` | Yes | Both | `direct` or `search` |
|
|
| `userdn` | Yes | direct | DN template with `%s` for username |
|
|
| `basedn` | Yes | search | Base DN for user search |
|
|
| `binddn` | Yes | search | Admin bind DN |
|
|
| `bindpass` | Yes | search | Admin bind password |
|
|
| `userfilter` | Yes | search | Search filter with `%s` for username |
|
|
| `groupdn` | No | search | Group DN for membership check |
|
|
| `accountattr` | No | Both | Attribute to use as account name |
|
|
| `timeout` | No | Both | Connection timeout in ms (default: 5000) |
|
|
| `priority` | No | Both | Provider priority (default: 100) |
|
|
|
|
#### Keycloak Provider
|
|
Authenticates via Keycloak using Resource Owner Password Credentials (ROPC) grant:
|
|
```
|
|
#IAUTH AUTH provider=keycloak url=https://keycloak.example.com realm=myrealm clientid=irc-client
|
|
```
|
|
|
|
With client secret (for confidential clients):
|
|
```
|
|
#IAUTH AUTH provider=keycloak url=https://keycloak.example.com realm=myrealm clientid=irc-client clientsecret=mysecret
|
|
```
|
|
|
|
Keycloak Options:
|
|
| Option | Required | Description |
|
|
|--------|----------|-------------|
|
|
| `url` | Yes | Keycloak server URL |
|
|
| `realm` | Yes | Realm name |
|
|
| `clientid` | Yes | Client ID |
|
|
| `clientsecret` | No | Client secret (for confidential clients) |
|
|
| `accountattr` | No | JWT claim to use as account name (default: preferred_username) |
|
|
| `timeout` | No | Request timeout in ms (default: 5000) |
|
|
| `priority` | No | Provider priority (default: 100) |
|
|
|
|
#### Multiple Providers (Fallback Chain)
|
|
Configure multiple providers with different priorities. Lower priority = tried first:
|
|
```
|
|
#IAUTH AUTH provider=ldap uri=ldaps://ldap.example.com mode=direct userdn=uid=%s,ou=users,dc=example,dc=com priority=10
|
|
#IAUTH AUTH provider=keycloak url=https://keycloak.example.com realm=irc clientid=irc-client priority=20
|
|
#IAUTH AUTH provider=file path=/path/to/local-users priority=100
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Command Line
|
|
|
|
```bash
|
|
node dist/index.js -c <configfile> [-v] [-d]
|
|
|
|
Options:
|
|
-c, --config Config file to read (required)
|
|
-v, --verbose Enable verbose output in iauthd
|
|
-d, --debug Enable debug output in IRCd
|
|
-h, --help Show help
|
|
```
|
|
|
|
### IRCd Configuration
|
|
|
|
Add to your `ircd.conf`:
|
|
|
|
```
|
|
IAuth {
|
|
program = "node" "/path/to/iauthd-ts/dist/index.js" "-v" "-c" "/path/to/ircd.conf";
|
|
};
|
|
```
|
|
|
|
For Docker deployments:
|
|
```
|
|
IAuth {
|
|
program = "node" "/home/nefarious/ircd/iauthd-ts/index.js" "-v" "-c" "/home/nefarious/ircd/ircd.conf";
|
|
};
|
|
```
|
|
|
|
### Monitoring
|
|
|
|
To see IAuth debug messages as an operator:
|
|
```
|
|
/quote mode YourNick +s 262144
|
|
```
|
|
|
|
### Statistics
|
|
|
|
IAuth statistics are available via `/stats iauth` and include:
|
|
- Uptime
|
|
- Cache size
|
|
- Total passed/rejected clients
|
|
- Per-DNSBL hit counts
|
|
|
|
## Development
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
iauthd-ts/
|
|
├── src/
|
|
│ ├── index.ts # CLI entry point
|
|
│ ├── iauth.ts # Main IAuth daemon class
|
|
│ ├── config.ts # Configuration parser
|
|
│ ├── dnsbl.ts # DNSBL lookup and caching
|
|
│ ├── sasl.ts # SASL authentication handling
|
|
│ ├── genhash.ts # Password hash generator utility
|
|
│ └── types.ts # TypeScript interfaces
|
|
├── users.example # Example SASL users file
|
|
├── tests/
|
|
│ ├── config.test.ts # Config parser tests
|
|
│ ├── dnsbl.test.ts # DNSBL function tests
|
|
│ ├── iauth.test.ts # Integration tests
|
|
│ └── stress.ts # Memory stress test
|
|
├── dist/ # Compiled JavaScript (after build)
|
|
├── package.json
|
|
├── tsconfig.json
|
|
└── vitest.config.ts
|
|
```
|
|
|
|
### Building
|
|
|
|
```bash
|
|
npm install # Install dependencies
|
|
npm run build # Compile TypeScript to dist/
|
|
```
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
npm test # Run all tests
|
|
npm run test:watch # Watch mode (re-run on changes)
|
|
npm run test:coverage # Generate coverage report
|
|
```
|
|
|
|
### Test Coverage
|
|
|
|
The test suite includes:
|
|
- **Unit tests** (45 tests): Config parsing, IP handling, DNSBL matching, caching
|
|
- **Protocol integration tests** (17 tests): IAuth protocol simulation
|
|
- **DNSBL integration tests** (32 tests): Full pipeline with mocked DNS responses
|
|
|
|
#### DNSBL Integration Tests
|
|
|
|
The DNSBL integration tests (`tests/dnsbl-integration.test.ts`) use a mock DNS resolver to test realistic scenarios:
|
|
|
|
| Category | Tests |
|
|
|----------|-------|
|
|
| Index matching | Exact match, multiple indices, no match, NXDOMAIN |
|
|
| Bitmask matching | Single bit, multiple bits, no bits match |
|
|
| block=all | Blocks both anonymous and authenticated |
|
|
| block=anonymous | Blocks anonymous, allows SASL-authenticated |
|
|
| Mark only | Accepts client but applies mark |
|
|
| Whitelist | Overrides subsequent blocking DNSBLs |
|
|
| Multiple DNSBLs | Accumulates marks, any block triggers rejection |
|
|
| Same server/different indices | AfterNET pattern (whitelist=2, rbl=250, cloud=251) |
|
|
| Class assignment | Assigns connection class from matching DNSBL |
|
|
| Real-world simulation | Full AfterNET production config scenarios |
|
|
|
|
### Stress Testing
|
|
|
|
The stress test simulates thousands of concurrent connections to verify:
|
|
- Memory stability (no leaks in client tracking)
|
|
- Cache behavior
|
|
- Performance under load
|
|
|
|
```bash
|
|
npm run stress # Default: 5000 clients, 120s max
|
|
npm run stress -- --clients=10000 # More clients
|
|
npm run stress -- --duration=60 # Shorter duration
|
|
npm run stress -- -v # Verbose output
|
|
```
|
|
|
|
Example output:
|
|
```
|
|
============================================================
|
|
iauthd-ts Memory Stress Test
|
|
============================================================
|
|
Clients to simulate: 5000
|
|
Max concurrent: 100
|
|
Max duration: 120s
|
|
============================================================
|
|
|
|
Sent: 5000 | Accepted: 4994 | Rejected: 6 | Active: 0 | Cache: 5000 | Rate: 520.3/s
|
|
|
|
============================================================
|
|
STRESS TEST RESULTS
|
|
============================================================
|
|
Total clients sent: 5000
|
|
Total clients accepted: 4994
|
|
Total clients rejected: 6
|
|
Unprocessed clients: 0
|
|
Total time: 9.61s
|
|
Average rate: 520.29 clients/s
|
|
|
|
MEMORY ANALYSIS
|
|
------------------------------------------------------------
|
|
✅ Memory usage appears stable.
|
|
✅ All clients were processed correctly.
|
|
============================================================
|
|
```
|
|
|
|
### Development Mode
|
|
|
|
Run directly from TypeScript without compiling:
|
|
```bash
|
|
npm run dev -- -c /path/to/config
|
|
```
|
|
|
|
## Comparison with iauthd.pl
|
|
|
|
| Feature | iauthd.pl (Perl) | iauthd-ts (TypeScript) |
|
|
|---------|------------------|------------------------|
|
|
| Runtime | Perl + POE | Node.js |
|
|
| Async Model | POE event loop | Native async/await |
|
|
| Type Safety | None | Full TypeScript types |
|
|
| Testing | None | 62 tests + stress test |
|
|
| Dependencies | 5 CPAN modules | 0 runtime deps |
|
|
|
|
## Troubleshooting
|
|
|
|
### iauthd-ts not starting
|
|
- Check that Node.js 18+ is installed: `node --version`
|
|
- Verify config file path is correct and readable
|
|
- Check for syntax errors in `#IAUTH` directives
|
|
|
|
### Clients timing out
|
|
- Increase `DNSTIMEOUT` if DNSBL servers are slow
|
|
- Check network connectivity to DNSBL servers
|
|
- Verify DNS resolution is working: `host 2.0.0.127.dnsbl.example.com`
|
|
|
|
### High memory usage
|
|
- Reduce `CACHETIME` to expire entries sooner
|
|
- Monitor with stress test: `npm run stress`
|
|
- Check for many unique IPs (each gets cached)
|
|
|
|
### Debug output
|
|
Run with `-v` flag and set operator snomask:
|
|
```
|
|
/quote mode YourNick +s 262144
|
|
```
|
|
|
|
## License
|
|
|
|
GPL-2.0 - Same as Nefarious IRCd
|