自建MinIO配合Cloudflare Tunnel穿透排障

大家好,今天分享一下使用家里云自建 MinIO 配合 CF Tunnel 内网穿透时遇到的问题以及解决方案。

主要的问题有三个:

  • 如何使用 docker compose 的方式搭建 MinIO ?
  • 第一次登录的时候,为何显示 Invalid credentials
  • 为什么无法在 rclone 中使用端点域名连接,只能采用 ip:port 连接?

使用 docker compose 搭建 MinIO

对于一般的用户,我们的 compose.yml 应该是像这样的:

services:
  minio:
    image: minio/minio
    command: server --console-address ":9001"  --address ":9000" /data
    environment:
      MINIO_ROOT_USER: myrootuser
      MINIO_ROOT_PASSWORD: myrootpassword
      # MINIO_SERVER_URL: "https://minio.yourdomain.com" 
      # 这个变量值的指定与否,会影响你是否能成功登录。我们先不指定
      
    volumes:
      - minio-data:/data
    ports:
      - "9000:9000"  # API 端口
      - "9001:9001"  # 控制台端口
    healthcheck:
      test: ["CMD", "curl", "-f", "https://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

volumes:
  minio-data:

然后运行 docker compose up -d 即可。国内的主机记得提前设置好速度快的 docker 镜像源。

内网穿透和首次登录出错

我们在 CF 准备好两个子域名,一个是 minio.yourdomain.com ,另一个是 console.yourdomain.com 。顾名思义,前者是用于 API 端点的,而后者是用于控制台的。

进入 Zero Trust 的 Network -> Tunnels,在这里你可以看到已经配置好内网穿透的家里云主机(内网穿透的教程看我之前的文章)。将 minio.yourdomain.com 指向 http://localhost:9000 , console.yourdomain.com 指向 http://localhost:9001,不出意外的话,打开 https://console.yourdomain.com 即可成功用 compose.yml 中设置的 root 用户名和密码登录控制台创建存储桶了。

但是有一些朋友可能会发现,即使自己使用了完全正确的 root 用户名和密码,MinIO 控制台依然提示 Invalid credentials 。这是为什么呢?

就我个人的经验而言,这是因为在 compose.yml 中配置了 MINIO_SERVER_URL: "https://minio.yourdomain.com" 这一变量,而又没有为 MinIO 容器配置 SSL 证书,导致访问者到 Cloudflare 再到控制台这一段使用的是 https 协议,MinIO 内部(控制台到端点)使用的却是 http 协议。由于 MinIO 预期的是全程 https,所以连接会被拒绝。

解决的方法也很简单,首先生成一个自签名证书,假设放在 ./minio/certs

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./certs/private.key -out ./certs/public.crt -subj "/CN=minio.yourdomain.com"

然后修改 compose.yml,让证书进入容器:

services:
  minio:
    image: minio/minio
    command: server --certs-dir /certs /data
    environment:
      MINIO_ROOT_USER: myrootuser
      MINIO_ROOT_PASSWORD: myrootpassword
      MINIO_SERVER_URL: "https://minio.yourdomain.com"
    volumes:
      - minio-data:/data
      - ./minio/certs:/certs  # 挂载证书目录
    ports:
      - "9000:9000"  # API 端口
      - "9001:9001"  # 控制台端口
    healthcheck:
      test: ["CMD", "curl", "-f", "https://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

volumes:
  minio-data:

这样应该就没问题了。

不过,这个问题可能的成因不止一个(比如这个),以上只是其中之一。

反代端点加 rclone 无法战胜?

提要:是的,确实无法战胜!

我在使用具有公网 ip 的服务器自建 MinIO 并且用 Caddy 反代端点的时候,会出现莫名其妙的 rclone 连接问题,具体表现为:使用同样的一对 Access Key 和 Secret Key,正确进行了 rclone 配置,唯一的变量是连接的端点填写的是 http://ip:porthttps://minio.yourdomain.com 。使用前者可以成功连接,而后者不可以。

rclone 配置如下:

rclone v1.69.1

- os/version: arch 25.0.0 (64 bit)

- os/kernel: 6.12.19-1-MANJARO (x86_64)

- os/type: linux

- os/arch: amd64

- go/version: go1.24.0

- go/linking: static

- go/tags: none

.config/rclone/rclone.conf

[minio]                                                                                                   
type = s3                                                                                                    

provider = Minio                                                                                              

access_key_id = xxx                                                                    

secret_access_key = xxxx                                                  

region = us-east-1                                                                                            

endpoint = https://minio.example.dev                                                                              

disable_http2 = true                                                                                          

force_path_style = true                                                                                       

v4_auth = true                                                                                                

no_check_certificate = true                                                                                                   

no_system_metadata = true

“无法连接” 在我这里的表现是这样的:

[yuki@manjaro ~]$ rclone ls minio: -vv

2025/04/02 22:11:34 DEBUG : rclone: Version "v1.69.1" starting with parameters ["rclone" "ls" "minio:" "-vv"]
2025/04/02 22:11:34 DEBUG : Creating backend with remote "minio:"
2025/04/02 22:11:34 DEBUG : Using config file from "/home/yuki/.config/rclone/rclone.conf"
2025/04/02 22:11:36 DEBUG : 5 go routines active
2025/04/02 22:11:36 NOTICE: Failed to ls: operation error S3: ListBuckets, https response error StatusCode: 403, RequestID: 183285C7252CB364, HostID: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.

起初,我看了一个类似的 GitHub Issue(对不起时间有点久找不到了,找到补上),是使用 Nginx 进行反代的,然后通过加特定的 headers 解决了问题。所以我想肯定是我的 Caddy 功夫还不过关,写的反代有问题导致的。但是在使用 CF 穿透的时候又一次遇到了这个问题,而这一次没有任何 web server 安装在家里云小主机上。

我对此百思不得其解,在电脑前折腾了整整一天一夜,终于……放弃了……

当我坐在马桶前沉思的时候,突然灵光乍现:有没有可能,这不是 MinIO 的问题,不是 Caddy 的问题,不是 CF 的问题,而是 rclone 的问题?

于是我立马使用 MinIO 官方的命令行工具 mc 使用一模一样的 Access Key 和 Secret Key 对,并且用 https://域名 作为端点。令我吃惊的是,这一次我可以成功进行 ls 操作了:

[yuki@manjaro ~]$ mc ls minio    
[2025-04-02 15:42:43 CST]     0B test/      
[yuki@manjaro ~]$ mc ls minio/test                  
[2025-04-02 16:40:17 CST]  33KiB STANDARD <picturename>.jpg

完全正确,没有出现 403 错误。

看来,我的 https 设置并没有问题,问题的确是出在 rclone 上。

在 Deepseek 和 Claude 的帮助下,我对比了两个工具的请求头(完整输出太长,只截取重要部分),已经把 troubleshooting 步骤发在 rclone forum

运行 mc --debug ls one/test 得到的 headers:

X-Amz-Date: 20250402T142607Z  
Authorization: AWS4-HMAC-SHA256 Credential=<accesskey>/20250402/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=xxx  

运行 rclone -vv --dump headers --dump auth ls minio: 得到的:

X-Amz-Date: 20250402T142710Z  
Authorization: AWS4-HMAC-SHA256 Credential=<accesskey>/20250402/us-east-1/s3/aws4_request, SignedHeaders=accept-encoding;amz-sdk-invocation-id;amz-sdk-request;host;x-amz-content-sha256;x-amz-date, Signature=xxx  

可以看到 Authorization 稍有不同,这是因为 rclone 默认为所有 S3 兼容存储添加了以下 headers,而这并不是 MinIO 期望的: accept-encoding;amz-sdk-invocation-id;amz-sdk-request;

类似的问题发生在 Exoscale 对象存储上

Rclone uses the official AWS SDK so I'd guess that this is a bug in exoscale.

问题的原因找到了,解决的方法嘛,那就是……还没有。rclone 暂时没有提供在配置文件里修改请求头的方法(也可能有,但是我没有成功),所以只能先用其它工具了。

自建MinIO配合Cloudflare Tunnel穿透排障
https://obsp.de/index.php/archives/minio-cloudflare-tunnel-troubleshooting.html
本文作者 神木友希
发布时间 2025-04-07
许可协议 CC BY-NC-SA 4.0
发表新评论