How to Protect a Self-Hosted Docker Application from Automated Attacks Using a WAF

1 4
calendar_today agoschedule9 min read

# How to Protect a Self-Hosted Docker Application from Automated Attacks Using
a WAF


## A. The Problem — Public Servers Under Constant Automated Attack

When you deploy a web application on a public VPS, you are not just exposing
it to your users. You are exposing it to a global network of automated
scanners that probe every reachable IP address for known vulnerabilities.

### 1. Understanding the Background Noise of the Internet

Services like Shodan and Censys continuously scan the entire IPv4 address
space and make the results searchable. According to a 2024 report from the
SANS Internet Storm Center, an unpatched server connected to the public
internet is typically discovered and probed by automated scanners within
minutes — sometimes under 60 seconds [1].

Common requests seen in Nginx access logs include:

GET /.env HTTP/1.1
GET /.git/config HTTP/1.1
GET /wp-login.php HTTP/1.1
GET /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php HTTP/1.1

These requests arrive regardless of what technology your application uses. If
your stack is Node.js and PostgreSQL, the scanners will still probe for PHP
configuration files and WordPress endpoints. The automation does not
discriminate.

### 2. Why HTTPS and Firewalls Are Not Enough

Many developers assume that enabling TLS encryption and configuring a firewall
is sufficient. These measures address different layers of the network stack:

  • Firewalls (Layer 3/4) control which ports and IP addresses can
    communicate
  • TLS (Layer 6) encrypts data in transit to prevent eavesdropping and
    tampering

Neither inspects the content of HTTP requests at Layer 7. An SQL injection
payload or a directory traversal attempt is a perfectly valid HTTP request
from the network's perspective. It arrives over an encrypted connection,
passes through the firewall on an allowed port, and reaches your application —
where it can cause real damage if input validation is insufficient.

Warning: Relying solely on HTTPS and firewall rules leaves your
application exposed to Layer 7 attacks including SQL injection, cross-site
scripting (XSS), credential stuffing, and automated vulnerability scanning.
These attacks account for a significant portion of real-world breaches [2].


## B. How to Identify If Your Server Is Affected

Before deploying any protective measures, verify the scope of the problem on
your own infrastructure. The method is straightforward and requires only
command-line access to your server.

Run the following command to view recent requests from your Nginx access log,
filtering for common scanner patterns:

`bash
# Display recent requests matching known scanner signatures
# -i makes the grep case-insensitive
# adjust the log path (/var/log/nginx/access.log) to match your system
sudo tail -n 5000 /var/log/nginx/access.log \

| grep -iE '\.env|\.git|wp-admin|phpmyadmin|\.asp|adminer' \
| awk '{print $1, $7}' \
| sort | uniq -c | sort -rn | head -20

# Example output:
# 47 192.168.x.x /.env
# 31 10.x.x.x /.git/config
# 18 172.x.x.x /wp-login.php
# 9 203.x.x.x /phpmyadmin/index.php

The awk '{print $1, $7}' portion extracts the client IP address and the
requested URI path. The sort | uniq -c | sort -rn pipeline counts occurrences
and displays the most frequent patterns at the top. If the output shows dozens
or hundreds of requests for paths your application does not serve, your
server is receiving automated scanning traffic.

Note: If you use a reverse proxy other than Nginx (such as Traefik or Caddy),
adjust the log file path accordingly. The key principle — looking for repeated
requests to non-existent or irrelevant paths — remains the same regardless of
which web server you use.


C. Deploying a Self-Hosted WAF as a Solution

A Web Application Firewall inspects HTTP traffic at Layer 7, comparing each
request against a set of rules that identify known attack patterns. Suspicious
requests are blocked before they reach the backend application.

  1. Choosing Between Cloud WAF and Self-Hosted WAF

There are two primary deployment models for WAFs:

Cloud WAF services such as Cloudflare WAF and AWS WAF operate at the edge of a
provider's network. You point your DNS records at the provider, and traffic
is filtered on their infrastructure before it reaches your server. This model
excels at DDoS mitigation and requires minimal operational effort, but it
introduces a dependency on a third party and typically does not cover internal
service-to-service communication.

Self-hosted WAF solutions such as ModSecurity (with the OWASP Core Rule Set)
and SafeLine WAF run on your own infrastructure. They provide full control
over traffic inspection and policy configuration, and they can protect
internal services that are never exposed to the public internet. The trade-off
is operational responsibility: you manage updates, tuning, and monitoring
yourself.

The choice depends on your architecture and threat model. For developers who
want direct control over their security layer without routing traffic through
an external provider, a self-hosted WAF is often the appropriate option.

  1. Step-by-Step Deployment with SafeLine WAF

SafeLine WAF, developed and maintained under the project name CyberServal
SafeLine, is an open-source WAF designed for Docker-based deployments. It
deploys as a reverse proxy that sits between the internet and your existing
web server, inspecting all incoming traffic without requiring changes to
application code [3].

Prerequisites:

  • A Linux server running Ubuntu 20.04 or newer
  • Docker Engine and Docker Compose installed
  • An existing reverse proxy (Nginx, Traefik, or Caddy)
  • At least 2 GB of available RAM and 2 CPU cores

Step 1 — Installation:

# Download and execute the official setup script
# The -fsSLk flags: fail silently, show errors, follow redirects,
# use TLS, and allow insecure connections for the initial download
bash -c "$(curl -fsSLk https://waf.chaitin.com/release/latest/setup.sh)"

# After the script completes, verify all containers are running
# You should see containers for the gateway (tengine), management
# service (manager), and detection engine (detector)
docker ps --filter "name=safeline"

The setup script handles pulling the required Docker images, initializing the
directory structure, and starting all services. Depending on your network
speed, this step typically completes in three to five minutes.

Step 2 — Access the Administration Panel:

Open a browser and navigate to https://<your-server-ip>:9443. Log in with the
credentials generated during installation — the setup script prints them to
the terminal at the end of the process. Change the default password
immediately on first login.

FAQ: What if port 9443 is blocked by my firewall?

If you cannot reach the administration panel, check whether your firewall or
cloud provider's security group allows inbound traffic on port 9443. You can
temporarily open it with: sudo ufw allow 9443/tcp. Consider restricting access
to this port by IP address once initial setup is complete.

Step 3 — Route Traffic Through the WAF:

SafeLine needs to intercept traffic before it reaches Nginx. The standard
approach is to move Nginx to a different port and configure SafeLine to
forward traffic to it.

On your server, edit the Nginx configuration to listen on an internal port
(for example, 8080 for HTTP and 8443 for HTTPS):

# /etc/nginx/sites-available/default or your site-specific config
# Change the listen directive from port 443 to port 8443
server {

  listen 8443 ssl;

Note: If you use a reverse proxy other than Nginx (such as Traefik or Caddy),
adjust the log file path accordingly. The key principle — looking for repeated
requests to non-existent or irrelevant paths — remains the same regardless of
which web server you use.


C. Deploying a Self-Hosted WAF as a Solution

A Web Application Firewall inspects HTTP traffic at Layer 7, comparing each
request against a set of rules that identify known attack patterns. Suspicious
requests are blocked before they reach the backend application.

  1. Choosing Between Cloud WAF and Self-Hosted WAF

┌─────────────────┬──────────────────────────────────────────────┐
│ Field │ Value │
├─────────────────┼──────────────────────────────────────────────┤
│ Domain │ your-domain.com │
├─────────────────┼──────────────────────────────────────────────┤
│ Upstream Server │ http://127.0.0.1:8443
├─────────────────┼──────────────────────────────────────────────┤
│ Enable TLS │ Enable if you want SafeLine to terminate SSL │
└─────────────────┴──────────────────────────────────────────────┘

After saving the configuration, the traffic path becomes:

User Request → SafeLine (port 443) → Nginx (port 8443) → Application Container

  1. Verifying the Deployment

To confirm the WAF is inspecting traffic:

# Send a test request containing a known SQL injection pattern
# SafeLine should block this and return a 403 Forbidden response
curl -I "https://your-domain.com/?id=1' OR '1'='1"

# Expected output:
# HTTP/2 403
# ...

# Now send a legitimate request to verify normal traffic passes through
curl -I "https://your-domain.com/"

# Expected output:
# HTTP/2 200
# ...

Check the SafeLine dashboard at https://<your-server-ip>:9443 — the attack
overview should now show at least one blocked request. Over the following
hours and days, the dashboard will populate with statistics about blocked
attacks, their types, and their originating IP addresses.

Tip: During the first week after deployment, monitor the dashboard regularly.
The default ruleset is intentionally aggressive — it is safer to block too
much than too little. Review blocked requests to identify any legitimate
traffic that was incorrectly flagged, and add appropriate whitelist rules.
This tuning period is normal and expected for any WAF deployment.


D. Key Takeaways

Deploying a self-hosted WAF addresses a gap that firewalls and TLS encryption
do not cover: inspection of HTTP traffic at Layer 7. Automated scanners
discover and probe public servers within minutes, and they do not distinguish
between large enterprises and small side projects — they scan everything.

SafeLine WAF provides a Docker-native solution that integrates into an
existing infrastructure stack without requiring application changes. The
deployment process — installation, port reassignment for the existing reverse
proxy, and upstream configuration — is achievable within a single maintenance
window. Once active, the WAF blocks known attack patterns and provides
visibility into traffic that would otherwise reach the application
unchallenged.

A self-hosted WAF is not a universal requirement. Development environments,
private VPN-only services, and architectures already protected by cloud WAFs
may not benefit from adding another security layer. The operational cost —
updates, monitoring, and occasional false positive tuning — should be weighed
against the protection it provides.

For developers running Docker workloads on public VPS instances, checking
access logs is the essential first step. If those logs show automated scanning
traffic, a self-hosted WAF offers a lightweight, controlled approach to
filtering malicious requests before they consume application resources.


E. References

[1] SANS Internet Storm Center, "Survival Time: How Long Before an Unpatched
System is Compromised," 2024. [Online]. Available:
https://isc.sans.edu/survivaltime.html

[2] OWASP Foundation, "OWASP Top 10 — 2021," 2021. [Online]. Available:
https://owasp.org/www-project-top-ten/

[3] Chaitin Technology, "SafeLine WAF — Documentation," 2024. [Online].
Available: https://docs.waf.chaitin.com/en/home


# If the test passes, reload Nginx gracefully (no dropped connections)
sudo nginx -s reload

# Verify Nginx is now listening on the new port
sudo ss -tlnp | grep nginx
# Expected output:
# LISTEN 0 511 0.0.0.0:8443 ... nginx

Then, in the SafeLine administration panel, navigate to Sites → Add Site, and
configure the upstream:

┌─────────────────┬──────────────────────────────────────────────┐
│ Field │ Value │
├─────────────────┼──────────────────────────────────────────────┤
│ Domain │ your-domain.com │
├─────────────────┼──────────────────────────────────────────────┤
│ Upstream Server │ http://127.0.0.1:8443
├─────────────────┼──────────────────────────────────────────────┤
│ Enable TLS │ Enable if you want SafeLine to terminate SSL │
└─────────────────┴──────────────────────────────────────────────┘

After saving the configuration, the traffic path becomes:

User Request → SafeLine (port 443) → Nginx (port 8443) → Application Container

  1. Verifying the Deployment

To confirm the WAF is inspecting traffic:

# Send a test request containing a known SQL injection pattern
# SafeLine should block this and return a 403 Forbidden response
curl -I "https://your-domain.com/?id=1' OR '1'='1"

# Expected output:
# HTTP/2 403
# ...

# Now send a legitimate request to verify normal traffic passes through
curl -I "https://your-domain.com/"

# Expected output:
# HTTP/2 200
# ...

Check the SafeLine dashboard at https://<your-server-ip>:9443 — the attack
overview should now show at least one blocked request. Over the following
hours and days, the dashboard will populate with statistics about blocked
attacks, their types, and their originating IP addresses.

Tip: During the first week after deployment, monitor the dashboard regularly.
The default ruleset is intentionally aggressive — it is safer to block too
much than too little. Review blocked requests to identify any legitimate
traffic that was incorrectly flagged, and add appropriate whitelist rules.
This tuning period is normal and expected for any WAF deployment.


D. Key Takeaways

Deploying a self-hosted WAF addresses a gap that firewalls and TLS encryption
do not cover: inspection of HTTP traffic at Layer 7. Automated scanners
discover and probe public servers within minutes, and they do not distinguish
between large enterprises and small side projects — they scan everything.

SafeLine WAF provides a Docker-native solution that integrates into an
existing infrastructure stack without requiring application changes. The
deployment process — installation, port reassignment for the existing reverse
proxy, and upstream configuration — is achievable within a single maintenance
window. Once active, the WAF blocks known attack patterns and provides
visibility into traffic that would otherwise reach the application
unchallenged.

A self-hosted WAF is not a universal requirement. Development environments,
private VPN-only services, and architectures already protected by cloud WAFs
may not benefit from adding another security layer. The operational cost —
updates, monitoring, and occasional false positive tuning — should be weighed
against the protection it provides.

For developers running Docker workloads on public VPS instances, checking
access logs is the essential first step. If those logs show automated scanning
traffic, a self-hosted WAF offers a lightweight, controlled approach to
filtering malicious requests before they consume application resources.


E. References

[1] SANS Internet Storm Center, "Survival Time: How Long Before an Unpatched
System is Compromised," 2024. [Online]. Available:
https://isc.sans.edu/survivaltime.html

[2] OWASP Foundation, "OWASP Top 10 — 2021," 2021. [Online]. Available:
https://owasp.org/www-project-top-ten/

[3] Chaitin Technology, "SafeLine WAF — Documentation," 2024. [Online].
Available: https://docs.waf.chaitin.com/en/home


🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Comparison: Universal Import vs. Plaid/Yodlee

Pocket Portfolio - Mar 12

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

The Interface of Uncertainty: Designing Human-in-the-Loop

Pocket Portfolio - Mar 10

Defending Against AI Worms: Securing Multi-Agent Systems from Self-Replicating Prompts

alessandro_pignati - Apr 2
chevron_left
147 Points5 Badges
2Posts
0Comments

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!