ModSecurity Configuration for Passbolt

ModSecurity is a module for Apache that acts as an “application firewall.” It uses rulesets to limit what kinds of actions are allowed when accessing Apache. Potentially dangerous HTTP actions are blocked, and exceptions are made for the actions required for the application to function. ModSecurity is powered by a large collection of rules that try to filter out anything that could potentially be dangerous. Most web applications will require some rule exceptions to function. ModSecurity rules are intentionally heavy by default.

This post assumes you already have knowledge about administering Apache with ModSecurity installed. If you are running Passbolt, this configuration in your VirtualHost block should allow Passbolt to function correctly:

<Location "/auth/verify.json">
        SecRuleRemoveById 200004 942100

<Location "/import/resources.json">
        SecRuleRemoveById 942100

<Location "/resources.json">
        SecRuleRemoveById 942100

<LocationMatch "^/resources/.*">
        SecRuleRemoveById 911100 980130 942100

<LocationMatch "^/users/.*">
        SecRuleRemoveById 911100

<LocationMatch "^/setup/completeRecovery/.*\.json">
        SecRuleRemoveById 980130 911100 949110

Disabling Insecure Protocols and Ciphers in Apache

You will find a lot of recommended Apache configurations to disable insecure ciphers in Apache, especially to try to make the Qualsys SSL Labs test happy. It took me a while to figure out one that isn’t over-complicated:

SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!SHA1:!SHA256:!SHA384:!RSA

When searching, you will generally find SSLProtocol settings that disable all protocols and then explicitly enable TLS 1.2 and TLS 1.3. I prefer to explicitly disable so that as Apache upgrades make new TLS versions available, they will automatically become available. This is obviously a rare event, so it’s all about what works best for you. You could argue that you shouldn’t make new protocols available without consciously deciding to. I see good and bad to both methods.

Editing PDFs in PowerShell

iText is an excellent library for editing PDFs, available for both Java and .NET. PowerShell is capable of loading and using .NET libraries, so with a little magic, we can utilize the .NET iText library (licensed under AGPL) to edit PDFs in PowerShell

Add-Type -Path "$PSScriptRoot\Common.Logging.Core.dll"
Add-Type -Path "$PSScriptRoot\Common.Logging.dll"
Add-Type -Path "$PSScriptRoot\"
Add-Type -Path "$PSScriptRoot\itext.kernel.dll"
Add-Type -Path "$PSScriptRoot\itext.forms.dll"
Add-Type -Path "$PSScriptRoot\itext.layout.dll"
Add-Type -Path "$PSScriptRoot\BouncyCastle.Crypto.dll"

The above DLLs include the iText libraries and dependencies. Here is a convenient ZIP file with the required DLL files:

There are additional iText DLLs you may want to load for additional features. The full AGPL iText library and links to dependencies are available at the following URL:

The full documentation is available at the following URL:

I won’t go into full detail on how to use this library in this post, but I will cover some basics and give a few examples to show how to use a .NET library in a PowerShell script.

First, we initialize a PdfReader object for accessing the PDF:

$Reader = [iText.Kernel.Pdf.PdfReader]::new("C:\temp\example.pdf")

Optionally, if you want to ignore password protection on a PDF, you can add the following line after:


Next, we initialize a PdfWriter object to write our modified PDF to:

$Writer = [iText.Kernel.Pdf.PdfWriter]::new("C:\example\new_example.pdf")

Now we generate a PdfDocument object to tie our PdfReader and PdfWriter together and allow us to start our work:

$PdfDoc = [iText.Kernel.Pdf.PdfDocument]::new($Reader, $Writer)

For this example, let’s find a form field and fill it with data. The PdfAcroForm class works with static PDF forms. We’ll start by collecting the form and all of the fields that belong to it:

$Form = [iText.Forms.PdfAcroForm]::getAcroForm($PdfDoc, $True)
$Fields = $Form.getFormFields()

getFormFields() returns a dictionary where the key is the name of the field and the value is the actual PdfFormField object. If we want to work with the field named “EmployeeName”, we can easily do so:

$Field = ($Fields | Where-Object {$_.key -eq "EmployeeName"}).Value

Now that we have the field object, we can easily set its value with the SetValue function:

$Field.SetValue("DOE, JOHN")

Once we’re done working with the document, committing our changes is as simple as calling the Close function on the PdfDocument object:


The file opened with the PdfWriter object will now exist with the changes.

fail2ban Jail for WordPress Logins

Failed WordPress logins do not result in an HTTP 400-series status code. There are some very good reasons for this, and it is a conscious decision on the part of the WordPress developers to return an HTTP 200. There is a long and interesting discussion on this on the WordPress bug tracker.

While this makes it slightly more difficult to programmatically detect failed logins, it is still possible without altering your WordPress install. WordPress login attempts are submitted via POST request to wp-login.php or xmlrpc.php. A failed login results in an HTTP 200 that returns the user to the same login page. A successful login results in an HTTP 302 redirect to the originally requested page or the admin dashboard. Ironically, this means we can rely on HTTP 200 as an indicator of a failed login.

To create a jail using this method, simply add a .conf file to /etc/fail2ban/filter.d. I chose to name mine wordpress-login.conf. The contents will look like this:

failregex = ^<HOST> -.*"POST /(wp-login|xmlrpc)\.php.* HTTP/.*" 200 .*$

Note that this regular expression is written specifically for the Apache access log. If you are using another web server, alter the expression accordingly. Just ensure you are looking for POSTs to wp-login.php or xmlrpc.php that result in an HTTP 200.

Once you’ve added your filter, update your jail.local to enable it:

enabled = true
port = http,https
logpath = %(apache_access_log)s

Restart fail2ban for the new configuration to take effect.

You can test by failing a login and then checking /var/log/fail2ban.log for the detection:

INFO    [wordpress-login] Found - 2020-01-29 20:58:01