Artica Wiki

FortiGate — Transparent Proxy via Policy Routing and Artica

Configure a FortiGate firewall and a Artica proxy to transparently intercept HTTP and HTTPS traffic from specific clients, without WCCP — using FortiGate Policy-Based Routing (PBR) to steer traffic to the proxy.

Configure a FortiGate firewall and a Artica proxy to transparently intercept HTTP and HTTPS traffic from specific clients, without WCCP — using FortiGate Policy-Based Routing (PBR) to steer traffic to the proxy.

Traffic Flow

HTTP (port 80)

  1. Client (190) :
    SYN → 93.184.216.34:80 (to FortiGate gateway)
  2. FortiGate
    PBR: gw=192.168.1.189 (policy route matched)
  3. Proxy ens192
    SYN arrives: dst=93.184.216.34:80
  4. mangle PREROUTING TPROXY: 
    mark=0x1, redirect port 35025
  5. ip rule
    fwmark 0x1 → table 100
  6. table 100
    local 0.0.0.0/0 dev lo  (all addresses "local")
  7. Artica Proxy port 35025
    Accepts; socket localaddr=93.184.216.34:80, peer=192.168.1.190
  8. Artica Proxy
    SYN-ACK: src=93.184.216.34:80 → 192.168.1.190:SPORT
  9. Proxy ens192
    SYN-ACK exits toward FortiGate
  10. FortiGate
    asymroute: allows SYN-ACK from internal even if src=internet IP
  11. Client (190)
    Receives SYN-ACK, sends ACK
  12. Proxy ens192
    ACK arrives: dst=93.184.216.34:80
  13. mangle PREROUTING
    socket --transparent: mark=0x1 (existing tproxy socket)
  14. Artica Proxy port 35025
    Receives ACK, connection established
  15. Artica Proxy
    Fetches http://example.com/ and returns response to client

HTTPS (port 443)

Same as HTTP steps 1–14 but with port 35026 (ssl-bump). After the TCP handshake:

15. Artica Proxy ssl-bump
TLS ClientHello arrives; Artica Proxy peeks at SNI

16. Artica Proxy
Generates certificate for target hostname, signed by Artica Proxy CA

17. Artica Proxy
Presents cert to client, establishes TLS with client

18. Artica Proxy
Opens separate TLS connection to real server (93.184.216.34:443)

19. Artica Proxy
Decrypts, inspects, re-encrypts all HTTPS traffic

FortiGate — Policy Routes

Policy routes are evaluated before the routing table and firewall policies. 
They change only the next-hop gateway, not the packet's destination IP.

Enforce client 192.168.1.190/32 to go to Proxy ( but you can add or change this record ). Proxy is defined as gateway ( 192.168.1.189 in our case )

Rule 1 — HTTP ( test with on 1 IP client)

config router policy
      edit 1
              set input-device internal
              set src 192.168.1.190/32
              set protocol 6
              set start-port 80
              set end-port 80
              set gateway 192.168.1.189
              set output-device internal
      next
end

Rule 2 — HTTPS ( test with on 1 IP client)

config router policy
      edit 2
              set input-device internal
              set src 192.168.1.190/32
              set protocol 6
              set start-port 443
              set end-port 443
              set gateway 192.168.1.189
              set output-device internal
      next
end

Proxy looping prevention (required when expanding to a subnet)

When intercepting an entire subnet (e.g., 192.168.1.0/24), the proxy's own outbound connections would loop. 
Add a bypass rule numbered lower than the redirect rules:

config router policy
      edit 1
              set input-device internal
              set src 192.168.1.189/32
              set gateway 10.10.1.254        ← real WAN gateway
              set output-device wan1
      next

      edit 2
              set input-device internal
              set src 192.168.1.0/24 ← Entire /24 network
              set protocol 6
              set start-port 80
              set end-port 443
              set gateway 192.168.1.189← Proxy as gateway
              set output-device internal
      next
end

With a single-host /32 rule (like 192.168.1.190/32), no bypass is needed because 192.168.1.189 (the proxy) does not match 192.168.1.190/32.

FortiGate — Hairpin Firewall Policy

When policy routing redirects traffic from internal back to internal (hairpin), FortiOS requires an explicit firewall policy. Without it, the traffic is silently dropped.

config firewall policy
      edit 15
              set srcintf internal
              set dstintf internal
              set srcaddr "192.168.1.190" or 192.168.1.0/24
              set dstaddr all
              set action accept
              set schedule always
              set service HTTP HTTPS
              set comments "Transparent-proxy-hairpin"
      next
end

Why dstintf internal?

After PBR sets gateway=192.168.1.189, FortiGate resolves 192.168.1.189 to the internal interface. 
The firewall policy lookup uses this resolved egress interface (not the original destination IP).

FortiGate — Asymmetric Routing

This is the critical setting for transparent proxy return paths.

When Artica Proxy sends a SYN-ACK back to the client, the packet source IP is the original server's internet IP (e.g., 93.184.216.34:80), preserved by the TPROXY mechanism. 
FortiGate receives this packet on its internal interface — but by default FortiOS expects a packet from 93.184.216.34 to arrive on the WAN interface (RPF check). 
Without asymroute enable, FortiOS silently drops the SYN-ACK; the client never completes the three-way handshake.

Enable asymmetric routing

config system settings
      set asymroute enable
end

get system settings | grep asymroute

Security note: asymroute enable applies globally to the VDOM. 
If strict RPF is a hard requirement on your FortiGate, use a policy-based narrower solution instead