前几天,终于发现了misskey不兼容阿里云oss的原因了,经过提Issue与PR,终于在12.69.0版本修正了这个历史遗留问题。

这个问题是这样产生的,misskey使用各种各样的对象存储实际上是通过aws-sdk里面的S3来实现的,一般来说各种对象存储应该与aws-sdk相兼容(至少是绝大部分功能),但有少数几个功能是不兼容的,阿里云就禁用了PathStyle这一项。简单来说,pathstyle是在调用对象存储的api时,在构造api的url时,把bucket名称放置到endpoint后面作为一级目录来使用,例如https://oss-cn-hangzhou.aliyun.com/<bucket>/这种形式,而禁用掉PathStyle之后,就只能用https://<bucket>.oss-cn-hangzhou.aliyun.com/这种多级域名的形式来区分不同的存储桶。而misskey为了兼容minio,直接在代码里进行了一个非常“奇葩”的操作。如果没有填写endpoint的url,说明用的是aws的s3,这个时候默认禁用掉PathStyle。如果填写了endpoint,说明用的是minio,需要强制开启PathStyle。这种简单粗暴的做法丝毫没有考虑到非S3且禁用PathStyle的对象存储服务(像阿里云OSS这样的),正确填写各种参数之后就会报错……12.69.0之后加了一个开关,可以手动控制PathStyle了……至于minio不能支持非PathStyle的原因很简单,minio不是nginx,没法在域名层面进行分流(或许可以通过nginx+lua把非PathStyle变成PathStyle?)……

既然问题解决了,minio就没有存在的必要了,在阿里云内网的机器从minio中转过渡到oss直连很容易,改一下endpoint就行了,但是在外部的机器直接使用却有点麻烦(如果你说不差钱,直接用oss的公网域名,那就不用往下看了……)。没了minio之后,还需要有一个东西来中转流量,在阿里云ECS上反向代理阿里云OSS的APi,没了minio,那干这个活的只有nginx了。

nginx天生就是搞反向代理的,与之前设置nginx代理签名私有OSS让外网访问私有文件不同,代理S3API时是不需要对请求进行签名的,对象存储所应用的SDK会自动进行签名的。由于在misskey所使用的aws-sdk的内部机制,其所访问的url实际上是<bucket>+.Endpoint 自动拼接好的url。例如,在endpoint里面填写a.neko.red,bucket名称为dogcraft,实际上最终发出请求的是dogcraft.a.neko.red这个url。所以在配置nginx的server_name与dns解析的时候一定要注意,不然会有错误。

然后配置文件就是这样的

server {
    listen 443 ssl http2;
    ssl_certificate /root/key/dneko.pem;
    ssl_certificate_key /root/key/dneko.key;
    server_name dogcraft.d.dogcraft.top d.dogcraft.top;
    client_max_body_size 512M;
    location / {
        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_set_header X-NginX-Proxy true;
        proxy_set_header Host dogcraft.oss-cn-hangzhou-internal.aliyuncs.com;
        proxy_pass https://dogcraft.oss-cn-hangzhou-internal.aliyuncs.com;
        proxy_redirect off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

然后就会报错,说签名检验失败。原因是这样的,oss等对api的鉴权是通过用api key对Header进行签名的,aws-SDK签名的时候请求头的Host是dogcraft.d.dogcraft.top,而通过nginx进行反向代理之后的Host变成了dogcraft.oss-cn-hangzhou-internal.aliyuncs.com,自然就会通不过签名校验……

解决问题的办法很简单,把nginx里面的Host改成dogcraft.d.dogcraft.top即可,同时在oss的控制台上添加自定义域名即可。

添加自定义域名
添加自定义域名

最终的nginx配置文件

server {
    listen 443 ssl http2;
    ssl_certificate /root/key/dneko.pem;
    ssl_certificate_key /root/key/dneko.key;
    server_name dogcraft.d.dogcraft.top d.dogcraft.top;
    client_max_body_size 512M;
    location / {
        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_set_header X-NginX-Proxy true;
        proxy_set_header Host dogcraft.d.dogcraft.top;
        proxy_pass https://dogcraft.oss-cn-hangzhou-internal.aliyuncs.com;
        proxy_redirect off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}