|
|
||
|---|---|---|
| .. | ||
| src | ||
| tests | ||
| CLAUDE.md | ||
| README.md | ||
| package-lock.json | ||
| package.json | ||
| tsconfig.json | ||
| users.example | ||
| vitest.config.ts | ||
README.md
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 │
└─────────────┘
- Client Connects: IRCd sends client info to iauthd-ts via the IAuth protocol
- DNSBL Lookup: iauthd-ts reverses the client IP and queries configured DNSBLs
- Result Processing: Responses are checked against configured index/bitmask rules
- Decision: Client is accepted (with optional marks/class) or rejected
- 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
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):
-
Create a users file (
usersor any path you prefer):# Format: username:passwordhash # Use genhash.ts to create hashes admin:$5$salt$hashedpassword user1:$6$salt$hashedpassword -
Generate password hashes:
npx tsx src/genhash.ts mypassword sha256 # Output: $5$randomsalt$hashedpasswordvalue -
Configure iauthd-ts:
#IAUTH POLICY RTAWUwFrS #IAUTH SASLDB /path/to/users #IAUTH SASLFAILMSG Invalid username or passwordThe
Sin the policy enables SASL handling. -
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
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
npm install # Install dependencies
npm run build # Compile TypeScript to dist/
Running Tests
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
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:
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
#IAUTHdirectives
Clients timing out
- Increase
DNSTIMEOUTif 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
CACHETIMEto 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