You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Nginx代理Minio流量并代为处理Minio认证以避免泄露密钥

如何配置Nginx代理Minio流量并代为处理Minio认证以避免泄露密钥

这种场景我在团队协作中太熟悉了——存储团队要把Minio的密钥牢牢攥在手里,不让开发侧接触到,同时还要让各个应用能正常访问自己的对象资源,Nginx确实是实现这个需求的完美中间层。不过Minio用的是AWS SigV4签名认证,不像Basic Auth那样能直接静态设置Authorization头,得用动态生成签名的方式来处理,下面给你一步步讲具体的实现方法:

核心原理说明

Minio的认证依赖AWS Signature Version 4(SigV4),这个签名是根据请求的HTTP方法、路径、时间戳、请求头等动态计算出来的,没法像Basic Auth那样提前生成一个固定的Header值。所以我们需要在Nginx里用Lua脚本实时生成这个签名,然后添加到请求头里转发给Minio。

前置准备

推荐使用OpenResty(它是Nginx的增强版,集成了LuaJIT和常用的Lua库,比如resty.hmac),这样不需要额外安装依赖。如果用标准Nginx,需要确保安装了ngx_http_lua_module,并手动安装lua-resty-hmac库。

第一步:编写SigV4签名生成脚本

创建一个Lua脚本(比如放在/etc/nginx/minio_sign.lua),脚本里嵌入Minio的Access Key和Secret Key,然后实现SigV4签名的生成逻辑。下面是一个可用的示例脚本:

local access_key = "YOUR_MINIO_ACCESS_KEY"
local secret_key = "YOUR_MINIO_SECRET_KEY"
local region = "us-east-1" -- 换成你的Minio集群区域,默认是us-east-1
local service = "s3"

-- 获取请求时间戳(UTC,格式为YYYYMMDD'T'HHMMSS'Z')
local function get_timestamp()
    return os.date("!%Y%m%dT%H%M%SZ")
end

-- 获取日期(用于签名的日期部分,格式为YYYYMMDD)
local function get_date(timestamp)
    return string.sub(timestamp, 1, 8)
end

-- HMAC-SHA256加密函数
local function hmac_sha256(key, data)
    local hmac = require "resty.hmac"
    local h, err = hmac:new(key, hmac.ALGOS.SHA256)
    if not h then
        ngx.log(ngx.ERR, "Failed to create HMAC: ", err)
        return nil
    end
    local ok = h:update(data)
    if not ok then
        ngx.log(ngx.ERR, "Failed to update HMAC")
        return nil
    end
    local digest = h:final()
    return digest
end

-- 生成签名密钥
local function get_signing_key(secret_key, date, region, service)
    local k_date = hmac_sha256("AWS4" .. secret_key, date)
    local k_region = hmac_sha256(k_date, region)
    local k_service = hmac_sha256(k_region, service)
    local k_signing = hmac_sha256(k_service, "aws4_request")
    return k_signing
end

-- 生成规范请求字符串
local function get_canonical_request(method, uri, query_string, headers, signed_headers, payload_hash)
    local canonical_headers = ""
    for _, key in ipairs(signed_headers) do
        canonical_headers = canonical_headers .. string.lower(key) .. ":" .. headers[key] .. "\n"
    end
    return method .. "\n"
        .. uri .. "\n"
        .. query_string .. "\n"
        .. canonical_headers .. "\n"
        .. table.concat(signed_headers, ";") .. "\n"
        .. payload_hash
end

-- 生成待签名字符串
local function get_string_to_sign(timestamp, date, region, service, canonical_request_hash)
    return "AWS4-HMAC-SHA256\n"
        .. timestamp .. "\n"
        .. date .. "/" .. region .. "/" .. service .. "/aws4_request\n"
        .. canonical_request_hash
end

-- 主逻辑
local method = ngx.req.get_method()
local uri = ngx.var.uri
local query_string = ngx.var.args or ""

-- 获取或设置X-Amz-Date头
local timestamp = get_timestamp()
ngx.req.set_header("X-Amz-Date", timestamp)
local date = get_date(timestamp)

-- 获取请求头,这里只保留必要的头,你可以根据需求调整
local headers = {
    ["host"] = ngx.var.host,
    ["x-amz-date"] = timestamp
}

-- 如果是PUT/POST请求,需要计算payload的哈希,这里简化处理用UNSIGNED-PAYLOAD(Minio支持)
local payload_hash = "UNSIGNED-PAYLOAD"

-- 签名的头列表
local signed_headers = {"host", "x-amz-date"}

-- 生成规范请求和哈希
local canonical_request = get_canonical_request(method, uri, query_string, headers, signed_headers, payload_hash)
local canonical_request_hash = ngx.encode_base64(ngx.sha256_bin(canonical_request))

-- 生成待签名字符串
local string_to_sign = get_string_to_sign(timestamp, date, region, service, canonical_request_hash)

-- 生成签名密钥和最终签名
local signing_key = get_signing_key(secret_key, date, region, service)
local signature = ngx.encode_base64(hmac_sha256(signing_key, string_to_sign))

-- 设置Authorization头
local auth_header = string.format(
    "AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request, SignedHeaders=%s, Signature=%s",
    access_key, date, region, service, table.concat(signed_headers, ";"), signature
)
ngx.req.set_header("Authorization", auth_header)

注意:把脚本里的YOUR_MINIO_ACCESS_KEY和YOUR_MINIO_SECRET_KEY替换成实际的Minio密钥,region换成你的Minio集群配置的区域(默认是us-east-1)。

第二步:配置Nginx反向代理

在Nginx的配置文件中添加一个server块,用来代理Minio的流量,同时调用上面的Lua脚本生成签名。示例配置如下:

server {
    listen 80;
    server_name minio-proxy.example.com; # 换成你的代理域名

    # 限制应用只能访问指定的bucket/path,比如只允许访问app1的bucket
    location /app1-bucket/ {
        # 调用Lua脚本生成签名
        access_by_lua_file /etc/nginx/minio_sign.lua;

        # 转发请求到Minio集群
        proxy_pass http://your-minio-cluster:9000; # 换成你的Minio集群地址

        # 传递必要的请求头
        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_buffering off;
        proxy_cache off;
    }

    # 可以添加多个location块,对应不同的应用bucket
    location /app2-bucket/ {
        access_by_lua_file /etc/nginx/minio_sign.lua;
        proxy_pass http://your-minio-cluster:9000;
        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_buffering off;
        proxy_cache off;
    }
}

关键注意事项

  • 时间同步:Nginx服务器和Minio集群的系统时间必须保持同步(误差不能超过15分钟),否则SigV4签名会被Minio拒绝。可以用NTP服务来同步时间。
  • 权限安全:确保Nginx的配置文件和Lua脚本的权限设置为600,只有存储团队的用户能读取,避免密钥泄露。
  • 请求方法覆盖:如果你的应用需要使用PUT/DELETE等修改类请求,要确保Lua脚本里的逻辑能正确处理这些方法(上面的脚本已经支持所有HTTP方法)。
  • 密钥轮换:定期轮换Minio的密钥时,记得同步更新Lua脚本里的密钥,然后重启Nginx生效。

验证配置

用curl测试一下代理是否正常工作:

curl http://minio-proxy.example.com/app1-bucket/path/to/your/object

如果能正常返回对象内容,说明配置生效了。同时可以查看Minio的访问日志,确认请求是通过SigV4认证成功的。

备注:内容来源于stack exchange,提问作者h.safe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 12:48:14