This guide still works with pfsense versionand the haproxy packaged versionThis is a rough guide on how to create and configure user lists and stick-tables using pfsense's HAproxy package to protect access to a backend and limit the number of failed login attempts. The guide is divided into two main sections. In the first one, we'll create a user list and add encrypted (SHA512) passwords for each of them. Then, in the second section, we'll create and configure stick-tables to limit the number of failed login attempts.Now, it's fairly simple to accomplish all those things by manually changing HAproxy's config file (e.g., https://gist.github.com/Iristyle/5005653 https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/ ). However, I found it difficult to find any sort of documentation on how to accomplish the same thing using only pfsense's graphical user interface (GUI). The GUI makes it very easy to implement and configure the core aspects of HAproxy (e.g., reverse proxy and load balancing) but at the same time, it seems to lack support for other features, such as adding basic authentication requests and using stick-tables to mitigate attempts to brute-force users' credentials. If that's what you'd like to do, then read on.

The prerequisites are the following:

- you're running pfsense version 2.4.4-RELEASE-p3 or similar

- you've installed the HAproxy package version 0.59_19 or similar

- you've already added backends and frontends with HAproxy using subdomains (e.g., https://blog.devita.co/pfsense-to-proxy-traffic-for-websites-using-pfsense/



Recommended:

- configure ACME SSL with HAproxy (e.g., Recommended:- configure ACME SSL with HAproxy (e.g., https://www.youtube.com/watch?v=5Frn96oADOU

# Basic user authentication

userlist UserGroup

group is-admin

group is-user

user example01 password sha512-encrypted01 groups is-admin

user example02 password

groups

...





The argument userlist defines the name of your user list, which in this case we're going to call UserGroup . Next, group defines the group that each user belongs to, which will be either a standard user ( is-user ) or admin ( is-admin ). (You don't need to define groups but it's a nice way of restricting access to critical services.) Below groups, we have the list of all users, their (SHA-512 encrypted) passwords and the group they belong to. You can add as many users as you want this way.









apt install whois in a terminal window , which has the mkpasswd command that we're going to user, and then run each of the followin commands, changing sha512-encrypted01 and sha512-encrypted02 for the actual (insecure) password string of those respective users : 2. For each user, create a SHA-512 encrypted password. There are many ways of doing that . If you're running Debian (or any Linux distro), run, which has thecommand that we're going to user, and then run each of the followin commands, changing

printf " sha512-encrypted01 " | mkpasswd --stdin --method=sha-512

printf " sha512-encrypted02 " | mkpasswd --stdin --method=sha-512 ...









3. Now copy each encrypted password and paste them over the respective sha512-encryptedXX string in the user list .txt file.

Name = UserAuth

Expression = Custom acl:

CS = blank

Not = no

Value = http_auth_group(UserGroup) is-user

Action = http-request auth

Parameters = See below

Condition acl names = blank

Actions = blank

realm = realm User unless UserAuth

# Stick-table to protect against brute-force

stick-table type ip size 1m expire 30m store conn_cur,conn_rate(3s),http_req_cnt,http_req_rate(10s),http_err_cnt,http_err_rate(20s)

type ip

size 1m

expire 30m

rate over 20s (

store conn_cur,conn_rate(3s),http_req_cnt,http_req_rate(10s),http_err_cnt,http_err_rate(20s)

). (We won't use all those data but I figure you might find useful for other applications. We'll only use the last two.)





2. Hit Save.





3. Now go the the Frontend tab edit the www.domain.com frontend (or whichever frontend is associated with the backend you just edited). Here, we'll create a couple of ACLs and actions that will make use of the data from the stick-table.





4. In

Name = acl-www-err-rate

Expression = Custom acl:

CS = blank

Not = no

Value = sc_http_err_rate(0) gt 10

Name = acl-www-err-total

Expression = Custom acl:

CS = blank

Not = no

Value = sc_http_err_cnt(0) gt 100

Action = Custom

Parameters = See below

Condition acl names = acl-

(or whichever name you're using for the Host matches: www.domain.com ACL)

customaction = http-request track-sc0 src table www.domain.com_ipvANY

Action = http-request deny

Parameters = See below

Condition acl names = acl-www-err-total

denystatus = 403

Action = Custom

Parameters = See below

Condition acl names = acl-www-err-rate

customaction = http-request silent-drop





In HAproxy, it's pretty simple to create a user list with encrypted passwords. There is basically three steps involved: (a) user and password list creation, (b) adding those to the global settings, and (c) creating an access control list (ACL) and action for each backend.1. Create a .txt file and write the following:4. Go to pfsense's GUI and in, go to the. Now findand.* When you're done, hitand then* If you've other things in the global pass thru, make sure to add the user list to the bottom of all other commands. Otherwise, you might get a few errors when trying to apply the settings.5. Now head to the. We're going to(e.g.,.domain.com). In, create a new ACL, as follows:(If you want to restrict access to admin, then you need to change the value from is-user to is-admin.) Then,, create a new one as follows:(As before, if you want to restrict access to admin, then in realm, change User for Admin.) Hitand then6. Rinse and repeat for each backend you want to protect with basic user authentication. Make sure to test your config before moving on to the next section.In our case, access to each backend is secured by a basic http authentication request. If the client does not provide correct credentials, it will be requested to enter new ones. By default, HAproxy will do that forever, which is not something that sounds desirable to me because it allows clients to brute force their way into my services. On HAproxy's official blog, you'll find a very instructive guide on how to protect your servers from bots, including a step-by-step procedure to help you mitigate brute-force attacks . In their case, however, the authentication is associated to a particular /login page, instead of using HAproxy's own http-request auth feature, and we'd like to use the latter instead.Fortunately, when a client provides incorrect credentials, the result is an http error code (401) and because 4xx errors should be fairly uncommon in a properly configured server, we can use the rate of such errors (http_err_rate) as a marker of misuse/brute-force. In HAproxy, a stick-table is used to keep track of a client's IP address and both the http_err_rate and the total number of errors (http_err_cnt). In pfsense's GUI, however, there's no point-and-click way of enabling/disabling this, so we'll need to make use of backend pass thru and write custom access control lists and actions.1. Edit one of your(e.g.,.domain.com) and in, add the following toThis will create a stick-table that will capture the IP address of each client (), will store up to 1MB of data (), will expire after 30min it was last matched/created/refreshed () and for each client (key), it will store the # of connections and their rate over 3s, the # of http requests and their rate over 10s, and the # of http errors and theiras follows:This acl applies to all clients that have more than 10 4xx errors over the last 10s.5. Now,as follows:This acl applies to all clients that have more than 100 4xx errors stored on the stick-table (so, in the last 30min).6., we will define what HAproxy will do in each of those cases. First, however, we need to. So, create a new action as follows:The last sections, customaction, tells HAproxy to use the stick-table from thedomain.com backend. By default, that table is called {backend-name}_ipvANY, so edit it accordingly.7. Now, we're going to deny requests from acl--err-total, as follows:This will return a Forbidden error to clients that have more than 100 errors8. Now, we're going to drop connections from clients that have a high error rate, as follows:This will cause the client to wait for a reply from our server but our server will never send one, so the client will hang in there until it gets a timeout. This alone should be an effective way of dealing with brute-force attacks but I like the idea of completely denying access to persistent offenders.9. Now move youraction to the bottom of the list and10.and check if it's working correctly in the(11. Rinse and repeat for other backends.)That's it! Let me remind you that the options described here were tuned to my own case and therefore, you should review them to make sure they'll work for yours as well. For example, you might want to set a shorter expire for a given stick-table than 30 min., or you may not want to use http-request deny instead of http-request silent-drop.Stick-tables are great and there are many other things you can do with them. For example, you can use a similar procedure to implement other recommendations mentioned in the official blog . This one has been working very well for me.If you have any suggestions and advice, please leave a comment bellow. I've started using pfsense and haproxy less than a month ago, so I'm pretty sure there's a lot more that I can learn.--CG