构建基于WAF的S3安全体系

需求描述

有线上项目需要对RGW的bucket的访问进行白名单控制,只允许白名单内的IP去访问指定的bucket,简单写了个demo,基本思路是通过openresty写一个WAF模块,去实现设置bucket、IP白名单设置。

基本原理

OpenResty 处理一个请求,它的处理流程请参考下图(从 Request start 开始):

图片

几个关键阶段的简介如下

set_by_lua*: 流程分支处理判断变量初始化

rewrite_by_lua*: 转发、重定向、缓存等功能(例如特定请求代理到外网)

access_by_lua*: IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙)

content_by_lua*: 内容生成

header_filter_by_lua*: 响应头部过滤处理(例如添加头部信息)

body_filter_by_lua*: 响应体过滤处理(例如完成应答内容统一成大写)

log_by_lua*: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)

openresty详细介绍可以参考:moonbingbing.gitbooks.io/openresty-b…

本文的原理非常简单:通过设置bucket和IP的白名单,在access_by_lua阶段对request里面的host和uri等字段进行规则匹配再决定是否放行。

nginx配置

配置文件路径 /etc/nginx/conf.d/default.conf

upstream zone_write {
    server 10.63.48.18:7480 weight=13;#对应后端RGW civetweb节点
    keepalive 30;
}

server {
    listen       80;
    server_name s3.ceph.work *.s3.ceph.work; #endpoint对应的域名

    location  / {
    proxy_ignore_client_abort on ;
    proxy_http_version 1.1; #指定http版本,减少低版本带来的安全隐患
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    access_by_lua_file /etc/nginx/conf.d/access.lua; #WAF脚本
    proxy_pass http://zone_write;
    }
}
复制代码

WAF脚本

脚本路径 /etc/nginx/conf.d/access.lua

local uri = ngx.var.uri
local client_ip = ngx.var.remote_addr
local host = ngx.var.host
local endpoint = 's3.ceph.work' #endpoint地址
local white_ip_list = {["127.0.0.1"]=true} #IP白名单
local bucket_list = {["bucket1"]=true,["bucket2"]=true} #bucket白名单

function get_bucketname(host,uri,endpoint)
    local bucket_name = string.match(host, '^[%w-]+.' .. tostring(endpoint) .. '$')
    if (string.match(host, '^' .. tostring(endpoint) .. '$')) then
        if (string.match(uri,'^/$')) then
            return
        end
        local bucket_name = string.match(uri, '^/[%w-]+/')
        return string.sub(bucket_name,2,string.len(bucket_name)-1)
    elseif bucket_name then
        return string.sub(bucket_name,1,string.len(bucket_name)-string.len(endpoint)-1)
    else
        return
    end
end

if true == bucket_list[get_bucketname(host,uri,endpoint)] then
    if true ~= white_ip_list[client_ip] then
        ngx.log(ngx.ERR,"Forbidden:",client_ip)
        ngx.exit(ngx.HTTP_FORBIDDEN)
    end
end
复制代码

功能验证

在一个IP白名单以外的机器访问

curl bucket1.s3.ceph.work/asdasd #virtual hosted style 方式
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>


curl s3.ceph.work/bucket2/1233 #path-style 方式
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
复制代码

对应的nginx日志

2017/09/21 14:05:42 [error] 30725#0: *28 [lua] access.lua:29: forbidden:10.xx.xx.xx, client: 10.xx.xx.xx, server: s3.ceph.work, request: "GET /asdasd HTTP/1.1", host: "bucket1.s3.ceph.work"

2017/09/21 14:02:47 [error] 30725#0: *22 [lua] access.lua:29: forbidden:10.xx.xx.xx, client: 10.xx.xx.xx, server: s3.ceph.work, request: "GET /bucket2/1233 HTTP/1.1", host: "s3.ceph.work"
复制代码

后话

通过上面的例子可以看到openresty在和RGW整合方面非常强大,可以渗透到整个S3的request处理过程,大家可以根据上面的例子去举一反三,实现对各种S3 RESTful接口的控制,摆脱对ceph版本的依赖,实现真正的松耦合以及标准自定义。最后,因为个人身体原因,可能本公众号会暂停更新一段时间,多谢各位一直以来的支持。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享