Изменить группу безопасности по умолчанию, созданную AWS для AWS Active Directory с помощью облачной информации

Когда AWS Active Directory(тип: "AWS::DirectoryService::MicrosoftAD") создается посредством формирования облака, AWS также создает группу безопасности для контроллеров домена. Описание такой группы безопасности "AWS created a security group for d-123456adb directory controllers".

Эта группа безопасности разрешает вход источника как 0.0.0.0/0 для всех портов.

Я должен вручную отредактировать / установить его на свой CIDR vpc после запуска формирования облака, а также я не могу получить его идентификатор внутри формирования облака.

Есть ли способ отредактировать ИЛИ самостоятельно определить группу безопасности при создании Microsoft AD("AWS::DirectoryService::MicrosoftAD") через формирование облака?

3 ответа

Это не совсем то, о чем спрашивали, но это связано и может быть полезно.

Когда создается VPC или другой ресурс, создается система безопасности, которая может иметь слишком разрешительные правила. Как описано на этой странице в разделе "Удалить правило по умолчанию", вы можете удалить правила по умолчанию, указав новые правила. Следующая часть шаблона CloudFormation может помочь удалить старые правила и заменить их чем-то менее разрешительным.

Я обнаружил, что удаление правила выхода 0.0.0.0/0 отлично работает. Он не удаляет входящее правило ссылки на себя для текущей группы безопасности, но это, вероятно, не так важно, как правило выхода.

# Remove default security group rules in the VPC
VpcDefaultSecurityGroupEgressRemove:
Type: AWS::EC2::SecurityGroupEgress
  Properties:    
    GroupId:
      Fn::GetAtt: [VPCReference, DefaultSecurityGroup]
    IpProtocol: icmpv6
    CidrIp: 127.0.0.1/32
    Description: Effectively no access

VpcDefaultSecurityGroupIngressRemove:
  Type: AWS::EC2::SecurityGroupIngress
  Properties:    
    GroupId:
      Fn::GetAtt: [VPCReference, DefaultSecurityGroup]
    IpProtocol: icmpv6
    CidrIp: 127.0.0.1/32
    Description: Effectively no access

Я протестировал это, и он работает нормально. Мне не удалось найти способ удалить все правила группы безопасности в CloudFormation, думаю, это должна быть лямбда.

Шаблон VPC

VPCDefaultSecurityGroup:
  Value: !GetAtt VPC.DefaultSecurityGroup
  Export:
    Name: "VPCDefaultSecurityGroup"

Шаблон группы безопасности

GroupId: !ImportValue VPCDefaultSecurityGroup

Этот ответ является отредактированным ответом службы поддержки AWS на мой запрос, аналогичный вопросу ОП.

В настоящее время эта функция недоступна, и команда разработчиков AWS CloudFormation знает об этой проблеме, и имеется запрос функции.

В качестве обходного пути вы можете использовать пользовательский ресурс с резервной копией Lambda, чтобы получить идентификатор безопасности и передать его в пользовательский ресурс, чтобы к нему можно было получить доступ в стеке CF.

При таком подходе вы создадите функцию Lambda, которая может принимать имя группы безопасности и VPC-id в качестве входных данных и давать идентификатор группы безопасности в качестве выходных данных. Созданный пользовательский ресурс представляет собой фрагмент кода, который сигнализирует лямбда-функции с именем группы и идентификатором VPC. Лямбда возвращает идентификатор группы безопасности этому пользовательскому ресурсу, вы можете получить sg-id, как показано ниже:

{ "Fn::GetAtt" : ["CustomResouce", "security_group_id"] }

Образец шаблона и пример лямбда-функции (для получения идентификатора группы безопасности) включены в конец этого сообщения. В шаблоне функция использовалась при выводе кода, но вы можете использовать то же самое в клиентской группе безопасности, как показано ниже:

"SourceSecurityGroupId" : { "Fn::GetAtt" : ["CustomResouce", "security_group_id"] },
"SourceSecurityGroupName" : { "Fn::Sub": [ "${Alias}_controllers", { "Alias": {"Ref" : "Alias" }} ]},

Пользовательская лямбда-функция customresouce.py (помещается в корзину S3, где Lambda может получить к ней доступ):

import json
import boto3
import time
from botocore.vendored import requests
def lambda_handler(event, context):
    print event['RequestType']
    try:
        if event['RequestType'] == 'Delete':
            print "delete"
            responseData = {'response': 'Delete'}
            responseStatus = 'SUCCESS'
        elif event['RequestType'] == 'Create':
            print "blabla"
            ec2 = boto3.resource('ec2')
            vpc = ec2.Vpc(event['ResourceProperties']['vpc'])
            security_group_iterator = vpc.security_groups.filter(GroupNames = [event['ResourceProperties']['security_group']])
            sg_id = list(security_group_iterator.filter(GroupNames = [event['ResourceProperties']['security_group']]))[0].id
            print sg_id
            responseData = {'security_group_id': sg_id}
        elif event['RequestType'] == 'Update':
            print "update"
            responseData = {'response': 'Update'}
            responseStatus = 'SUCCESS'
        responseStatus = 'SUCCESS'
    except:
        responseStatus = 'FAILED'
        responseData = {'FAILED': 'Something bad happened.'}
    sendResponse(event, context, responseStatus, responseData)

def sendResponse(event, context, responseStatus, responseData, reason=None, physical_resource_id=None):
    responseBody = {'Status': responseStatus,
                    'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
                    'PhysicalResourceId': physical_resource_id or context.log_stream_name,
                    'StackId': event['StackId'],
                    'RequestId': event['RequestId'],
                    'LogicalResourceId': event['LogicalResourceId'],
                    'Data': responseData}
    print 'RESPONSE BODY:n' + json.dumps(responseBody)
    responseUrl = event['ResponseURL']
    json_responseBody = json.dumps(responseBody)
    headers = {
        'content-type' : '',
        'content-length' : str(len(json_responseBody))
    }
    try:
        response = requests.put(responseUrl,
                                data=json_responseBody,
                                headers=headers)
        print "Status code: " + response.reason
    except Exception as e:
        print "send(..) failed executing requests.put(..): " + str(e)

Пример шаблона, который использует пользовательскую функцию Lambda:

{
"Resources": {
  "myDirectory" : {
    "Type" : "AWS::DirectoryService::SimpleAD",
    "Properties" : {
      "Name" : "corp.example.com",
      "Password" : "P@ssword",
      "Size" : "Small",
      "VpcSettings" : {
        "SubnetIds" : [ "subnet_value-1", "subnet_value-2" ],
        "VpcId" : "your_vpc-id"
      }
    }
  },

  "CustomResouce": {
         "DependsOn": "myDirectory",
         "Type": "Custom::GettingsecuritygroupId",
         "Version" : "1.0",
         "Properties" : {
            "ServiceToken": {"Fn::GetAtt" : ["Mylambda","Arn"]},
            "vpc" : "vpc-b0ee43c9",
            "security_group" : { "Fn::Sub": [ "${Alias}_controllers", {"Alias":{"Fn::GetAtt" : ["myDirectory","Alias"]} }]}
         }
      },

  "Mylambda":{
  "Type" : "AWS::Lambda::Function",
  "Properties" : {
    "Code" : {
      "S3Bucket": "Your_s3_bucket_name",
      "S3Key": "customresource.py.zip"
    },
    "Handler" : "customresource.lambda_handler",
    "Role" : "Role_whic_has_permissions_ec2:*",
    "Runtime" :"python2.7",
    "Timeout" : "60"
  }
}

 },

 "Outputs":{
   "SGID" : {
     "Value" : { "Fn::GetAtt" : ["CustomResouce", "security_group_id"] }
   }
 }
}

Скорее глупо, но кажется, что оно может работать как временное решение, пока AWS не приступит к реализации функциональности в своих API /CloudFormation/Hosted AD. ПРИМЕЧАНИЕ. У меня еще не было возможности протестировать вышеизложенное, но я публикую его здесь для ОП и всех, кто может искать решение этой проблемы.

Рекомендации:

Пользовательская ссылка на ресурс

Пользовательские ресурсы AWS с лямбда-поддержкой

AWS::Lambda:: Функция

Вот рабочая версия, использующая собственный ресурс с внутренней лямбда-функцией.

      lambda:
  Type: AWS::Lambda::Function
  Properties:
    FunctionName: !Sub ${prefix}-function
    Runtime: python3.9
    Role: !GetAtt role.Arn
    Handler: index.lambda_handler # default file name of 'index.py', then function name
    Timeout: 60
    Code:
      ZipFile: |
        import boto3
        import cfnresponse

        def lambda_handler(event, context):
            print (event['RequestType'])
            responseStatus = 'SUCCESS'

            try:
                if event['RequestType'] == 'Delete':
                    responseData = {'response': 'Delete'}
                elif event['RequestType'] == 'Create':
                    ec2 = boto3.client('ec2')
                    groups = ec2.describe_security_groups()
                    for group in groups['SecurityGroups']:
                        if(group['GroupName'] == event['ResourceProperties']['security_group_name']):
                            responseData = {'security_group_id': group['GroupId'] }
                elif event['RequestType'] == 'Update':
                    responseData = {'response': 'Update'}
            except Exception as e:
                responseStatus = 'FAILED'
                responseData = {'FAILED': str(e)}
            
            try:
                cfnresponse.send(event, context, responseStatus, responseData)
                print ('SUCCESS')
            except Exception as e:
                print ('FAILURE: ' + str(e))

custom:
  Type: Custom::GetDirectorySecurityGroup
  Properties:
    ServiceToken: !GetAtt lambda.Arn
    vpc: !Ref vpc
    security_group_name: !Sub ${directory}_controllers
  DependsOn:
    - directory
    - lambda

ingressTCP: 
  Type: AWS::EC2::SecurityGroupIngress
  Properties:
    GroupId: !GetAtt custom.security_group_id # calls custom to get new sg
    IpProtocol: tcp
    FromPort: 49152
    ToPort: 65535
    SourceSecurityGroupId: !Ref security # id of other security group
  DependsOn:
    - security # logical-id of new sg from full cloudformation
    - directory # logical-id of ds from full cloudformation

Я публикую это для всех, кто хочет изменить правила входящего трафика для новой службы каталогов. Я пробовал всеми способами получить идентификатор группы безопасности, и это единственный метод, который сработал.