Outbound Firewall Block Rule creation from Context Menu.

I recently had a need to create local firewall rules for certain executables which would block all outbound traffic for those executables. Whilst not particularly difficult I thought it would be useful to be able to do so from a “right-click” option.

There seemed to be a lot of things on the web about this but as a little pet project I decided to write my own method. This is the end result.

Firstly created a directory to hold your scripts , in the following scripts the directory is :

C:\Scripts\FirewallBlock

In that directory create a file called BlockFile.ps1 , this is the script which creates the outbound firewall block rule for the selected executable. The script ensures the command is run from an elevated session. The contents of the script should be :

Function Check-RunAsAdministrator()
{
  #Get current user context
  $CurrentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
  
  #Check user is running the script is member of Administrator Group
  if($CurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))
  {
       Write-host "Script is running with Administrator privileges!"
  }
  else
    {
       #Create a new Elevated process to Start PowerShell
       $ElevatedProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";
 
       # Specify the current script path and name as a parameter
       $ElevatedProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'" + " " + $filetobeblocked
 
       #Set the Process to elevated
       $ElevatedProcess.Verb = "runas"
       
       #Start the new elevated process
       [System.Diagnostics.Process]::Start($ElevatedProcess) 
 
       #Exit from the current, unelevated, process
       Exit
        
    }
}

$filetobeblocked=$args

#Check Script is running with Elevated Privileges
Check-RunAsAdministrator

Write-Host "The file path that will be blocked is $($filetobeblocked)"
$filename = $filetobeblocked.split("\")[-1]

New-NetFirewallRule -DisplayName "Custom Outbound File Block - $filename" -Description "Custom Rule created from context menu" -Direction Outbound -Program "$($filetobeblocked)" -Action Block

start-sleep -seconds 2

Next , In that directory create a file called UnBlockFile.ps1 , this is the script which deletes the outbound firewall block rule for the selected executable. The script ensures the command is run from an elevated session. This script makes use of the description added to the rule. Should you change the description in the “Block” script, you should change it to match in the “UnBlock” script. The contents of the script should be :

Function Check-RunAsAdministrator()
{
  #Get current user context
  $CurrentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
  
  #Check user is running the script is member of Administrator Group
  if($CurrentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))
  {
       Write-host "Script is running with Administrator privileges!"
  }
  else
    {
       #Create a new Elevated process to Start PowerShell
       $ElevatedProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";
 
       # Specify the current script path and name as a parameter
       $ElevatedProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'" + " " + $filetobeunblocked
 
       #Set the Process to elevated
       $ElevatedProcess.Verb = "runas"
       
       #Start the new elevated process
       [System.Diagnostics.Process]::Start($ElevatedProcess) 
 
       #Exit from the current, unelevated, process
       Exit
        
    }
}

$filetobeunblocked=$args

#Check Script is running with Elevated Privileges
Check-RunAsAdministrator

Write-Host "The file path that will be unblocked is $filetobeunblocked"

$matchingapprules = Get-NetFirewallApplicationFilter -Program "$($filetobeunblocked)" | Get-NetFirewallRule
$wantedrules = $matchingapprules | Where-Object Description -eq "Custom Rule created from context menu"

$x=0
foreach ($rule in $wantedrules){
    $DisplayName = $rule.DisplayName
    Write-Host "Removing Rule $DisplayName"
    Remove-NetFirewallRule $rule.Name
    $x++
}

Write-Host "$($x) rule(s) removed. "
start-sleep -seconds 5

Finally, in a directory create a “reg” file, add_firewall_block_unblock.reg, which when imported in the registry (just double-click the file) will create the context menus. The contents of the reg file are:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Outbound Firewall Block - Add]

[HKEY_CLASSES_ROOT\*\shell\Outbound Firewall Block - Add\command]
@="powershell.exe -ExecutionPolicy Bypass -File \"C:\\Scripts\\FirewallBlock\\BlockFile.ps1\" \"%1\""

[HKEY_CLASSES_ROOT\*\shell\Outbound Firewall Block - Remove]

[HKEY_CLASSES_ROOT\*\shell\Outbound Firewall Block - Remove\command]
@="powershell.exe -ExecutionPolicy Bypass -File \"C:\\Scripts\\FirewallBlock\\UnBlockFile.ps1\" \"%1\""

And that is it. Once the directory and files have been created and the registry keys imported you should have two new context menus:

Just choose an executable, right click and select the “Add” option to create the outbound block rule and when finished select “Remove” to delete the rule.

The files as detailed above can be found at : Efforts/FirewallBlock at main · CluelessAtCoding/Efforts · GitHub

Should you modify any entry names, script name, file paths etc just make sure you modify all references.

13th August 2025: Edit – Updated both block and unblock scripts to handle paths with spaces.

How to reset a forgotten or lost Guacamole user password in a Postgres Database

This week I found myself booting up an old environment which used Guacamole to present some resources. Whilst I had server credentials , I did not have any of the Guacamole user credentials so I looked in to how I could reset the accounts. My environment uses Postgres.

After a bit of messing about I came up with the following process. Which basically updates the table with a non hashed password. You should always change your password in guacamole immediately after logging in with this process.

First log on to the server and generate a SHA256 hash of your “temporary” password, replace <NewPassword> accordingly:

echo -n <NewPassword> | sha256sum

Having made note of the SHA256 hash , connect to the Guacamole Postgresql database:

psql -h 127.0.0.1 -d guacamole_db -U guacamole_user -W

When prompted you will need to enter the password of the guacamole_user account.

Next step is to find the entity_id of the account we wish to reset, this is done by querying the guacamole_entity table, replace <username> with the name of the account you are resetting the password of:

SELECT entity_id FROM guacamole_entity WHERE name='<username>';

For example if you wanted to find the entity_id of guacadmin the command would be:

SELECT entity_id FROM guacamole_entity WHERE name='guacadmin';

We can then reset the password. This is done by setting the salt to null and updating the record with the non-salted password hash. Replace <SHA256HASH> with the hash generated in the first step and replace <entity_id> with the entity_id you just obtained:

UPDATE guacamole_user
SET
    password_salt = NULL,
    password_hash = '\x<SHA256HASH>'
WHERE
    entity_id = '<entity_id>';

When that is done, you should be able to log in to guacamole with new password.

As mentioned at the beginning, there is one final but very important step, once you have logged in with the account you should immediately change your password via guacamole, this will ensure the table is updated with a SALTED password.

Useful Link: https://guacamole.apache.org/doc/gug/jdbc-auth.html#modifying-data-manually

Automated Script to install HAProxy 2.8.12 , OpenSSL 3.0.15 and Modsecurity.

So ….. My latest effort has been a bash script that runs on both Ubuntu 22.04 and Alma 8.6 and installs HAProxy 2.8.12, OpenSSL 3.0.15 and Modsecurity.

The starting point is a clean build, with latest OS patches installed and no additional packages. This script downloads, extracts and installs the version of OpenSSL 3 and HAPoxy 2.8 specified at the beginning of the script.

Simply make the script executable and run using sudo or as root.

When the script is done all you need to do is make the final changes to /etc/haproxy/haproxy.conf and start the services. Modsecurity is not configured to block until you modify its config file.

If you want to see what is going on when the script is running simply change the log value to standard out. I will add an option for this later.

Update 14th March 2023: This script had two massive errors, one in the haproxy service configuration file and one in the make command of haproxy. Both of these have now been fixed.

Update 10th August 2023: The script has been changed so the default is OpenSSL 3.0.9 and the HAProxy 2.8.1. I have also fixed an issue with permissions on the /var/log/haproxy folder which stopped log files being written.

Update 27th November 2024: The script has been changed so the default is OpenSSL 3.0.15 and the HAProxy 2.8.12.

You can find the script at:

Efforts/Misc at main · CluelessAtCoding/Efforts · GitHub

Python script to parse a Sonicwall Firewall exp file

I had need to review a firewall config but was provided with an exp file. I did not want to pay for a tool do do the work and what I found on the internet did not work with my file version, so I wrote a script to do some of the heavy lifting for me.

This python script decodes the exp file and exports key information to worksheets in an Excel spreadsheet.

The script was written for an exp file from a NSa 3650 , I have no idea if it works with other variants.

You can find it at:

https://github.com/CluelessAtCoding/Efforts/tree/main/Firewall%20Scripts/Sonicwall/parse_sonicwall_exp

I am still learning Python , so apologies if it is a bit of a mess!

Automated Script to install Covenant on Ubuntu 20.04

Following on from my previous post I have created a noddy script to install Covenant on an Ubuntu 20.04 box.

It can be found at : https://github.com/CluelessAtCoding/Efforts/tree/main/RedTeamTools

Download the script to the Ubuntu machine, change the username specified at the beginning of the script if you so wish, then:

chmod +x ./covenant_install.sh
sudo ./covenant_install.sh

Installing Covenant on Ubuntu 20.04

Understanding how Red Team tools work and are used enables the Blue Team to develop better detections in their EDR/Security software.

This short article details how to install Covenant (https://github.com/cobbr/Covenant) on Ubuntu 20.04 so you can use it as part of a Purple Team exercise.

Build a minimal Ubuntu 20.04 Server, with OpenSSH Server installed as the only additional package. Then connect log on to the server via SSH or the Console.

Next, ensure it is fully updated:

sudo apt update && sudo apt -y upgrade

And has all the necessary pre-requisite packages:

sudo apt update && sudo apt install -y --no-install-recommends libc6 libgcc1 libgssapi-krb5-2 libicu66 libssl1.1 libstdc++6 zlib1g 

Once that is complete create a “covenant” user, providing a password when prompted. You can specify any username but ensure you modify the subsequent commands to reflect this.

sudo adduser covenant

Once the account has been created, switch to that account providing the password when prompted and change to the accounts home directory:

su covenant
cd ~

Next, download the Linux .Net 3.1 SDK archive:

curl https://download.visualstudio.microsoft.com/download/pr/4fd83694-c9ad-487f-bf26-ef80f3cbfd9e/6ca93b498019311e6f7732717c350811/dotnet-sdk-3.1.422-linux-x64.tar.gz -o dotnet-sdk-3.1.422-linux-x64.tar.gz

Once the archive has downloaded , create a directory and extract the archive to that directory:

mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-3.1.422-linux-x64.tar.gz -C $HOME/dotnet

When that extraction is complete, clone the Covenant GitHub Repository:

git clone --recurse-submodules https://github.com/cobbr/Covenant

It would be good for Covenant to start when the server boots, so once the clone is complete, exit back to your sudo enabled account and using your favourite editor create a systemd service entry:

exit
sudo vi /etc/systemd/system/covenant.service

The contents of the file should be as follows, if earlier you chose a different username to “covenant” ensure you modify the paths and User variable accordingly:

[Unit]
Description=Covenant - https://github.com/cobbr/Covenant
After=network.target
[Service]
ExecStart=/home/covenant/dotnet/dotnet run
WorkingDirectory=/home/covenant/Covenant/Covenant
SyslogIdentifier=covenant
Environment=DOTNET_ROOT=/home/covenant/dotnet
User=covenant
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

Once saved refresh the systemd service cache, enable the service to start at boot and start the service:

sudo systemctl daemon-reload
sudo systemctl enable covenant
sudo systemctl start covenant

The first time you start the service it will take a little time as the app has to be compiled, you can check on it with:

sudo systemctl status covenant

● covenant.service - Covenant - https://github.com/cobbr/Covenant
     Loaded: loaded (/etc/systemd/system/covenant.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-12-11 06:53:10 UTC; 3s ago
   Main PID: 3775 (dotnet)
      Tasks: 35 (limit: 4575)
     Memory: 79.4M
     CGroup: /system.slice/covenant.service
             ├─3775 /home/covenant/dotnet/dotnet run
             └─3812 dotnet exec /home/covenant/dotnet/sdk/3.1.422/MSBuild.dll -maxcpucount -verbosity:m -restore /home/covenant/Covenant/Covenant/Covenant.csproj -nologo -verbosity:quiet -distributedlogger:Microsoft.DotNet.Tools.MSBuild.MSBuildLogger,/home/covenant/dotnet/sdk/3.1.422/>

Dec 11 06:53:11 covenant covenant[3775]:
Dec 11 06:53:11 covenant covenant[3775]: Read more about .NET Core CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry
Dec 11 06:53:11 covenant covenant[3775]: ----------------
Dec 11 06:53:11 covenant covenant[3775]: Explore documentation: https://aka.ms/dotnet-docs
Dec 11 06:53:11 covenant covenant[3775]: Report issues and find source on GitHub: https://github.com/dotnet/core
Dec 11 06:53:11 covenant covenant[3775]: Find out what's new: https://aka.ms/dotnet-whats-new
Dec 11 06:53:11 covenant covenant[3775]: Learn about the installed HTTPS developer cert: https://aka.ms/aspnet-core-https
Dec 11 06:53:11 covenant covenant[3775]: Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli-docs
Dec 11 06:53:11 covenant covenant[3775]: Write your first app: https://aka.ms/first-net-core-app
Dec 11 06:53:11 covenant covenant[3775]: --------------------------------------------------------------------------------------

When the compile is complete, you will see:

sudo systemctl status covenant

● covenant.service - Covenant - https://github.com/cobbr/Covenant
     Loaded: loaded (/etc/systemd/system/covenant.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-12-11 06:53:10 UTC; 27min ago
   Main PID: 3775 (dotnet)
      Tasks: 31 (limit: 4575)
     Memory: 436.8M
     CGroup: /system.slice/covenant.service
             ├─3775 /home/covenant/dotnet/dotnet run
             └─3911 /home/covenant/Covenant/Covenant/bin/Debug/netcoreapp3.1/Covenant

Dec 11 06:53:11 covenant covenant[3775]: Write your first app: https://aka.ms/first-net-core-app
Dec 11 06:53:11 covenant covenant[3775]: --------------------------------------------------------------------------------------
Dec 11 06:53:42 covenant covenant[3911]: Found default JwtKey, replacing with auto-generated key...
Dec 11 06:53:43 covenant covenant[3911]: warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
Dec 11 06:53:43 covenant covenant[3911]:       Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data, this mode should only be enabled during development.
Dec 11 06:53:48 covenant covenant[3911]: WARNING: Running Covenant non-elevated. You may not have permission to start Listeners on low-numbered ports. Consider running Covenant elevated.
Dec 11 06:53:48 covenant covenant[3911]: Covenant has started! Navigate to https://127.0.0.1:7443 in a browser
Dec 11 06:53:48 covenant covenant[3911]: Creating cert...
Dec 11 06:53:48 covenant covenant[3911]: warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
Dec 11 06:53:48 covenant covenant[3911]:       No XML encryptor configured. Key {a8191cae-af14-4a34-9abb-4caed81c0055} may be persisted to storage in unencrypted form.

You can connect to the app with your web browser, the default port is 7443. Please note the portal uses a self-signed cert so you will need to acknowledge the browser security warning to proceed.

Once connected you will be greeted with “Register Initial User” page:

Here you specify the initial admin account name and password and then click “Register”, this creates the account and takes you in to the app.

Now you can play. The wiki can be found at https://github.com/cobbr/Covenant/wiki

Microsoft EDGE Version 105.0.1343.25 not launching

I recently upgraded to Microsoft Edge Version 105.0.1343.25 only to find that it would not launch.

The fix according to a page I found on the interweb, which worked for me, was to delete the MetricsReportingEnabled registry key in

HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Edge

You may need to do the same in

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge

The issue may have been caused by a piece of software called ShutUp 10. This seems quite probable as I know I used this software.

If you use ShutUp10 , ensure you are using the latest version.

Another article about the issue can be found at https://www.bleepingcomputer.com/news/microsoft/microsoft-edge-105-wont-start-due-to-old-group-policy-how-to-fix/

CVE-2021-4034 – Polkit Vulnerability

With Proof of Concept exploits already on github, you need to ensure you are patched against this vulnerability.

Details of the vulnerability can be found at https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

Most vendors have released patches, so update

Installing HAProxy 2.4.7 with ModSecurity on AlmaLinux 8.4

This article is almost identical to my previous post regarding HAProxy on CentOS, but as Centos 8 is going End Of Life at the end of this year I thought I would revisit it on AlmaLinux.

I used the 64-Bit minimal ISO that can be found at the appropriate mirror : https://mirrors.almalinux.org/isos/x86_64/8.4.html . By using the 8.4 version of AlmaLinux , which natively supports OpenSSL 1.1.1 and LUA5.3, there is less package compilation and ongoing updates should be easier to manage as well as supporting TLS 1.3. Going with a minimal install and only installing the components and services you need helps with your security posture and attack surface area.

As before, the first things to install are the EPEL Repo, Development tools and a bunch of pre-reqs for compiling HAProxy and Modsecurity:

dnf -y install epel-release
dnf config-manager --set-enabled powertools
dnf update
dnf groupinstall "Development Tools"
dnf -y install openssl-devel perl pcre-devel zlib-devel systemd-devel wget net-tools libxml2 libxml2-devel expat-devel httpd-devel curl-devel yajl-devel libevent libevent-devel readline-devel ssdeep ssdeep-devel lua lua-devel

Once that is done, change to the /usr/src directory:

cd /usr/src

Before we start on the HAProxy install, you need to make a patch file that makes some adjustments to the HAProxy Modsecurity Makefile so it works with AlmaLinux and Modsecurity 2.9.4.

Create a file in /tmp called spoa-modsecurity_Makefile.patch and put the following in it:

--- Makefile	2019-10-23 07:06:13.000000000 +0100
+++ /root/contrib_modsec_Makefile	2019-11-01 14:09:51.537626204 +0000
@@ -6,19 +6,19 @@
 LD = $(CC)
 
 ifeq ($(MODSEC_INC),)
-MODSEC_INC := modsecurity-2.9.1/INSTALL/include
+MODSEC_INC := ModSecurity/INSTALL/include
 endif
 
 ifeq ($(MODSEC_LIB),)
-MODSEC_LIB := modsecurity-2.9.1/INSTALL/lib
+MODSEC_LIB := ModSecurity/INSTALL/lib
 endif
 
 ifeq ($(APACHE2_INC),)
-APACHE2_INC := /usr/include/apache2
+APACHE2_INC := /usr/include/httpd
 endif
 
 ifeq ($(APR_INC),)
-APR_INC := /usr/include/apr-1.0
+APR_INC := /usr/include/apr-1
 endif
 
 ifeq ($(LIBXML_INC),)
@@ -35,7 +35,7 @@
 
 CFLAGS  += -g -Wall -pthread
 INCS += -Iinclude -I$(MODSEC_INC) -I$(APACHE2_INC) -I$(APR_INC) -I$(LIBXML_INC) -I$(EVENT_INC)
-LIBS += -lpthread  $(EVENT_LIB) -levent_pthreads -lcurl -lapr-1 -laprutil-1 -lxml2 -lpcre -lyajl
+LIBS += -lpthread  $(EVENT_LIB) -levent_pthreads -lcurl -lapr-1 -laprutil-1 -lxml2 -lpcre -lyajl -llua-5.3 -lfuzzy
 
 OBJS = spoa.o modsec_wrapper.o
 

Making sure your are in /usr/src, download the latest version of HAProxy, check the checksum and once that is done, extract it:

wget http://www.haproxy.org/download/2.4/src/haproxy-2.4.7.tar.gz
wget http://www.haproxy.org/download/2.4/src/haproxy-2.4.7.tar.gz.sha256
sha256sum -c haproxy-2.4.7.tar.gz.sha256
tar xfvz haproxy-2.4.7.tar.gz

Once that is done, change in to the haproxy directory you have just extracted and clone the spoa-modsecurity repo. This is necessary as contributions have been split out from the main package:

cd haproxy-2.4.7
git clone https://github.com/haproxy/spoa-modsecurity.git

Next you have to modify the spoa-modsecurity Makefile using the patch created earlier:

cd spoa-modsecurity
patch Makefile < /tmp/spoa-modsecurity_Makefile.patch

Once the Makefile has been patched clone the modsecurity 2.9.4 repo:

git clone --branch v2/master https://github.com/SpiderLabs/ModSecurity.git

Once the repo has been cloned, carry out the following to configure, build and install the standalone modsecurity module:

cd ModSecurity
./autogen.sh
./configure --prefix=$PWD/INSTALL --disable-apache2-module --enable-standalone-module --enable-pcre-study --enable-pcre-jit --enable-lua-cache
make
make -C standalone install
mkdir -p $PWD/INSTALL/include
cp standalone/*.h $PWD/INSTALL/include
cp apache2/*.h $PWD/INSTALL/include

cd ..
make
make install

Once the install has finished, it is time to install HAProxy:

cd ..

mkdir -p /etc/haproxy/cert
make TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1
make install

id -u haproxy &> /dev/null || useradd -s /usr/sbin/nologin -r haproxy

Next thing to tackle is the OWASP Rules, so download and extract those then set up a symbolic link to help with updates:

cd /opt/
wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v3.3.2.tar.gz
tar xfvz v3.3.2.tar.gz 

ln -s coreruleset-3.3.2 /opt/owasp-modsecurity-crs

With the rules extracted you need to manipulate a few files:

cd owasp-modsecurity-crs
cp crs-setup.conf.example crs-setup.conf

cd rules

mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

And copy out a few:

mkdir /opt/modsecurity

cp /usr/src/haproxy-2.4.7/spoa-modsecurity/ModSecurity/unicode.mapping /opt/modsecurity/unicode.mapping

cp /usr/src/haproxy-2.4.7/spoa-modsecurity/ModSecurity/modsecurity.conf-recommended /opt/modsecurity/modsecurity.conf

In order for modsecurity to know about the CRS config and rules (and avoid a potential issue mentioned in the contrib README) you need to append the following to the end of /opt/modsecurity/modsecurity.conf:

include /opt/owasp-modsecurity-crs/crs-setup.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
include /opt/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
include /opt/owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

There is one other possible change needed to /opt/modsecurity/modsecurity.conf to ensure it can find the unicode.mapping file. Find the section below and ensure it points to the correct location:

# Specify your Unicode Code Point.
# This mapping is used by the t:urlDecodeUni transformation function
# to properly map encoded data to your language. Properly setting
# these directives helps to reduce false positives and negatives.
#
SecUnicodeMapFile /opt/modsecurity/unicode.mapping 20127

As the standalone modsecurity module is based on spoe you need to create a config file. Create /etc/haproxy/spoe-modsecurity.conf containing the following:

[modsecurity]
    spoe-agent modsecurity-agent
        messages     check-request
        option       var-prefix  modsec
        timeout      hello       100ms
        timeout      idle        30s
        timeout      processing  1s
        use-backend  spoe-modsecurity
    spoe-message check-request
        args   unique-id method path query req.ver req.hdrs_bin req.body_size req.body
        event  on-frontend-http-request

The next file to create is your HAProxy config file, so create /etc/haproxy/haproxy.cfg containing your haproxy configuration, mine was:

global
    maxconn 20480
    ssl-dh-param-file /etc/haproxy/dhparam.pem
    log 127.0.0.1 local0
    stats socket 127.0.0.1:14567
    tune.ssl.default-dh-param	2048
    server-state-file /tmp/haproxy_server_state
    ssl-default-bind-options ssl-min-ver TLSv1.2
    ssl-default-server-options ssl-min-ver TLSv1.2
    ssl-default-bind-ciphers TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:TLS13-CHACHA20-POLY1305-SHA256:EECDH+AESGCM:EECDH+CHACHA20:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256
    ssl-default-server-ciphers TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:TLS13-CHACHA20-POLY1305-SHA256:EECDH+AESGCM:EECDH+CHACHA20:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256

defaults
    log global
    mode http
    option httplog
    timeout connect 5s
    timeout client  50s
    timeout server 50s
    # Newly added timeouts
    timeout http-request 10s
    timeout http-keep-alive 2s
    timeout queue 5s
    timeout tunnel 2m
    timeout client-fin 1s
    timeout server-fin 1s
	
frontend myfrontend
    # primary cert is /etc/haproxy/cert/haproxy.pem
    bind *:443 ssl crt /etc/haproxy/cert/haproxy.pem alpn h2,http/1.1
    option			http-keep-alive
    option			forwardfor
    acl https ssl_fc
    filter spoe engine modsecurity config /etc/haproxy/spoe-modsecurity.conf
    http-request deny if { var(txn.modsec.code) -m int gt 0 }
    http-request set-header		X-Forwarded-Proto http if !https
    http-request set-header		X-Forwarded-Proto https if https
    timeout client		30000
    acl			ACL1	var(txn.txnhost) -m str -i haproxy.domain.tld
    http-request set-var(txn.txnhost) hdr(host)
    use_backend example1  if  ACL1
    
backend spoe-modsecurity
      mode tcp
      balance roundrobin
      timeout connect 5s
      timeout server  3m
      server modsec1 127.0.0.1:12345

backend example1
    # a https backend
    http-response set-header Strict-Transport-Security max-age=31536000;\ includeSubDomains;\ preload;
    http-response set-header X-Frame-Options DENY
    http-response set-header X-XSS-Protection 1;mode=block
    http-response set-header X-Content-Type-Options nosniff
    http-response set-header Referrer-Policy same-origin
    http-response set-header Cache-Control private,\ no-cache,\ no-store,\ max-age=0,\ no-transform,\ must-revalidate
    http-response set-header Pragma no-cache
    http-response del-header Server
    http-response del-header X-Powered-By
    server backendhost backendhost.otherdomain.tld:443 ssl verify none

The important lines in this from a modsecurity point of view are:

Frontend 
    ....
    filter spoe engine modsecurity config /etc/haproxy/spoe-modsecurity.conf
    http-request deny if { var(txn.modsec.code) -m int gt 0 }
    ....

backend spoe-modsecurity
      mode tcp
      balance roundrobin
      timeout connect 5s
      timeout server  3m
      server modsec1 127.0.0.1:12345

These define the modsecurity spoe filter with associated config file and the backend referenced in that config file.

Once you have done that make sure your certificate for your listener exists as /etc/haproxy/cert/haproxy.pem and set up your Diffie-Hellman parameters file by running:

openssl dhparam -out /etc/haproxy/dhparam.pem 2048

With that completed , we are now in to the home straight. Ideally we need HAProxy and the standalone modsecurity module to run as services. So lets get that sorted.

Created a service file for HAProxy, /lib/systemd/system/haproxy.service , containing the following:

[Unit]
Description=HAProxy Load Balancer
After=network.target

[Service]
EnvironmentFile=-/etc/default/haproxy
EnvironmentFile=-/etc/sysconfig/haproxy
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
ExecStartPre=/usr/local/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS
ExecStart=/usr/local/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE $EXTRAOPTS
ExecReload=/usr/local/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=always
SuccessExitStatus=143
Type=notify

[Install]
WantedBy=multi-user.target

Then create a service file for modsecurity, /lib/systemd/system/modsecurity.service, containing the following:

[Unit]
Description=Modsecurity Standalone
After=network.target

[Service]
EnvironmentFile=-/etc/default/modsecurity
EnvironmentFile=-/etc/sysconfig/modsecurity
Environment="CONFIG=/opt/modsecurity/modsecurity.conf" "PIDFILE=/run/modesecurity.pid" "EXTRAOPTS=-d -n 1"
ExecStart=/usr/local/bin/modsecurity $EXTRAOPTS -f $CONFIG
ExecReload=/usr/local/bin/modsecurity $EXTRAOPTS -f $CONFIG
ExecReload=/bin/kill -USR2 $MAINPID
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

Enable and Start both services by running:

systemctl enable modsecurity.service
systemctl enable haproxy.service
systemctl start modsecurity.service
systemctl start haproxy.service

The HAProxy and modsecurity processes then launch and listen on their respective ports:

[root@haproxy opt]# netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:14567         0.0.0.0:*               LISTEN      8007/haproxy
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      889/sshd
tcp        0      0 0.0.0.0:12345           0.0.0.0:*               LISTEN      7981/modsecurity
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      8007/haproxy

If you havent already allowed HTTPS in through the host firewall, make sure you do:

firewall-cmd --zone=public --add-service=https --permanent
firewall-cmd --reload

You can confirm the firewall has updated by listing the services:

[root@haproxy haproxy]# firewall-cmd --zone=public --list-services
cockpit dhcpv6-client https ssh

You can then test things work; The initial mode that is configured is Detection Only, which is why the Headers that get added by the backend get returned:

curl -I https://haproxy.domain.tld/?../etc/passwd

HTTP/2 302
date: Thu, 28 Oct 2021 10:50:53 GMT
content-security-policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' ; object-src 'self' ; worker-src 'self' blob:
content-type: text/html
last-modified: Thu, 28 Oct 2021 10:50:53 GMT
accept-ranges: bytes
location: /?../etc/passwd/
strict-transport-security: max-age=31536000; includeSubDomains; preload;
x-frame-options: DENY
x-xss-protection: 1;mode=block
x-content-type-options: nosniff
referrer-policy: same-origin
cache-control: private, no-cache, no-store, max-age=0, no-transform, must-revalidate
pragma: no-cache

But you can see in the modsecurity logs via journalctl, that the attempted “attack” is being detected:

Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029525 [00] <17> New Client connection accepted and assigned to worker 01
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029564 [01] <17> read_frame_cb
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029573 [01] <17> New Frame of 129 bytes received
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029577 [01] <17> Decode HAProxy HELLO frame
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029580 [01] <17> Supported versions : 2.0
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029584 [01] <17> HAProxy maximum frame size : 16380
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029587 [01] <17> HAProxy capabilities : pipelining,async
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029590 [01] <17> HAProxy supports frame pipelining
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029592 [01] <17> HAProxy supports asynchronous frame
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029594 [01] <17> HAProxy engine id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029597 [01] <17> Encode Agent HELLO frame
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029600 [01] <17> Agent version : 2.0
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029601 [01] <17> Agent maximum frame size : 16380
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029603 [01] <17> Agent capabilities :
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029611 [01] <17> write_frame_cb
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.029649 [01] <17> Frame of 54 bytes send
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030394 [01] <17> read_frame_cb
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030409 [01] <17> New Frame of 128 bytes received
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030411 [01] <17> Decode HAProxy NOTIFY frame
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030413 [01] <17> STREAM-ID=62 - FRAME-ID=1 - unfragmented frame received - frag_len=0 - len=128 - offset=7
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030422 [01] Process frame messages : STREAM-ID=62 - FRAME-ID=1 - length=121 bytes
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.030425 [01] Process SPOE Message 'check-request'
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.031793 [00] [client 127.0.0.1] ModSecurity: Warning. Matched phrase "etc/passwd" at ARGS_NAMES:../etc/passwd. [fi>
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.031925 [00] [client 127.0.0.1] ModSecurity: Warning. Matched phrase "etc/passwd" at ARGS_NAMES:../etc/passwd. [fi>
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032226 [00] [client 127.0.0.1] ModSecurity: Warning. Operator GE matched 5 at TX:anomaly_score. [file "/opt/owasp>
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032341 [00] [client 127.0.0.1] ModSecurity: Warning. Operator GE matched 5 at TX:inbound_anomaly_score. [file "/o>
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032486 [01] Encode Agent ACK frame
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032493 [01] STREAM-ID=62 - FRAME-ID=1
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032495 [01] Add action : set variable code=4294967195
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032506 [01] <17> write_frame_cb
Oct 28 11:50:50 haproxy.domain.tld modsecurity[7981]: 1635418250.032542 [01] <17> Frame of 30 bytes send
Oct 28 11:50:51 haproxy.domain.tld modsecurity[7981]: 1635418251.433138 [01] 1 clients connected

In order for things to work correctly you need to enable the engine in /opt/modsecurity/modsecurity.conf:

SecRuleEngine On

And change the default action in /opt/owasp-modsecurity-crs/crs-setup.conf:

# Default: Anomaly Scoring mode, log to error log, log to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
#   and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
#   perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
#SecDefaultAction "phase:1,log,auditlog,pass"
#SecDefaultAction "phase:2,log,auditlog,pass"

# Example: Anomaly Scoring mode, log only to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
#   and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
#   perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
# SecDefaultAction "phase:1,nolog,auditlog,pass"
# SecDefaultAction "phase:2,nolog,auditlog,pass"

# Example: Self-contained mode, return error 403 on blocking
# - In this configuration the default disruptive action becomes 'deny'. After a
#   rule triggers, it will stop processing the request and return an error 403.
# - You can also use a different error status, such as 404, 406, et cetera.
# - In Apache, you can use ErrorDocument to show a friendly error page or
#   perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
 SecDefaultAction "phase:1,log,auditlog,deny,status:403"
 SecDefaultAction "phase:2,log,auditlog,deny,status:403"

Then restart modsecurity:

systemctl restart modsecurity.service

Testing now shows the block with HTTP 403:

curl -I https://haproxy.domain.tld/?../etc/passwd

HTTP/2 403
content-length: 93
cache-control: no-cache
content-type: text/html

And Journalctl also shows the block:

Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906768 [00] ModSecurity for nginx (STABLE)/2.9.4 (http://www.modsecurity.org/) configured.
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906823 [00] ModSecurity: APR compiled version="1.6.3"; loaded version="1.6.3"
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906831 [00] ModSecurity: PCRE compiled version="8.42 "; loaded version="8.42 2018-03-20"
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906833 [00] ModSecurity: LUA compiled version="Lua 5.3"
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906836 [00] ModSecurity: YAJL compiled version="2.1.0"
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906838 [00] ModSecurity: LIBXML compiled version="2.9.7"
Oct 28 12:00:02 haproxy.domain.tld modsecurity[8101]: 1635418802.906895 [00] ModSecurity: StatusEngine call: "2.9.4,nginx,1.6.3/1.6.3,8.42/8.42 2018-03-20,Lua 5.3,2.9.7,964323a65>
Oct 28 12:00:03 haproxy.domain.tld modsecurity[8101]: 1635418803.028566 [00] ModSecurity: StatusEngine call successfully sent. For more information visit: http://status.modsecuri>
Oct 28 12:00:03 haproxy.domain.tld modsecurity[8101]: 1635418803.031965 [00] Worker 01 initialized
Oct 28 12:00:03 haproxy.domain.tld modsecurity[8101]: 1635418803.032009 [00] Server is ready [fragmentation=false - pipelining=false - async=false - debug=true - max-frame-size=1>
Oct 28 12:00:03 haproxy.domain.tld modsecurity[8101]: 1635418803.032051 [01] Worker ready to process client messages
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583223 [00] <1> New Client connection accepted and assigned to worker 01
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583263 [01] <1> read_frame_cb
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583307 [01] <1> New Frame of 129 bytes received
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583312 [01] <1> Decode HAProxy HELLO frame
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583319 [01] <1> Supported versions : 2.0
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583323 [01] <1> HAProxy maximum frame size : 16380
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583325 [01] <1> HAProxy capabilities : pipelining,async
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583329 [01] <1> HAProxy supports frame pipelining
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583332 [01] <1> HAProxy supports asynchronous frame
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583334 [01] <1> HAProxy engine id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583338 [01] <1> Encode Agent HELLO frame
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583342 [01] <1> Agent version : 2.0
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583343 [01] <1> Agent maximum frame size : 16380
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583345 [01] <1> Agent capabilities :
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583355 [01] <1> write_frame_cb
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.583396 [01] <1> Frame of 54 bytes send
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584082 [01] <1> read_frame_cb
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584097 [01] <1> New Frame of 128 bytes received
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584100 [01] <1> Decode HAProxy NOTIFY frame
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584102 [01] <1> STREAM-ID=64 - FRAME-ID=1 - unfragmented frame received - frag_len=0 - len=128 - offset=7
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584110 [01] Process frame messages : STREAM-ID=64 - FRAME-ID=1 - length=121 bytes
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.584113 [01] Process SPOE Message 'check-request'
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.585807 [00] [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched phrase "etc/passwd" at>
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.586022 [01] Encode Agent ACK frame
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.586049 [01] STREAM-ID=64 - FRAME-ID=1
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.586055 [01] Add action : set variable code=403
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.586065 [01] <1> write_frame_cb
Oct 28 12:00:06 haproxy.domain.tld modsecurity[8101]: 1635418806.586099 [01] <1> Frame of 22 bytes send
Oct 28 12:00:08 haproxy.domain.tld modsecurity[8101]: 1635418808.032927 [01] 1 clients connected

One last thing to confirm is the supported ciphers, a quick nmap scan takes care of that:

Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-28 12:26 GMT Summer Time

Nmap scan report for haproxy (xxx.xxx.xxx.xxx)

Host is up (0.0015s latency).
Not shown: 90 filtered tcp ports (no-response), 8 filtered tcp ports (admin-prohibited)

PORT    STATE SERVICE
22/tcp  open  ssh
443/tcp open  https
| ssl-enum-ciphers: 
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (ecdh_x25519) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.3: 
|     ciphers: 
|       TLS_AKE_WITH_AES_128_CCM_SHA256 (ecdh_x25519) - A
|       TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|     cipher preference: client
|_  least strength: A

That all looks good. Hopefully someone will find this of some use. Take Care.

CentOS 8 EOL – Which Distro to move to?

CentOS 8 going end of life at the end of 2021 has left many people with a choice to make.

  • Do I stay with an unsupported OS?
  • Do I move to CentOS Stream ?
  • Do I move to a different distro ?

Most people use CentOS as it is binary compliant with RHEL, that is certainly the main reason why I do. It is also downstream from RHEL which means it is pretty stable. So what to do??

Staying with an unsupported Operating System is not an option for me, especially not in the current security landscape. Exploits and vulnerabilities are seemingly detected daily in most operating systems so it is important to use one that is supported and updated regularly. You must make sure to apply those updates of course.

CentOS Stream is upstream of RHEL and is the public development branch of RHEL. That is the best explanation I could find for its position and I found that at: https://www.centos.org/cl-vs-cs/

Whilst using a distro that is effectively a development branch is fine for home use, if you are using it for anything a little more serious you should probably be looking elsewhere.

I personally am looking to move away to another distro and the two I am considering are:

I have done a bit of testing of both and at this stage I consider AlmaLinux to be a little more mature. This is not based on extensive testing of all aspects, just the parts I tend to use in the way I tend to use them.

Both distro’s have the backing of some major names, but whether either of them is suitable for your needs is ultimately down to you to find out.

I am about to re-validate my HAProxy installation process on AlmaLinux, using latest versions and builds where possible. This should be a good test to see if there are any gotchas. Look out for it in the not too distant future.