Follow these steps in order. At the end, https://app.example.com/ is the application https://app.example.com/mikiri-waf-test is answered with 403 by Filter.
This path is Debian 13, three servers, one site. PostgreSQL, API WMC share the API server. Filter is %WAF SERVER%. MLC is 10.10.10.1. The application is 10.10.10.2.
%WAF SERVER%), MLC (10.10.10.1) the API server that also runs PostgreSQL and WMC.On the API server, on the Filter on the MLC server, connect the repository:
echo "deb https://s.mikiri.ai/repo/stable/debian trixie mikiri-waf" > /etc/apt/sources.list.d/mikiri-waf.list
curl -s https://s.mikiri.ai/repo/gpg.key | gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/trusted.gpg --import
chmod 644 /etc/apt/trusted.gpg.d/trusted.gpg
apt update
On the API server:
apt install postgresql mikiri-waf-api mikiri-waf-wmc
su - postgres -c "psql -c \"CREATE DATABASE api;\""
su - postgres -c "psql -c \"CREATE ROLE api PASSWORD 'YOUR_PASSWORD';\""
su - postgres -c "psql -c \"ALTER DATABASE api OWNER TO api;\""
su - postgres -c "psql -c \"ALTER ROLE api WITH LOGIN;\""
su - postgres -c "psql -c \"CREATE DATABASE wmc;\""
su - postgres -c "psql -c \"CREATE ROLE wmc PASSWORD 'YOUR_PASSWORD';\""
su - postgres -c "psql -c \"ALTER DATABASE wmc OWNER TO wmc;\""
su - postgres -c "psql -c \"ALTER ROLE wmc WITH LOGIN;\""
systemctl reload postgresql
On the Filter:
apt install mikiri-waf-filter-nginx-1.30.5
On the MLC server:
apt install mikiri-waf-mlc
Replace YOUR_PASSWORD. Write that password into /var/www/mikiri-waf-api/settings.py and /var/www/mikiri-waf-wmc/.env for the databases api and wmc.
On the Filter, /etc/nginx/mikiri-waf/filter/global/waf.conf:
waf_product_key=1234567890;
waf_api_url http://api.example.com:8080;
waf_log /var/log/mikiri-waf/filter/filter.log info;
1234567890 is the license key. One key belongs to one Filter.
On the Filter, in /etc/nginx/nginx.conf, open RabbitMQ to the MLC server:
stream {
server {
listen 5673;
proxy_pass 127.0.0.1:5672;
allow 10.10.10.1;
deny all;
}
}
Replace 10.10.10.1 with the IP address of the server where MLC is installed.
On the MLC server, /etc/mikiri-waf-mlc/mlc.conf:
waf_product_key = 1234567890
waf_api_url = http://api.example.com:8080
waf_rabbitmq = guest:guest@%WAF SERVER%:5673
Apply and restart:
/bin/bash /etc/nginx/mikiri-waf/venv/set_permissions.sh
systemctl restart nginx rabbitmq-server memcached mikiri-waf-mla mikiri-waf-af
/bin/bash /etc/mikiri-waf-mlc/venv/set_permissions.sh
systemctl restart mikiri-waf-mlc memcached
Restart nginx, memcached, mikiri-waf-api mikiri-waf-api-aux on the API server after settings.py is saved.
The Filter answers 403. /var/log/mikiri-waf/filter/filter.log contains CRL ID 1 in PATH. DNS is unchanged.
curl -i http://%WAF SERVER%/mikiri-waf-test
The status line is HTTP/1.1 403. The log line contains CRL ID 1 in PATH. Any other result is described in A test request is not blocked.
Step 1 is complete. WMC is installed on the API server.
Open WMC in a browser at http://<API server>:8080.
None. The WMC was installed in step 1.
The WMC loads. The attacks page shows the request from step 1.
The attacks page lists path /mikiri-waf-test and signature ID 1.
The application is a virtual host on the Filter. Create it in WMC, under Web server. Select the CID of this Filter. The fields are described in Web server settings.
The WMC from step 2 is open. You have the certificate and the private key for app.example.com.
None. The WMC from step 2 is enough.
SSL → Add SSL Certificate. Name app.example.com. Paste the certificate and the private key of that name.
Virtual hosts → Add a virtual host named app.example.com-http:
| Field | Value |
|---|---|
| server_name | app.example.com |
| listen | 80 |
| return | 301 https://$server_name$request_uri |
| Active | on |
Do not add a location on this host. Save.
Add a virtual host named app.example.com:
| Field | Value |
|---|---|
| server_name | app.example.com |
| listen | 443 ssl |
| SSL certificate | app.example.com |
| Active | on |
Add location /. 10.10.10.2 is the address the application already answers on.
| Field | Value |
|---|---|
| pass_type | proxy_pass |
| proxy_pass | http://10.10.10.2 |
proxy_set_header:
| Header | Value |
|---|---|
| Host | $http_host |
| X-Real-IP | $remote_addr |
Save.
Both hosts are in the list app.example.com is Active.
curl -i http://%WAF SERVER%/mikiri-waf-test
The Filter still answers 403. DNS is still unchanged.
The location / on app.example.com proxies to 10.10.10.2. The Filter can open a connection to that address.
From the Filter, before DNS changes:
curl -i -H 'Host: app.example.com' http://10.10.10.2/
proxy_pass on the location from step 3. 10.10.10.2 is the address the application already answers on.
The status is the one the application already returns, often 200. It is not a Filter 403.
The response body is the application. The attacks page has no new event for /.
The certificate app.example.com was added in step 3.
None. The certificate was added in step 3.
The host app.example.com listens with ssl and uses the certificate app.example.com. A certificate is required when listen contains ssl.
The SSL settings of app.example.com show that certificate.
In WMC, the host app.example.com lists the certificate app.example.com. Visitors use it after DNS points at the Filter.
The upstream check in step 4 returned the application. The certificate is on the Filter.
Change the DNS record of app.example.com so it points at the Filter.
Visitors must reach the Filter. A visitor that still opens 10.10.10.2 directly is not analyzed.
The name resolves to the Filter.
getent hosts app.example.com
The address is the Filter. The request in step 8 is the proof through HTTPS.
The Filter analyzes a request when the virtual host proxies it. Blocking is on when the host is not listed in waf_host_dm.
The virtual host from step 3 exists and DNS points at the Filter.
In WMC, open WAF settings → Common and select Detection mode for the host.
Do not add app.example.com do not add *. While the host stays in that list, the Filter only records attacks. The same option is waf_host_dm.
Detection mode for app.example.com is off.
In Detection mode for the host, app.example.com is absent and * is absent.
DNS points at the Filter. app.example.com is not in waf_host_dm.
curl -I https://app.example.com/
The path is /. It does not contain mikiri-waf-test.
The application answers. When the application returns success, the status line is:
HTTP/1.1 200
or HTTP/2 200 if a proxy in front of the Filter speaks HTTP/2. The attacks page has no new event for this request.
The status is the application's status WMC has no new row for GET /.
Step 8 reached the application. The host is still absent from waf_host_dm.
curl -i https://app.example.com/mikiri-waf-test
None. The host is still absent from waf_host_dm.
HTTP/1.1 403
or HTTP/2 403 if a proxy in front of the Filter speaks HTTP/2. The request does not reach the application. The attacks page shows app.example.com, path /mikiri-waf-test, signature ID 1. /var/log/mikiri-waf/filter/filter.log contains CRL ID 1 in PATH.
The status is 403. The attacks page shows app.example.com, path /mikiri-waf-test, signature ID 1. The application access log has no line for this request.
The application is protected. Behavioral models are a later change: Activating AI protection.