AWS: CloudFormation – S3 корзина для логов Application Load Balancer

Автор: | 27/11/2018

Имеется уже созданный стек с Application Load Balancer, для которого требуется включить сбор логов в S3 корзину.

Общая документация – тут>>>.

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

Запуск CloudFormation выполняется из Ansible с помощью модуля cloudformation, и значения параметров будут заданы в файлах group_vars/envname.yml.

Параметры

ALBLogsBucketName

Добавляем первый параметр, который будет указывать имя корзины для логов:

...
    "ALBLogsBucketName": {
      "Description": "S3 bucket name for ALB logs",
      "Type": "String"
    }
...

В Ansible задаём значение, например файл group_vars/mobilebackend-dev.yml:

...
alb_logs_bucket_name: "mobilebackend-dev-alb-logs"
...

ALBRegionAccountID

Второй необходимый параметр – ALBRegionAccountID, в котором задаём ALB Account ID, который зависит от региона. Полный список ID есть тут>>>.

Т.к. тут есть явная зависимость от региона – то его лучше задать в Mappings шаблона.

Задаём маппинг для трёх регионов (у нас стеки могут быть только в них):

...
  "Mappings": {
    "ALBRegionAccountIDs": {
      "us-east-2": {
        "ALBRegionAccountID": "033677994240"
      },
      "us-east-1": {
        "ALBRegionAccountID": "127311923021"
      },
      "eu-west-1": {
        "ALBRegionAccountID": "156460612806"
      }
    },
...

Далее используем Fn::FindInMap для получения значений.

AWSAccountID

Для создания политики доступа к корзине – потребуется AWS Account ID, добавляем параметр для него:

...
    "AWSAccountID": {
      "Description": "AWS Console Account ID for S3 Bucket Policy",
      "Type": "String"
    }
...

И добавляем в Ansible.

Т.к. значение не зависит от рабочего окружения – передаём через group_vars/all.yml:

...
aws_account_id: "534***385"
...

Можно зашифровать с помощью ansible-vault.

С параметрами вроде всё – переходим к ресурсам.

Ресурсы

Application Load Balancer attributes

Тут потребуется добавить несколько аттрибутов:

...
        "LoadBalancerAttributes" : [
          {
            "Key": "idle_timeout.timeout_seconds",
            "Value": 50
          },
          {
            "Key": "routing.http2.enabled",
            "Value": true
          },
          {
            "Key": "access_logs.s3.prefix"
          },
          {
            "Key": "deletion_protection.enabled",
            "Value": true
          },
          {
            "Key": "access_logs.s3.bucket",
            "Value": {"Ref": "ALBLogsBucketName"}
          },
          {
            "Key": "access_logs.s3.enabled",
            "Value": true
          }
        ],
...

В access_logs.s3.bucket передаём собственно имя корзины, в которую ALB будет складывать логи.

Кроме того – добавляем явное указание на зависимость ресурса ALB от корзины и её политики через аттрибут DependsOn:

...
    "MBApploadBalancer" : {
      "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
      "DependsOn": ["AlbLogsBucket", "BucketPolicy"],
      "Properties": {
        "Scheme": "internet-facing",
...

AWS::S3::Bucket

Добавляем создание корзины:

...
    "AlbLogsBucket": {
      "Type": "AWS::S3::Bucket",
      "DeletionPolicy": "Delete",
      "Properties": {
        "BucketName": {
          "Ref": "ALBLogsBucketName"
        }
      }
    },
...

AWS::S3::BucketPolicy

Что бы ALB имел доступ на запись логов в корзину – создаём политику доступа к корзине:

...
    "BucketPolicy": {
      "Type": "AWS::S3::BucketPolicy",
      "Properties": {
        "PolicyDocument": {
          "Id": "MyPolicy",
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                  "AWS": {
                    "Fn::Sub": [
                      "arn:aws:iam::${ALBRegionAccountID}:root", { "ALBRegionAccountID": {
                        "Fn::FindInMap": [ "ALBRegionAccountIDs", {"Ref" : "StackRegion"}, "ALBRegionAccountID" ] }
                      }
                    ]
                  }
              },
              "Action": "s3:PutObject",
              "Resource": {
                "Fn::Join" : [ "",
                  [
                    "arn:aws:s3:::", {"Ref" : "ALBLogsBucketName"}, "/AWSLogs/", {"Ref" : "AWSAccountID"}, "/*"
                  ]
                ]
              }
            }
          ]
        },
        "Bucket": {
          "Ref": "AlbLogsBucket"
        }
      }
    },
...
  • Principal: тут используем Fn::Sub, из которой формируем строку вида arn:aws:iam::033677994240:root, используя параметр StackRegion для выборки значения ALBRegionAccountID из Mappings, который создали выше
  • Resource: используя Fn::Join – формируем строку вида arn:aws:s3:::mobilebackend-dev-alb-logs/AWSLogs/534***385/*

Вроде ОК?

Тут я использую скрипт, аналогичный этому>>> – запускаем проверку:

[simterm]

$ ./ansible_exec.sh -c

Tags: infra
Env: mobilebackend-dev
...
Running dry-run...
...
Dry-run check passed.
...
PLAY RECAP ****

dev.backend-bastion.example.world : ok=3    changed=1    unreachable=0    failed=0

Provisioning done.

[/simterm]

Проверяем аттрибуты ALB:

И корзину с логами:

Готово.

P.S. Позже сюда ещё добавил LifecycleConfiguration с ExpirationInDays:

...
    "AlbLogsBucket": {
      "Type": "AWS::S3::Bucket",
      "DeletionPolicy": "Delete",
      "Properties": {
        "BucketName": {
          "Ref": "ALBLogsBucketName"
        },
        "LifecycleConfiguration": {
          "Rules": [
            {
              "Id": "CleanLogs",
              "Prefix": "",
              "Status": "Enabled",
              "ExpirationInDays": { "Ref": "ALBLogsExpirationInDays" }
            }
          ]
        }
      }
    },
...

ALBLogsExpirationInDays опять-таки передаём в Ansible роль:

...
ALBLogsExpirationInDays: "{{ alb_logs_expiration_in_days }}"
...

Из файлов в group_vars:

...
alb_logs_expiration_in_days: 3
...

Вот теперь – точно готово.