CloudWatch sin autorización para acceder a SNS topic cifrado

Cuando intentamos configurar una alarma de CloudWatch para que envíe notificaciones a un topic SNS cifrado, podemos encontrarnos con un error como este, indicando que nos falta una autorización para poder comunicar los dos recursos:

Failed to execute action arn:aws:sns:eu-west-1:0123456789:alarms-topic.
Received error: "CloudWatch Alarms does not have authorization to access the SNS topic encryption key."

A primera vista, todo parece correcto: tenemos el tópico SNS creado, cifrado con la clave por defecto alias/aws/sns, y la alarma configurada para usarlo. Sin embargo, las alarmas no llegan a ejecutarse. Vamos a ver como arreglar este error.

La causa

El problema está en la clave KMS.

  • Cuando usas la clave gestionada por AWS (alias/aws/sns), no es posible editar su key policy.
  • Esa policy por defecto no concede permisos a CloudWatch Alarms para hacer llamadas como kms:Decrypt o kms:GenerateDataKey*.
  • Como resultado, la acción de la alarma falla en el momento de publicar en el tópico cifrado.

AWS lo documenta en re:Post: para que CloudWatch publique en un tópico SNS cifrado, es obligatorio usar una Customer Managed Key (CMK) con una policy explícita que otorgue acceso.

La solución

1. Crear una CMK para SNS

Creamos una clave simétrica en KMS y definimos una key policy que permita:

  • A SNS usar la clave para el tópico concreto.
  • A CloudWatch Alarms generar data keys y descifrar.

Ejemplo en Terraform:

resource "aws_kms_key" "sns_alarms" {
  description = "CMK for SNS alarms"
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid       = "AllowRoot",
        Effect    = "Allow",
        Principal = { AWS = "arn:aws:iam::0123456789:root" },
        Action    = "kms:*",
        Resource  = "*"
      },
      {
        Sid       = "AllowSNS",
        Effect    = "Allow",
        Principal = { Service = "sns.amazonaws.com" },
        Action    = [
          "kms:Encrypt","kms:Decrypt","kms:ReEncrypt*",
          "kms:GenerateDataKey*","kms:DescribeKey"
        ],
        Resource  = "*",
        Condition = {
          StringEquals = { "AWS:SourceAccount": "0123456789" },
          ArnLike      = { "aws:SourceArn": "arn:aws:sns:eu-west-1:0123456789:alarms-topic" }
        }
      },
      {
        Sid       = "AllowCloudWatchAlarms",
        Effect    = "Allow",
        Principal = { Service = "cloudwatch.amazonaws.com" },
        Action    = ["kms:Decrypt","kms:GenerateDataKey*"],
        Resource  = "*",
        Condition = {
          StringEquals = { "AWS:SourceAccount": "0123456789" },
          ArnLike      = { "aws:SourceArn": "arn:aws:cloudwatch:eu-west-1:0123456789:alarm:*" }
        }
      }
    ]
  })
}

2. Apuntar el tópico SNS a la CMK

En el recurso aws_sns_topic cambiamos:

resource "aws_sns_topic" "alarms" {
  name              = "alarms-topic"
  kms_master_key_id = aws_kms_key.sns_alarms.arn
}

3. Ajustar la policy del tópico SNS

Añadimos un statement para permitir que CloudWatch publique:

data "aws_iam_policy_document" "sns_topic_policy" {
  statement {
    sid       = "AllowCloudWatchToPublish"
    effect    = "Allow"
    actions   = ["sns:Publish"]
    resources = [aws_sns_topic.alarms.arn]
    principals {
      type        = "Service"
      identifiers = ["cloudwatch.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "AWS:SourceAccount"
      values   = ["0123456789"]
    }
    condition {
      test     = "ArnLike"
      variable = "aws:SourceArn"
      values   = ["arn:aws:cloudwatch:eu-west-1:0123456789:alarm:*"]
    }
  }
}

4. Probar

Publicación manual al topic

aws sns publish \ --region eu-west-1 \ --topic-arn arn:aws:sns:eu-west-1:0123456789:alarms-topic \ --message "Test"

Forzar la alarma a estado ALARM

aws cloudwatch set-alarm-state \ --region eu-west-1 \ --alarm-name "eb-health-degraded-red" \ --state-value ALARM \ --state-reason "Test after CMK"

En ambos casos el mensaje debe entregarse con éxito.

Conclusión

El error "CloudWatch Alarms does not have authorization to access the SNS topic encryption key" ocurre porque las AWS-managed keys (alias/aws/sns) no permiten a CloudWatch usar la clave.

La solución definitiva es usar una CMK propia, dar permisos explícitos a SNS y CloudWatch en la key policy, y actualizar el tópico SNS para usar esa clave.

De esta forma, podemos mantener el tópico cifrado y compatible con las alarmas de CloudWatch. La verdad es que esta no me la sabía pero ha estado bien arreglar que CloudWatch tenga la autorización necesaria para enviar con SNS a un topic cifrado. Una cosa menos.

Links

Leave a Reply

Your email address will not be published. Required fields are marked *