PCG logo
Artikel

Reverse DNS in großem Umfang für die gesamte AWS-Organisation

Zugegeben, Reverse DNS ist nicht das aufregendste Thema überhaupt, aber es ist in bestimmten Situationen, z. B. bei der Kerberos-Authentifizierung, unerlässlich. Reverse DNS (rDNS) ist der Prozess, bei dem eine IP-Adresse auf einen Hostnamen zurückgeführt wird, was das Gegenteil des häufigeren Forward DNS-Lookups ist, bei dem ein Hostname auf eine IP-Adresse zurückgeführt wird.

AWS aktiviert automatisch die Funktion "Autodefined rules for reverse DNS resolution" für alle VPCs. Diese Funktion erstellt Reverse-DNS-Einträge für alle IP-Adressen innerhalb einer VPC, die wie folgt formatiert sind: ip-192-168-1-40.eu-central-1.compute.internal. Dies ist jedoch für Szenarien wie die Kerberos-Authentifizierung nicht ausreichend, bei denen host.example.com z. B. auf 192.168.1.40 aufgelöst werden sollte und der Reverse-Lookup zurück zu host.example.com anstelle von ip-192-168-1-40.eu-central-1.compute.internal führen sollte.

Die große Herausforderung besteht darin, die Erstellung von PTR-Datensätzen für Reverse-DNS zu automatisieren, idealerweise für eine gesamte AWS-Organisation.

In diesem Artikel werde ich eine Lösung zur Automatisierung der Erstellung von Reverse-DNS-Einträgen (PTR) für eine gesamte AWS-Organisation vorstellen. Immer wenn ein A-Eintrag in einer Route 53 Private Hosted Zone erstellt wird, wird der entsprechende PTR in der Reverse-Lookup-Zone (in-addr.arpa) automatisch generiert. Diese PTR-Datensätze sind in der gesamten AWS-Organisation und, falls gewünscht, auch von lokalen Umgebungen aus zugänglich.

Kurzer Überblick über die Lösung

image-a19faafdf7d2

Ereignisbasierter Aktualisierungsprozess:

1. Eine EventBridge-Regel leitet das Ereignis "ChangeResourceRecordSets" von Route53 an das zentrale Netzwerkkonto weiter.

2. Die Ereignisse werden in einer SQS-Warteschlange gesammelt und von einer Lambda-Funktion verarbeitet.

3. Die Lambda-Funktion prüft, ob es sich bei dem Ereignis um einen A-Datensatz handelt und ob es eine entsprechende Reverse-Lookup-Zone für die IP-Adresse gibt.

4. Je nach Ereignis wird der PTR-Eintrag erstellt oder gelöscht.

Geplanter Aktualisierungsprozess

(Dieser Prozess ist wichtig, um zunächst alle PTRs zu erstellen, wenn bereits A-Datensätze in der AWS-Organisation vorhanden sind)

1. EventBridge plant die Lambda-Funktion im Netzwerkkonto so, dass sie einmal täglich ausgeführt wird.

2. Die Lambda-Funktion erkennt den Detailtyp "Geplantes Ereignis".

3. Die Lambda-Funktion durchläuft alle Konten in der AWS-Organisation und sucht nach Route53 Private Hosted Zones.

4. Alle gefundenen A-Datensätze werden in eine Python-Liste geschrieben.

5. Am Ende wird geprüft, ob für jeden gefundenen A-Eintrag bereits ein PTR vorhanden ist. Wenn nicht, wird eine erstellt.

Auflösen von PTR-Einträgen innerhalb der AWS-Organisation

1. Die Route53 Private Hosted Zones für die Reverse-Lookup-Zonen, z. B. 168.192.in-addr.arpa., sind mit der Network Account VPC verbunden.

2. Das Netzwerkkonto verfügt sowohl über einen Route53 Inbound Resolver als auch über einen Outbound Resolver.

3. Im Netzwerkkonto gibt es eine Route53-Resolver-Regel, die den Outbound-Resolver verwendet und auf die IP-Adresse des Inbound-Resolvers abzielt. Diese Regel wird über den AWS Resource Access Manager (RAM) für die gesamte AWS-Organisation freigegeben.

4. Die Projekt-VPCs sind mit den Route53-Resolver-Regeln verbunden.

5. Infolgedessen werden alle Reverse-DNS-Anfragen für 168.192.in-addr.arpa. an den Inbound Resolver gesendet, der sie auflösen kann.

Auflösen von PTR-Datensätzen von vor Ort

1. Auf dem lokalen DNS-Server ist eine bedingte Weiterleitung konfiguriert, z. B. für 168.192.in-addr.arpa. Das Ziel ist auf den Inbound Resolver eingestellt.

2. Der Inbound Resolver kennt die Route53 Private Hosted Zones, so dass er die PTR-Datensätze auflösen und an den lokalen DNS-Server antworten kann.

Detaillierte Lösung

Einrichten von Route53 Private Hosted Zones für Reverse Lookup Zones

Die Private Hosted Zones (PHZ) für Reverse-DNS-Lookups müssen dem in-addr.arpa-Schema folgen. Das bedeutet, dass der Domänenname aus den IP-Adressblöcken in umgekehrter Reihenfolge besteht, gefolgt von .in-addr.arpa. Zum Beispiel ist 168.192.in-addr.arpa die Reverse-Lookup-Zone für 192.168. Da die CIDR-Notation hier nicht verwendet werden kann, müssen mehrere PHZs erstellt werden, wenn Sie Reverse-Lookup-Zonen nur für bestimmte Subnetze wie 192.168.1.0/24 und 192.168.2.0/24 wünschen.

PHZ for 192.168.1.0/24:

  • Domain Name: 1.168.192.in-addr.arpa
  • IP Range Covered: 192.168.1.0 to 192.168.1.255

PHZ for 192.168.2.0/24

  • Domain Name: 2.168.192.in-addr.arpa
  • IP Range Covered: 192.168.2.0 to 192.168.2.255
yaml
Code kopiert!copy-button
 ReverseLookup1921681:
    Type: AWS::Route53::HostedZone
    Properties:
      HostedZoneConfig:
        Comment: "Reverse Lookup Zone for 192.168.1.0/24"
      Name: "1.168.192.in-addr.arpa"
      VPCs:
        - VPCId: !Ref NetworkVpc
          VPCRegion: "eu-central-1"

Route53-Resolver Rule und RAM-Freigabe einrichten

Damit VPCs umgekehrte DNS-Einträge (PTR) auflösen können, muss im Netzwerkkonto eine Route53-Auflösungsregel erstellt werden. Diese Regel wird über den AWS Resource Access Manager (RAM) für das gesamte Unternehmen freigegeben. Die Auflösungsregeln müssen mit jeder VPC verknüpft werden, die die Fähigkeit zur umgekehrten Auflösung von DNS-Namen benötigt.

yaml
Code kopiert!copy-button
InboundResolverForwardingRuleReverseDNSLookup1921681:
    Type: AWS::Route53Resolver::ResolverRule
    Properties: 
      DomainName: "1.168.192.in-addr.arpa." 
      Name: "1-168-192.in-addr.arpa"
      ResolverEndpointId: !Ref OutboundResolverEndpoint
      RuleType: FORWARD
      TargetIps: 
        - Ip:
          Port: 53
        - Ip:
          Port: 53

  ResolverForwardingRuleShare:
    Type: AWS::RAM::ResourceShare
    Properties: 
      AllowExternalPrincipals: False
      Name: route-53-resolver-share
      Principals: 
        - !Sub arn:aws:organizations::${OrgAccountId}:organization/${OrgId}
      ResourceArns: 
        - !GetAtt InboundResolverForwardingRuleReverseDNSLookup1921681.Arn

Wichtig! Automatische AWS-Aktivierung von "Autodefined Rules for Reverse DNS Resolution"

AWS aktiviert automatisch die Funktion "Autodefined rules for reverse DNS resolution" für alle VPCs. Diese Funktion finden Sie unter Route 53 -> Resolver -> VPCs.

image-be6c03617741

Daher werden automatisch Reverse-DNS-Einträge für alle IP-Adressen innerhalb einer VPC erstellt, die wie folgt formatiert sind: ip-192-168-1-40.eu-central-1.compute.internal.

Code kopiert!copy-button
nslookup 192.168.1.40
192-168-1-40.in-addr.arpa  name = ip-10-0-1-40.eu-central-1.compute.internal.

Das Problem dabei ist, dass diese automatische Auflösung manchmal gegenüber der Route 53-Auflösungsregel bevorzugt wird, wodurch unsere PTRs aus der Private Hosted Zone nicht aufgelöst werden. Um sicherzustellen, dass unsere PTRs aus der Private Hosted Zone korrekt aufgelöst werden, sollte diese Funktion für alle VPCs deaktiviert werden, die unsere Reverse-DNS-Lösung verwenden möchten. Dies kann mit dem folgenden Beispiel-Python-Boto3-Befehl automatisiert werden, der für jede VPC ausgeführt werden kann:

python
Code kopiert!copy-button
def update_resolver_config(vpcId, r53_client):
    try:
        resolverConfig = r53_client.get_resolver_config(ResourceId=vpcId)
        if resolverConfig['ResolverConfig']['AutodefinedReverse'] != 'DISABLE':
            r53_client.update_resolver_config(
                ResourceId=vpcId,
                AutodefinedReverseFlag='DISABLE'
            )
            print(f'Reverse DNS lookups disabled for VPC {vpcId}')
    except ClientError as e:
        print(f'Failed to update resolver config for VPC {vpcId}: {e}')

Erstellung von eingehenden und ausgehenden Resolvern

Die Route 53-Resolver für eingehende und ausgehende Verbindungen sollten im Netzwerkkonto erstellt werden. Außerdem muss die designierte Sicherheitsgruppe den DNS-Port 53 sowohl für TCP- als auch für UDP-Datenverkehr geöffnet haben.

yaml
Code kopiert!copy-button
 InboundResolverEndpoint:
    Type: AWS::Route53Resolver::ResolverEndpoint
    Properties: 
      Direction: INBOUND
      IpAddresses:
        - SubnetId: !Ref DnsSubnetA
          Ip: !GetAtt IPAddressesCustomResource.FifthIpSubnetA     
        - SubnetId: !Ref DnsSubnetB
          Ip: !GetAtt IPAddressesCustomResource.FifthIpSubnetB
      Name: inbound-resolver-1
      SecurityGroupIds: 
        - !Ref DnsSecurityGroup

  OutboundResolverEndpoint:
    Type: AWS::Route53Resolver::ResolverEndpoint
    Properties: 
      Direction: OUTBOUND
      IpAddresses:
        - SubnetId: !Ref DnsSubnetA
          Ip: !GetAtt IPAddressesCustomResource.SixthIpSubnetA
        - SubnetId: !Ref DnsSubnetB
          Ip: !GetAtt IPAddressesCustomResource.SixthIpSubnetB
      Name: outbound-resolver-1
      SecurityGroupIds: 
        - !Ref DnsSecurityGroup

  DnsSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: String
      GroupName: 'DnsSecurityGroup'
      SecurityGroupEgress: 
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 192.168.0.0/16
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: 192.168.0.0/16
      SecurityGroupIngress:
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 192.168.0.0/16
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: 192.168.0.0/16
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 192.168.0.0/16
      VpcId: !Ref DnsVpc

EventBridge-Regel in allen Konten erstellen

Um die Route53 "ChangeResourceRecordSets"-Ereignisse von allen Konten an das Netzwerkkonto weiterzuleiten, muss in jedem Konto eine EventBridge-Regel erstellt werden. Es ist wichtig, diese Regel in der Region us-east-1 zu erstellen, da Route53 ein globaler Dienst ist und die CloudTrail-Ereignisse von dort ausgehen. Für das EventBridge-Ziel wird das Netzwerkkonto ausgewählt, und es besteht die Möglichkeit, in diesem Stadium eine andere Region zu wählen.

yaml
Code kopiert!copy-button
 AutomatedPTRforReverseDNSlookup:
    Type: "AWS::Events::Rule"
    Condition: IsNotNetworkAccount
    Properties: 
      Description: forwards the ChangeResourceRecordSets event to the Network Account
      EventPattern: 
        source: 
          - "aws.route53"
        detail-type: 
          - "AWS API Call via CloudTrail"
        detail: 
          eventSource: 
            - "route53.amazonaws.com"
          eventName: 
            - "ChangeResourceRecordSets"
      Name: "AutomatedPTRforReverseDNSlookup"
      Targets: 
        - Arn: !Sub "arn:aws:events:eu-central-1:${NetworkAccount}:event-bus/default"
          Id: "AutomatedPTRforReverseDNSlookup"
          RoleArn: !GetAtt EventBridgeRuleRole.Arn

  EventBridgeRuleRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "EventBridgeRuleRole"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "events.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"

  RolePolicies: 
    Type: "AWS::IAM::Policy"
    Properties: 
      PolicyName: "EventBridgeRuleRolePolicy"
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Action: "events:PutEvents"
            Resource: !Sub "arn:aws:events:eu-central-1:${NetworkAccount}:event-bus/default"
      Roles: 
        - Ref: "EventBridgeRuleRole"

Entwicklung einer zentralen Update-PTR-Logik

Im Netzwerkkonto muss eine EventBridge-Regel für "ChangeResourceRecordSets"-Ereignisse eingerichtet werden. Das Ziel für diese Regel sollte eine SQS-Warteschlange sein. Die Lambda-Funktion wird von dieser SQS-Warteschlange über ein EventSourceMapping ausgelöst.

EventBridge-Regel für ereignisbasierte und geplante:

yaml
Code kopiert!copy-button
EventBridgeRule:
    Type: "AWS::Events::Rule"
    Properties:
      Description: "Create a PTR record in reverse DNS zones whenever an A record in a PHZ is created, modified, or deleted."
      State: ENABLED
      EventPattern: !Sub
        - |
          {
            "source": [
              "aws.route53"
            ],
            "detail-type": [
              "AWS API Call via CloudTrail"
            ],
            "detail": {
              "eventSource": [
                "route53.amazonaws.com"
              ],
              "eventName": [
                "ChangeResourceRecordSets"
              ]
            }
          }
        - { eventNames: !Join [ '", "', !Ref Events ] }
      Name: AutomatedPTRforReverseDNSlookup
      Targets:
        - Arn: !GetAtt SQSQueue.Arn
          Id: AutomatedPTRforReverseDNSlookup

  ScheduledEventBridgeRule:
    Type: "AWS::Events::Rule"
    Properties:
      Description: "Checks once a day if all PTR Records are set up correctly"
      State: ENABLED
      ScheduleExpression: !Ref Schedule
      Name: !Sub "AutomatedPTRforReverseDNSlookupScheduled"
      Targets:
        - Arn: !GetAtt SQSQueue.Arn
          Id: AutomatedPTRforReverseDNSlookup

SQS Queue:

yaml
Code kopiert!copy-button
SQSQueue:
    Type: "AWS::SQS::Queue"
    Properties:
      QueueName: AutomatedPTRforReverseDNSlookup
      MessageRetentionPeriod: 1000
      ReceiveMessageWaitTimeSeconds: 0
      VisibilityTimeout: 950

  SQSQueuePolicy:
    Type: "AWS::SQS::QueuePolicy"
    Properties:
      PolicyDocument: !Sub |
              {
                "Version": "2012-10-17",
                "Id": "${SQSQueue.Arn}/SQSDefaultPolicy",
                "Statement": [
                  {
                    "Effect": "Allow",
                    "Principal": {
                      "Service": "events.amazonaws.com"
                    },
                    "Action": "sqs:SendMessage",
                    "Resource": "${SQSQueue.Arn}",
                    "Condition": {
                      "ArnEquals": {
                        "aws:SourceArn": [
                          "${CloudWatchRule.Arn}",
                          "${ScheduledCloudWatchRule.Arn}"
                          ]
                      }
                    }
                  }
                ]
              }
      Queues:
        - !Ref SQSQueue 

Lambda Function, Lambda Role und EventSourceMapping:

yaml
Code kopiert!copy-button
 LambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: AutomatedPTRforReverseDNSlookup
      Description: Creates a PTR record in reverse DNS zones when an A record in a PHZ is created, modified, or deleted.
      Handler: !Sub ${PythonFileName}.lambda_handler
      Role: !GetAtt LambdaRole.Arn
      MemorySize: 128
      Timeout: 900
      Runtime: "python3.12"
      Code:
        S3Bucket: !Ref S3Bucket
        S3Key: !Ref S3Key
      Environment:
        Variables:
          LambdaRole: !Ref CrossAccountLambdaRole
          OrganizationAccountId: !Ref OrganizationAccount
          ReverseLookupZone1921681Id: !Ref ReverseLookupZone1921681

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AutomatedPTRforReverseDNSlookupRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AutomatedPTRforReverseDNSlookupRolePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                Resource: "*"
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - Fn::Sub: 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*'
              - Effect: Allow
                Action:
                  - sqs:ReceiveMessage
                  - sqs:DeleteMessage
                  - sqs:GetQueueAttributes
                Resource: !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:AutomatedPTRforReverseDNSlookup"
              - Effect: Allow
                Action:
                  - sts:AssumeRole
                Resource: !Sub "arn:aws:iam::*:role/${CrossAccountLambdaRole}"
              - Effect: "Allow"
                Action:
                  - route53:ChangeResourceRecordSets
                  - route53:ListResourceRecordSets
                  - route53:GetHostedZone
                Resource: 
                  - !Sub "arn:aws:route53:::hostedzone/${ReverseLookupZone1921681}"
              - Effect: Allow
                Action:
                  - logs:CreateLogDelivery
                  - logs:DeleteLogDelivery
                Resource: "*"

  EventSourceMapping:
    Type: "AWS::Lambda::EventSourceMapping"
    Properties:
      BatchSize: 10
      Enabled: true
      EventSourceArn: !GetAtt SQSQueue.Arn
      FunctionName: !Ref LambdaFunction

Die Lambda-Funktion verarbeitet CloudTrail-Ereignisse für die Aktion "ChangeResourceRecordSets". Sie prüft, ob das Ereignis einen A-Datensatz beinhaltet und ob die IP-Adresse innerhalb einer der vordefinierten Route53 Private Hosted Zones (PHZ) für Reverse-Lookup liegt. Wenn die IP-Adresse übereinstimmt, erstellt oder löscht die Funktion den entsprechenden PTR-Eintrag auf der Grundlage der Ereignisaktion.

Außerdem verarbeitet der Lambda ein geplantes Ereignis, das täglich von EventBridge ausgelöst wird. Dieses geplante Ereignis durchläuft alle Konten in der AWS-Organisation, um Route53 Private Hosted Zones zu finden. Es sammelt alle A-Datensätze in einer Python-Liste. Am Ende der Iteration prüft der Lambda, ob für jeden gefundenen A-Datensatz ein PTR-Datensatz existiert; falls nicht, wird einer erstellt.

Nachfolgend finden Sie den Python-Code für die Lambda-Funktion:

python
Code kopiert!copy-button
import json
import boto3
import os
from ipaddress import ip_address
from botocore.exceptions import ClientError

# Environment variables for the zone IDs
REVERSELOOKUPZONE1921681ID = os.environ['ReverseLookupZone1921681Id']

# Environment variable for ORG AccountID
ORGACCOUNTID = os.environ['OrganizationAccountId']

# Mapping IP prefixes to zone IDs
zone_mapping = {
    '192.168.1': REVERSELOOKUPZONE10200ID
}

# Initialize the Route53 client
route53_client = boto3.client('route53')

def lambda_handler(event, context):
    messages = [json.loads(record['body']) for record in event['Records']]
    for message in messages:
        try:
            cleanedEvent = json.loads(message['Message'])
        except:
            cleanedEvent = message
        print(f'CleanedEvent: {cleanedEvent}')
        if cleanedEvent["detail-type"] == "AWS API Call via CloudTrail":
            print('API Call')
            # Nur aufrufen, wenn der Event ein A-Record betrifft
            if is_a_record_event(cleanedEvent):
                handle_called_event(cleanedEvent, context)
        elif cleanedEvent["detail-type"] == "Scheduled Event":
            print('Scheduled')
            handle_scheduled_event(cleanedEvent, context)

def is_a_record_event(event_detail):
    eventName = event_detail['detail']['eventName']
    if eventName == 'ChangeResourceRecordSets':
        changes = event_detail['detail']['requestParameters']['changeBatch']['changes']
        for change in changes:
            if change.get('resourceRecordSet', {}).get('type') == 'A':
                return True
    return False

def handle_called_event(event, context):
    eventName = event['detail']['eventName']

    if eventName == 'ChangeResourceRecordSets':
        changes = event['detail']['requestParameters']['changeBatch']['changes']
        for change in changes:
            action = change['action']
            resource_record_set = change.get('resourceRecordSet', {})
            resource_records = resource_record_set.get('resourceRecords', [])
            dns_name = resource_record_set.get('name', 'Unknown DNS Name')
            for record in resource_records:
                ip_address = record['value']
                record_info = {'DNS Name': dns_name, 'IP Address': ip_address}
                if action == 'CREATE':
                    manage_ptr_record(record_info, action='CREATE')
                elif action == 'DELETE':
                    manage_ptr_record(record_info, action='DELETE')

def manage_ptr_record(record_info, action):
    ip_addr_str = record_info['IP Address']
    dns_name = record_info['DNS Name']

    if not ip_addr_str.startswith(('192.168.1'')):
        print(f'The IP {ip_addr_str} is not in the valid range.')
        return

    zone_prefix = ip_addr_str.split('.')[1]
    zone_id = zone_mapping.get('192.' + zone_prefix)
    if not zone_id:
        print(f"No zone ID found for the IP prefix 192.{zone_prefix}.")
        return

    ip_blocks = ip_addr_str.split('.')
    reversed_ip_blocks = ip_blocks[::-1]
    reversed_ip = '.'.join(reversed_ip_blocks) + '.in-addr.arpa.'

    ptr_record_name = dns_name

    # Überprüfe, ob der PTR-Eintrag bereits existiert
    paginator = route53_client.get_paginator('list_resource_record_sets')
    record_exists = False
    try:
        for page in paginator.paginate(HostedZoneId=zone_id):
            for record_set in page['ResourceRecordSets']:
                if (record_set['Type'] == 'PTR' and
                    record_set['Name'].rstrip('.') == reversed_ip.rstrip('.') and
                    any(rr['Value'].rstrip('.') == ptr_record_name.rstrip('.') for rr in record_set.get('ResourceRecords', []))):
                        record_exists = True
                        break
            if record_exists:
                # Schleife verlassen, wenn der Eintrag gefunden wurde
                break
    except ClientError as e:
        print(f"An error occurred during PTR record check: {e}")
        return

    if not record_exists and action == 'DELETE':
        print(f"No existing PTR record for {ip_addr_str} to delete.")
        return

    # If it does not exist and the action is CREATE, or if it exists and the action is DELETE, implement the change.
    if (not record_exists and action == 'CREATE') or (record_exists and action == 'DELETE'):
        change_batch = {
            'Comment': f'{action} PTR record for {ip_addr_str}',
            'Changes': [{
                'Action': action,
                'ResourceRecordSet': {
                    'Name': reversed_ip,
                    'Type': 'PTR',
                    'TTL': 300,
                    'ResourceRecords': [{'Value': ptr_record_name}]
                }
            }]
        }

        try:
            response = route53_client.change_resource_record_sets(
                HostedZoneId=zone_id,
                ChangeBatch=change_batch
            )
            print(f'Successfully {action} PTR record for {ip_addr_str} to name {ptr_record_name} : {response}')
        except ClientError as e:
            print(f'An error occurred: {e}')
    else:
        if action == 'CREATE':
            print(f"PTR record for {ip_addr_str} to {ptr_record_name} already exists. Skipping CREATE.")
        elif action == 'DELETE':
            print(f"PTR record for {ip_addr_str} to {ptr_record_name} does not exist. Skipping DELETE.")

def handle_scheduled_event(event, context):
    # List to store all A-Record data
    a_records_list = []

    ## Iterate through all accounts
    for accountId in get_accounts():
        print(f'############## CHECKING Account {accountId} ##############')

        region = 'eu-central-1'
        roleName = os.environ['LambdaRole']
        route53_client = get_session(accountId, region, roleName).client('route53')

        # Paginate through all the hosted zones
        hosted_zones_paginator = route53_client.get_paginator('list_hosted_zones')
        for hosted_zones_page in hosted_zones_paginator.paginate():
            for hosted_zone in hosted_zones_page['HostedZones']:
                hosted_zone_id = hosted_zone['Id']
                print(f'############## Checking Hosted Zone {hosted_zone_id} ##############')
                
                record_sets_paginator = route53_client.get_paginator('list_resource_record_sets')
                for record_sets_page in record_sets_paginator.paginate(HostedZoneId=hosted_zone_id):
                    for record_set in record_sets_page['ResourceRecordSets']:
                        if record_set['Type'] == 'A':  # Check for 'A' records
                            # Use get() with a default value to avoid KeyError
                            resource_records = record_set.get('ResourceRecords', [])
                            # Store the A-Record data in the list
                            for ip_record in resource_records:
                                a_records_list.append({
                                    'DNS Name': record_set['Name'],
                                    'IP Address': ip_record['Value']
                                })
                                print(f"A-Record found: {record_set['Name']} - {ip_record}")
    # Process each A-Record in the list using the function 'manage_ptr_record'
    for a_record in a_records_list:
        manage_ptr_record(a_record, action='CREATE')  


def get_accounts():
    roleName = os.environ['LambdaRole']
    org_client = get_session(ORGACCOUNTID, 'eu-central-1', roleName).client('organizations')
    accountList = []
    accountPaginator = org_client.get_paginator('list_accounts')
    accountIterator = accountPaginator.paginate()
    for accounts in accountIterator:
        for account in accounts['Accounts']:
            if account['Status'] == "ACTIVE":
                accountList.append(account['Id'])
    return accountList

def get_session(accountId, region, roleName):
    sts_client = boto3.client('sts')
    assumed_role_object = sts_client.assume_role(
        RoleSessionName='xyz', RoleArn=f'arn:aws:iam::{accountId}:role/{roleName}')
    credentials = assumed_role_object['Credentials']
    access_key_id = credentials['AccessKeyId']
    secret_access_key = credentials['SecretAccessKey']
    session_token = credentials['SessionToken']
    new_session = boto3.Session(
        aws_access_key_id=access_key_id,
        aws_secret_access_key=secret_access_key,
        aws_session_token=session_token,
        region_name=region
    )
    return new_session

Die IAM-Rolle für jedes Konto, das von der Lambda-Funktion im Netzwerkkonto für den täglichen Planer verwendet wird, erfordert die folgenden Berechtigungen:

yaml
Code kopiert!copy-button
 ReverseDNSLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: CrossAccountReverseDNSLambdaRoleRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub 'arn:aws:iam::${NetworkAccountId}:root'
            Action:
              - sts:AssumeRole
      MaxSessionDuration: 3600
      Path: /
      Policies:
        - PolicyName: CrossAccountReverseDNSLambdaRoleRolePolicy
          PolicyDocument: 
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - organizations:ListAccounts
                  - organizations:DescribeAccount
                Resource: "*"
              - Effect: Allow
                Action:
                  - route53:ListResourceRecordSets
                  - route53:GetHostedZone
                  - route53:ListHostedZones
                Resource: "*"
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: 'arn:aws:iam::*:role/*'

Schlussbemerkungen

Einige der CloudFormation-Stacks müssen als StackSets in der gesamten AWS-Organisation bereitgestellt werden. Daher wird empfohlen, Tools wie CfCTExternal Link oder LZAExternal Link, zu verwenden, insbesondere wenn ControlTower im Einsatz ist.

Ich hoffe, ich konnte eine mögliche Lösung zur Automatisierung der Erstellung von Reverse-DNS-Einträgen (PTR) in einer gesamten AWS-Organisation aufzeigen. Darüber hinaus habe ich gezeigt, wie die Reverse-DNS-Einträge innerhalb der AWS-Organisation und von vor Ort aufgelöst werden können.


Genutzte Services

Weiterlesen

Neuigkeiten
Über den Wolken: PCG's Glanzleistung bei der AWS LeadMaster Challenge 2024

Wow, was für ein Triumph! Die Public Cloud Group hat bei der AWS Summit 2024 Lead Master Challenge abgeräumt wie ein Rockstar beim Grammy-Verleih.

Mehr erfahren
Artikel
AWS-Veranstaltungen 2025: Die Zukunft ist Cloud

Als führender AWS Premier Partner blicken wir mit Spannung auf das Jahr 2025 und die wegweisenden AWS-Veranstaltungen.

Mehr erfahren
Artikel
AWS Lambda: Vermeiden Sie diese Fallstricke

Ein großartiges Angebot, um schnell Ergebnisse zu erzielen, aber wie jedes gute Tool muss es richtig eingesetzt werden.

Mehr erfahren
Fallstudie
Finanzdienste
Cloud Migration
Die Cloud Journey der VHV Gruppe - Mit Strategie zum Erfolg

Wie meistert ein Versicherungskonzern mit über 4.000 Mitarbeitern den Spagat zwischen Compliance, Modernisierung und Kosteneffizienz?

Mehr erfahren
Alles sehen

Gemeinsam durchstarten

United Kingdom
Arrow Down