Main Page

Unprivileged endlessh on FreeBSD

23 December 2021

endlessh is a tar pit for SSH connections. It ties up the resources of bots trying to connect to your box. It's very easy to set up, but the most activity from bots will be found on port 22, which must be bound as a privileged user. Unfortunately, endlessh doesn't drop privileges, so we need a workaround to run it as a user other than root.

First, make sure you have set SSH to listen on a different port. I won't go over this here, but I will assume you've chosen port 9001. Then, make sure you have git installed. Finally, we'll clone endlessh and build it.

        $ sudo pkg install git
        $ git clone
        $ cd endlessh
        $ make

Now, in the same directory, we need to create the config file, which I'm calling endlessh.conf. For an explanation of each line, see the endlessh documentation. Essentially, we're binding to port 2222, logging events non-verbosely, and listening over both IPv4 and IPv6.

        Port 2222
        Delay 10000
        MaxLineLength 32
        MaxClients 4096
        LogLevel 1
        BindFamily 0

Now, to get traffic destined for port 22 to our instance of endlessh, we need to use pf to redirect it over. I'll assume you don't have pf set up already.

First let's create /etc/pf.conf and add in a basic configuration to allow some common services. I'll add a comment before each line explaining what it does.

        # do not filter packets for loopback
      set skip on lo0

        # redirect all TCP traffic destined for port 22 on vtnet0 to port 2222
        # if you already have pf set up, this is the important line, and
        # position in the file matters.
      rdr on vtnet0 inet proto tcp from any to port 22 -> port 2222

        # start by dropping inbound traffic.
      block drop in all

        # all outbound traffic is allowed.
      pass out all keep state

        # inbound ICMP traffic is allowed.
      pass in proto { icmp, icmp6 }

        # allow inbound tcp traffic to these ports.
      tcpPorts = "{ 22 80 443 2222 9001 }"
      pass in proto tcp to port $tcpPorts

This is pretty minimal. Substitute 9001 for whatever port you have SSH actually running on. If you have services using UDP, duplicate the last two lines but s/tcp/udp. Don't forget to change vtnet0 to whatever your external interface is called. Also, we're allowing traffic directly to 2222 as well as redirecting because it's a common substitute port for SSH. We'll catch a few more bots there.

Time to enable pf and allow IP forwarding. The latter is necessary for the redirection. Run the following:

        $ sudo sysrc pf_enable="YES"
        $ sudo sysrc pf_rules="/etc/pf.conf"
        $ sudo sysrc pflog_enable="YES"
        $ sudo sysrc pflog_logfile="/var/log/pflog"
        $ sudo sysctl net.inet.ip.forwarding=1
        $ echo 'net.inet.ip.forwarding=1' | sudo tee -a /etc/sysctl.conf
        $ sudo service pf start
        $ sudo service pflog start

You'll need to pick a user to run endlessh as. I am just going to use the preexisting games user because its shell is already set to /usr/sbin/nologin and, well, this is kind of a game, right? If you want to use a different one, run adduser and set its shell to /usr/bin/nologin

Now, let's create the log files. Replace games with the user you picked.

        $ sudo -u games touch endlessh.log
        $ sudo -u games touch endlessh.err

Edit the user's crontab to have endlessh start on boot.

        $ sudo -u games crontab -e

        @reboot /path/to/endlessh -f /path/to/endlessh.conf >/path/to/endlessh.log 2>/path/to/endlessh.err

Now restart the box. We're all set up! Check endlessh.log after a few minutes to see the inevitable bot connections, along with total time for each, bytes sent, etc.