解决 Cloudreve 无法通过 HTTPS 连接自签名证书 MinIO

这是一次真实事件案例改编。用户A在部署 Cloudreve 存储策略时,集成了用户内网环境下的 MinIO S3 服务。但是当用户上传资料时发现Cloudreve无法工作,无法将资源上传到MinIO中。 我也是第一次接触Cloudreve,经过搜索发现Cloudreve是一个自托管文件管理与共享系统,支持多存储提供商。那么现在的问题就变成了Cloudreve如何配置使用了自定义证书的S3服务Minio。 由于用户的Minio服务使用了DDNS映射到公网,我首先想到的是,使用可以通过mc访问到用户的Minio服务。经过初步排查: 确认内网穿透链路正常。 使用 MinIO 客户端工具 mc 测试,发现必须带上 –insecure 参数才能正常访问,明确了核心矛盾在于 SSL 证书验证失败。 尝试将证书放置在自定义目录并使用 mc –config-dir 指定,可以连接成功,但 Cloudreve 还是无法正常工作,无法直接复用该配置,且即便在同目录下放置证书也未能生效。 经过搜索和分析,知道需要将自定义 CA 证书导入系统信任库,才能使 Cloudreve(Go 环境)能够原生识别。那么现在的问题就变成了如何在 CentOS 系统中安装并激活自定义证书? 经过一番折腾摸索,终于搞定,这里记录一下历程。 1,检查证书是否PEM格式? Go 程序通常识别PEM格式的证书。所以需要先检查自定义证书是否是PEM格式?可以使用以下命令检查证书文件(如public.crt): cat public.crt 如果开头是 —–BEGIN CERTIFICATE—–,则是 PEM 格式。 如果是一堆乱码,则是 DER 格式。 如果是DER格式,需要转换为PEM格式,转换的命令如下: openssl x509 -inform der -in public.crt -out public.pem 2,放置证书 在CentOS Linux中,需要将证书文件放在/etc/pki/ca-trust/source/anchors/目录下,并且后缀必须为.crt。如果文件名为public.pem,需要改为public.crt。这是血的经验和教训。 首先确认文件是否放置正确? ls /etc/pki/ca-trust/source/anchors/ 输出中应包含我们的证书,如果没有,需要重新放置。 3,激活证书 当放置好证书后,必须进行激活,否则系统不认。可以执行以下命令激活,让系统重新扫描目录并生成全局信任束,依次执行: update-ca-trust extract update-ca-trust 4,验证证书是否激活成功 我们需要确认是否激活成功?确认的方法就是检查系统全局证书合集ca-bundle.crt是否已包含你的证书信息: tail -n 20 /etc/pki/tls/certs/ca-bundle.crt 可以查看最近的20行中是否包含自己的证书,如果还没有成功,则执行命令追加: cat public.crt | sudo tee -a /etc/pki/tls/certs/ca-bundle.crt 最后,使用 grep 再次搜索证书中关键的信息 ...

2026-03-11 · 1 min · Eagle

记一次 Windows DLL 动态链接库并供第三方多语言调用

我使用go写了一个http转WebSocket服务,这是一个命令行程序,双击可以直接运行,今天,有个新的需求,需要将这个命令行程序转为dll,然后供第三方使用。 1,改造程序 这个命令行程序很小,核心逻辑就是启动了一个HTTP服务,然后接收连接,然后通过WebSocket将内容转发出去。所以,在调整为dll时,仅仅需要导出两个函数即可。 1,StartServer,启动http服务,监听端口地址。因为不能阻塞,所以需要在监听服务时使用go方法启动一个携程。 2,StopServer,关闭http服务,需要提供给第三方,当它关闭时,需要调用它,释放监听地址等资源。 下面开始改造代码: package main import "C" // 必须导入 C 包 import ( // ... 原有导入 ... ) // 保持原有的全局变量和函数逻辑 (例如transDataGet, connWs 等) // 需要注意下面的//export这中间不能有空格,否则无法导出头文件 //export StartServer func StartServer() { // 将原本 main 函数里的逻辑放在这里 gin.SetMode(gin.ReleaseMode) r := gin.New() // ... 注册路由 ... r.Run("127.0.0.1:9988") } // 必须保留一个空的 main 函数 func main() {} // 其它参考StartServer,如果需要导出,一定要添加//export 2,编译命令 我是在windows环境,需要安装cgo环境,我安装了Mingw-w64支持C编译环境,执行以下命令: go build -buildmode=c-shared -o trans.dll main.go 执行后会生成trans.h和trans.dll两个文件。 3,验证dll 除了一些可以查看dll的工具外,还可以使用第三方语言进行测试。例如我这里使用Python。 import ctypes import time lib = ctypes.CDLL("./trans.dll") lib.StartServer() print("服务已在后台启动...") time.sleep(10) # 模拟第三方程序运行 lib.StopServer() print("服务已关闭") 4,再次优化代码 当实际测试的时候,我发现它会阻塞调用者,为了解决这个问题,我们需要再次改造程序。将启动服务改为非阻塞模式。将Gin的启动逻辑放入协程,并利用http.Server提供的Shutdown方法来实现优雅退出。 ...

2026-01-29 · 5 min · Eagle

记一次因直接使用unwrap导致Rust上传工具崩溃问题

最近,更新了上传文章工具 rust 版本,这个版本使用的库为 nwg,即 native-windows-gui 库,该库支持老的 Windows GUI。 昨天,当我准备在另一台电脑上录制视频时,发现运行不起来,GUI 界面闪现一下,就退出了。以为版本太老,我先升级了 Rust 版本到最新版本(😭,不应该的原因,固定版本反而更可靠稳定一些)。 rustup update 升级完成后,cargo clean ,再 cargo run 重新运行。 郁闷,还是报错。 将 main.rs 文件中该行注释,#![windows_subsystem = "windows"],可以在命令行中查看报错信息,提示是找不到文件。我想了下,只有图片是文件,然后我将图片的路径修改,发现修改之后,编辑器还提示错误,找不到文件。重新改回去后,编辑器提示错误消失。说明不是这里的错误。 我真的晕了。从网上查找问题原因,没有找到此类问题的解决方法,郁闷。在查找时,发现了另一个 Windows 官方支持的 rust 库,就叫 windows,打算有时间了用这个库重写一下。 今天,我又查看了下另一台电脑上的这个项目,发现可以正常运行,将 rust 升级到最新版本后,还是可以运行。在看到配置文件后,我才恍然大悟,原来是查找的是这个配置文件。我赶紧扒拉代码确认,发现确实是这个问题。 let conf = Ini::load_from_file("conf.ini").unwrap(); 上面是使用文件的地方,找不到文件 panic。我这里优化了一下,让错误提醒的更明显一些。这样我就瞬间能知道问题原因了。 优化后的代码如下: let conf = Ini::load_from_file("conf.ini").expect("please config conf.ini file"); 有些问题其实很简单,但由于时间长了,还是会一头懵,如果报错信息提示完善一点,会很快定位到问题。

2024-01-01 · 1 min · Eagle

构建高可靠 APK 上传工具,彻底终结网络超时与手误引发的生产事故

记录一个未公开的 Rust 实战小工具,分享如何通过技术手段解决网络超时与手误操作带来的生产事故。 在自学 Rust 的那段日子里,我除了开发开源项目,还为日常工作量身定制了一些不公开的内部工具。其中最令我印象深刻的,是一个看似简单的 APK 文件上传小工具。 虽然它只是一个 Rust + native-windows-gui (NWG) 编写的单一界面应用,但它解决的两个核心生产问题,至今仍对我有着深远的影响。 工具画像:极简与高效 这个工具的界面极其克制: 交互区:一个醒目的文件拖拽控件,文案提示“请把 APK 文件拖拽到这里”。 表单区:版本号、Version Code、更新描述、是否强制升级(开关)。 执行区:一个大大的“上传”按钮。 它的核心逻辑非常纯粹:校验必填项 -> 调用内部上传 API -> 上传文件 -> 返回成功或失败的对话框。 故事一:消失的 60 秒与隐藏的超时限制 工具上线初期,一切运行平稳。由于办公室带宽充足,APK 的上传时间通常维持在 40 秒左右。 事故发生: APK 在集成广告后,经常会发生失败的情况,客户端弹出“超时”的报错。 排查过程: 我首先检查了后端服务,发现后端接口完全没有超时限制,服务器日志显示连接是被客户端主动断开的。 多次重复上传文件使用有线和无线对比,经排查是文件变大,由于公司无线网络波动,上传速度时好时坏,就会偶尔出现失败的情况。对比排查,发现失败的情况上传时间都超过了 60 秒。 回到 Rust 代码中,我发现当时为了追求简洁,直接使用了 HTTP 客户端的默认配置。而这个默认配置的 Timeout 为 60 秒。 解决方案: 在那个瞬间我意识到,工具不能只考虑“理想状态”。我手动将本地超时时间放宽至 5 分钟,并增加了状态提示。上传时不会再出现超时的情况了。 教训:永远不要依赖默认的超时设置,尤其是在处理文件上传这类长耗时操作时,本地的容错范围必须根据业务场景精细化配置。 故事二:包名校验——把人为失误挡在门外 这是这个工具最有价值的一次功能进化。 事故发生: 有一次,同事不小心将应用 A 的打包产物当作应用 B 上传到了官网,导致用户下载后发现软件张冠李戴。这属于严重的生产事故。 排查过程: 这个客户端软件调用内部API后,会自动将上传的文件名修改为固定的软件下载文件名,显示在网站上。经测试,如果将应用A的打包产物使用应用B上传,在官网就会出现相同的情况。解决这个方法也非常简单,就是上传的时候小心细致一点,应用A使用A客户端软件,应用B使用B客户端软件。但是人总会犯错,尤其是在面对多个长相相似的 .apk 文件时。通过肉眼观察文件名来区分应用,是极其不可靠的。 ...

2023-01-02 · 1 min · Eagle