Come posso risolvere l'errore che ricevo in CloudFormation quando provo a pubblicare log lenti su CloudWatch Logs?

6 minuti di lettura
0

Voglio risolvere l'errore che ricevo in AWS CloudFormation quando provo a pubblicare log lenti su File di log Amazon CloudWatch. L'errore è: "La policy di accesso alle risorse specificata per il gruppo di log di CloudWatch Logs /aws/aes/domains/search/search-logs non concede autorizzazioni sufficienti ad Amazon Elasticsearch Service per la creazione di un flusso di log."

Breve descrizione

Per risolvere questo errore, utilizza una policy separata a livello di gruppo di log per consentire ad Amazon Elasticsearch Service (Amazon ES) di inviare i log a CloudWatch Logs. Quindi, utilizza AccessPolicies nella risorsa AWS: :Elasticsearch::Domain per impostare le autorizzazioni per i domini Amazon ES.

I passaggi seguenti mostrano come pubblicare log lenti su CloudWatch con CloudFormation utilizzando una risorsa personalizzata supportata da AWS Lambda creata in Python 3.6. La risorsa personalizzata attiva una funzione Lambda, che attiva l'API PutResourcePolicy per pubblicare log lenti.

Nota: Quando CloudFormation abilita la pubblicazione dei log su CloudWatch, la risorsa AWS::Logs::LogGroup non dispone di una proprietà per assegnare una policy di accesso alle risorse. La policy di accesso alle risorse specificata per il gruppo di log CloudWatch Logs dovrebbe concedere autorizzazioni sufficienti per consentire ad Amazon ES di pubblicare il flusso di log. Non puoi creare un'autorizzazione alla policy di accesso direttamente utilizzando una risorsa CloudFormation. Questo perché la chiamata API PutResourcePolicy per la risorsa AWS::Logs::LogGroup non è supportata da CloudFormation.

Soluzione

Nota: Se ricevi errori durante l'esecuzione dei comandi dell'interfaccia della linea di comando (AWS CLI), assicurati di utilizzare la versione più recente di AWS CLI.

Il seguente modello CloudFormation utilizza una risorsa personalizzata per ottenere il nome del gruppo di log. Quindi, il modello applica la policy che consente al servizio Amazon ES di effettuare chiamate API sul gruppo di log.

1.    Crea un modello CloudFormation chiamato ESlogsPermission.yaml:

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

AWSTemplateFormatVersion: 2010-09-09
Description: AWS cloudFormation template to publish slow logs to Amazon CloudWatch Logs.
Parameters:
  LogGroupName:
    Type: String
    Description: Please don't change the log group name while updating
  ESDomainName:
    Description: A name for the Amazon Elastic Search domain
    Type: String
  LambdaFunctionName:
    Description: Lambda Function Name
    Type: String
Resources:
  AwsLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Ref LogGroupName
  LambdaLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Sub '/aws/lambda/${LambdaFunctionName}'
  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: root1
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: !Sub >-
                  arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:log-stream:*
        - PolicyName: root2
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                Resource:
                  - !Sub >-
                    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}
                  - !Sub >-
                    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${LogGroupName}
              - Effect: Allow
                Action:
                  - 'logs:PutResourcePolicy'
                  - 'logs:DeleteResourcePolicy'
                Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*'
  logGroupPolicyFunction:
    DependsOn: LambdaLogGroup
    Type: 'AWS::Lambda::Function'
    Properties:
      FunctionName: !Ref LambdaFunctionName
      Code:
        ZipFile: >
          import urllib3

          import json

          import boto3

          http = urllib3.PoolManager()

          SUCCESS = "SUCCESS"

          FAILED = "FAILED"

          def send(event, context, responseStatus, responseData,
          physicalResourceId=None, noEcho=False):
              responseUrl = event['ResponseURL']
              print(responseUrl)
              responseBody = {}
              responseBody['Status'] = responseStatus
              responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
              responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
              responseBody['StackId'] = event['StackId']
              responseBody['RequestId'] = event['RequestId']
              responseBody['LogicalResourceId'] = event['LogicalResourceId']
              responseBody['NoEcho'] = noEcho
              responseBody['Data'] = responseData
              json_responseBody = json.dumps(responseBody)
              print("Response body:\n" + json_responseBody)
              headers = {
                  'content-type' : '',
                  'content-length' : str(len(json_responseBody))
              }
              try:
                  response = http.request('PUT',responseUrl,body=json_responseBody.encode('utf-8'),headers=headers)
                  print("Status code: " + response.reason)
              except Exception as e:
                  print("send(..) failed executing requests.put(..): " + str(e))
          def handler(event, context):
              logsgroup_policy_name=event['ResourceProperties']['CWLOGS_NAME']
              cw_log_group_arn=event['ResourceProperties']['CWLOG_ARN']
              cwlogs = boto3.client('logs')
              loggroup_policy={
                  "Version": "2012-10-17",
                  "Statement": [{
                  "Sid": "",
                  "Effect": "Allow",
                  "Principal": { "Service": "es.amazonaws.com"},
                  "Action":[
                  "logs:PutLogEvents",
                  " logs:PutLogEventsBatch",
                  "logs:CreateLogStream"
                      ],
                  'Resource': f'{cw_log_group_arn}'
                  }]
                  }
              loggroup_policy = json.dumps(loggroup_policy)
              if(event['RequestType'] == 'Delete'):
                  print("Request Type:",event['RequestType'])
                  cwlogs.delete_resource_policy(
                  policyName=logsgroup_policy_name
                 )
                  responseData={}
                  send(event, context, SUCCESS, responseData)
              elif(event['RequestType'] == 'Create'):
                  try:
                      cwlogs.put_resource_policy(
                      policyName = logsgroup_policy_name,
                      policyDocument = loggroup_policy
                       )
                      responseData={}
                      print("Sending response to custom resource")
                      send(event, context, SUCCESS, responseData)
                  except Exception as  e:
                      print('Failed to process:', e)
                      send(event, context, FAILED, responseData)
              elif(event['RequestType'] == 'Update'):
                  try:
                      responseData={}
                      print("Update is not supported on this resource")
                      send(event, context, SUCCESS, responseData)
                  except Exception as  e:
                      print('Failed to process:', e)
                      send(event, context, FAILED, responseData)
      Handler: index.handler
      Role: !GetAtt
        - LambdaExecutionRole
        - Arn
      Runtime: python3.6
  logGroupPolicycustomresource:
    Type: 'Custom::LogGroupPolicy'
    Properties:
      ServiceToken: !GetAtt
        - logGroupPolicyFunction
        - Arn
      CWLOGS_NAME: !Ref LogGroupName
      CWLOG_ARN: !GetAtt
        - AwsLogGroup
        - Arn
  ElasticsearchDomain:
    Type: 'AWS::Elasticsearch::Domain'
    DependsOn: logGroupPolicycustomresource
    Properties:
      DomainName: !Ref ESDomainName
      ElasticsearchVersion: '6.2'
      EBSOptions:
        EBSEnabled: true
        VolumeSize: 10
        VolumeType: gp2
      LogPublishingOptions:
        SEARCH_SLOW_LOGS:
          CloudWatchLogsLogGroupArn: !GetAtt
            - AwsLogGroup
            - Arn
          Enabled: true

2.    Per avviare uno stack CloudFormation con il file ESlogsPermission.yaml, utilizza la console CloudFormation o il seguente comando AWS CLI:

aws cloudformation create-stack --stack-name yourStackName --template-body file://yourTemplateName --parameters ParameterKey=LogGroupName,ParameterValue=Your-LogGroup-Name, ParameterKey=ESDomainName,ParameterValue=Your-ES-Name --capabilities CAPABILITY_NAMED_IAM --region yourRegion

**Nota:**Sostituisci yourStackName, yourTemplateName, Your-LogGroup-name, Your-ES-Name e yourRegion con i tuoi valori.

Il modello CloudFormation esegue le seguenti operazioni per te:

1.    Crea un gruppo di log.

2.    Crea una funzione Lambda. La funzione Lambda ottiene il nome del gruppo di log dalla sezione Parametri del modello CloudFormation utilizzando una risorsa personalizzata. La funzione Lambda chiama l'API PutResourcePolicy per il nome del gruppo di log. Il gruppo di log deve disporre di una policy per consentire al dominio Amazon ES di inserire i log.

3.    Crea una risorsa personalizzata supportata da Lambda per richiamare la funzione Lambda creata nel passaggio 2. La risorsa personalizzata aiuta ad applicare PutResourcePolicy sul gruppo di log del nome della risorsa Amazon (ARN) in modo che Amazon ES possa trasmettere i log. Nel modello, CloudFormation utilizza una risorsa personalizzata per creare un dominio Amazon ES con LogPublishingOption.


AWS UFFICIALE
AWS UFFICIALEAggiornata 3 anni fa