Docker Compose

docker 容器内部网络的 yaml 文件:

version: "3.5"
services:

  alpine-network:
    image: alpine:3.17.0
    container_name: alpine-network
    restart: always
    tty: true
    networks:
      - intranet

networks:
  intranet:
      driver: bridge

clash 及可视化界面的 yaml 文件:

version: "3.5"
services:

  clash: # 这个是 clash 的本体
    image: dreamacro/clash
    container_name: clash
    restart: always
    volumes: # 这个目录是一定要映射的,在目录下创建 config.yaml 文件。
        - 本地的 clash 配置目录:/root/.config/clash
    ports:
        - 7890:7890 # 默认 http 和 sock5 代理端口都是 7890,也就是混合代理端口
        - 9090:9090 # api 控制端口,上面的是做代理转发,这个是控制 clash 隧道的选择以及其他的一些配置
    networks:
      - network_intranet

  clash-web: # 这个是 clash 的可视化界面
    image: haishanh/yacd
    restart: always
    container_name: clash-web
    depends_on: # 必须依赖 clash
        - clash
    networks:
      - network_intranet

networks:
  network_intranet:
    external: true

nginxyaml 文件:

services:

  nginx:
    image: nginx:1.25
    container_name: nginx
    restart: always
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /service/proxy/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /service/proxy/nginx/conf.d:/etc/nginx/conf.d
      - /service/proxy/nginx/tcp.d:/etc/nginx/tcp.d
      - /service/proxy/nginx/vhost.d:/etc/nginx/vhost.d
      - /service/proxy/nginx/logs:/var/log/nginx
      - /service/proxy/nginx/html:/usr/share/nginx/html
      - /service/proxy/nginx/certs:/etc/nginx/certs:ro
      - /service/kodcloud/static/www/html:/var/www/html
      - /service/kodcloud/static/run:/var/run
    networks:
      - network_intranet

networks:
  network_intranet:
    external: true

nginx 代理配置

nginx 代理 clash 的配置:

upstream clash {
    server clash:9090;
}

server {
    listen 443 ssl;
    server_name 你的域名;

    ssl_certificate pem 文件;
    ssl_certificate_key key 文件;
    ssl_session_timeout 5m;

    rewrite ^(.*)$  http://$host$1 permanent;
}

server {
    listen  80; 
    server_name 你的域名;

    location / {
        proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass http://clash;
    }
} 

nginx 代理 clash 可视化界面的配置:

upstream clash-web {
    server clash-web:80;
}

server {
    listen 443 ssl;
    server_name 你的域名;

    ssl_certificate pem 文件;
    ssl_certificate_key key 文件;
    ssl_session_timeout 5m;

    rewrite ^(.*)$  http://$host$1 permanent;
}

server {
    listen  80; 
    server_name 你的域名;

    location / {
        proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass http://clash-web;
    }
} 

clash 代理配置示例

这个配置文件,去本地的 clash 配置目录中可以直接找到。

下面是一个示例的配置文件:

# port of HTTP
port: 7890

# port of SOCKS5
socks-port: 7891

# redir port for Linux and macOS
# redir-port: 7892

allow-lan: false

# Only applicable when setting allow-lan to 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: "*"

# rule / global / direct (default is rule)
mode: rule

# set log level to stdout (default is info)
# info / warning / error / debug / silent
log-level: info

# RESTful API for clash
external-controller: 127.0.0.1:9090

# you can put the static web resource (such as clash-dashboard) to a directory, and clash would serve in `${API}/ui`
# input is a relative path to the configuration directory or an absolute path
# external-ui: folder

# Secret for RESTful API (Optional)
# secret: ""

# experimental feature
experimental:
  ignore-resolve-fail: true # ignore dns resolve fail, default value is true
  # interface-name: en0 # outbound interface name

# authentication of local SOCKS5/HTTP(S) server
# authentication:
#  - "user1:pass1"
#  - "user2:pass2"

# # hosts, support wildcard (e.g. *.clash.dev Even *.foo.*.example.com)
# # static domain has a higher priority than wildcard domain (foo.example.com > *.example.com > .example.com)
# # +.foo.com equal .foo.com and foo.com
# hosts:
#   '*.clash.dev': 127.0.0.1
#   '.dev': 127.0.0.1
#   'alpha.clash.dev': '::1'
#   '+.foo.dev': 127.0.0.1

# dns:
  # enable: true # set true to enable dns (default is false)
  # ipv6: false # default is false
  # listen: 0.0.0.0:53
  # # default-nameserver: # resolve dns nameserver host, should fill pure IP
  # #   - 114.114.114.114
  # #   - 8.8.8.8
  # enhanced-mode: redir-host # or fake-ip
  # # fake-ip-range: 198.18.0.1/16 # if you don't know what it is, don't change it
  # fake-ip-filter: # fake ip white domain list
  #   - '*.lan'
  #   - localhost.ptlogin2.qq.com
  # nameserver:
  #   - 114.114.114.114
  #   - tls://dns.rubyfish.cn:853 # dns over tls
  #   - https://1.1.1.1/dns-query # dns over https
  # fallback: # concurrent request with nameserver, fallback used when GEOIP country isn't CN
  #   - tcp://1.1.1.1
  # fallback-filter:
  #   geoip: true # default
  #   ipcidr: # ips in these subnets will be considered polluted
  #     - 240.0.0.0/4

proxies:
  # shadowsocks
  # The supported ciphers(encrypt methods):
  #   aes-128-gcm aes-192-gcm aes-256-gcm
  #   aes-128-cfb aes-192-cfb aes-256-cfb
  #   aes-128-ctr aes-192-ctr aes-256-ctr
  #   rc4-md5 chacha20-ietf xchacha20
  #   chacha20-ietf-poly1305 xchacha20-ietf-poly1305
  - name: "ss1"
    type: ss
    server: server
    port: 443
    cipher: chacha20-ietf-poly1305
    password: "password"
    # udp: true

  # old obfs configuration format remove after prerelease
  - name: "ss2"
    type: ss
    server: server
    port: 443
    cipher: chacha20-ietf-poly1305
    password: "password"
    plugin: obfs
    plugin-opts:
      mode: tls # or http
      # host: bing.com

  - name: "ss3"
    type: ss
    server: server
    port: 443
    cipher: chacha20-ietf-poly1305
    password: "password"
    plugin: v2ray-plugin
    plugin-opts:
      mode: websocket # no QUIC now
      # tls: true # wss
      # skip-cert-verify: true
      # host: bing.com
      # path: "/"
      # mux: true
      # headers:
      #   custom: value

  # vmess
  # cipher support auto/aes-128-gcm/chacha20-poly1305/none
  - name: "vmess"
    type: vmess
    server: server
    port: 443
    uuid: uuid
    alterId: 32
    cipher: auto
    # udp: true
    # tls: true
    # skip-cert-verify: true
    # servername: example.com # priority over wss host
    # network: ws
    # ws-path: /path
    # ws-headers:
    #   Host: v2ray.com
  
  - name: "vmess-http"
    type: vmess
    server: server
    port: 443
    uuid: uuid
    alterId: 32
    cipher: auto
    # udp: true
    # network: http
    # http-opts:
    #   # method: "GET"
    #   # path:
    #   #   - '/'
    #   #   - '/video'
    #   # headers:
    #   #   Connection:
    #   #     - keep-alive

  # socks5
  - name: "socks"
    type: socks5
    server: server
    port: 443
    # username: username
    # password: password
    # tls: true
    # skip-cert-verify: true
    # udp: true

  # http
  - name: "http"
    type: http
    server: server
    port: 443
    # username: username
    # password: password
    # tls: true # https
    # skip-cert-verify: true

  # snell
  - name: "snell"
    type: snell
    server: server
    port: 44046
    psk: yourpsk
    # obfs-opts:
      # mode: http # or tls
      # host: bing.com

  # trojan
  - name: "trojan"
    type: trojan
    server: server
    port: 443
    password: yourpsk
    # udp: true
    # sni: example.com # aka server name
    # alpn:
    #   - h2
    #   - http/1.1
    # skip-cert-verify: true

proxy-groups:
  # relay chains the proxies. proxies shall not contain a relay. No UDP support.
  # Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
  - name: "relay"
    type: relay
    proxies:
      - http
      - vmess
      - ss1
      - ss2

  # url-test select which proxy will be used by benchmarking speed to a URL.
  - name: "auto"
    type: url-test
    proxies:
      - ss1
      - ss2
      - vmess1
    # tolerance: 150
    url: 'http://www.gstatic.com/generate_204'
    interval: 300

  # fallback select an available policy by priority. The availability is tested by accessing an URL, just like an auto url-test group.
  - name: "fallback-auto"
    type: fallback
    proxies:
      - ss1
      - ss2
      - vmess1
    url: 'http://www.gstatic.com/generate_204'
    interval: 300

  # load-balance: The request of the same eTLD will be dial on the same proxy.
  - name: "load-balance"
    type: load-balance
    proxies:
      - ss1
      - ss2
      - vmess1
    url: 'http://www.gstatic.com/generate_204'
    interval: 300

  # select is used for selecting proxy or proxy group
  # you can use RESTful API to switch proxy, is recommended for use in GUI.
  - name: Proxy
    type: select
    proxies:
      - ss1
      - ss2
      - vmess1
      - auto
  
  - name: UseProvider
    type: select
    use:
      - provider1
    proxies:
      - Proxy
      - DIRECT

proxy-providers:
  provider1:
    type: http
    url: "url"
    interval: 3600
    path: ./hk.yaml
    health-check:
      enable: true
      interval: 600
      url: http://www.gstatic.com/generate_204
  test:
    type: file
    path: /test.yaml
    health-check:
      enable: true
      interval: 36000
      url: http://www.gstatic.com/generate_204

rules:
  - DOMAIN-SUFFIX,google.com,auto
  - DOMAIN-KEYWORD,google,auto
  - DOMAIN,google.com,auto
  - DOMAIN-SUFFIX,ad.com,REJECT
  # rename SOURCE-IP-CIDR and would remove after prerelease
  - SRC-IP-CIDR,192.168.1.201/32,DIRECT
  # optional param "no-resolve" for IP rules (GEOIP IP-CIDR)
  - IP-CIDR,127.0.0.0/8,DIRECT
  - GEOIP,CN,DIRECT
  - DST-PORT,80,DIRECT
  - SRC-PORT,7777,DIRECT
  # FINAL would remove after prerelease
  # you also can use `FINAL,Proxy` or `FINAL,,Proxy` now
  - MATCH,auto

主要需要对几个地方做调整:

mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
external-controller: "0.0.0.0:9090"
secret: "sfasdfsdafashfjkasjfhajssdfds353"

mixed-port:是混合代理端口,这个不需要动。

bind-address 调整为 '*',然后 external-controller 更改为 0.0.0.0:9000,这块主要是为了方便可视化界面调用 clash 控制 api

然后 secretclash 的控制 api 鉴权用的,最好也设置复杂一点。

因为 clash 的控制 api 是要暴露在公网的,如果是 127.0.0.1,公网是无法访问的。而暴露在公网,别人是可以扫到这个 api 的,为了避免不那么美好的事情,所以最好加一个 secret 来做鉴权。

部署流程

首先将三个 Docker Compose 文件创建复制好,然后将 nginxclash 的配置文件也创建复制好。

clash 的配置文件需要放在 clashDocker Compose 文件中指定的 本地的 clash 配置目录 下,文件名为 config.yaml

然后依次启动就好了。

问题

一、clash 启动失败,日志提示:can't initial MMDB: can't download MMDB

这个情况是因为下载 Country.mmdb 文件失败,可以本地下载好,再传给服务器。

下载地址:https://cdn.jsdelivr.net/gh/Dreamacro/maxmind-geoip@release/Country.mmdb

把这个文件放到,服务器上 clash 挂载的配置目录下即可,也就是和 clash 的配置文件在一个目录下。

文件名一定要是 Country.mmdb

二、clash api 访问无反应

如果公网访问 clash api 地址无反应,可以试试容器内请求是否正常,以及看看日志。

如果容器内请求正常,就看下 9090 这个端口有没有通过 nginx 代理出去。如果是直接映射的,就看看服务器的 9090 端口是否被占用?是否放行?

三、clash 可视化界面登录无反应

直接使用 clash 可视化界面登录有概率会有问题,建议使用 url query 的方式传参登录。

也就是浏览器直接访问 http://你的域名/?hostname=api的地址&port=api的端口&secret=api的密钥

1736861400818.png

四、clash 可视化界面还是进不去

可以打开网络控制台看下,是不是有个 config 的请求被拦截了,类似下图:

1736861491448.png

这个是因为浏览器尝试通过 HTTPS 加载一个 HTTP 资源,然后被安全拦截了。

可能原因是:可视化界面可是通过 HTTPS 访问的,而它向 clashapi 发起的是 HTTP 请求。

最好的方法是都使用 http 访问,上面的 nginx 就做了 https 自动跳转 http 的处理。

另外,我做这个跳转处理还有一个原因是因为,我使用的是通配符 ssl 证书,配置文件都是模版生成的,所以稍微调整下就好了。

测试

执行 curl -x http://ip:7890 https://www.google.com -o google.html,然后双击在浏览器打开 google.html,会显示下图:

1736862229079.png

如果不使用代理,则会报错:curl: (28) Failed to connect to www.google.com port 443 after 75002 ms: Couldn't connect to server