Skip to content
On this page

As the saying goes, at least one of your body and mind must be on the road. The body is inside the wall, but the mind doesn't have to. So I'll share some simple solutions.

Prepare

Buy a vps that can be connected directly in China, use Ubuntu 20.04 LTS, and install docker, acme.sh, nginx-full.

sh
sudo apt-get update -y

# docker
sudo apt-get install -y \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update -y

sudo apt-get install -y docker-ce docker-ce-cli containerd.io

sudo usermod -aG docker $USER

# acme.sh
sudo apt-get install -y git
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m [email protected]

# nginx-full
sudo apt-get install -y nginx-full

Issue certs with acme.sh

sh
sudo mkdir -p /var/www/acmefiles
sudo chown $USER /var/www/acmefiles

acme.sh --issue -d gost.example.com -w /var/www/acmefiles/gost.example.com

acme.sh \
  --install-cert -d gost.example.com \
  --key-file /etc/nginx/certs/gost.example.com.key \
  --fullchain-file /etc/nginx/certs/gost.example.com.cer \
  --reloadcmd "sudo nginx -s reload && docker restart gost"

Install gost

sh
USER="aaron1984"
PASS="8d1HTRn1uSjynLpbxg4otZEPnd52DqAX"
DOMAIN="gost.example.com"
PORT=4430

BIND_IP=0.0.0.0
CERT_DIR=/etc/nginx/certs
CERT=/certs/${DOMAIN}.crt
KEY=/certs/${DOMAIN}.key

docker run --name gost \
  --net=host \
  -v ${CERT_DIR}:/certs:ro \
  -d --restart=unless-stopped \
  ginuerzh/gost -L "http2://${USER}:${PASS}@${BIND_IP}:${PORT}?cert=${CERT}&key=${KEY}&probe_resist=code:403&knock=www.google.com"

# test
curl -v "https://ip.gs" --proxy "https://${DOMAIN}:4430" --proxy-user "${USER}:${PASS}"

Install shadowsocks

sh
docker run --name ss \
  -p 0.0.0.0:1984:1984 \
  -d --restart=unless-stopped \
  mritd/shadowsocks -s "-s 0.0.0.0 -p 1984 -m chacha20-ietf-poly1305 -k UiGGQvG6TszhLATP --fast-open"

Nginx and gost share 443 port

The server is deployed with nginx for web services and gost for https proxy. Both require port 443. Forwarding directly to gost with proxy_pass in the nginx configuration does not work. Because proxy_pass is forwarded at the layer 7 application layer, gost requires raw tcp traffic. So it needs to be forwarded at the layer 4 transport layer, which fortunately nginx supports.

Nginx provides the ngx_stream_ssl_preread_module module to get the SNI information at layer 4 and then dispatch the traffic. It is not started by default and needs to be started manually.

Nginx provides the ngx_http_realip_module module to hide known proxy IPs and get the real user IPs.

Nginx supports proxy protocol, which insert client IPs and ports into tcp traffic.

Ubuntu 20.04 LTS apt install nginx installs nginx without the above module, so you need to use apt install nginx-full.

update nginx configurations

/etc/nginx/nginx.conf

# traffic forwarding core configuration
stream {
  # Here is the SNI identification, mapping the domain name to a configuration name
  map $ssl_preread_server_name $backend_name {
    gost.example.com proxy_gost;
    default web;
  }

  # The subsequent virtual host configuration should listen to 44300 instead of 443
  upstream web {
    server 127.0.0.1:44300;
  }

  upstream proxy_gost {
    server 127.0.0.1:44301;
  }
  upstream gost {
    server 127.0.0.1:4430;
  }

  # listen to 443 and enable ssl_preread and proxy_protocol
  server {
    listen 443 reuseport;
    listen [::]:443 reuseport;
    proxy_pass $backend_name;
    proxy_protocol on;
    ssl_preread on;
  }

  # The server here is the middle layer used to unload the proxy protocol for gost
  # The original upstream gost configuration does not need to be changed
  server {
    listen 44301 proxy_protocol;
    proxy_pass gost;
  }
}

http {
  # ... unchanged
}

/etc/nginx/conf.d/web.example.com.conf

server {
  listen 44300 ssl http2 proxy_protocol;
  server_name web.example.com;
  ssl_certificate certs/web.example.com.cer;
  ssl_certificate_key certs/web.example.com.key;

  # Hide the IP of the forwarding layer and set remote_addr to the real client IP
  # Because of this traffic forwarding configuration, which is equivalent to opening another tcp link to this 44300 port at layer 4, the client IP becomes the local IP
  # So the proxy_protocol carries the original client ip, and then the following configuration changes the client ip to the real ip instead of the forwarding layer ip.
  # If you don't do this, the following ip restrictions won't work either.
  set_real_ip_from 172.17.0.0/24;
  set_real_ip_from 127.0.0.1;
  real_ip_header proxy_protocol;
  real_ip_recursive on;

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://127.0.0.1:10088;
  }

  access_log logs/web.example.com_access.log;
}

gost/ss behind airport with clash

Considering the price, secrecy, privacy, network speed, etc., the best option is to use ss transit through the airport. The next best option is to use gost directly.

proxy

yaml
proxies:
  - name: "gost"
    type: http
    server: gost.example.com
    port: 443
    username: "aaron1984"
    password: "8d1HTRn1uSjynLpbxg4otZEPnd52DqAX"
    tls: true # https

  - name: "ss"
    type: ss
    server: "IP"
    port: 1984
    cipher: chacha20-ietf-poly1305
    password: "UiGGQvG6TszhLATP"

proxy-providers:
  Airport:
    type: file
    path: ./airport.yaml
    health-check:
      enable: true
      interval: 36000
      url: http://www.gstatic.com/generate_204

proxy-groups:
  - name: "Proxy"
    type: select
    use:
      - Airport
    proxies: ["TestPing", "gost", "airport-ss"]

  - name: "TestPing"
    type: url-test
    use:
      - Airport
    proxies: []
    url: "http://www.gstatic.com/generate_204"
    interval: 300

  - name: "airport-ss"
    type: relay
    proxies:
      - TestPing
      - ss

rules:
  - GEOIP,CN,DIRECT
  - MATCH,Proxy