DEV Community

Carrie
Carrie

Posted on

Demystifying the Technology Architecture of Open Source WAF, SafeLine

What is SafeLine

SafeLine (https://waf.chaitin.com/) is a free and open source docker-based, easy to use, self-hosted web application firewall (WAF) that protects your website from cyber attacks such as SQL injection, XSS, OS command injection, CRLF injection, x path injection, RCE, XXE, SSRF, directory traversal, backdoors, brute force, http-flood, bot abused, among others.

Demystifying the Technology Architecture of SafeLine

Let’s look at the architecture diagram of SafeLine (the diagram is somewhat outdated, so consider it a rough reference). The top section enclosed by a dashed line represents the data flow, specifically the flow of traffic data accessing the business server. The middle section outlines the various services within SafeLine.

Image description

Explanation of Various Containers and Services

Image description

Functions for Admin Management:

For backend administrators, the node that can be communicated with directly is the management service safeline-mgt, responsible for:

  • Pushing custom configurations to the Tengine gateway and utilizing NGINX commands for reload and hot updates.
  • Custom detection rules (black/white lists, etc.) pushed to the detection engine safeline-detector.
  • Directly reading the PostgreSQL database, returning logs, statistics, current configurations, etc., to backend administrators.

Configuration Files Explanation

.env File

Used to set environment variables referenced in compose.yaml.

echo "SAFELINE_DIR=$(pwd)" >> .env  # Set the current path as the root path of Thunderpool Community Edition.
echo "IMAGE_TAG=latest" >> .env  # Set the image tag.
echo "MGT_PORT=9443" >> .env  # Port used by the management container service.
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env  # Generate a random PostgreSQL password.
echo "SUBNET_PREFIX=172.22.222" >> .env  # Define the subnet prefix for the docker virtual network card.

Enter fullscreen mode Exit fullscreen mode

compose.yml File

Used to start multiple containers.

networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce
services:
  postgres:
    container_name: safeline-postgres
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    cap_drop:
      - net_raw
    command: [postgres, -c, max_connections=200]

  management:
    container_name: safeline-mgt-api
    restart: always
    image: chaitin/safeline-mgt-api:${IMAGE_TAG:?image tag required}
    volumes:
      - ${SAFELINE_DIR?safeline dir required}/resources/management:/resources/management
      - ${SAFELINE_DIR}/resources/nginx:/resources/nginx
      - ${SAFELINE_DIR}/logs:/logs
      - /etc/localtime:/etc/localtime:ro
    ports:
      - ${MGT_PORT:-9443}:1443
    environment:
      - MANAGEMENT_RESOURCES_DIR=/resources/management
      - NGINX_RESOURCES_DIR=/resources/nginx
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@127.0.0.1/safeline-ce
      - MANAGEMENT_LOGS_DIR=/logs/management
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
    cap_drop:
      - net_raw

  detector:
    container_name: safeline-detector
    restart: always
    image: chaitin/safeline-detector:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
    cap_drop:
      - net_raw

  mario:
    container_name: safeline-mario
    restart: always
    image: chaitin/safeline-mario:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/mario:/resources/mario
      - ${SAFELINE_DIR}/logs/mario:/logs/mario
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/mario
      - GOGC=100
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.6
    cap_drop:
      - net_raw

  tengine:
    container_name: safeline-tengine
    restart: always
    image: chaitin/safeline-tengine:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/nginx:/etc/nginx
      - ${SAFELINE_DIR}/resources/management:/resources/management
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/nginx:/var/log/nginx
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
      - /etc/resolv.conf:/etc/resolv.conf
    environment:
      - MGT_ADDR=${SUBNET_PREFIX}.4:9002
    ulimits:
      nofile: 131072
    network_mode: host
Enter fullscreen mode Exit fullscreen mode

Running Logs of Each Service

docker ps  # Check the status of each container
docker logs -f <container_name>  # Output the std logs of the container

Some services’ runtime persistence will also be stored on the disk. The directory structure is as follows:

root@user:/path/to/safeline-ce/logs# tree
.
├── detector
│   └── snserver.log    # Output logs of the detection container
├── management
│   ├── nginx.log     # NGINX log output in the Tengine container
│   └── webserver.log    # Log output of the safeline-mgt-api container
├── mario
│   └── mario.log    # Traffic log output
└── nginx
    ├── error.log  # NGINX error log
    └── tcd.log  # Tcd is the network proxy process used by Tengine to communicate with safeline-mgt-api, this file stores their communication logs.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)