静看光阴荏苒
不管不顾不问不说也不念

利用mihomo实现多容器无感透明代理

我经常遇到在服务器上部署的容器需要用到代理的情况,给容器配置代理,最常见的是使用HTTP_PROXY环境变量,但是这个变量并不是所有App都认的,有些跑在容器里面的App根本不鸟这个变量,这时候就有点蛋疼了。

今天分享一个利用mihomo让容器全部无感透明代理的思路,这套配置简洁、优雅、易于使用,且适用所有容器内的App,不管容器内跑的是Java还是Node.js亦或者其它的什么东西,都能够直接连上mihomo的节点实现代理访问互联网。

其实sing-box也可以做到同样的效果,只要代理软件支持TUN,且具备自动接管路由表的能力,都可以用这种配置方式,只是我不想搓sing-box的json配置文件,太麻烦了,想比较之下mihomo的yaml配置文件搓起来简单点。。

新建compose文件:

mkdir /opt/mihomo && cd /opt/mihomo && nano docker-compose.yml

写入如下内容:

services:
  mihomo:
    image: metacubex/mihomo:latest
    container_name: mihomo
    restart: always
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./mihomo_config:/root/.config/mihomo
    ports:
      - "19090:9090" # 外部控制面板(可选)
      - "8080:8080" # 如果应用容器有自己的端口要暴露,端口必须映射在这里

  # 你的实际应用容器,这里的容器仅为演示代理效果
  my_app:
    image: nicolaka/netshoot:latest
    container_name: my_app
    network_mode: "service:mihomo" # 共享mihomo代理容器的网络栈
    depends_on:
      - mihomo
    command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]

这套配置的核心就这一行:network_mode: "service:mihomo",让我简单解释一下其作用。在Docker中,默认情况下每个容器都有自己独立的“网络空间”(Network Namespace),里面有独立的网卡、路由表和iptables规则。

但是当你配置network_mode: "service:mihomo"时,等于把应用容器(App)强行塞进了代理容器(Proxy)的“网络空间”里。此时:它们共享同一张虚拟网卡,共享同一套路由表。

当代理软件在容器内开启TUN模式时,它会在这个“网络空间”创建一个虚拟网卡,并把默认路由指向虚拟网卡。由于应用容器也在这个“网络空间”里,所以理所当然它的流量也会被路由表直接导向虚拟网卡。

在这个模式下,你可能会遇到一些“坑点”,这里我也简单说一下。

1.端口映射(Ports)必须声明在“代理容器”上:一旦配置了network_mode: "service:mihomo",你的应用容器(本文示例:my_app)就已经失去了独立网络主权,它无法再声明自己的端口。假设你的应用是一个Web服务(比如NGINX监听8080端口),你必须把-"8080:8080"写在mihomo容器的ports列表里。

2.容器间通信名字变了:一旦容器加入了代理的“网络空间”,它就失去了在Docker默认网桥上的“独立姓名”,容器间通过服务名(如nginx、postgres)互相寻址的机制就会失效。在默认情况下,其他Docker容器可以通过http://my_app:8080访问该应用,但现在my_app在Docker默认网络中相当于“隐身”了。你应该将所有的内部互访全部改写为127.0.0.1(本地回环)例如对于本文的示例而言,应修改为:http://127.0.0.1:8080

其实我之前有一篇文章介绍过GlueTun这个项目,其采用的是与本文一样的方式,只是这个GlueTun不太适合在中国内地使用,它支持的几乎全部都是收费的VPN,对于我们平时常用的代理协议是全部都不支持。

那么现在让我们继续对mihomo进行配置,新建一个目录用于存放mihomo的配置文件和其它资源(如:控制面板文件、rule-set文件)

mkdir mihomo_config

新建mihomo的配置文件:

nano mihomo_config/config.yaml

这是我的示例配置:

mixed-port: 7890
allow-lan: true
tcp-concurrent: true
find-process-mode: strict
mode: rule
log-level: info
ipv6: false
keep-alive-interval: 30
unified-delay: true

profile:
  store-selected: true
  store-fake-ip: false

external-controller: 0.0.0.0:9090
external-controller-cors:
  allow-origins:
    - '*'
  allow-private-network: true
secret: "89641937"             
external-ui: "./ui"                      
external-ui-name: zashboard
external-ui-url: "https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages.zip"

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-redirect: false
  auto-detect-interface: true
  dns-hijack:
    - any:53
  strict-route: true
  mtu: 1500

dns:
  enable: true
  cache-algorithm: arc
  prefer-h3: false
  use-hosts: true
  use-system-hosts: true
  listen: 127.0.0.1:6868
  ipv6: false
  enhanced-mode: redir-host
  default-nameserver:
    - 8.8.8.8
    - 1.1.1.1
  nameserver:
    - https://cloudflare-dns.com/dns-query
    - https://dns.google/dns-query
  proxy-server-nameserver:
    - https://cloudflare-dns.com/dns-query
    - https://dns.google/dns-query
  direct-nameserver:
    - https://dns.google/dns-query
    - https://cloudflare-dns.com/dns-query
  respect-rules: true

sniffer:
  enable: true
  force-dns-mapping: true
  parse-pure-ip: true
  sniff:
    HTTP:
      ports:
        - 80
        - 8080-8880
      override-destination: true
    TLS:
      ports:
        - 443
        - 8443

proxies:
  - name: proxy1
    type: anytls
    server: 8.9.6.4
    port: 8443
    password: "hidden"
    client-fingerprint: chrome
    udp: true
    idle-session-check-interval: 30
    idle-session-timeout: 30
    min-idle-session: 5
    sni: "anytls.example.com"
    alpn:
      - h2
    skip-cert-verify: false

proxy-groups:
  - name: PROXY
    icon: https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Hijacking.png
    type: select
    proxies:
      - proxy1

rule-providers:
  geosite-youtube:
    type: http
    behavior: domain
    format: mrs
    url: https://github.com/MetaCubeX/meta-rules-dat/raw/meta/geo/geosite/youtube.mrs
    path: ./rule-sets/youtube.mrs
    interval: 86400
    proxy: proxy1

rules:
  - DOMAIN-SUFFIX,ipinfo.io,PROXY
  - RULE-SET,geosite-youtube,PROXY
  - MATCH,DIRECT

该配置并非最佳实践,里面包含一些无用或者多余的配置项,但并不会影响实际使用。此配置是根据我的这篇文章随便改了下而来的,又不是不能用。

在rules这块,兜底走的是MATCH,DIRECT(直连)而非MATCH,PROXY(代理)是因为我将其部署在境外的服务器上,境外本来就没有GFW这种垃圾东西(伊朗和俄罗斯这种国家除外),自然没有必要将所有流量都走代理,能直连就尽量直连。我更多的是为了解决服务器自身IP太脏,被Youtube等流媒体平台拉黑的问题,当然具体怎么使用取决于你自己的网络环境。

启动:

docker compose up -d

测试一下应用容器内是否成功走了代理:

补充一点内容,本文提供的mihomo配置文件内启用了zashboard控制面板:

http://serverip:19090/ui/zashboard/ 
密码:89641937

如果要使用这个控制面板,请根据如图所示进行配置(修改主机的IP地址为你的服务器公网IP,端口19090):

赞(0)
未经允许不得转载:荒岛 » 利用mihomo实现多容器无感透明代理
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

分享创造快乐

广告合作资源投稿