tailwind-nextjs-blog/data/blog/build-an-frp-using-xray-acme.sh-docker-compose.md
Ivan Li 97c637f050
All checks were successful
continuous-integration/drone/push Build is passing
feat(blog): 使用 Xray、acme.sh、Docker Compose 搭建内网穿透服务。
2022-09-04 19:37:15 +08:00

272 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 使用 Xray、acme.sh、Docker Compose 搭建内网穿透服务
date: '2022-06-11'
tags: ['xray', 'acme', 'acme.sh', 'docker', 'docker compose', '内网穿透']
draft: false
summary: 为了能在外直接访问家中网络,我组建了三套方案,一是 [[Xray]],二是 [[ZeroTier]],三是 [[NPS]]。今天,我准备在我上个月购入的服务器上再部署一套 Xray 服务,提高可用性。本次准备完全仰仗 Docker 容器,让我未来迁移服务更加省事。
---
## 简介
为了能在外直接访问家中网络,我组建了三套方案,一是 [Xray](/tags/xray),二是 [ZeroTier](/tags/zerotier),三是 [NPS](/tags/nps)。今天,我准备在我上个月购入的服务器上再部署一套 Xray 服务,提高可用性。本次准备完全仰仗 Docker 容器,让我未来迁移服务更加省事。
## 目标与方案
个人自用,成本得控制到零(bushi),安全性还是得做得好些,所以选用 Xray 来承载功能,使用免费的 TLS CA 来签发证书。由于免费的证书一般有效期比较短 (常见的是 90 天),所以还需要实现自动续签。
Let's Encrypt 和 acme.sh 是不错的组合。不过听说 Let's Encrypt 被收购了,不知道是否有安全风险,未来需要再确认下。由于财力并不雄厚,考虑到未来可能服务会”流离失所“,用容器方案比较好迁移。
## 技术栈
- Xray
一款支持加密传输、内网穿透的网络工具。由 GoLang 编写,支持很多平台。
_官方站点[Project X](https://xtls.github.io/)_
- acme.sh
用于签发 TLS 证书。顾名思义,支持 ACME 协议签发、自动续签证书的脚本。
_官方站点[acmesh-official/acme.sh](https://github.com/acmesh-official/acme.sh)_
- Caddy
用于反向代理部署在家里的 Web 服务。它是现代的反向代理服务。
_官方站点[Caddy 2](https://caddyserver.com/v2)_
- Docker Compose
众所周知?
## 搭建步骤
### Docker Compose
首先需要拥有并运行 Docker 和 Docker Compose。
创建一个用于存放配置文件目录,并进入该目录。
创建 Compose 配置文件:
```bash
touch docker-compose.yml
vim docker-compose.yml
```
文件内容:
```yaml
version: '3.9'
networks:
caddy:
name: caddy
xray:
name: xray
volumes:
caddy-data:
name: caddy-data
caddy-config:
name: caddy-config
acme-sh-data:
name: acme-sh-data
services:
caddy:
image: caddy:2
container_name: caddy
restart: always
ports:
- 80:80
- 443:443
networks:
- caddy
volumes:
- $PWD/caddy/Caddyfile:/etc/caddy/Caddyfile
- $PWD/site:/srv
- caddy-data:/data
- caddy-config:/config
xray:
image: teddysun/xray
container_name: xray
restart: always
networks:
- xray
- caddy
ports:
- 3332-3334:3332-3334
volumes:
- ./xray:/etc/xray
- acme-sh-data:/certs
command: "xray -c=/etc/xray/config.yml"
acme.sh:
image: neilpang/acme.sh
container_name: acme.sh
# restart: always
volumes:
- acme-sh-data:/acme.sh
env_file: acme.env
command: "daemon"
```
### 签发证书
使用 DNS Challenge 来签发证书,所以需要 DNS 服务商的 API 来实现自动化签发流程。
以阿里云举例:
1. 创建 RAM 子账户,并只允许访问 API
2. 复制 key 和 secret
3. 为 RAM 子账户授权 DNS 解析的管理权限。
在当前目录创建 `acme.env` 文件:
```zsh
touch acme.env
vim acme.env
```
文件内容:
```zsh
Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
```
启动 compose 服务:
```zsh
docker-compose up -d
```
前面我们启动了刚刚创建的 compose 服务,现在,我们使用 `acme.sh` 容器运行以下命令签发证书:
```zsh
docker exec acme.sh acme.sh --log --issue --dns dns_ali --server letsencrypt -d ivanli.cc -d "*.ivanli.cc"
```
签发成功后你将会在输出末尾看到如下内容:
![签发成功时,程序输出图示](https://notes.ivanli.cc/assets/image_1654257070519_0.png)
注意,签发通配符证书时,需要一次性将所有通配的子域都写在同一条命令上,使用 `-d` 参数追加。
### 配置 Xray
因为前面挂载了 `acme.sh` 的数据卷,所以默认的证书位于 `/certs/ivanli.cc/` 目录下。证书要使用 `fullchain` 的,避免证书链不完整,导致客户端连接验证失败。
创建 Xray 配置文件:
```zsh
mkdir ./xray
vim ./xray/config.yml
```
内容如下:
```yml
inbounds:
# listening for host-name.home
- tag: host-name.home.in
listen: 0.0.0.0
port: 3332
protocol: vless
settings:
clients:
- id: <uuid> # 你的 UUID
flow: xtls-rprx-direct
decryption: none
streamSettings:
network: tcp
security: xtls
xtlsSettings:
serverName: ivanli.cc
alpn:
- http/1.1
certificates:
- certificateFile: /certs/ivanli.cc/fullchain.cer
keyFile: /certs/ivanli.cc/ivanli.cc.key
# reverse ssh to host-name.home
- tag: ssh.host-name.home.in
listen: 0.0.0.0
port: 3334
protocol: dokodemo-door
settings:
network: tcp
address: 127.0.0.1
port: 22
# reverse http to 101.home
- tag: http.host-name.home.in
listen: 0.0.0.0
port: 3333
protocol: dokodemo-door
settings:
network: tcp
address: 127.0.0.1
port: 80
outbounds:
- protocol: freedom
tag: direct
- tag: blocked
protocol: blackhole
reverse:
portals:
- tag: host-name.home.portal
domain: host-name.home.reverse
routing:
- type: field
inboundTag:
- ssh.host-name.home.in
- http.host-name.home.in
outboundTag: host-name.home.portal
- type: field
domain:
- full:host-name.home.reverse
outboundTag: host-name.home.portal
```
配置说明
- `3332` 端口用于客户端连接服务端;
- `3333` 端口用于 HTTP 穿透,映射了 `server:3333 <--> client:80` 端口;
- `3334` 端口用于 SSH 穿透。
- 如果需要连接更多的内网主机和端口,可以继续依葫芦画瓢地加。
### 配置 Caddy
为了让我们的 Web 站点能够公开到互联网,并且增强可控性,没有直接公开 Xray 的端口,而是使用 Caddy 反向代理 Xray 的穿透的本地端口。
创建 Caddy 配置文件:
```zsh
mkdir ./caddy
vim ./caddy/Caddyfile
```
内容如下:
```caddyfile
{
servers {
protocol {
allow_h2c
}
}
admin off
}
any-service.ivanli.cc, another-service.ivanli.cc {
reverse_proxy http://localhost:3333
}
```
端口 `3333` 是 Xray Server 映射家里 HTTP 服务的端口,所以我们这里反向代理服务器上的 3333 端口就好了。
因为 Caddy 会自动从 CA 签发证书,所以这里不需要我们手动配置证书。
配置完成后,重启服务就好
```zsh
docker-compose restart
```
现在,你拥有一个安全的内网穿透服务了~
用户通过 HTTPS 协议访问服务器,服务器通过 TLS 加密连接与内网主机通讯。
TODO 自动重启