Skip to content

Instantly share code, notes, and snippets.

@MutoMagic
Last active June 18, 2023 09:13
Show Gist options
  • Save MutoMagic/785cfdb5c37fd7561032f1d78c5ca679 to your computer and use it in GitHub Desktop.
Save MutoMagic/785cfdb5c37fd7561032f1d78c5ca679 to your computer and use it in GitHub Desktop.
clash+tproxy(firewalld)+docker

事前准备

  • 局域网 172.16.0.0/16
  • 服务器(ens192) 172.16.0.200
  • docker0 172.17.0.0/16
  • dockerX 172.18.0.0/16

发行版本 CentOS Linux release 7.9.2009 (Core),内核版本 Linux localhost.localdomain 3.10.0-1160.66.1.el7.x86_64。

dockerX 是 docker network 的预留地址,可用 com.docker.network.bridge.name 配置接口名称。

clash 的部分配置会在下面列出,本文的侧重点在 firewalld.direct(iptables),多余的内容就不列出来了。

config.yaml

# Transparent proxy server port for Linux and macOS (Redirect TCP and TProxy UDP)
redir-port: 1082

# Transparent proxy server port for Linux (TProxy TCP and TProxy UDP)
tproxy-port: 1081

# HTTP(S) and SOCKS4(A)/SOCKS5 server on the same port
mixed-port: 1080

# Set to true to allow connections to the local-end server from
# other LAN IP addresses
allow-lan: true

# This is only applicable when `allow-lan` is `true`
# '*': bind all IP addresses
# 192.168.122.11: bind a single IPv4 address
# "[aaaa::a8aa:ff:fe09:57d8]": bind a single IPv6 address
bind-address: '*'

# Clash router working mode
# rule: rule-based packet routing
# global: all packets will be forwarded to a single endpoint
# direct: directly forward the packets to the Internet
mode: Script

# Clash by default prints logs to STDOUT
# info / warning / error / debug / silent
log-level: info

# When set to false, resolver won't translate hostnames to IPv6 addresses
ipv6: true

# RESTful web API listening address
external-controller: :9090

# A relative path to the configuration directory or an absolute path to a
# directory in which you put some static web resource. Clash core will then
# serve it at `http://{{external-controller}}/ui`.
# external-ui: .

# Secret for the RESTful API (optional)
# Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
# ALWAYS set a secret if RESTful API is listening on 0.0.0.0
# secret: ""

# Outbound interface name
interface-name: ens192

# fwmark on Linux only
routing-mark: 6666

experimental:
  # Is true will ignore dns resolve fail, keep matching the rules.
  ignore-resolve-fail: true

  # This will replace the original domain name, but not all sni are domain, so
  # clash will only replace the host is empty and the sni that is a domain
  # If you find any network requests that are not working properly, remove it.
  sniff-tls-sni: false

# Static hosts for DNS server and connection establishment (like /etc/hosts)
#
# Wildcard hostnames are supported (e.g. *.clash.dev, *.foo.*.example.com)
# Non-wildcard domain names have a higher priority than wildcard domain names
# e.g. foo.example.com > *.example.com > .example.com
# P.S. +.foo.com equals to .foo.com and foo.com
hosts:
  # '*.clash.dev': 127.0.0.1
  # '.dev': 127.0.0.1
  # 'alpha.clash.dev': '::1'

profile:
  # Store the `select` results in $HOME/.config/clash/.cache
  # set false If you don't want this behavior
  # when two different configurations have groups with the same name, the selected values are shared
  store-selected: false

  # open tracing exporter API
  tracing: true

  # persistence fakeip
  store-fake-ip: true

# DNS server settings
# This section is optional. When not present, the DNS server will be disabled.
dns:
  enable: true
  listen: 0.0.0.0:53
  ipv6: true # when the false, response to AAAA questions will be empty

  # These nameservers are used to resolve the DNS nameserver hostnames below.
  # Specify IP addresses only
  default-nameserver:
    - 114.114.114.114
    - 1.2.4.8
  enhanced-mode: fake-ip # or redir-host (not recommended)
  fake-ip-range: 198.18.0.1/16 # Fake IP addresses pool CIDR
  use-hosts: true # lookup hosts and return IP record

  # Hostnames in this list will not be resolved with fake IPs
  # i.e. questions to these domain names will always be answered with their
  # real IP addresses
  fake-ip-filter:
    - '*.lan'
    - localhost.ptlogin2.qq.com
    - dns.msftncsi.com
    - www.msftncsi.com
    - www.msftconnecttest.com
    - '+.local'

  # Supports UDP, TCP, DoT, DoH. You can specify the port to connect to.
  # All DNS questions are sent directly to the nameserver, without proxies
  # involved. Clash answers the DNS question with the first result gathered.
  nameserver:
    - 119.29.29.29
    - 223.5.5.5
    - dhcp://ens192 # dns from dhcp

  # When `fallback` is present, the DNS server will send concurrent requests
  # to the servers in this section along with servers in `nameservers`.
  # The answers from fallback servers are used when the GEOIP country
  # is not `CN`.
  fallback:
    - 1.1.1.1 # default value
    - 8.8.8.8 # default value
    - '[2001:4860:4860::8888]:53'
    - '[2606:4700:4700::1111]:53'
    - tls://1.1.1.1:853 # DNS over TLS
    - tls://8.8.8.8:853
    - https://cloudflare-dns.com/dns-query # DNS over HTTPS
    - https://dns.google/dns-query

  # If IP addresses resolved with servers in `nameservers` are in the specified
  # subnets below, they are considered invalid and results from `fallback`
  # servers are used instead.
  #
  # IP address resolved with servers in `nameserver` is used when
  # `fallback-filter.geoip` is true and when GEOIP of the IP address is `CN`.
  #
  # If `fallback-filter.geoip` is false, results from `nameserver` nameservers
  # are always used if not match `fallback-filter.ipcidr`.
  #
  # This is a countermeasure against DNS pollution attacks.
  fallback-filter:
    geoip: true
    geoip-code: CN
    ipcidr:
      - 240.0.0.0/4
    domain:
      - '+.google.com'
      - '+.facebook.com'
      - '+.youtube.com'
  
  # Lookup domains via specific nameservers
  # nameserver-policy:
  #   'www.baidu.com': '114.114.114.114'
  #   '+.internal.crop.com': '10.0.0.1'

防火墙规则

#!/bin/bash

sysctl -w net.ipv4.ip_forward=1

ip rule add fwmark 1 table 100
ip route add local default dev lo table 100

firewall-cmd --permanent --direct --add-chain ipv4 mangle clash
firewall-cmd --permanent --direct --add-chain ipv4 mangle clash_local

firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 0.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 10.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 127.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 169.254.0.0/16 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 172.16.0.0/12 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 192.168.0.0/16 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 224.0.0.0/4 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -d 240.0.0.0/4 -j RETURN
# 以下3条规则在链中的顺序不能变,因为clash无法正确处理回环请求,即便将上面的rules设置为DIRECT。
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -s 172.17.0.0/16 -j MARK --set-mark 2
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -s 172.18.0.0/16 -j MARK --set-mark 2
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 1 -m mark --mark 2 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 2 -p tcp -j TPROXY --on-port 1081 --tproxy-mark 1
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash 2 -p udp -j TPROXY --on-port 1081 --tproxy-mark 1

firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -m mark --mark 6666 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 0.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 10.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 127.0.0.0/8 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 169.254.0.0/16 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 172.16.0.0/12 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 192.168.0.0/16 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 224.0.0.0/4 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 1 -d 240.0.0.0/4 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 2 -p tcp -j MARK --set-mark 1
firewall-cmd --permanent --direct --add-rule ipv4 mangle clash_local 2 -p udp -j MARK --set-mark 1

firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 1 -j clash
firewall-cmd --permanent --direct --add-rule ipv4 mangle OUTPUT 1 -j clash_local

# 必须劫持DNS,以确保所有DNS查询都要经过clash。
# 光配nameserver(/etc/resolv.conf)是不行的,总有漏网之鱼,比如从本机dockerd发出的请求,以及备用DNS发出的请求等。
# DNS同时占用UDP和TCP端口53是公认的。
# 需要跳过clash自身发出的流量,以避免死循环。
firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 1 -p udp --dport 53 -j REDIRECT --to-port 53
firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 1 -p tcp --dport 53 -j REDIRECT --to-port 53
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 1 -p udp --dport 53 -m mark ! --mark 6666 -j REDIRECT --to-port 53
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 1 -p tcp --dport 53 -m mark ! --mark 6666 -j REDIRECT --to-port 53

# 修复ICMP(ping)
# 这并不能保证结果有效(clash不支持ICMP转发),只是让它有响应而已。
sysctl -w net.ipv4.conf.all.route_localnet=1
firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 1 -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 1 -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1

# 与上文mark2对应,可以使用-i docker0替换-s 172.17.0.0/16,由于提前划分了网段,所以这里就用的地址。
# 在桥接模式下,所有容器发出的请求都会从头开始,并非OUTPUT链起步。
# 理论上-j TPROXY是可行的,但现实很骨感,具体原因不明,神奇的是-j REDIRECT就没这个问题。
# REDIRECT不支持UDP,如果有条件的话,建议将Docker分离,都装一起,怕不是有病。
firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 2 -m mark --mark 2 -p tcp -j REDIRECT --to-ports 1082

firewall-cmd --set-default-zone=trusted

firewalld 与 iptables 的关系都烂大街了,这里不再重复。

网上给的 iptables 流程图不一定对,请以官方文档为主。

clash_local 用来处理本机发出的请求,其它主机必须更改网关和DNS才可使用。

内核属性持久化

# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.route_localnet = 1
# sysctl -p

策略路由持久化

问题描述

NetworkManager supports policy-routing, but rules are not supported yet.

解决方案

安装 NetworkManager-config-routing-rules 并启用 NetworkManager-dispatcher 可以修复它:

$ sudo yum install NetworkManager-config-routing-rules
$ sudo systemctl enable NetworkManager-dispatcher.service
$ sudo systemctl start NetworkManager-dispatcher.service

/etc/iproute2/rt_tables:定义路由表名称到数字的映射:

100 clash

/etc/sysconfig/network-scripts/rule-ens192:定义规则,将流量路由到特定的路由表:

from all fwmark 1 lookup clash

/etc/sysconfig/network-scripts/route-ens192:定义默认路由:

local default dev lo table clash

最后别忘记重启网络服务:

$ sudo systemctl restart network.service

相关文献

firewalld

firewall-cmd — firewalld command line client

firewalld.direct — firewalld direct configuration file

iptables

Iptables Tutorial 1.2.2

iptables的基本概念和数据包流程图

Linux网络调试:iptables规则、连接跟踪表、报文跟踪

tproxy

Transparent proxy support

TProxy 探秘

围绕透明代理的又一次探究

Linux使用TPROXY进行UDP的透明代理

clash

DNS污染对Clash(for Windows)的影响

Clash TProxy Mode

树莓派 Clash 透明代理(TProxy)

使用 iptables 的 tproxy 完成本机访问家里内网

docker

使用 TPROXY 透明代理后 docker 无法联网

Setting up a transparent proxy for your Docker containers

Configure Docker to use a proxy server

docker network create

策略路由

Task-centered iproute2 user guide

Chapter 24. Configuring policy-based routing to define alternative routes

How do I create CentOS 7 Persistent Policy-Based-Routing Rules?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment