# Hopm Install Guide In this guide, we'll setup and configure [hopm](/https://github.com/ircd-hybrid/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](/stopm/stopm) Before you begin, you must read the [README](https://github.com/ircd-hybrid/hopm/blob/master/README.md) and [[INSTALL](/https://github.com/ircd-hybrid/hopm/blob/master/INSTALL.md) docs. Before you begin, you must read the [README](https://github.com/ircd-hybrid/hopm/blob/master/README.md) and [[INSTALL](/https://github.com/ircd-hybrid/hopm/blob/master/INSTALL.md) docs. ## Installation Upgrades from older versions of hopm can also follow these instructions. You can see the version running in the log file `/home/hopm/hopm/var/log/hopm.log`. 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](/https://github.com/ircd-hybrid/hopm/tags), 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](/https://github.com/ircd-hybrid/hopm/blob/master/doc/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: ([^ ]+) \\(([^##ENDCODEBLOCK## +)@([^\\)]+)\\) \\[([0-9a-f\\.:]+)\\].*";` kline = "KLINE *@%25h 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](/https://github.com/ngircd/ngircd/blob/master/doc/Modes.txt) 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 `*@%25i` with `*@%25h` (to allow hopm to work with old cloak hostmasks, which have been broken since April 2020. Note that **hopm will not work with +x cloaking**). 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 *@%25h 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 will define a few blacklists: dronbl, efnet, and ircbl. 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 *@%25h 3600 :You have a host listed in the DroneBL. For more information, visit https://dronebl.org/lookup_branded?ip=%25i&network="; }; The name of the first blacklist is [dnsbl.dronebl.org](/https://dronebl.org/docs/howtouse). It supports both ipv4 and ipv6 addresses. We use A record replies. We don't want to ban unknown types. **Note**: Replace with a unique network name. The Network parameter set in ngircd.conf should suffice. For the kline, we again make sure to put the hostmask before the time (as ngircd requires). We also use %25h instead of %25i 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 *@%25h 3600 :Blacklisted proxy found. For more information, visit https://rbl.efnetrbl.org/?i=%25i"; }; blacklist { name = "tor.efnetrbl.org"; type = "A record reply"; ban_unknown = no; reply { 1 = "TOR"; }; kline = "KLINE *@%25h 3600 :TOR exit node found. For more information, visit https://rbl.efnetrbl.org/?i=%25i"; }; 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 *@%25h 3600 :Compromised host on this IP. See https://ircbl.org/lookup?ip=%25i&network= for more information."; }; blacklist { name = "tor-irc.dnsbl.oftc.net"; type = "A record reply"; reply { 2 = "Tor exit server"; }; ban_unknown = yes; kline = "KLINE *@%25h 3600 :Please use our tor onion addresses. If this is in error, please email support@ircnow.org"; }; }; **Note**: Again, replace with a unique network name. The Network parameter set in ngircd.conf should suffice. 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 (:ifend:) ## 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 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](/Rcd/Configure)\\ After you've created the rc.d script, append to /etc/rc.conf.local: hopm_user=hopm ##ENDCODEBLOCK## # 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.