【问题标题】:AWS ElasticSearch Service - Set Encryption options from CF templateAWS ElasticSearch Service - 从 CF 模板设置加密选项
【发布时间】:2020-02-13 00:57:59
【问题描述】:

我正在创建一个云形成模板以在 AWS 中配置 elasticsearch 服务域。

我想将加密下的这个属性设置为 true “对域的所有流量都需要 HTTPS”,但我无法在 AWS 文档中找到这样做的方法。

用于设置加密属性的其他选项,例如 “启用静态数据加密”和“节点到节点加密”有据可查。

有谁知道如何从 CF 模板设置“所有到域的流量都需要 HTTPS”属性?

【问题讨论】:

    标签: amazon-web-services elasticsearch amazon-cloudformation


    【解决方案1】:

    我这样做的方式是确保安全组只允许 HTTPS (443) 访问集群。

    不完全确定这是否是您要查找的内容,但如果不是,请提供更多详细信息,我会看看是否可以帮助您。

      mySecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          VpcId: !Ref VpcId
          GroupName: !Ref SecurityGroup
          GroupDescription: !Ref GroupDescription
          SecurityGroupIngress:
            - FromPort: '443'
              IpProtocol: tcp
              ToPort: '443'
              CidrIp: 0.0.0.0/0
    

    【讨论】:

    • 我又看了一些。使用控制台时,似乎有一个标题为:“对域的所有流量都需要 HTTPS”的复选框。但是,在浏览了 ES 的 CFN Docs 之后,我找不到任何可以设置此选项的地方(当他们这样做时我讨厌它)。所以我会坚持我上面的答案,当你使用 CFN 模板来做这个时,只使用一个安全组。这将与您根据规则在没有 HTTPS 的情况下无法连接的情况几乎相同。希望有帮助。 :D
    【解决方案2】:

    这是我为静态加密和节点到节点加密汇总的内容:

    现在,我必须添加/编辑大量活动部分,因此我将在此处发布整个模板,以便您可以看到我在不同部分所做的工作,以便您可以提取出您需要/想要的内容。我使用了条件,因为您可以选择是否启用它。我还确保在这里和那里添加一些 cmets。如果您愿意,可以随意剥离它们,但它们根本不会影响模板。

    edit:我知道模板中有几个部分是不需要的,但我喜欢让我的模板看起来漂亮且有条理(在使用时)。我知道我需要在模板本身内部进行组织工作,但这一切都在那里。 :P

    edit2:以下模板假定您将使用现有 VPC/子网/安全组,您可以查看我的存储库(在下面的评论中)以了解此模板的其他版本。

    我确实验证了这个模板并且能够成功构建域。

    AWSTemplateFormatVersion: 2010-09-09
    Description: >-
      **NOTE** In order to create Elastisearch Domain in AWS using CloudFormation
      verify you have the following Service Role created in IAM!!
      -- AWSServiceRoleForAmazonElasticsearchService --
      If you do not have this Role, create it using the following CLi Command -- 
      aws iam create-service-linked-role --aws-service-name es.amazonaws.com
    
    Metadata:
      AWS::CloudFormation::Interface:
        ParameterGroups:
          -
            Label:
              default: "Configure Cluster"
            Parameters:
              - DomainName
              - ElasticsearchVersion
              - ZoneAwareness
              - SnapShotHour
          -
            Label:
              default: "Data Instances"
            Parameters:
              - InstanceType
              - DataInstanceCount
          -
            Label:
              default: "Dedicated Master Instances"
            Parameters:
              - DedicatedMaster
              - MasterInstanceType
              - MasterInstanceCount
          -
            Label:
              default: "Storage Config"
            Parameters:
              - StorageSize
          -
            Label:
              default: "Network Config"
            Parameters:
              - VpcId
              - SubNet1
              - SubNet2
              - SecurityGroup
          -
            Label:
              default: "Encryption Settings"
            Parameters:
              - EncryptionAtRest
              - KmsKey
              - NodetoNode
          -
            Label:
              default: "IAM User Restriction Policy"
            Parameters:
              - IamUserArn
        ParameterLabels:
          DomainName:
            default: "Name of the ElasticSearch Domain (lowecase, no spaces) - If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the domain name"
          ElasticsearchVersion:
            default: "Select the ElasticSearch version desired"
          InstanceType:
            default: "Instance Size of Data Instances"
          DataInstanceCount:
            default: "Number of Data Instances Required"
          DedicatedMaster:
            default: "Select if a Dedicated Master Instance is required"
          MasterInstanceType:
            default: "Instance Size of Master Instances"
          MasterInstanceCount:
            default: "How many Dedicated Master Instances are needed? (0, 3 or 5)"
          StorageSize:
            default: "Storage Size in GB"
          VpcId:
            default: "Select the VPC to deploy into (must already exist)"
          SubNet1:
            default: "Select the First Subnet"
          SubNet2:
            default: "Select the Second Subnet"
          SecurityGroup:
            default: "Select the Security Group"
          IamUserArn:
            default: "Enter the ARN for the IAM User to give initial access to the stack"
          ZoneAwareness:
            default: "Enable Zone Awareness (Availability Zone Replication) (recommended)"
          SnapShotHour:
            default: "Set the hour to run the Automated Snapshot (0-23) (Default: UTC Timezone)"
          EncryptionAtRest:
            default: "Enable Encryption at Rest"
          KmsKey:
            default: "If Encryption at Rest is enabled, supply the KMS Key used for encryption"
          NodetoNode:
            default: "Enable Node to Node Encryption"
    
    Parameters:
      DomainName:
        Type: String
        Default: "elasticsearchstack-cf"
        MaxLength: '128' 
        ConstraintDescription: "Must be lowercase, numbers/letters and/or a dash"
      SnapShotHour:
        Type: Number
        Default: 0
        MinValue: 0
        MaxValue: 23
      ElasticsearchVersion:
        Type: String
        Default: 7.1
        AllowedValues: [7.1, 6.8, 6.7, 6.6, 6.5] # Remove this line for free-form number entry
      InstanceType:
        Type: String
        Default: r5.large.elasticsearch
        AllowedValues: [t2.small.elasticsearch, t2.medium.elasticsearch,
          c4.large.elasticsearch, c4.xlarge.elasticsearch, c4.2xlarge.elasticsearch, c4.4xlarge.elasticsearch, c4.8xlarge.elasticsearch,
          c5.large.elasticsearch, c5.xlarge.elasticsearch, c5.2xlarge.elasticsearch, c5.4xlarge.elasticsearch, c5.9xlarge.elasticsearch, c5.18xlarge.elasticsearch,
          m3.medium.elasticsearch, m3.large.elasticsearch, m3.xlarge.elasticsearch, m3.2xlarge.elasticsearch,
          m4.large.elasticsearch, m4.xlarge.elasticsearch, m4.2xlarge.elasticsearch, m4.4xlarge.elasticsearch, m4.10xlarge.elasticsearch,
          m5.large.elasticsearch, m5.xlarge.elasticsearch, m5.2xlarge.elasticsearch, m5.4xlarge.elasticsearch, m5.12xlarge.elasticsearch,
          r3.large.elasticsearch, r3.xlarge.elasticsearch, r3.2xlarge.elasticsearch, r3.4xlarge.elasticsearch, r3.8xlarge.elasticsearch,
          r4.large.elasticsearch, r4.xlarge.elasticsearch, r4.2xlarge.elasticsearch, r4.4xlarge.elasticsearch, r4.16xlarge.elasticsearch,
          r5.large.elasticsearch, r5.xlarge.elasticsearch, r5.2xlarge.elasticsearch, r5.4xlarge.elasticsearch, r5.12xlarge.elasticsearch,
          i2.xlarge.elasticsearch, i2.2xlarge.elasticsearch,
          i3.large.elasticsearch, i3.xlarge.elasticsearch, i3.2xlarge.elasticsearch, i3.4xlarge.elasticsearch, i3.8xlarge.elasticsearch, i3.16xlarge.elasticsearch]
        ConstraintDescription: "Must be a valid EC2 Elasticsearch instance type."
      DataInstanceCount:
        Type: Number
        Default: 2
        AllowedValues: [2, 4, 6, 8, 10] # Remove this line for free-form number entry
      MasterInstanceType:
        Type: String
        Default: r5.large.elasticsearch
        AllowedValues: [t2.small.elasticsearch, t2.medium.elasticsearch,
          c4.large.elasticsearch, c4.xlarge.elasticsearch, c4.2xlarge.elasticsearch, c4.4xlarge.elasticsearch, c4.8xlarge.elasticsearch,
          c5.large.elasticsearch, c5.xlarge.elasticsearch, c5.2xlarge.elasticsearch, c5.4xlarge.elasticsearch, c5.9xlarge.elasticsearch, c5.18xlarge.elasticsearch,
          m3.medium.elasticsearch, m3.large.elasticsearch, m3.xlarge.elasticsearch, m3.2xlarge.elasticsearch,
          m4.large.elasticsearch, m4.xlarge.elasticsearch, m4.2xlarge.elasticsearch, m4.4xlarge.elasticsearch, m4.10xlarge.elasticsearch,
          m5.large.elasticsearch, m5.xlarge.elasticsearch, m5.2xlarge.elasticsearch, m5.4xlarge.elasticsearch, m5.12xlarge.elasticsearch,
          r3.large.elasticsearch, r3.xlarge.elasticsearch, r3.2xlarge.elasticsearch, r3.4xlarge.elasticsearch, r3.8xlarge.elasticsearch,
          r4.large.elasticsearch, r4.xlarge.elasticsearch, r4.2xlarge.elasticsearch, r4.4xlarge.elasticsearch, r4.16xlarge.elasticsearch,
          r5.large.elasticsearch, r5.xlarge.elasticsearch, r5.2xlarge.elasticsearch, r5.4xlarge.elasticsearch, r5.12xlarge.elasticsearch,
          i2.xlarge.elasticsearch, i2.2xlarge.elasticsearch,
          i3.large.elasticsearch, i3.xlarge.elasticsearch, i3.2xlarge.elasticsearch, i3.4xlarge.elasticsearch, i3.8xlarge.elasticsearch, i3.16xlarge.elasticsearch]
        ConstraintDescription: "Must be a valid EC2 Elasticsearch instance type."
      MasterInstanceCount:
        Type: Number
        Default: 0
        AllowedValues: [0, 3, 5] # Remove this line for free-form number entry
      VpcId:
        Type: AWS::EC2::VPC::Id
        ConstraintDescription: "Must be the VPC ID of an existing Virtual Private Cloud."
      SubNet1:
        Type: AWS::EC2::Subnet::Id
        ConstraintDescription: "Must be the Subnet ID of an existing Subnet."
      SubNet2:
        Type: AWS::EC2::Subnet::Id
        ConstraintDescription: "Must be the Subnet ID of an existing Subnet."    
      SecurityGroup:
        Type: AWS::EC2::SecurityGroup::Id
        ConstraintDescription: "Must be and existing Security Group."    
      DedicatedMaster:
        Description: True or False
        Type: String
        Default: False
        AllowedValues:
          - True
          - False
      StorageSize:
        Type: Number
        Default: 20
        MinValue: 10 # Remove this line for free-form number entry (suggested to keep this line)
        MaxValue: 1000 # Remove this line for free-form number entry
      IamUserArn:
        Type: String
        Default: "arn:aws:iam::<AccountNumber>:user/<username>"
      ZoneAwareness:
        Description: True or False
        Type: String
        Default: True
        AllowedValues:
          - True
          - False
      EncryptionAtRest:
        Description: True or False
        Type: String
        Default: False
        AllowedValues:
          - True
          - False
      KmsKey:
        Type: String
      NodetoNode:
        Description: True or False
        Type: String
        Default: False
        AllowedValues:
          - True
          - False
    Conditions: # Checks to see if Conditional Values are True
      DedicatedMasterYes: !Equals [ !Ref DedicatedMaster, True]
      EncryptionAtRestYes: !Equals [ !Ref EncryptionAtRest, True]
    
    Resources:
      ElasticsearchDomain:
        Type: AWS::Elasticsearch::Domain
        Properties:
          DomainName: !Ref DomainName
          ElasticsearchVersion: !Ref ElasticsearchVersion
          ElasticsearchClusterConfig: 
            DedicatedMasterEnabled: !Ref DedicatedMaster
            InstanceCount: !Ref DataInstanceCount
            ZoneAwarenessEnabled: !Ref ZoneAwareness
            InstanceType: !Ref InstanceType
            DedicatedMasterType: # If Dedicated Master is True, then use !Ref, if not, use NoValue (NULL) for both settings below
              !If [DedicatedMasterYes, !Ref MasterInstanceType, !Ref "AWS::NoValue"]
            DedicatedMasterCount: 
              !If [DedicatedMasterYes, !Ref MasterInstanceCount, !Ref "AWS::NoValue"]
          EBSOptions:
            EBSEnabled: True
            Iops: 0
            VolumeSize: !Ref StorageSize
            VolumeType: "gp2"
          SnapshotOptions:
            AutomatedSnapshotStartHour: !Ref SnapShotHour
          AccessPolicies:
            Version: 2012-10-17
            Statement:
              - Effect: Deny
                Principal:
                  AWS: !Ref IamUserArn
                Action: 'es:*'
                Resource: "*"
          AdvancedOptions: # BOTH of these settingsd are REQUIRED (regardless of what the documentation states) - Bug filed: https://forums.aws.amazon.com/thread.jspa?messageID=768527
            rest.action.multi.allow_explicit_index: 'true'
            indices.fielddata.cache.size: !Ref "AWS::NoValue"
          VPCOptions:
            SubnetIds:
              - !Ref SubNet1
              - !Ref SubNet2
            SecurityGroupIds:
              - !Ref SecurityGroup
          EncryptionAtRestOptions: # If Encryption At Rest is True, then use !Ref, if not, use NoValue (NULL) for both settings below
              !If [EncryptionAtRestYes, !Ref EncryptionAtRest, !Ref "AWS::NoValue"]
          KmsKeyId:
              !If [EncryptionAtRestYes, !Ref KmsKey, !Ref "AWS::NoValue"]
          NodeToNodeEncryptionOptions:
            Enabled: !Ref NodetoNode
    
    Outputs:
      DomainArn:
        Value: !GetAtt ElasticsearchDomain.DomainArn
      DomainEndpoint:
        Value: !GetAtt ElasticsearchDomain.DomainEndpoint
      SecurityGroupId:
        Value: !Ref SecurityGroup
      SubnetId1:
        Value: !Ref SubNet1
      SubnetId2:
        Value: !Ref SubNet2
    

    【讨论】:

    • 仅供参考,如果您需要更多选项,可以在此处查看我的一些模板:github.com/Talderon/AWS_CloudFormation 如果您更愿意使用 JSON 版本,也可以在那里找到它们。希望可以指向我的仓库。
    【解决方案3】:

    由于该功能是最近发布的,它在 CloudFormation 或 Terraform 中尚不可用,但我们可以通过在 CF 模板中使用自定义资源来实现。这个自定义资源只不过是一个 lambda 函数,它将作为云形成堆栈创建的一部分执行。

    完全自动化可分为3个部分,

    1. Lambda 函数开发
    2. Lambda 函数创建
    3. Lambda 函数配置为 Cloudformation 堆栈中的自定义资源

    Lambda函数开发

    正如here 解释的那样,请按照以下步骤创建 lambda 函数。

    创建一个用于放置 Lambda 源的空文件夹。然后使用 pip 将 crhelper 安装到文件夹中,并创建 lambda_function.py 文件以放入您的资源代码。

    mkdir sum_function
    cd sum_function
    pip install -t . crhelper
    # on some systems pip may fail with a distutils error, if you run into this, try 
    running pip with the –system argument
    # pip install —system -t . crhelper
    touch lambda_function.py
    

    现在打开 lambda_function.py 文件并将以下代码放入 lambda_function.py 文件中。

    from crhelper import CfnResource
    import boto3
    import logging
    
    logger = logging.getLogger(__name__)
    helper = CfnResource()
    
    @helper.create
    @helper.update
    def update_es_domain_config(event, context):
        logger.info("Updating elasticsearch domain config with HTTPS")
        es = boto3.client('es')
        response = es.update_elasticsearch_domain_config(
            DomainName=event['ResourceProperties']['ESDomainName'],
            DomainEndpointOptions={
                'EnforceHTTPS': True,
                'TLSSecurityPolicy': 'Policy-Min-TLS-1-0-2019-07'
            }
        )
        logger.info("results:", response)
    @helper.delete
    def no_op(_, __):
        pass
    
    def handler(event, context):
        helper(event, context)
    

    然后压缩整个目录

    zip -r ../sum.zip ./
    

    Lambda 函数创建

    现在创建 lambda 函数,我正在使用 CLI。根据您的环境替换这些值。

    aws lambda create-function \
        --function-name "update-es-domain" \
        --handler "lambda_function.handler" \
        --timeout 900 \
        --zip-file fileb://../sum.zip \
        --runtime python3.7 \
        --role "arn:aws:iam::123412341234:role/lambda-cli-role"
    

    您需要从输出中删除 FunctionArn 以在下一步中使用。

    Lambda 函数配置为 Cloudformation 堆栈中的自定义资源

    现在在 CF 模板的资源部分,在 Elasticsearch 资源之后,添加自定义资源,如下所示。将服务令牌替换为您在上一步中获得的 functionArn 作为输出。将 ESDomainName 替换为您要修改的 elasticsearch 域。

      ESDomainUpdate:
        Type: "Custom::elasticsearch"
        Properties:
          ServiceToken: "arn:aws:lambda:us-east-1:123412341234:function:update-es-domain"
          ESDomainName: "advantagehelp"
    

    PFB Complete Cloudformation 模板供参考。

    AWSTemplateFormatVersion: 2010-09-09
    Description: AWS CloudFormation templates to create AWS Elasticsearch Service domain.
    Parameters:
      NodeType:
        Description: The node type to be provisioned for the Elasticsearch cluster
        Type: String
        Default: m4.large.elasticsearch
        AllowedValues:
          - m4.large.elasticsearch
          - m4.xlarge.elasticsearch
          - c4.large.elasticsearch
          - c4.xlarge.elasticsearch
          - r4.large.elasticsearch
          - r4.xlarge.elasticsearch
        ConstraintDescription: must be a valid Elasticsearch node type.
      NodeCount:
        Description: The number of nodes in the Elasticsearch cluster.
        Type: Number
        Default: '1'
      AllowedESGetIpList:
        Type: CommaDelimitedList
        Description: List of ip and cidr for elasticsearch retrieval
      AllowedESUpdateIpList:
        Type: CommaDelimitedList
        Description: List of ip and cidr for elasticsearch update
      ESDomainName:
        Type: String
        Description: Name of Elastic Search Domain
        Default: advantagehelp
    Resources:
      ElasticsearchDomain:
        Type: 'AWS::Elasticsearch::Domain'
        Metadata:
          cfn_nag:
            rules_to_suppress:
              - id: W28
                reason: "Needed to name elasticsearch domain"
        Properties:
          DomainName: !Ref ESDomainName
          ElasticsearchClusterConfig:
            DedicatedMasterEnabled: false
            InstanceCount: !Ref NodeCount
            ZoneAwarenessEnabled: false
            InstanceType: !Ref NodeType
          ElasticsearchVersion: '6.4'
          AccessPolicies:
            Version: 2012-10-17
            Statement:
              - Action:
                  - 'es:ESHttpGet'
                  - 'es:ESHttpHead'
                Principal: '*'
                Effect: Allow
                Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ESDomainName}/*
                Condition:
                  IpAddress:
                    'aws:SourceIp': !Ref AllowedESGetIpList
              - Action:
                  - 'es:ESHttpGet'
                  - 'es:ESHttpHead'
                  - 'es:ESHttpPost'
                  - 'es:ESHttpPut'
                Principal: '*'
                Effect: Allow
                Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ESDomainName}/*
                Condition:
                  IpAddress:
                    'aws:SourceIp': !Ref AllowedESUpdateIpList
          EBSOptions:
            EBSEnabled: true
            Iops: 0
            VolumeSize: 10
            VolumeType: gp2
          SnapshotOptions:
            AutomatedSnapshotStartHour: 1
          AdvancedOptions:
            rest.action.multi.allow_explicit_index: "true"
      ESDomainUpdate:
        Type: "Custom::elasticsearch"
        Properties:
          ServiceToken: "arn:aws:lambda:us-east-1:123412341234:function:update-es-domain"
          ESDomainName: "advantagehelp"
        DependsOn: ElasticsearchDomain
    Outputs:
      KibanaURL:
        Description: Kibana URL
        Value: !Join
          - ''
          - - !GetAtt
              - ElasticsearchDomain
              - DomainEndpoint
            - /_plugin/kibana/
      ElasticsearchEndpoint:
        Description: Elasticsearch domain endpoint
        Value: !GetAtt
          - ElasticsearchDomain
          - DomainEndpoint
      ElasticsearchDomainARN:
        Description: Elasticsearch domain ARN
        Value: !GetAtt
          - ElasticsearchDomain
          - DomainArn
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-12
      • 2018-08-03
      • 1970-01-01
      • 1970-01-01
      • 2020-12-21
      • 1970-01-01
      相关资源
      最近更新 更多