I agree with @thelogix that this is probably a Very Bad Idea because it would block legitimate users and - depending on its implementation - possibly slow down your service.
But if you still want to to it, here are two ways of doing it.
First thing, you want to set up a local DNS cache to speed things up. Set high TTLs for negative caching (in case of Bind look for 'max-ncache-ttl'). And make sure your system is actually using it.
Now you have two possible strategies: When a new client request comes in, do you want to
First make a reverse DNS lookup and wait for its result before you decide whether or not to accept the connection? Veeerryy slowwww
Allow the TCP-handshake to proceed, and only block the IP address in hindsight once you learned that is has no valid PTR record?
For strategy 1 (check before allow): Many webservers can do client authentication based on reverse DNS. You might have to play with Regex a bit or compile a list of all TLDs in order to setup a rule that matches any domain name, but no IP address.
- For Apache, look up Require host from the 'mod_authz_host' module, e.g.
Require host .com .net .org .edu .us .uk .jp # ... all TLDs
- For nginx, look up rdns_allow from the 3rd party module 'HttpRdnsModule'. I haven't used it myself, but something like this should work:
rdns_allow .*[a-zA-Z];
deny all;
If your web service doesn't support this feature, you could put nginx or apache as a reverse http proxy in front.
For strategy 2 (block lazily): Make the webservice logs the reverse DNS name of the client (Apache: HostnameLookups; nginx: not sure, maybe above rDNS module again). Then install fail2ban. Most Linux distributions come with a reasonably pre-configured fail2ban package. Configure it to watch the web log, match (non-resolved) IP addresses and block that very IP address.
Note that fail2ban by default blocks in the iptables 'filter' table. In most cases, that will not block TCP connections that are already established. If you want fail2ban to cut established connections, configure it to 'DROP' in the 'mangle' table (e.g. the mangle chains 'PREROUTING' or 'INPUT')