Not very long ago, I didn’t see any benefit to having Pluggable Authentication Modules on a Linux system. The configurations can be difficult to understand, and arguably easy to get wrong so that services that use it don’t behave as intended. Time passes, and Pluggable Authentication Modules remain in Linux, thankfully with improved default configurations, so that more often than not services that use them function as intended right out of the box.
One of the benefits of Pluggable Authentication Modules, however, is made clear right there in the name: it’s a modular approach to access control and authentication. Most recently, I realized that I could leverage that modularity and create a custom PAM module that uses DNSBLs (DNS-based blocklists) to control access to services like SSH on Linux. I’ve gone ahead and created this module and have released it to the general public.
The goal is simple: before a user is authenticated, check the connecting IP against:
- A local whitelist (always allow)
- A local blocklist (always deny)
- One or more configured DNSBL zones (deny if listed)
If the remote IP is found on any configured DNSBL, the connection is denied; otherwise it’s allowed. Results are cached in memory with configurable TTLs.
Why I created this module
Email administrators have been using DNSBL checks for many years as one layer in an effective approach to spam reduction. It’s possible to apply the technique for web services, to reduce infiltration attempts,
bot-account creation attempts, post and comment spamming attempts, and other forms of unauthorized activity.
Web and mail services are not the only services that are routinely left potentially accessible from the broad Internet. I wanted a way to use DNSBL-style lists to control access to such services, in particular when
it isn’t feasible to apply a default-deny access policy, with a small defined set of IP addresses from which the service would be available.
Pluggable Authentication Modules were a natural choice, permitting:
- An efficient module, with no external dependencies beyond PAM and the system resolver
- Configuration entirely via PAM arguments and simple text files
- Easy build and deployment on Linux and other UNIX-like systems
How it works
In your PAM configuration for the intended service (for example /etc/pam.d/sshd), you add an account line early in the stack:
account required pam_dnsbl.so
dnsbl=zen.spamhaus.org
dnsbl=dnsbl.sorbs.net
whitelist=/etc/pam_dnsbl/whitelist
blocklist=/etc/pam_dnsbl/blocklist
cache_listed_ttl=3600
cache_clean_ttl=900
cache_error_ttl=300
syslog_facility=auth
debug
For each incoming connection:
The remote IP is checked against the whitelist file. If it matches, access is allowed.
If not whitelisted, the IP is checked against the blocklist file. If it matches, access is denied.
If still undecided, the module queries each configured DNSBL zone in order:
- It constructs a DNSBL query name (reversed IP + zone) and performs an A record lookup.
- If any zone returns a “listed” response
(127.0.0.2 - 127.255.255.254), access is denied. - If no zones list the IP, access is allowed.
- All results are cached in memory with separate TTLs for listed, clean, and error states.
Getting the code
The source is available on my GitHub repository.
The repository includes:
- pam_dnsbl.c: the module implementation
- pam_dnsbl.8: a manual page
- Makefile: simple build rules (make, make install)
- Example pam.d/sshd snippet
- Example whitelist and blocklist files
- README.md with build, install, and usage instructions
Feedback and contributions
Feel free to open issues or send patches via the GitHub repository.
