一些服务不带认证,凡是可以访问到服务端口,都可以正常使用该服务,方便是方便,但是不够安全。
比如ollama默认安装后就是这样。现在据说网上扫一下端口11434,免apikey的ollama服务一大堆。。。
那我们怎样将本机安装的ollama能提供给其他用户使用,并且可以加apikey的限制呢?其实方案有很多,我说一个轻量级的解决方案,nginx代理转发,并且进行header中的Authorization信息的检查。
固定值的可以直接写在nginx.conf配置文件里,最简单;如果是使用json文件来存放apikey信息的话,nginx需要使用lua模块来读取json文件,进行解码,windows版的nginx默认不带lua模块,可以用openresty,它相当于集成了lua模块的nginx。json内容可以由其他应用来维护,不过内容有变化需要考虑,如果要求实时更新,那么在应用最好修改json文件内容时也同时通知nginx来更新;或者干脆由另外一个web服务来负责header中apikey的检查,nginx里要写lua代码去执行调用,根据返回结果来决定是否放行;还有一个就是我最终选择的方案-redis,apikey维护服务在apikey生效或者失效时更新redis的集合,nginx从redis里检查集合里是否包含请求头中的apikey。nginx.conf里的lua部分相关代码如下:
...
http {
# 引入 Lua 模块
lua_package_path "lualib/?.lua;;";
lua_package_cpath "lualib/?.so;;";
# 定义 Redis 连接参数
upstream redis_backend {
server 127.0.0.1:6379; # Redis 服务器地址
keepalive 10; # 保持连接
}
...
server {
listen 443 ssl;
...
location /ollama/ {
#if ($request_method = 'OPTIONS') {
# return 204;
# }
access_by_lua_block {
-- 获取请求头中的 Authorization
local auth_header = ngx.var.http_Authorization
if not auth_header then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Unauthorized: Missing Authorization header")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- 连接 Redis
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 设置超时时间为 1 秒
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.say("Internal Server Error: Failed to connect to Redis")
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- 查询 Redis 中是否存在该密钥
local is_member, err = red:sismember("auth_keys", auth_header)
if not is_member then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.say("Internal Server Error: Failed to query Redis")
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- 关闭 Redis 连接
local ok, err = red:set_keepalive(10000, 100)
if not ok then
ngx.log(ngx.ERR, "Failed to set keepalive: ", err)
end
-- 检查密钥是否有效
if is_member == 0 then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Unauthorized: Invalid Authorization key")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
}
# 如果验证通过,代理到目标服务器
#add_header 'Access-Control-Allow-Origin' '*';
#add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
#add_header 'Access-Control-Max-Age' 1728000;
#proxy_set_header origin http://127.0.0.1:11434;
#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 Authorization $http_authorization;
#proxy_set_header Host localhost:11434;
proxy_pass http://127.0.0.1:11434;
}
...
}
}
...
主要是可以参考下lua里将header里认证信息与redis集合进行匹配,其他nginx转发设置头信息跟后端服务要求而定。