Advanced Usage · Clash documentation

Rule-Based OpenConnect

Bring an OpenConnect interface into Clash rule-based routing, verify the interface name, routing table, DIRECT policy, and target rules, and prevent the VPN from replacing the default route unexpectedly.

  • OpenConnect
  • vpn-slice
  • systemd
  • interface-name
  • DOMAIN-SUFFIX
Advanced Usage

The following OpenConnect configurations are supported:

  • Cisco AnyConnect SSL VPN
  • Juniper Network Connect
  • Palo Alto Networks (PAN) GlobalProtect SSL VPN
  • Pulse Connect Secure SSL VPN
  • F5 BIG-IP SSL VPN
  • FortiGate SSL VPN
  • Array Networks SSL VPN

For example, suppose your company uses Cisco AnyConnect for internal network access. Here is how to use OpenConnect with policy routing provided by Clash.

First, install vpn-slice. This tool overrides OpenConnect's default routing-table behavior; put simply, it prevents the VPN from replacing your default route.

Next, create a script such as tun0.sh with the following contents:

sh
#!/bin/bash
ANYCONNECT_HOST="vpn.example.com"
ANYCONNECT_USER="john"
ANYCONNECT_PASSWORD="foobar"
ROUTING_TABLE_ID="6667"
TUN_INTERFACE="tun0"

# 如果服务器在中国大陆, 请添加 --no-dtls. 中国大陆的 UDP 会很卡.
echo "$ANYCONNECT_PASSWORD" | \
  openconnect \
    --non-inter \
    --passwd-on-stdin \
    --protocol=anyconnect \
    --interface $TUN_INTERFACE \
    --script "vpn-slice
if [ \"\$reason\" = 'connect' ]; then
  ip rule add from \$INTERNAL_IP4_ADDRESS table $ROUTING_TABLE_ID
  ip route add default dev \$TUNDEV scope link table $ROUTING_TABLE_ID
elif [ \"\$reason\" = 'disconnect' ]; then
  ip rule del from \$INTERNAL_IP4_ADDRESS table $ROUTING_TABLE_ID
  ip route del default dev \$TUNDEV scope link table $ROUTING_TABLE_ID
fi" \
    --user $ANYCONNECT_USER \
    https://$ANYCONNECT_HOST

Next, configure it as a systemd service. Create /etc/systemd/system/tun0.service:

ini
[Unit]
Description=Cisco AnyConnect VPN
After=network-online.target
Conflicts=shutdown.target sleep.target

[Service]
Type=simple
ExecStart=/path/to/tun0.sh
KillSignal=SIGINT
Restart=always
RestartSec=3
StartLimitIntervalSec=0

[Install]
WantedBy=multi-user.target

Next, enable and start the service.

shell
chmod +x /path/to/tun0.sh
systemctl daemon-reload
systemctl enable tun0
systemctl start tun0

Check the logs to confirm that it is running correctly. A simple check is whether the tun0 interface has been created.

As with Wireguard, using a TUN device as an outbound is straightforward: add a policy group.

yaml
proxy-groups:
  - name: Cisco AnyConnect VPN
    type: select
    interface-name: tun0
    proxies:
      - DIRECT

...and it is ready to use.

Add the rules you need:

yaml
rules:
  - DOMAIN-SUFFIX,internal.company.com,Cisco AnyConnect VPN

If you encounter a problem, review the debug-level logs.