Notes on How to Install Coraza Web Application Firewall (WAF) + OWASP CRS on Debian 13.7.0 Linux Server

Subject: Notes on How to Install Coraza Web Application Firewall (WAF) + OWASP CRS on Debian 13.7.0 Linux Server


Good day from Singapore,


Author: Mr. Turritopsis Dohrnii Teo En Ming

Date: 27 Sep 2026 Sunday 12.47 AM

Country: Singapore


Install Debian 13.7.0

======================


nano /etc/apt/sources.list


#deb cdrom:[Debian GNU/Linux 13.7.0 _Trixie_ - Official amd64 DVD Binary-1 with firmware 20260912-09:36]/ trixie contrib main non-free-firmware


apt update


apt full-upgrade -y


apt install -y curl wget git ca-certificates gnupg unzip tar jq


reboot


cat /etc/os-release


Test connectivity to your existing HTTPS server

=================================================


curl -vk https://192.168.88.8 # (This is a VMware ESXi 8.0 Update 3e Server)


Install Go

============


apt install -y golang-go


go version


Then install xcaddy:


GOBIN=/usr/local/bin go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest


xcaddy version


Build Caddy with Coraza

===========================


mkdir -p /usr/local/src/caddy-coraza

cd /usr/local/src/caddy-coraza


xcaddy build --with github.com/corazawaf/coraza-caddy/v2


./caddy version


./caddy list-modules | grep -i waf


install -m 755 ./caddy /usr/bin/caddy


/usr/bin/caddy version


Create Caddy user/directories

================================


groupadd --system caddy 2>/dev/null || true

useradd --system \

  --gid caddy \

  --create-home \

  --home-dir /var/lib/caddy \

  --shell /usr/sbin/nologin \

  caddy 2>/dev/null || true

  

  

mkdir -p /etc/caddy

mkdir -p /etc/coraza

mkdir -p /etc/coraza/crs

mkdir -p /var/log/caddy

mkdir -p /var/log/coraza



chown -R root:caddy /etc/caddy

chown -R root:caddy /etc/coraza

chown -R caddy:caddy /var/log/caddy

chown -R caddy:caddy /var/log/coraza


chmod 750 /etc/caddy

chmod 750 /etc/coraza


Download OWASP Core Rule Set

===============================


cd /opt


git clone https://github.com/coreruleset/coreruleset.git coreruleset


cd /opt/coreruleset

git status


Install the CRS files

========================


cp -a /opt/coreruleset/. /etc/coraza/crs/


ls -la /etc/coraza/crs


ls -la /etc/coraza/crs/rules | head -30


chown -R root:caddy /etc/coraza


find /etc/coraza -type d -exec chmod 750 {} \;

find /etc/coraza -type f -exec chmod 640 {} \;


Create Coraza base configuration

====================================


nano /etc/coraza/coraza.conf


SecRuleEngine DetectionOnly


SecRequestBodyAccess On

SecResponseBodyAccess Off


SecRequestBodyLimit 13107200

SecRequestBodyNoFilesLimit 131072


SecAuditEngine RelevantOnly

SecAuditLogParts ABIJDEFHZ

SecAuditLogType Serial

SecAuditLog /var/log/coraza/audit.log


Configure CRS

================


cp /etc/coraza/crs/crs-setup.conf.example \

   /etc/coraza/crs/crs-setup.conf

   

Configure Caddy + Coraza

=========================


nano /etc/caddy/Caddyfile


{

    order coraza_waf first

}


https://192.168.88.7 {


    coraza_waf {

        directives `

            Include /etc/coraza/coraza.conf

            Include /etc/coraza/crs/crs-setup.conf

            Include /etc/coraza/crs/rules/*.conf

        `

    }


    reverse_proxy https://192.168.88.8 {

        transport http {

            tls

            tls_insecure_skip_verify

        }

    }


    log {

        output file /var/log/caddy/access.log

    }

}


***NOTICE: Please note that 192.168.88.7 is the Coraza WAF and 192.168.88.8 is the HTTPS web server it is protecting.***


Better solution: trust the self-signed certificate

====================================================


openssl s_client \

  -connect 192.168.88.8:443 \

  -showcerts </dev/null

  

nano /etc/coraza/backend.crt


-----BEGIN CERTIFICATE-----

---snipped---

-----END CERTIFICATE-----



chmod 644 /etc/coraza/backend.crt


Validate Caddy configuration

===============================


caddy validate \

  --config /etc/caddy/Caddyfile \

  --adapter caddyfile

  

Create systemd service

=========================


nano /etc/systemd/system/caddy.service


[Unit]

Description=Caddy with Coraza WAF

Documentation=https://caddyserver.com/

After=network-online.target

Wants=network-online.target


[Service]

Type=notify

User=caddy

Group=caddy


ExecStart=/usr/bin/caddy run \

  --environ \

  --config /etc/caddy/Caddyfile


ExecReload=/usr/bin/caddy reload \

  --config /etc/caddy/Caddyfile \

  --force


TimeoutStopSec=5s

LimitNOFILE=1048576

PrivateTmp=true

ProtectSystem=full


AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE


[Install]

WantedBy=multi-user.target



chown -R root:caddy /etc/coraza


find /etc/coraza -type d -exec chmod 750 {} \;

find /etc/coraza -type f -exec chmod 640 {} \;



mkdir -p /var/log/caddy

chown -R caddy:caddy /var/log/caddy

chmod 750 /var/log/caddy


chown caddy:caddy /var/log/caddy/access.log

chmod 640 /var/log/caddy/access.log


mkdir -p /var/log/coraza

chown -R caddy:caddy /var/log/coraza

chmod 750 /var/log/coraza


chown caddy:caddy /var/log/coraza/audit.log

chmod 640 /var/log/coraza/audit.log


systemctl daemon-reload

systemctl enable caddy

systemctl start caddy


systemctl status caddy --no-pager -l


journalctl -u caddy -n 100 --no-pager


Confirm ports

==============


ss -lntp | grep -E ':80|:443'


LISTEN 0      4096               *:443             *:*    users:(("caddy",pid=11197,fd=7))

LISTEN 0      4096               *:80              *:*    users:(("caddy",pid=11197,fd=9))


Test normal website traffic

============================


Open https://192.168.88.7 (Coraza WAF) in a Google Chrome web browser.


The site should work normally.


Then watch Coraza/Caddy:


journalctl -u caddy -f


tail -f /var/log/coraza/audit.log


Test SQL injection detection

================================


While still in:


SecRuleEngine DetectionOnly


send a harmless test request:


curl -k 'https://192.168.88.7/?id=1%27%20OR%20%271%27=%271'


tail -100 /var/log/coraza/audit.log


You should see CRS alerts associated with SQL injection.


Test XSS detection

====================


curl -k 'https://192.168.88.7/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E'


tail -100 /var/log/coraza/audit.log


Again, in DetectionOnly mode the request isn't supposed to be blocked; you're checking that CRS recognizes it.


Turn blocking on

==================


nano /etc/coraza/coraza.conf


Change to


SecRuleEngine On


caddy validate \

 --config /etc/caddy/Caddyfile \

 --adapter caddyfile

 

systemctl reload caddy


Now repeat the SQLi test.


Open https://192.168.88.7/?id=1%27%20OR%20%271%27=%271 in Google Chrome web browser.


Access to 192.168.88.7 was denied

You don't have authorization to view this page.

HTTP ERROR 403


Automatically update OWASP CRS

===============================


<EMPTY>


Create CRS update script

==========================


nano /usr/local/sbin/update-coraza-crs.sh


#!/bin/bash

set -euo pipefail


WORKDIR="/var/tmp/coraza-crs-update"

INSTALLDIR="/etc/coraza/crs"

BACKUPDIR="/etc/coraza/crs-backup"


rm -rf "$WORKDIR"


git clone --depth 1 \

  https://github.com/coreruleset/coreruleset.git \

  "$WORKDIR"


# Preserve local CRS configuration

if [ -f "$INSTALLDIR/crs-setup.conf" ]; then

    cp "$INSTALLDIR/crs-setup.conf" \

       "$WORKDIR/crs-setup.conf"

else

    cp "$WORKDIR/crs-setup.conf.example" \

       "$WORKDIR/crs-setup.conf"

fi


# Backup existing CRS

rm -rf "$BACKUPDIR"

cp -a "$INSTALLDIR" "$BACKUPDIR"


# Install candidate rules

rm -rf "${INSTALLDIR}.new"

cp -a "$WORKDIR" "${INSTALLDIR}.new"


chown -R root:caddy "${INSTALLDIR}.new"

chmod -R g+rX "${INSTALLDIR}.new"


# Temporarily switch directories

mv "$INSTALLDIR" "${INSTALLDIR}.old"

mv "${INSTALLDIR}.new" "$INSTALLDIR"


# Validate complete Caddy/Coraza configuration

if /usr/bin/caddy validate \

    --config /etc/caddy/Caddyfile \

    --adapter caddyfile

then


    systemctl reload caddy


    rm -rf "${INSTALLDIR}.old"


    logger -t coraza-crs-update \

      "OWASP CRS successfully updated"


else


    logger -t coraza-crs-update \

      "CRS update FAILED validation; rolling back"


    rm -rf "$INSTALLDIR"

    mv "${INSTALLDIR}.old" "$INSTALLDIR"


    exit 1

fi


rm -rf "$WORKDIR"



Make executable:


chmod 750 /usr/local/sbin/update-coraza-crs.sh


Automate it with systemd

==========================


Instead of cron, use a systemd timer.


nano /etc/systemd/system/coraza-crs-update.service


[Unit]

Description=Update OWASP Core Rule Set for Coraza


[Service]

Type=oneshot

ExecStart=/usr/local/sbin/update-coraza-crs.sh




nano /etc/systemd/system/coraza-crs-update.timer


[Unit]

Description=Daily OWASP CRS update check


[Timer]

OnCalendar=*-*-* 03:30:00

Persistent=true

RandomizedDelaySec=30m


[Install]

WantedBy=timers.target



This checks approximately once per day around 03:30.



systemctl daemon-reload


systemctl enable --now coraza-crs-update.timer


systemctl list-timers | grep coraza


Test the updater manually first

====================================


Do not wait until 03:30 for the first run.


systemctl start coraza-crs-update.service


systemctl status coraza-crs-update.service


journalctl \

  -u coraza-crs-update.service \

  -n 100 \

  --no-pager

  

 

systemctl status caddy


Then access the website.


https://192.168.88.7 (Coraza WAF)


Automatic Debian security updates

==================================


You should also keep Debian patched.


apt install -y unattended-upgrades


dpkg-reconfigure unattended-upgrades


Select Yes.


systemctl status unattended-upgrades




That's all.


Regards,


Mr. Turritopsis Dohrnii Teo En Ming

Republic of Singapore

27 Sep 2026 Sunday 1.00 am Singapore Time





REFERENCES

=============


[1] https://lists.debian.org/debian-user/2026/09/msg00395.html


[2] https://mail-archive.com/debian-user@lists.debian.org/msg824615.html


[3] https://marc.info/?l=debian-user&m=179044276368797&w=2


[4] https://lore.kernel.org/netdev/yqxeDm9kL1GLsZWM0v8J0Hpn6x2MsqxwFiUmjvlzGIJ-eRiqA19xrbIwpIQxEqIUTPN3B-Ap26_4odDUYzlWj6NCBgjlwgb5kH6zl2RCWEY=@protonmail.com/


[5] mail-archive.com - NIL


[6] https://marc.info/?l=linux-netdev&m=179047826082629&w=2


[7] https://lists.freebsd.org/archives/freebsd-chat/2026-September/000088.html


[8] mail-archive.com - NIL


[9] https://marc.info/?l=freebsd-chat&m=179048031483151&w=2

Comments

Popular posts from this blog

Estimated Total Expenses in Taiwan from 6 Aug 2025 to 12 Aug 2025 (Version 15 Aug 2025)

Teo En Ming’s Book 2 (13 June 2025 Edition) Cloud Storage Download Links

Linux 6.18 Released With Many New Features, Likely This Year's LTS Kernel