Hopm Install Guide
In this guide, we'll setup and configure hopm, an open proxy monitor that kills spam bots.
Advantages:
- Pure C
- Compatible with every IRC server
- Fast scanning and DNSBL support.
Disadvantages:
- Occasionally bans innocent users because it cannot perform statistical analysis
Before you begin, you must read the README and INSTALL docs.
Installation
Let's create the user hopm:
$ doas useradd -m -g =uid -c "hopm" -d /home/hopm -s /bin/ksh hopm
Then we switch to the user hopm and change to its home folder:
$ doas su hopm $ cd
We download the latest release, extract it, then build it:
$ ftp https://github.com/ircd-hybrid/hopm/archive/1.1.10.tar.gz $ tar xvzf 1.1.10.tar.gz $ cd hopm-1.1.10 $ ./configure $ make $ make install
hopm will now be installed in ~/hopm.
/home/hopm/hopm/etc/reference.conf contains a sample template.
We'll create a new /home/hopm/hopm/etc/hopm.conf from scratch to keep it short:
options { pidfile = "var/run/hopm.pid"; command_queue_size = 64; command_interval = 10 seconds; command_timeout = 180 seconds; negcache_rebuild = 12 hours; dns_fdlimit = 64; dns_timeout = 5 seconds; scanlog = "var/log/scan.log"; };
The only thing we change is we uncomment scanlog so that we have a record of all users that connect. It will be stored in /home/hopm/hopm/var/log/scan.log
.
irc { nick = "MyHopm"; realname = "Hybrid Open Proxy Monitor"; username = "hopm"; server = "127.0.0.1"; port = 16667; tls = no; readtimeout = 15 minutes; reconnectinterval = 30 seconds; nickserv = "SQUERY NickServ :IDENTIFY MyHopm PASSWORD"; oper = "MyHopm PASSWORD"; mode = "+BcFiIoqRsw"; away = "I'm a bot. Your messages will be ignored."; channel { name = "#hopm"; key = "somekey"; invite = "SQUERY ChanServ :INVITE #hopm"; };
connregex = "Client connecting: ([^ ]+) \\(([^@]+)@([^\\)]+)\\) \\[([0-9a-f\\.:]+)\\].*";
kline = "KLINE *@%h 3600 :Open proxy found on your host. Please contact support@example.com if this is in error."; notice = "To prevent spam and abuse, we scan users for open proxies."; };
Change the nick
, realname
, username
. server
, port
, tls
specify how to connect to your ircd. You will want to use 127.0.0.1 port 16667 with no TLS.
ngircd uses SQUERY
for nickserv
: SQUERY NickServ :IDENTIFY MyHopm PASSWORD
-- you'll want to replace MyHopm
with the real nick and PASSWORD
with the real password. For oper: MyHopm PASSWORD
-- you'll want to replace MyHopm
and PASSWORD
with the operator name and password.
We change the mode to +BcFiIoqRsw
. I recommend you change channel's name
from #hopm
to your team channel. invite
has been modified to match ngircd; replace #hopm
with your team channel.
For kline
, make sure to replace *@%i
with *@%h
(to allow hopm to work when you cloak hostmasks). You must also change the order for kline: in ngircd, kline expects the hostmask before the time. You will also want to replace support@example.com
with your actual support email.
WARNING: You must change the order for kline for ngircd:
kline = "KLINE *@%h 3600 :Open proxy found on your host. Please contact support@example.com if this is in error.";
The hostmask must come before the time.
opm {
In our OPM block, we define two blacklists: one for dronbl, and another for efnet.
blacklist { name = "dnsbl.dronebl.org"; address_family = ipv4, ipv6; type = "A record reply"; ban_unknown = no; reply { 2 = "Sample data used for heuristical analysis"; 3 = "IRC spam drone (litmus/sdbot/fyle)"; 5 = "Bottler (experimental)"; 6 = "Unknown worm or spambot"; 7 = "DDoS drone"; 8 = "Open SOCKS proxy"; 9 = "Open HTTP proxy"; 10 = "ProxyChain"; 11 = "Web Page Proxy"; 12 = "Open DNS Resolver"; 13 = "Automated dictionary attacks"; 14 = "Open WINGATE proxy"; 15 = "Compromised router / gateway"; 16 = "Autorooting worms"; 17 = "Automatically determined botnet IPs (experimental)"; 18 = "Possibly compromised DNS/MX type hostname detected on IRC"; 19 = "Abused VPN Service"; 255 = "Uncategorized threat class"; }; kline = "KLINE *@%h 3600 :You have a host listed in the DroneBL. For more information, visit https://dronebl.org/lookup_branded?ip=%i&network=Network"; };
The name of the first blacklist is dnsbl.dronebl.org. It supports both ipv4 and ipv6 addresses. We use A record replies. We don't want to ban unknown types.
For the kline, we again make sure to put the hostmask before the time (as ngircd requires). We also use i to kline by hostmask instead of by IP, since ngircd may be cloaking user IPs.
blacklist { name = "rbl.efnetrbl.org"; type = "A record reply"; ban_unknown = no; reply { 1 = "Open proxy"; 2 = "spamtrap666"; 3 = "spamtrap50"; 4 = "TOR"; 5 = "Drones / Flooding"; }; kline = "KLINE *@%h 3600 :Blacklisted proxy found. For more information, visit https://rbl.efnetrbl.org/?i=%i"; }; blacklist { name = "tor.efnetrbl.org"; type = "A record reply"; ban_unknown = no; reply { 1 = "TOR"; }; kline = "KLINE *@%h 3600 :TOR exit node found. For more information, visit https://rbl.efnetrbl.org/?i=%i"; };
The two blacklists from efnet are the same.
blacklist { name = "rbl.ircbl.org"; type = "A record reply"; reply { 2 = "Open proxy (2)"; 6 = "Mail or NS server (6)"; 10 = "D regex pattern (10)"; 11 = "Drone / compromised (11)"; 13 = "Join/part flood (13)"; 14 = "Drone / compromised 2 (14)"; 16 = "Spam bot (16)"; 17 = "Drone (17)"; 18 = "Drone 2 (18)"; 19 = "Web abuse (19)"; 20 = "Drone/flood bot (20)"; 21 = "Compromised host (21)"; 22 = "Open Proxy (22)"; 23 = "Open Proxy (23)"; 24 = "Mass advertising (24)"; 30 = "Drone (30)"; 31 = "Drone 2 (31)"; 32 = "Open proxy (32)"; 42 = "Open proxy (42)"; }; ban_unknown = yes; kline = "KLINE 180 *@%i :Compromised host on this IP. See https://ircbl.org/lookup?ip=%i&network=<your_network_name> for more information."; }; };
This is another blacklist.
Next, we define a scanner block. hopm will try to get the user to connect to the target_ip:target_port using the listed port/protocol. A target_string will be sent through the proxy and then checked. If the data is found, the user is an proxy.
NOTE: target_ip must be an actual ip address. Replace 127.0.0.1 with your public IPv4 address.
scanner { name = "default"; protocol = HTTP:80; protocol = HTTP:8080; protocol = HTTP:3128; protocol = HTTP:6588; # protocol = HTTPS:443; # protocol = HTTPS:8443; protocol = SOCKS4:1080; protocol = SOCKS5:1080; protocol = ROUTER:23; protocol = WINGATE:23; protocol = DREAMBOX:23; protocol = HTTPPOST:80; # protocol = HTTPSPOST:443; # protocol = HTTPSPOST:8443; # bind = "127.0.0.1"; fd = 512; max_read = 4 kbytes; timeout = 30 seconds; target_ip = "127.0.0.1"; target_port = 6667; target_string = "NOTICE * :*** Looking up your hostname and checking ident"; };
Two more scanner blocks:
scanner { name = "extended"; protocol = HTTP:81; protocol = HTTP:8000; protocol = HTTP:8001; protocol = HTTP:8081; protocol = HTTPPOST:81; protocol = HTTPPOST:6588; protocol = HTTPPOST:4480; protocol = HTTPPOST:8000; protocol = HTTPPOST:8001; protocol = HTTPPOST:8080; protocol = HTTPPOST:8081; protocol = SOCKS4:4914; protocol = SOCKS4:6826; protocol = SOCKS4:7198; protocol = SOCKS4:7366; protocol = SOCKS4:9036; protocol = SOCKS5:4438; protocol = SOCKS5:5104; protocol = SOCKS5:5113; protocol = SOCKS5:5262; protocol = SOCKS5:5634; protocol = SOCKS5:6552; protocol = SOCKS5:6561; protocol = SOCKS5:7464; protocol = SOCKS5:7810; protocol = SOCKS5:8130; protocol = SOCKS5:8148; protocol = SOCKS5:8520; protocol = SOCKS5:8814; protocol = SOCKS5:9100; protocol = SOCKS5:9186; protocol = SOCKS5:9447; protocol = SOCKS5:9578; protocol = SOCKS5:10000; protocol = SOCKS5:64101; protocol = SOCKS4:29992; protocol = SOCKS4:38884; protocol = SOCKS4:18844; protocol = SOCKS4:17771; protocol = SOCKS4:31121; fd = 400; }; scanner { name = "ssh"; protocol = SSH:22; target_string = "SSH-1.99-OpenSSH_5.1"; target_string = "SSH-2.0-dropbear_0.51"; target_string = "SSH-2.0-dropbear_0.52"; target_string = "SSH-2.0-dropbear_0.53.1"; target_string = "SSH-2.0-dropbear_2012.55"; target_string = "SSH-2.0-dropbear_2013.62"; target_string = "SSH-2.0-dropbear_2014.63"; target_string = "SSH-2.0-OpenSSH_4.3"; target_string = "SSH-2.0-OpenSSH_5.1"; target_string = "SSH-2.0-OpenSSH_5.5p1"; target_string = "SSH-2.0-ROSSSH"; target_string = "SSH-2.0-SSH_Server"; };
user { mask = "*!*@*"; scanner = "default"; }; user { mask = "*!~*@*"; mask = "*!squid@*"; mask = "*!nobody@*"; mask = "*!www-data@*"; mask = "*!cache@*"; mask = "*!CacheFlowS@*"; mask = "*!*@*www*"; mask = "*!*@*proxy*"; mask = "*!*@*cache*"; scanner = "extended"; }; exempt { mask = "*!*@127.0.0.1"; };
Run Hopm
$ /home/hopm/hopm/bin/hopm -d
Cronjob
Put this script in /home/hopm/hopm/bin/autohopm
#!/bin/sh HOPMPATH=/home/hopm/hopm if test -r $HOPMPATH/var/run/hopm.pid; then HOPMPID=$(cat $HOPMPATH/var/run/hopm.pid) if $(kill -0 $HOPMPID >/dev/null 2>&1) then exit 0 fi fi $HOPMPATH/bin/hopm &> /dev/null
Then make sure execute privileges are set:
$ chmod 754 /home/hopm/hopm/bin/autohopm
$ crontab -e */5 * * * * /home/hopm/hopm/bin/autohopm
Troubleshooting
If you see this error:
[2021-01-23T09:59:14-0600] IRC -> connect(): error connecting to username.coconut.ircnow.org: Connection refused [2021-01-23T09:59:14-0600] IRC -> Connection to (username.coconut.ircnow.org) failed, reconnecting. [2021-01-23T09:59:14-0600] IRC -> connect(): error connecting to username.coconut.ircnow.org: Connection refused
This may be due to a configuration issue with ngircd. In particular, if the hostname has an AAAA record, hopm may be trying to connect via IPv6 but ngircd does not listen to IPv6 connections.
$ doas pkg_add torsocks
$ torsocks nc irc.example.com 6667 nick toruser user toruser * * :toruser
In the #hopm channel, you should see:
23:16 <MyHopm> DNSBL -> toruser!~toruser@vps-16fb7987.vps.ovh.ca [51.79.69.241] appears in BL zone rbl.efnetrbl.org (TOR)
Run Hopm as System Daemon
For this refer to this page
After you've created the rc.d script, append to /etc/rc.conf.local:
hopm_user=hopm
- Syntax errors when hopm is running in foreground. This is either the result of missing brackets where needed in config file, or that the file has DOS encodings. See https://github.com/ircd-hybrid/hopm/issues/22#issuecomment-301276082 here. The missing brackets where it was needed may come from the previous section, compared to the line/s where it is indicated by hopm when executed.
- If the service fails to start, check and make sure /home/hopm/hopm/var/log/hopm.log is owned by hopm.