Protecting an SSH Service Using HAProxy

30 minutes
  • 2 Learning Objectives

About this Hands-on Lab

HAProxy isn’t just for securing HTTP; it can also be used to protect TCP-based services as well. One service that, when exposed to the Internet, is always under constant attack is SSH. In this lab, we’re going to get hands-on with HAProxy, using it to protect our SSH service. We’ll proxy our SSH service. Then we’ll set some connection boundaries in HAProxy, which should cut down on malicious traffic to our SSH service. Upon completion of the lab, you will be able to configure an HAProxy installation to protect a TCP-based service.

Learning Objectives

Successfully complete this lab by achieving the following learning objectives:

Protect an SSH Service with HAProxy

Before we get started, let’s confirm that our SSH service is working.

  • Check our containers using podman
  • Try pulling a file from our SSH container, directly, on the HAProxy server, using scp:
    • StrictHostKeyChecking=no
    • UserKnownHostsFile=/dev/null
    • port=2223
    • cloud_user@ssh.site3.com
    • file: /sshfiles/ssh-test.txt

Securing Our SSH Service Using HAProxy

In order to secure our SSH service, we need to add a frontend and a backend to /etc/haproxy/haproxy.cfg.

Let’s make some changes to our /etc/haproxy/haproxy.cfg file

  • Add a frontend for SSH named ssh-in:

    • bind port 2222 to all addresses
    • tcp mode
    • use the sshd1 backend
  • Add a backend for SSH named sshd1:

    • tcp mode
    • one server, sshd1-server1
    • localhost, port 2223
    • add a check for it
  • Restart the haproxy service

  • Try pulling a file from our SSH container, via HAProxy, on the HAProxy server, using scp

    • StrictHostKeyChecking=no
    • UserKnownHostsFile=/dev/null
    • port=2222
    • cloud_user@ssh.site3.com
    • file: /sshfiles/ssh-test.txt

Running Some Basic Tests

Before we get started with protecting our sites with HAProxy, let’s take a look at what a stock HAProxy configuration looks like when presented with a large number of requests.

We’ll open our web browser, connect to port 8050 on our public IP/DNS, and go get the stats information for our HAProxy installation.

Let’s generate some SSH traffic on our local HAProxy host.

  • Use a for loop to launch 1000 scp operations
    • Put each in the background
    • Try pulling a file from our SSH container, via HAProxy, on the HAProxy server, using scp:
    • StrictHostKeyChecking=no
    • UserKnownHostsFile=/dev/null
    • port=2222
    • cloud_user@ssh.site3.com
    • file: /sshfiles/ssh-test.txt

We can see traffic moving through our ssh-in frontend and sshd1 backend. We’re ready to start setting some boundaries on SSH connections.

Setting Some SSH Restrictions

We just dropped 1,000 SSH connections on our SSH service in short order. Even though our service has no problem handling that from a single client, it’s not a normal use case. We’d like to set some connection restrictions on our SSH service, so it can’t be overwhelmed by a single client.

We’re going to create a new backend to hold a stick-table to track SSH connections by client, named ssh_per_ip_connections:

  • type ip
  • size 1m
  • expire 1m
  • store
    • conn_cur
    • conn_rate(1m)

We’ll add the following to our SSH frontend: Use the TCP log format, set a 1 minute client timeout, and track our SSH connections in the ssh_per_ip_connections stick-table we created. We’ll reject connections over 2 per client or if there are more than 10 connections in the span of 1 minute.

Test Your SSH Attack Protection

Testing Our SSH Restrictions

Now that we’ve set some boundaries on SSH connections, let’s test our work!

Before we proceed, let’s restart the haproxy service to pick up our configuration changes and reset our statistics.

Let’s generate some SSH traffic on our local HAProxy host

  • Use a for loop to launch 1000 scp operations
    • Put each in the background
    • Try pulling a file from our SSH container, via HAProxy, on the HAProxy server, using scp
    • StrictHostKeyChecking=no
    • UserKnownHostsFile=/dev/null
    • port=2222
    • cloud_user@ssh.site3.com
    • file: /sshfiles/ssh-test.txt

Looking at the HAProxy stats web interface using a web browser, we can see that 2 connections succeeded, and 998 failed. Things are working!

Let’s give our HAProxy server a minute to recover, then try some serial SSH connections.

  • Try pulling a single file from our SSH container, via HAProxy, on the HAProxy server, using scp
    • StrictHostKeyChecking=no
    • UserKnownHostsFile=/dev/null
    • port=2222
    • cloud_user@ssh.site3.com
    • file: /sshfiles/ssh-test.txt

Repeating this, one scp at a time, until we are blocked, we can go until we hit our rate limit of 10 sessions per minute. Our SSH service is protected!

Additional Resources

SSH needs our protection!

Our SSH service has been under constant attack, and it's only a matter of time before it succumbs to the flood of assaults. We might not be able to prevent all attempts to storm the castle via SSH, but we can thwart a good part of this malicious traffic. Fortunately for us, HAProxy is up to the task!

Let's give SSH a helping hand!

You have been provided with one HAProxy/web/SSH server for testing.

When the lab starts, you will want to open an SSH connection to your lab instance:

ssh cloud_user@PUBLIC_IP_ADDRESS

Replace PUBLIC_IP_ADDRESS with either the public IP or DNS of the instance(s). The 'cloud_user' password has been provided with the instance information.

Entries for www.site1.com and www.site2.com have been created in /etc/hosts that point to 127.0.0.1. Additionally, SSL certificates for HAProxy have been generated in /etc/haproxy/certs/.

On our system, we have two sites, site1 and site2, configured, with three web server containers in each, running rootlessly by the cloud_user account. They've been pre-populated with a test text file at /test.txt that identifies which site and server we're accessing.

The nginx containers are configured as follows:

  • site1_server1: web server accessible on port 8081
  • site1_server2: web server accessible on port 8082
  • site1_server3: web server accessible on port 8083
  • site2_server1: web server accessible on port 8084
  • site2_server2: web server accessible on port 8085
  • site2_server3: web server accessible on port 8086

The sshd container is configured as follows:

  • sshd1_server1: sshd server accessible on port 2223

Good luck and enjoy!

What are Hands-on Labs

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Sign In
Welcome Back!

Psst…this one if you’ve been moved to ACG!

Get Started
Who’s going to be learning?