亚马逊AWS官方博客

亚马逊云科技 WAF 部署小指南(三)使用 OpenSearch 进行 WAF 安全调查

方案介绍

当我们按照WAF部署小指南(一) 构建好WAF的架构以后。日常安全运维的工作也要随之展开。在WAF部署小指南(一) 、(二)中我们已经介绍了两种日志存储归档和查询的方法,分别是S3存储+Athena查询以及Log insight查询的方法。这两种方法成本比较低。适合查询工作不频繁的使用场景。

动态资源和数据资源丰富的网站,由于对攻击者具有比较大的吸引力。需要根据安全事件响应的要求开展专业的安全运营工作。这时我们就需要一个安全日志分析系统(SIEM)来帮助我们更高效的完成日常的安全日志初步分析,以及深入分析的工作。由于这些工作对于这一类组织发生的频度较高。所以通常由承担安全运营职责的专职人员来完成,而且用户通常会选择以图形化菜单操作为主的方式完成日志分析和调查的工作。从而提高每一个安全事件响应调查的效率。满足组织对安全调查响应的服务时效要求。

亚马逊云科技WAF可以支持与主流的图形化调查SIEM产品如OpenSearch, Elastic Search, Splunk,Sumo Logic的集成。本文我们将主要介绍如何快速的启用亚马逊云科技的OpenSearch作为WAF的SIEM平台。快速开展安全运营工作。

本文的环境搭建完成后,您可以对亚马逊云科技WAF日志图形化分析以及安全运营有一个清楚的了解。本文为了让大家对亚马逊云科技的WAF及SIEM系统有一个系统的认识。特意演示了每一个组件手动操作配置的步骤,并解释了技术原理。如果想要更快部署,亚马逊云科技有一个专门的Log hub解决方案,可以用Cloudformation自动化部署一套和本方案使用体验完全一致、兼具安全和成本灵活性的架构。对于安全,定制化有更高要求的组织。Log hub也很适合。目前已经有很多用户升级到了Log hub方案。具体可以参考WAF部署小指南(四) 。

内容简介:

本文分为六个步骤为您讲述如何创建使用OpenSearch进行WAF安全调查的环境。

步骤一: 按照WAF部署小指南(一) 的步骤完成WAF的部署,确保通过WAF能够访问后端的Echo-Server网页。

步骤二: 创建OpenSearch访问环境。

步骤三: 导入OpenSearch Dashboard

步骤四: 创建Kinesis Firehose, 将WAF日志转发到OpenSearch集群中

步骤五: 映射Firehose IAM Role 到OpenSearch用户

步骤六: 使用OpenSearch Dashboard,开展WAF日志调查工作

架构图:

步骤一: 按照WAF部署小指南(一) 的步骤完成WAF的部署,确保通过WAF能够访问后端的Echo-Server网页。

步骤二: 创建OpenSearch访问环境。

1.登录亚马逊云科技控制台,选择OpenSearch 服务,选择Create domain:

2.选择Production(生产环境),并选择1.1版本

  1. 等待15分钟左右,OpenSearch 节点准备好了以后进行下一步

  1. 完成以后得到OpenSearch Dashboards URL:


  1. 点击Dashboard URL进入login状态。输入创建dashboard时输入的用户名密码
  1. 选择Global tanent:

注意如果选错了,可以到右上角切换tenant改过来。Private tenant各用户的数据不能共享。不适合SIEM的场景。

这样我们就准备好了OpenSearch的日志存储集群。

步骤三: 导入OpenSearch Dashboard

准备好了OpenSearch的集群,我们就拥有了一个可以通过HTTP API接口注入日志由OpenSearch进行索引的日志搜索平台。纯日志的搜索已经可以通过Discover菜单开展了。为了使得安全日志可以使用图形化界面进行搜索。我们需要导入dashboard,为图形化日志分析做准备:

  1. 按照下图导入Dashboard的ndjson文件
    *dashboard的ndjson文件链接在这里

点击导入的dashboard,已经可以看到WAF dashboard的框架。下面我们再把OpenSearch的输入Index Template设定一下:

步骤四: Index Template的设定

Index Template的设定是让OpenSearch在对WAF输入的日志进行索引写入的时候。对每个json单元做不同的数据类型设定。以便后面查询的时候可以根据特定字段来进行查询和汇总。这个部分如果预先不设定好,有些Dashboard的图形化显示会不能正常工作。

*注意我们WAF部署小指南(四) 的Log hub解决方案里这部分工作都是自动完成的。有兴趣大家可以尝试。

*如果Mapping设置未完成而日志已经写入。则已经写入的日志不能有效搜索。需要等下一个index周期(一小时或者一天)以后,新的index生成才能生效。

  1. 点击OpenSearch的Dev Tool。

  1. 在左边边框输入我们准备好的Mapping 设置,得到“Acknowledged”表示设置成功了。

*注意我们这里的json设置是对每个WAF日志字段做过精细调整的。目的是把WAF日志的可搜索性再提高一些。所以设置的json文件会比较长。

具体的设置命令由于太长放在附录一里。

至此,我们的OpenSearch分析日志的准备工作基本完成。可以开始做WAF日志的设定,把日志引入OpenSearch了。

步骤四: 创建Kinesis Firehose, 将WAF日志转发到OpenSearch集群中

  1. 找到我们在WAF部署小指南(一) 准备的WAF Web ACL。把日志输出转到步骤二创建的OpenSearch集群里。

  1. 创建一个新的Kinesis Data Firehose Delivery stream:
    注意这里的Delivery stream的名称仍然需要以 aws-waf-logs- 开始。


等待2分钟左右Delivery Stream创建完毕。

  1. 创建好以后关联到WAF logging设置里。

步骤五: 映射Firehose IAM Role 到OpenSearch用户

完成这个工作以后,我们需要把Firehose Delivery Stream的IAM Role映射到OpenSearch的用户里。从而使得Firehose写入OpenSearch日志的动作不被拒绝。

  1. 获取Kinesis Delivery Stream的IAM Role ARN:


  1. 点击IAM Role进入IAM界面拷贝Role ARN。
    得到类似arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs-us-east-1-1234567890406 的IAM Role ARN。这是Firehose做写入动作的服务角色。下面需要在OpenSearch里赋予它写入OpenSearch的能力。

  1. 进入之前的OpenSearch 左侧菜单点击OpenSearch Plugins – Security – Roles – all_access:


  1. 点击Mapped users:

  1. 进入Manage Mapping设置加入Kinesis Firehose的IAM ARN

6. 添加Backend roles,填入之前的Firehose IAM Role ARN “arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890406”. 点击Map,完成添加动作。

注意以上步骤如果不做,我们的OpenSearch内部是看不到日志的。并且会在Delivery Stream的如下位置看到OpenSearch返回的错误信息:

具体错误信息如下:

Error received from the Amazon OpenSearch Service cluster. {"error":{"root_cause":[{"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000, backend_roles=[arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000], requestedTenant=null]"}],"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000, backend_roles=[arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000], requestedTenant=null]"},"status":403}

我们在配置中遇到这个错误的技术人员不在少数。虽然大家经过仔细分析,最终都解决了这个问题。但也花了不少时间。所以特别在这里着重强调一下。

步骤六: 使用OpenSearch Dashboard,开展WAF日志调查工作

完成以上工作以后,我们的WAF日志分析系统就基本完成了。

现在来看看整体的Dashboard效果:

  1. 首先我们会有全局的一个了解。知道WAF在过去的特定时间段允许和拒绝的请求情况。并且可以根据WAF ACL Rule的匹配来做特定的检查。注意这里我们是全量的日志分析。

  1. 然后,对于要进行深入安全调查工作的人员,我们主要会使用下面一个表:

  1. 现在我们来用点击的方式制作几个过滤条件来做组合查询。

首先选择Block的WAF请求:

然后选择表中的httpRequest.country。点击浮窗里的+号。即过滤同是来自法国的请求。

然后我们添加第三个条件,选择httpRequest.clientIp。点击浮窗里的 – 号。即过滤除了来自92.42.109.58 的client请求。

三个过滤条件叠加查询的结果,让我们了解到来自法国被阻断的请求中还有21条来自另外一个clientIp的请求:

值得一说的是,Dashboard里的构成的三个过滤条件,都是以DQL这种json格式的查询语句来构成的。执行下面的操作可以看到这些语句:




可以看到,每一个条件都是以DSL的json格式表达出来的。有了dashboard,我们就可以把灵活的查询语言以拖拉拽的方式组合成一个复杂的查询策略。完成我们每天不同的查询任务。

总结:

通过以上的步骤,我们使用亚马逊云科技的OpenSearch创建了一个WAF的日志分析系统。这个系统,对于有安全运营需求的企业是非常必要的。通过搭建这个系统,我们了解了WAF日志如何通过Kinesis Firehose导入。如何创建一个基于OpenSearch的托管日志分析平台。导入我们准备好的WAF日志分析Dashboard。并完成OpenSearch index template的设定,保证WAF日志的有效搜索。最后我们演示了如何用鼠标点击的方式完成一个比较复杂的日志搜索任务,在Dashboard上成功显示了搜索结果。

有了这个日志分析系统,我们就可以以无代码的方式完成安全运营的工作了。本文的操作略显复杂,但比起从安装ELK套件开始搭建一个日志分析平台还是简单了很多。如果想要进一步简化,我们可以参考WAF部署小指南(四) ,尝试使用自动化部署的Log hub解决方案。

附录一:

OpenSearch WAF 日志index mapping的设置:

PUT _index_template/awswaf-waf-logs
    {
    "index_patterns": [
        "awswaf-waf-*"
    ],
    "template": {
        "settings": {
            "index": {
                "number_of_shards": "5",
                "number_of_replicas": "1"
            }
        },
        "mappings": {
            "properties": {
                "terminatingRuleId": {
                    "type": "keyword"
                },
                "terminatingRuleType": {
                    "type": "keyword"
                },
                "ruleGroupList": {
                    "properties": {
                        "terminatingRule": {
                            "properties": {
                                "action": {
                                    "type": "keyword"
                                },
                                "ruleId": {
                                    "type": "keyword"
                                }
                            }
                        },
                        "ruleGroupId": {
                            "type": "keyword"
                        }
                    }
                },
                "httpSourceId": {
                    "type": "keyword"
                },
                "labels": {
                    "properties": {
                        "name": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        }
                    }
                },
                "webaclId": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "ignore_above": 256,
                            "type": "keyword"
                        }
                    }
                },
                "@timestamp": {
                    "path": "timestamp",
                    "type": "alias"
                },
                "webaclName": {
                    "type": "keyword"
                },
                "action": {
                    "type": "keyword"
                },
                "httpRequest": {
                    "properties": {
                        "args": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        },
                        "country": {
                            "type": "keyword"
                        },
                        "headers": {
                            "properties": {
                                "name": {
                                    "type": "keyword"
                                },
                                "value": {
                                    "type": "text",
                                    "fields": {
                                        "keyword": {
                                            "ignore_above": 256,
                                            "type": "keyword"
                                        }
                                    }
                                }
                            }
                        },
                        "httpVersion": {
                            "type": "keyword"
                        },
                        "requestId": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        },
                        "clientIp": {
                            "type": "ip"
                        },
                        "httpMethod": {
                            "type": "keyword"
                        },
                        "uri": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        }
                    }
                },
                "httpSourceName": {
                    "type": "keyword"
                },
                "formatVersion": {
                    "type": "keyword"
                },
                "timestamp": {
                    "format": "epoch_millis",
                    "type": "date"
                }
            }
        },
        "aliases": {
            "awswaf-waf": {}
        }
    }
}

如果您对本文有任何问题,请在此处留言。

亚马逊云科技 WAF 部署小指南系列文章

本篇作者

崔俊杰

亚马逊云科技解决方案架构师,负责亚马逊云科技边缘云安全相关的服务产品。为亚马逊云用户提供DDoS防御/网站前端安全防御/域名安全相关的产品咨询。对Cloudfront, Shield, WAF, Route53,Global Accelerator等边缘云安全相关产品有深入了解。在计算机安全,数据中心和网络领域有多年的工作经验。

孙健

孙健,AWS大数据解决方案架构师,负责基于AWS的大数据解决方案的咨询与架构设计,同时致力于大数据方面的研究和推广。在大数据运维调优、容器解决方案,湖仓一体以及大数据企业应用等方面有着丰富的经验。

戴晓斌

亚马逊云科技创新解决方案架构师,主要负责云上解决方案的设计与研发。在无服务器,容器,数据分析等方面有丰富的经验。

任少鹏

亚马逊云科技解决方案架构师,负责传统金融客户的云上解决方案技术支持,有近20年软件架构设计及前后端开发工作经验。