Zum Inhalt springen

How to Get Real IPs Through FRP with SafeLine WAF

If you’re self-hosting SafeLine WAF and exposing internal web services to the internet via FRP, you may notice that all incoming requests appear to come from 127.0.0.1 or your internal proxy server. That’s a problem — you lose visibility of the real attacker IPs.

Starting from version 7.5.0 (released Jan 2, 2025), SafeLine now supports proxy_protocol, which means you can forward real client IP addresses through FRP v2 and have them properly recorded in the SafeLine dashboard.

In this tutorial, we’ll show you how to:

  • Configure FRP client (frpc) to enable proxy_protocol
  • Patch SafeLine’s Nginx config to trust real IP headers
  • Batch-patch all site configs via a script (with whitelist support)
  • Test and verify the results

Scenario Setup

We have the following setup:

  • SafeLine and FRP client are deployed on the same internal server: 192.168.2.103
  • We want to expose multiple HTTPS subdomains via FRP to the outside world
  • Our goal: Make SafeLine log the real external IPs of incoming requests

Step 1: Configure FRP Client (frpc.toml)

In your FRP client config, just add transport.proxyProtocolVersion = "v2" to each HTTPS proxy.

root@safeline:~# cat /etc/frp/frpc.toml
[[proxies]]
name = "web1"
type = "https"
localIP = "192.168.2.103"
localPort = 443
subdomain = "web1"
transport.proxyProtocolVersion = "v2"

[[proxies]]
name = "web2"
type = "https"
localIP = "192.168.2.103"
localPort = 443
subdomain = "web2"
transport.proxyProtocolVersion = "v2"

Save and restart the FRP client.

Step 2: Modify the proxy_params Configuration File

SafeLine’s web UI doesn’t currently expose proxy_protocol settings — but you can enable it manually by editing the Nginx config.

Edit this file:

/data/safeline/resources/nginx/proxy_params

Replace contents with:

# Internal IP ranges
set_real_ip_from 192.168.0.0/16;  # Covers 192.168.0.0 to 192.168.255.255
set_real_ip_from 172.16.0.0/12;   # Covers 172.16.0.0 to 172.31.255.255
set_real_ip_from 10.0.0.0/8;      # Covers 10.0.0.0 to 10.255.255.255

# Public IP address of the FRPS server
set_real_ip_from YOUR.FRPS.SERVER.IP;

real_ip_header proxy_protocol;

# Additional proxy headers
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_hide_header X-Powered-By;

# Add conditional logic to support both proxy_protocol and non-proxy_protocol connections
set $proxy_x_forwarded_for $proxy_add_x_forwarded_for;
set $proxy_x_real_ip $remote_addr;

if ($proxy_protocol_addr) {
    set $proxy_x_forwarded_for "$proxy_protocol_addr, $proxy_x_forwarded_for";
    set $proxy_x_real_ip $proxy_protocol_addr;
}

proxy_set_header X-Forwarded-For $proxy_x_forwarded_for;
proxy_set_header X-Real-IP $proxy_x_real_ip;

Step 3: Create the config-proxy_protocol.sh Script

Create a shell script at:

vim /data/safeline/resources/nginx/sites-enabled/config-proxy_protocol.sh

This script will loop through all SafeLine backend config files (e.g., IF_backend_*) and enable or disable proxy_protocol support for each domain. You can also define a whitelist of subdomains that should be excluded.

We’ll cover the script content next.

Create a script at:

/data/safeline/resources/nginx/sites-enabled/config-proxy_protocol.sh

Script contents:

#!/bin/bash

main_domain="example.com"
whitelist_subdomains=("admin" "monitor")

show_help() {
  echo "Usage: $0 [true|false]"
  echo "  true  - Add proxy_protocol support"
  echo "  false - Remove proxy_protocol support"
  exit 0
}

case "$1" in
  true|false) action=$1 ;;
  ""|"?") show_help ;;
  *) echo "Invalid arg. Use true or false."; exit 1 ;;
esac

modify_config() {
  local file=$1
  local add_proxy=$2
  local changed=false

  while IFS= read -r line; do
    if [[ $line =~ listen.*:443 ]]; then
      if $add_proxy && [[ ! $line =~ proxy_protocol ]]; then
        line="${line/ssl http2;/ssl http2 proxy_protocol;}"
        changed=true
      elif ! $add_proxy && [[ $line =~ proxy_protocol ]]; then
        line="${line/ proxy_protocol/}"
        changed=true
      fi
    fi
    echo "$line"
  done < "$file" > "${file}.tmp"

  if $changed; then
    mv "${file}.tmp" "$file"
    echo "Updated: $file"
  else
    rm "${file}.tmp"
    echo "No change: $file"
  fi
}

for file in IF_backend_*; do
  [ -f "$file" ] || continue
  server_name=$(grep "server_name" "$file" | awk '{print $2}' | tr -d ';n')
  subdomain=${server_name%%.*}
  if [[ " ${whitelist_subdomains[*]} " =~ " $subdomain " ]]; then
    echo "Skipped (whitelisted): $file ($server_name)"
    continue
  fi
  modify_config "$file" $action
done

# Test and reload
echo "Testing Nginx config..."
if docker exec safeline-tengine nginx -t; then
  echo "Reloading..."
  docker exec safeline-tengine nginx -s reload
else
  echo "❌ Config test failed. Not reloading."
  exit 1
fi

Make it executable:

chmod +x config-proxy_protocol.sh

Step 4: Run the Script

You can now enable or disable proxy_protocol across all domains.

Enable proxy_protocol

bash config-proxy_protocol.sh true

Disable proxy_protocol

bash config-proxy_protocol.sh false

Help

bash config-proxy_protocol.sh ?

⚠️ About Nginx Warnings

If some domains are excluded (via whitelist), and others use proxy_protocol on the same IP/port, you may see this warning:

nginx: [warn] protocol options redefined for 0.0.0.0:443 in ...

This just means some listeners have proxy_protocol and others don’t. You can safely ignore this warning. If all domains use proxy_protocol, the warning disappears.

Re-run After Reboot

If you reboot the server or restart SafeLine and the IF_backend_* files get regenerated, simply re-run the script:

bash config-proxy_protocol.sh true

Step 5: Test the Result

Once everything is in place, visit your public subdomain. Go to SafeLine’s dashboard and check the IP attribution map — if you see your real location showing up, it’s working!

Bonus Tips

  • If SafeLine regenerates config on reboot, just re-run the script.
  • Always remember to add your FRP server IP in proxy_params.
  • Use the whitelist array to skip admin, ops, or monitoring domains.

Join the SafeLine Community

If this helped, drop a like, share with your devops/security friends, or leave a comment below. Safe web infra is one config away! 💪

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert