亚马逊AWS官方博客

使用AWS VPC, KMS, Lambda和ElasticSearch 实现安全和加密的数据搜索

机遇与挑战

搜索无处不在。无论是在电子商务、社交媒体还是视频应用中,搜索都发挥了举足轻重的作用。搜索作为信息的入口,能够帮助用户快速从海量信息中找到想要的内容,极大地提高了终端用户的使用体验。Amazon OpenSearch(Amazon ElasticSearch) 提供了一个高度可扩展的系统,使用户可以有效地探索他们的数据, 从而快速而轻松地实现业务需求。

但是同时我们知道,安全性是您应用程序的首要任务。安全几乎贯穿了产品研发的每一个环节,作为产品架构设计人员,开发,运维人员,使用系统级别的安全防护手段,可以有效的提高产品的安全性。在本文中,我们将向您介绍如何使用 Amazon VPC,Amazon KMS,Amazon Lambda 以及Amazon OpenSearch(Amazon ElasticSearch)  保护您的数据。

操作步骤

建立VPC,并创建公有子网和私有子网

步骤 1: 为您的 NAT 网关创建弹性 IP 地址

NAT 网关需要您的公有子网中的弹性 IP 地址

  1. 打开 Amazon VPC 控制台。
  2. 在左侧导航窗格中,选择Elastic IPs
  3. 选择分配

步骤 2: 运行 VPC 向导

VPC 向导 是一个简单的工具,可以帮助您创建具有公有和私有子网的 VPC,以及VPC相关资源,包括安全组、路由表、NAT 网关。

  1. 打开 Amazon VPC 控制台。
  2. 在左侧导航窗格中,选择 VPC 仪表板,然后单击橙色按钮 Launch VPC Wizard
  3. 在左侧选择 VPC with Public and Private Subnets
  4. 点击 Select

  1. 填写表格,设置如下图所示。 您可以使用任何您喜欢的 IP 地址,并输入您刚刚分配的弹性 IP。
  2. 选择创建vpc

  1. 可以看到VPC向导创建的如下资源



  1. 创建更多子网可以有效提高高可用性,这部分内容在本文中就不进行展开,您可以参考下文进行了配置

Tutorial: Creating a VPC with Public and Private Subnets for Your Clusters.

步骤 3: 配置安全组规则

  1. 在左侧导航窗格中,选择安全组。 可以找到刚刚创建的安全组。
  2. 对于安全组,按照如下配置编辑入站规则,使其只接受 VPC 内特定端口的安全流量。

创建IAM role

创建 Lambda 函数将使用的角色(IAM Role)。 我们稍后创建好ElasticSearch 域后,会将策略附加到该角色。

使用 VPC 和 KMS 加密创建 ElasticSearch 域

  1. 打开 OpenSearch Service(Amazon Elasticsearch Service)控制台。
  2. 选择创建新域
  3. 在选择开发类型中,选择开发和测试,因为我们不在生产中,只在一个可用区中创建了一个子网。 对于生产中的多个可用区,子网数必须与可用区数相同。
  4. 对于版本,选择 Elasticsearch 7.10
  5. 选择下一步。

  1. 输入域的名称。 此域名将成为您的 VPC 终端节点的一部分。
  2. 选择下一步。

  1. 选择VPC接入
  2. 对于 VPC,选择您创建的 VPC。
  3. 对于子网,选择您创建的私有子网。

  • 对于安全组,选择您创建的安全组。
  1. 使用 MyESRole 设置访问控制

  1. 对于 Access Control Policy, 选择JSON defined access policy.

输入如下策略

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
            "AWS": [
            "arn:aws:iam::670164030314:role/MyESRole"
            ]
            },
            "Action": [
                "es:ESHttpGet",
                "es:ESHttpPost",
                "es:ESHttpPut",
                "es:ESHttpDelete"
                ],
            "Resource": "arn:aws:es:us-west-1:670164030314:domain/my-es-domain/*"
        }
    ]

14. 选择启用节点到节点加密和启用静态数据加密 和使用 AWS KMS Key。}

创建IAM 策略

  1. 创建 ElasticSearch 域后,您可以为 Lambda 函数创建策略以允许访问 ElasticSearch 域并执行操作。
{
    "Version": "2012-10-17",
    "Statement": [
        {
        "Effect": "Allow",
        "Action": [
            "es:ESHttpPost",
            "es:ESHttpGet",
            "es:ESHttpDelete",
            "es:ESHttpPut"
        ],
        "Resource": "arn:aws:es:us-west-2:XXXXXXXXXXXX:domain/my-es-domain"
        }
    ]
}
  1. 为 Lambda 创建策略以设置 VPC。
{
    "Version": "2012-10-17",
    "Statement": [
        {
        "Effect": "Allow",
        "Action": [
            "ec2:CreateNetworkInterface",
            "ec2:DeleteNetworkInterface",
            "ec2:DescribeNetworkInterfaces"
        ],
        "Resource": "*"
        }
    ]
}
  1. 为 Lambda 日志创建策略
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-west-1:670164030314:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-west-1:670164030314:log-group:/aws/lambda/my-es-lambda-function:*"
            ]
        }
    ]
}
  1. 将上述策略附加到您之前创建的 IAM 角色(IAM ROLE)

在VPC中创建lambda

我们将在 VPC 中创建一个简单的 Python lambda 函数来在 ElasticSearch 上执行操作。

  1. 打开 Lambda 控制台,选择创建函数
  2. 对于运行时,选择 Python 3.8

  1. 选择 Change default execution role,选择您创建的 IAM 角色

  1. 在网络部分,对于 VPC,选择您创建的 VPC。
  2. 对于子网,选择您创建的公有子网。 在生产中,建议您在多个可用区中创建多个子网以获得高可用性。
  3. 对于安全组,选择您创建的安全组。 您可以看到入站规则和出站规则。

测试与验证

  1. 所需依赖

requirements.txt

boto3==1.12.39
requests==2.23.0
requests-aws4auth==0.9
  1. 上传数据
    示例代码:
import json
import requests
import boto3
from requests_aws4auth import AWS4Auth

def lambda_handler(event, context):
    region = "us-west-1"
    service = 'es'
    credentials = boto3.Session().get_credentials()
    awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)
    host = "https://vpc-my-es-domain-6kw5zu33hepvneqdsgeqhjqphu.us-west-1.es.amazonaws.com"
    index = "test-index"
    url =host + '/' + index+ '/_doc/1'
    headers = { "Content-Type": "application/json" }

    input ={
        "product": "toy",
        "price": 5,
        "description": "dog toy"
    }

    r = requests.put(url, auth=awsauth, headers=headers, data=json.dumps(input))

    return {
        'statusCode': 200,
        'body': json.loads(r.content)
    }

示例返回:

{
    "statusCode": 200,
    "body": {
        "_index": "test-index",
        "_type": "_doc",
        "_id": "1",
        "_version": 2,
        "result": "updated",
        "_shards": {
            "total": 2,
            "successful": 1,
            "failed": 0
        },
        "_seq_no": 1,
        "_primary_term": 1
    }
}

3. 搜索文件

示例代码:

import json
import requests
import boto3
from requests_aws4auth import AWS4Auth

def lambda_handler(event, context):
    region = "us-west-1"
    service = 'es'
    credentials = boto3.Session().get_credentials()
    awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)
    host = "https://vpc-my-es-domain-6kw5zu33hepvneqdsgeqhjqphu.us-west-1.es.amazonaws.com"
    index = "test-index"
    url =host + '/' + index+ '/_search'
    headers = { "Content-Type": "application/json" }

    query = {
        "size": 250,
        "query": {
            "query_string": {
                "query": "toy"
            }
        }
    }

    r = requests.post(url, auth=awsauth, headers=headers, data=json.dumps(query))

    return {
        'statusCode': 200,
        'body': json.loads(r.content)
    }

示例返回:

{
    "statusCode": 200,
    "body": {
        "took": 658,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": {
            "value": 1,
            "relation": "eq"
            },
            "max_score": 0.2876821,
            "hits": [
                {
                "_index": "test-index",
                "_type": "_doc",
                "_id": "1",
                "_score": 0.2876821,
                "_source": {
                    "product": "toy",
                    "price": 5,
                    "description": "dog toy"
                }
                }
            ]
        }
    }
}

 

总结:

本文通过网络层面的隔离,身份控制,安全组的加入,以及数据加密等方式,去管理和保护我们的数据,这是对应用程序和网站安全来说非常重要且有效的手段。但是,这仅仅只是冰山的一角,安全性是一项需要投入持续努力的工作。在您的开发和部署过程中,安全的重要性怎么强调都不为过。

希望本文可以帮助您,了解和掌握安全方面的一些知识,有效的提升和解决环境中的安全问题和隐患。

 

本篇作者

张晓功

AWS云原生应用架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内的应用和推广。精通微服务架构设计、治理、容器编排、监控熔断等性能和可靠性等具体功能落地。具有丰富的互联网产品开发、大规模并行计算、性能优化等经验。

刘红雨

云原生应用工程师,负责基于AWS的云计算方案架构的设计和实施开发。拥有丰富的互联网产品的开发经验,负责多个项目的搜索功能的设计和开发,熟悉搜索的性能优化,对公有云、机器学习、DevOps、基于云原生的微服务架构、敏捷加速研发效能等有深入的研究和热情。