Part 6 Configuring CrowdSec Trusted IPs for Internal Pangolin Traffic

Part 6: Configuring CrowdSec Trusted IPs for Internal Pangolin Traffic

1. Understanding Trusted IPs Configuration

1.1 Types of Trusted IPs

  • ForwardedHeaders: IPs trusted for X-Forwarded-For headers
  • ClientTrusted: IPs exempt from CrowdSec checks
  • Internal Networks: Private network ranges
  • VPN Networks: Custom VPN subnets

1.2 Default Private Networks

Common private network ranges:

private_networks:
  - "10.0.0.0/8"      # Class A private network
  - "172.16.0.0/12"   # Class B private network
  - "192.168.0.0/16"  # Class C private network
  - "100.64.0.0/10"   # Carrier-grade NAT
  - "fd00::/8"        # Unique local IPv6

2. Traefik Configuration

2.1 Update Dynamic Config

Modify /config/traefik/dynamic_config.yml:

http:
  middlewares:
    crowdsec:
      plugin:
        crowdsec:
          enabled: true
          forwardedHeadersTrustedIPs:
            - "0.0.0.0/0"          # Cloudflare/CDN IPs
          clientTrustedIPs:
            - "10.0.0.0/8"         # Internal networks
            - "172.16.0.0/12"
            - "192.168.0.0/16"
            - "100.89.137.0/20"    # Pangolin VPN network
          trustedForwardXFor: true
          skipAuthPath:
            - "/api/v1/health"
            - "/metrics"
    
    default-whitelist:
      ipWhiteList:
        sourceRange:
          - "10.0.0.0/8"
          - "172.16.0.0/12"
          - "192.168.0.0/16"
          - "100.89.137.0/20"

2.2 Configure Security Headers

Add security headers configuration:

    security-headers:
      headers:
        customResponseHeaders:
          Server: ""
          X-Powered-By: ""
        sslProxyHeaders:
          X-Forwarded-Proto: "https"
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        customFrameOptionsValue: "SAMEORIGIN"
        contentTypeNosniff: true
        forceSTSHeader: true

3. CrowdSec Configuration

3.1 Update Acquisition Configuration

Modify /config/crowdsec/acquis.yaml:

filenames:
  - /var/log/auth.log
  - /var/log/syslog
labels:
  type: syslog
---
filenames:
  - /var/log/traefik/*.log
labels:
  type: traefik
whitelists:
  ips:
    - "10.0.0.0/8"
    - "172.16.0.0/12"
    - "192.168.0.0/16"
    - "100.89.137.0/20"

3.2 Configure Trusted IPs in Profiles

Update /config/crowdsec/profiles.yaml:

name: trusted_ips
filters:
  - Source.IP in ipnets('10.0.0.0/8','172.16.0.0/12','192.168.0.0/16','100.89.137.0/20')
decisions:
  - type: bypass
    duration: 168h
on_success: break

---
name: captcha_remediation
filters:
  - Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http"
  - Source.IP not in ipnets('10.0.0.0/8','172.16.0.0/12','192.168.0.0/16','100.89.137.0/20')
decisions:
  - type: captcha
    duration: 4h
on_success: break

4. Network Security Configuration

4.1 Configure Internal Services

Update docker-compose.yml for internal services:

services:
  pangolin:
    networks:
      - pangolin
    labels:
      - "traefik.http.routers.pangolin.middlewares=default-whitelist@docker"

  gerbil:
    networks:
      - pangolin
    extra_hosts:
      - "pangolin:172.19.0.2"
      - "crowdsec:172.19.0.4"

4.2 Service Discovery Configuration

Create /config/traefik/conf/service-discovery.yaml:

tcp:
  services:
    pangolin:
      loadBalancer:
        servers:
          - address: "172.19.0.2:3001"
    crowdsec:
      loadBalancer:
        servers:
          - address: "172.19.0.4:9090"

5. Testing and Verification

5.1 Test Internal Access

# Test internal network access
docker exec -it gerbil curl http://pangolin:3001/api/v1/health

# Test CrowdSec bypass
docker exec -it crowdsec cscli decisions add -i 10.0.0.1 -t ban -d 1h
curl -H "X-Forwarded-For: 10.0.0.1" https://pangolin.testing.hhf.technology

5.2 Verify IP Configurations

# Check trusted IPs
docker exec -it crowdsec cscli config show | grep -A 10 "trusted_ips"

# Test whitelist functionality
docker exec crowdsec cscli decisions add -i 172.16.0.1 -t ban -d 1h
curl -H "X-Forwarded-For: 172.16.0.1" https://pangolin.testing.hhf.technology

6. Monitoring and Logging

6.1 Configure Logging

Update Traefik logging configuration:

log:
  level: "INFO"
  format: "json"

accessLog:
  filePath: "/var/log/traefik/access.log"
  format: json
  fields:
    headers:
      names:
        X-Forwarded-For: keep
        X-Real-IP: keep

6.2 Monitor Trusted IP Activity

Create monitoring script /config/scripts/monitor-trusted.sh:

#!/bin/bash

echo "=== Monitoring Trusted IP Activity ==="

# Check CrowdSec decisions
echo "Current Decisions:"
docker exec -it crowdsec cscli decisions list

# Check Traefik logs for trusted IPs
echo "Trusted IP Access Logs:"
docker exec -it crowdsec tail -f /var/log/traefik/access.log | grep -E "10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|100\.89\.137\."

7. Troubleshooting

7.1 Common Issues

  1. IP Forward Issues:
# Check X-Forwarded-For headers
docker compose logs traefik | grep "X-Forwarded-For"

# Verify trusted IP configuration
docker exec -it crowdsec cat /etc/crowdsec/config.yaml | grep -A 10 "trusted_ips"
  1. Network Connectivity:
# Test internal network connectivity
docker exec -it gerbil ping pangolin
docker exec -it gerbil ping crowdsec

# Check network configuration
docker network inspect pangolin

7.2 Debug Tools

Create debugging script /config/scripts/debug-trusted.sh:

#!/bin/bash

echo "=== CrowdSec Trusted IP Debug ==="

# Check CrowdSec configuration
docker exec crowdsec cscli config show --debug

# Test internal network connectivity
for service in pangolin crowdsec gerbil; do
    echo "Testing connectivity to $service"
    docker exec -it gerbil ping -c 1 $service
done

# Check current decisions
docker exec crowdsec cscli decisions list

# Display trusted IP configurations
echo "Traefik trusted IPs:"
grep -r "trustedIPs" /config/traefik/

echo "CrowdSec trusted IPs:"
docker exec crowdsec cscli config show | grep -A 10 "trusted_ips"

Next Steps and Best Practices

  1. Regular Maintenance:

    • Review and update trusted IP lists
    • Monitor for suspicious activity from trusted IPs
    • Regular security audits
  2. Security Recommendations:

    • Use specific IP ranges instead of broad network ranges
    • Implement regular IP whitelist reviews
    • Monitor trusted IP access patterns
  3. Documentation:

    • Maintain documentation of trusted IP ranges
    • Document reasons for IP trust
    • Keep change logs for IP modifications

Part 1 Integrating Crowdsec with Pangolin - Networking - HHF Technology Forums
Part 2 Configuring Crowdsec and bouncer integration with pangolin - Networking - HHF Technology Forums
Part 3 Testing and advanced configuration of Crowdsec with Pangolin - Networking - HHF Technology Forums
Part 4 Implementing CrowdSec AppSec WAF with Pangolin - Networking - HHF Technology Forums
Part 5 Implementing CrowdSec Captcha Protection in Pangolin - Networking - HHF Technology Forums

All Files

docker-compose.yml

networks:
  pangolin:
    external: true
services:
  pangolin:
    image: fosrl/pangolin:1.0.0-beta.9
    container_name: pangolin
    restart: unless-stopped
    networks:
      - pangolin
    volumes:
      - ./config:/app/config
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: "3s"
      timeout: "3s"
      retries: 5


  gerbil:
    image: fosrl/gerbil:1.0.0-beta.3
    container_name: gerbil
    restart: unless-stopped
    depends_on:
      pangolin:
        condition: service_healthy
    networks:
      - pangolin
    extra_hosts:
      - "pangolin:172.19.0.2"  # Add host mapping
      - "crowdsec:172.19.0.4"  # Add CrowdSec host mapping
    command:
      - --reachableAt=http://gerbil:3003
      - --generateAndSaveKeyTo=/var/config/key
      - --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
      - --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
    volumes:
      - ./config/:/var/config
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    ports:
      - 51820:51820/udp
      - 443:443 # Port for traefik because of the network_mode
      - 80:80 # Port for traefik because of the network_mode

  # Add CrowdSec services
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    environment:
      GID: "${GID-1000}"
      COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
      ENROLL_INSTANCE_NAME: "pangolin-crowdsec"
      PARSERS: crowdsecurity/whitelists
      ENROLL_KEY: your-key
      ACQUIRE_FILES: "/var/log/traefik/*.log"
      ENROLL_TAGS: docker
    networks:
      - pangolin
    healthcheck:
      test: ["CMD", "cscli", "capi", "status"]
    depends_on:
      - gerbil
    labels:
      - "traefik.enable=false"
    volumes:
      # crowdsec container data
      - ./config/crowdsec:/etc/crowdsec
      - ./config/crowdsec/db:/var/lib/crowdsec/data
      # log bind mounts into crowdsec
      - ./config/crowdsec_logs/auth.log:/var/log/auth.log:ro
      - ./config/crowdsec_logs/syslog:/var/log/syslog:ro
      - ./config/crowdsec_logs:/var/log
      - ./config/traefik/logs:/var/log/traefik
    ports:
      - 9090:9090 # port mapping for local firewall bouncers
      - 6060:6060 # metrics endpoint for prometheus
    expose:
      - 9090 # http api for bouncers
      - 6060 # metrics endpoint for prometheus
      - 7422 # appsec waf endpoint
    restart: unless-stopped
    command: -t # Add test config flag to verify configuration


  traefik:
    image: traefik:v3.1
    container_name: traefik
    restart: unless-stopped

    network_mode: service:gerbil # Ports appear on the gerbil service

    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --configFile=/etc/traefik/traefik_config.yml
    volumes:
      - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
      - ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
      - ./config/traefik/logs:/var/log/traefik
      - ./config/traefik/conf/:/etc/traefik/conf/

traefik_config.yml

api:
  insecure: true
  dashboard: true

providers:
  http:
    endpoint: "http://pangolin:3001/api/v1/traefik-config"
    pollInterval: "5s"
  file:
    filename: "/etc/traefik/dynamic_config.yml"

experimental:
  plugins:
    badger:
      moduleName: "github.com/fosrl/badger"
      version: "v1.0.0-beta.3"
    crowdsec:
      moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
      version: "v1.3.5"
log:
  level: "INFO"
  format: "json"

accessLog:
  filePath: "/var/log/traefik/access.log"
  format: json
  filters:
    statusCodes:
      - "200-299"  # Success codes
      - "400-499"  # Client errors
      - "500-599"  # Server errors
    retryAttempts: true
    minDuration: "100ms"  # Increased to focus on slower requests
  bufferingSize: 100      # Add buffering for better performance
  fields:
    defaultMode: drop     # Start with dropping all fields
    names:
      ClientAddr: keep
      ClientHost: keep
      RequestMethod: keep
      RequestPath: keep
      RequestProtocol: keep
      DownstreamStatus: keep
      DownstreamContentSize: keep
      Duration: keep
      ServiceName: keep
      StartUTC: keep
      TLSVersion: keep
      TLSCipher: keep
      RetryAttempts: keep
    headers:
      defaultMode: drop
      names:
        User-Agent: keep
        X-Real-Ip: keep
        X-Forwarded-For: keep
        X-Forwarded-Proto: keep
        Content-Type: keep
        Authorization: redact  # Redact sensitive information
        Cookie: redact        # Redact sensitive information


certificatesResolvers:
  letsencrypt:
    acme:
      httpChallenge:
        entryPoint: web
      email: "discourse@hhf.technology"
      storage: "/letsencrypt/acme.json"
      caServer: "https://acme-v02.api.letsencrypt.org/directory"

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: "30m"
    http:
      tls:
        certResolver: "letsencrypt"
      middlewares:                                                                   # CHANGE MADE HERE (BOUNCER ENABLED) !!!
        - crowdsec@file

serversTransport:
  insecureSkipVerify: true

dynamic_config.yml

http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https
    default-whitelist:
      ipWhiteList:
        sourceRange:
        - "10.0.0.0/8"
        - "192.168.0.0/16"
        - "172.16.0.0/12"
    # Basic security headers
    security-headers:
      headers:
        customResponseHeaders:
          Server: ""
          X-Powered-By: ""
          X-Forwarded-Proto: "https"
        sslProxyHeaders:
          X-Forwarded-Proto: "https"
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        contentTypeNosniff: true
        customFrameOptionsValue: "SAMEORIGIN"
        referrerPolicy: "strict-origin-when-cross-origin"
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsSeconds: 63072000
        stsPreload: true
    # CrowdSec configuration with proper IP forwarding
    crowdsec:
      plugin:
        crowdsec:
          enabled: true
          logLevel: INFO
          updateIntervalSeconds: 15
          updateMaxFailure: 0
          defaultDecisionSeconds: 15
          httpTimeoutSeconds: 10
          crowdsecMode: live
          crowdsecAppsecEnabled: true
          crowdsecAppsecHost: 172.19.0.4:7422
          crowdsecAppsecFailureBlock: true
          crowdsecAppsecUnreachableBlock: true
          crowdsecLapiKey: "your-api-key"
          crowdsecLapiHost: 172.19.0.4:9090
          crowdsecLapiScheme: http
          captchaProvider: turnstile
          captchaSiteKey: "your-key"
          captchaSecretKey: "your-key"
          captchaGracePeriodSeconds: 1800
          captchaHTMLFilePath: "/etc/traefik/conf/captcha.html"
          forwardedHeadersTrustedIPs:
            - "0.0.0.0/0"                                                              # Cloudflare tunnel IP address
          clientTrustedIPs:
            - "10.0.0.0/8"                                                              # Internal LAN IP addresses
            - "172.16.0.0/12"                                                              # Internal LAN IP addresses
            - "192.168.0.0/16"                                                              # Internal LAN IP addresses
            - "100.89.137.0/20"                                                              # Internal LAN IP addresses  

  routers:
    # HTTP to HTTPS redirect router
    main-app-router-redirect:
      rule: "Host(`pangolin.testing.hhf.technology`)"
      service: next-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https

    # Next.js router (handles everything except API and WebSocket paths)
    next-router:
      rule: "Host(`pangolin.testing.hhf.technology`) && !PathPrefix(`/api/v1`)"
      service: next-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers
      tls:
        certResolver: letsencrypt

    # API router (handles /api/v1 paths)
    api-router:
      rule: "Host(`pangolin.testing.hhf.technology`) && PathPrefix(`/api/v1`)"
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers
      tls:
        certResolver: letsencrypt

    # WebSocket router
    ws-router:
      rule: "Host(`pangolin.testing.hhf.technology`)"
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers
      tls:
        certResolver: letsencrypt

  services:
    next-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3002"  # Next.js server

    api-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3000"  # API/WebSocket server