Cuando el cifrado rompe tus notificaciones de Slack: una historia de KMS, SNS y AWS Chatbot
Comenzó de manera bastante inocente: un escaneo de seguridad de Trivy marcó 9 vulnerabilidades de alta severidad en nuestra configuración de Terraform. ¿El problema? Tópicos de SNS sin cifrar. La solución parecía sencilla: agregar una clave KMS, cifrar los tópicos, desplegar a dev para validación. ¿Qué podría salir mal?
La Configuración
# Antes: Tópico SNS sin cifrar
resource "aws_sns_topic" "application_alarms" {
name = "application-alarms-${var.environment}"
# Sin cifrado - vulnerabilidad de seguridad de Trivy
}
# Después: Tópico SNS cifrado
resource "aws_sns_topic" "application_alarms" {
name = "application-alarms-${var.environment}"
kms_master_key_id = module.sns_kms.key_arn # ✅ ¡Cifrado!
}
El PR pasó la revisión de código, las pruebas pasaron, y desplegamos al entorno de desarrollo. El escaneo de Trivy se puso verde. ¡Victoria!
Pero antes de promover a producción, quería validar el cambio adecuadamente. Menos mal que lo hice: después de esperar un día para observar el entorno de dev, noté algo preocupante: ninguna notificación de alarma de CloudWatch estaba apareciendo en nuestro canal de Slack. La infraestructura se veía bien, pero el silencio era sospechoso.
La Investigación
Fase 1: Todo Parece Estar Bien
Las verificaciones iniciales no mostraron problemas obvios:
- ✅ Las alarmas de CloudWatch se estaban activando
- ✅ Los tópicos de SNS existían y estaban configurados correctamente
- ✅ AWS Chatbot estaba conectado a nuestro workspace de Slack
- ✅ Las suscripciones de SNS estaban activas
Pero las notificaciones no llegaban a Slack. Era hora de profundizar.
Fase 2: Las Métricas de SNS
Verificar las métricas de SNS reveló algo interesante:
aws cloudwatch get-metric-statistics \
--namespace AWS/SNS \
--metric-name NumberOfNotificationsDelivered \
--dimensions Name=TopicName,Value=application-alarms-dev \
--statistics Sum
Resultado: 0 entregas cuando las alarmas de CloudWatch se activaron. Pero cuando publicamos manualmente un mensaje de prueba:
aws sns publish \
--topic-arn arn:aws:sns:us-west-2:123456789:application-alarms-dev \
--message "Test message"
Resultado: ¡Mensaje entregado exitosamente! Las métricas de SNS mostraron 1 entrega exitosa.
Entonces SNS podía entregar mensajes, pero las alarmas de CloudWatch no podían llegar a SNS. La trama se complica.
Fase 3: Los Logs de CloudWatch Cuentan la Verdad
Habilitamos CloudWatch Logs para AWS Chatbot y activamos otra prueba:
{
"message": "Event received is not supported",
"eventType": "CloudWatchAlarm"
}
Espera, ¿Chatbot estaba recibiendo mensajes pero rechazándolos? Intentemos activar la alarma real de CloudWatch:
Resultado: Sin logs en absoluto. Los mensajes nunca llegaron a Chatbot.
Esto lo redujo a: CloudWatch no podía publicar al tópico SNS cifrado.
La Causa Raíz: Tres Permisos Faltantes
El problema no era solo un permiso faltante: eran tres problemas separados:
Problema 1: CloudWatch No Puede Publicar a SNS Cifrado
Cuando cifras un tópico de SNS con KMS, CloudWatch Alarms necesita permiso explícito para usar esa clave. Esto está documentado, pero es fácil de pasar por alto:
# Lo que teníamos (incorrecto):
services = [{
name = "sns.amazonaws.com"
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
}]
# Lo que necesitábamos:
services = [
{
name = "sns.amazonaws.com"
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
},
{
name = "cloudwatch.amazonaws.com" # ← ¡Faltaba!
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
}
]
¿Por qué? CloudWatch cifra los datos de alarma antes de enviarlos a SNS. Sin permisos de KMS, no puede cifrar, así que no puede publicar.
Problema 2: AWS Chatbot Usa Dos Roles Diferentes
Este nos tomó por sorpresa. AWS Chatbot en realidad usa dos roles IAM separados:
Rol del Canal - Configurado en la consola de Chatbot
- Usado para: Consultar CloudWatch, describir recursos
- Ejemplo:
aws-chatbot-notifications-{env}
Rol Service-Linked - Auto-creado por AWS
- Usado para: Suscripción SNS y descifrado de mensajes
- Siempre:
AWSServiceRoleForAWSChatbot
¡Habíamos otorgado permisos de KMS al rol del canal, pero las suscripciones de SNS usan el rol service-linked!
# Verificar quién está realmente suscrito:
aws sns list-subscriptions-by-topic \
--topic-arn arn:aws:sns:us-west-2:123456789:application-alarms-dev
# Resultado:
{
"SubscriptionArn": "...",
"Principal": "arn:aws:iam::123456789:role/aws-service-role/management.chatbot.amazonaws.com/AWSServiceRoleForAWSChatbot"
}
¡No el rol al que otorgamos permisos!
Problema 3: Permisos Sobre-Privilegiados
Mientras arreglábamos los dos primeros problemas, notamos que habíamos otorgado kms:GenerateDataKey a ambos roles de Chatbot. Pero Chatbot solo descifra mensajes: nunca cifra nada. Esto viola el principio de menor privilegio.
La Solución
Paso 1: Crear la Política de Clave KMS
module "sns_kms" {
source = "./modules/kms"
alias_name = "/alias/${var.project}/sns/${var.environment}"
description = "Clave KMS usada para cifrar/descifrar tópicos SNS"
# Service principals que pueden usar esta clave
services = [
{
# SNS necesita cifrar mensajes en reposo
name = "sns.amazonaws.com"
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
},
{
# CloudWatch necesita cifrar mensajes de alarma
name = "cloudwatch.amazonaws.com"
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
}
]
# AWS principals (roles IAM) que pueden usar esta clave
additional_principals = [{
type = "AWS"
identifiers = [
# Rol del canal de Chatbot (para consultas de CloudWatch)
aws_iam_role.chatbot_notifications.arn,
# Rol service-linked de Chatbot (para suscripciones SNS)
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/management.chatbot.amazonaws.com/AWSServiceRoleForAWSChatbot"
]
actions = [
"kms:Decrypt", # Requerido
"kms:DescribeKey" # Opcional pero útil
# NO kms:GenerateDataKey - ¡Chatbot no cifra!
]
}]
}
Paso 2: Crear el Rol IAM de Chatbot en Terraform
Anteriormente, habíamos creado esto manualmente en la consola. Era hora de codificarlo:
resource "aws_iam_role" "chatbot_notifications" {
name = "aws-chatbot-notifications-${var.environment}"
assume_role_policy = data.aws_iam_policy_document.chatbot_assume_role.json
}
data "aws_iam_policy_document" "chatbot_assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["chatbot.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
# Acceso de solo lectura a CloudWatch
resource "aws_iam_role_policy" "chatbot_cloudwatch_readonly" {
name = "CloudWatchReadOnlyAccess"
role = aws_iam_role.chatbot_notifications.id
policy = data.aws_iam_policy_document.chatbot_cloudwatch_readonly.json
}
# Descifrado KMS para mensajes SNS
resource "aws_iam_role_policy" "chatbot_kms_decrypt" {
name = "SNSKMSDecryptAccess"
role = aws_iam_role.chatbot_notifications.id
policy = data.aws_iam_policy_document.chatbot_kms_decrypt.json
}
data "aws_iam_policy_document" "chatbot_kms_decrypt" {
statement {
sid = "AllowDecryptSNSMessages"
effect = "Allow"
actions = [
"kms:Decrypt",
"kms:DescribeKey"
]
resources = [module.sns_kms.key_arn]
}
}
Paso 3: Configuración Manual (El Problema)
Aquí es donde se vuelve frustrante: las configuraciones de AWS Chatbot no pueden ser gestionadas por Terraform (a partir de finales de 2024). Tienes que actualizarlas manualmente en la consola:
- Navegar a AWS Chatbot → Slack → Tu Workspace
- Hacer clic en tu configuración de canal
- Actualizar el Rol IAM para usar el rol gestionado por Terraform
- Crítico: Asegúrate de que la app de AWS Chatbot de Slack esté instalada en tu workspace
- Crítico: Agrega el bot
@AWS Chatbota tu canal de Slack
¿Faltan los pasos 4 o 5? Fallos silenciosos. Sin errores, sin logs, simplemente… nada.
El Flujo de Mensajes
Cuando todo está configurado correctamente:
Pruebas y Verificación
Después de desplegar la solución:
# 1. Verificar que la política KMS incluye todos los principals
aws kms get-key-policy \
--key-id <key-id> \
--policy-name default
# 2. Activar una alarma de prueba
aws cloudwatch set-alarm-state \
--alarm-name "your-alarm-name" \
--state-value ALARM \
--state-reason "Testing encryption fix"
# 3. Verificar logs de Chatbot para procesamiento
aws logs tail /aws/chatbot/your-config-name --follow
# 4. Verificar que se recibió la notificación de Slack
Criterios de éxito:
- CloudWatch Logs muestra: “Sending message to Slack”
- Las métricas de SNS muestran entrega exitosa
- El canal de Slack recibe la notificación
Puntos Clave
SNS cifrado requiere TRES service principals:
sns.amazonaws.com- para cifrar mensajes en reposocloudwatch.amazonaws.com- para publicar alarmas cifradas- Ambos roles de Chatbot - para descifrar mensajes
AWS Chatbot usa dos roles diferentes:
- Rol del canal (configurado en consola)
- Rol service-linked (usado por suscripciones SNS)
- Ambos necesitan permisos de descifrado KMS
El menor privilegio importa:
- Chatbot solo necesita
kms:Decrypt - No
kms:GenerateDataKey(no cifra) - Sobre-privilegiar aumenta la superficie de ataque
- Chatbot solo necesita
Los pasos manuales son inevitables:
- Las configuraciones de Chatbot no están en Terraform
- La app de Slack debe estar instalada
- El bot debe estar agregado a los canales
- Documenta estos pasos para tu equipo
Prueba exhaustivamente en no-producción:
- Publicación manual de SNS ≠ alarma de CloudWatch
- Diferentes rutas de código, diferentes permisos
- Siempre prueba con disparadores reales antes de promover
- Espera para observar el comportamiento, no te apresures a prod
CloudWatch Logs es tu amigo:
- Habilita logging para Chatbot en dev
- Revela problemas de formato de mensaje temprano
- Muestra errores reales (no solo “sin notificaciones”)
- Crítico para depurar problemas de cifrado
Las Consecuencias
Después de desplegar esta solución a desarrollo y validar exhaustivamente:
- ✅ Notificaciones de Slack funcionando correctamente
- ✅ Cumplimiento de seguridad logrado (SNS cifrado)
- ✅ Infraestructura como código (rol IAM de Chatbot)
- ✅ Permisos de menor privilegio (eliminado GenerateDataKey innecesario)
- ✅ Validado en dev antes del despliegue a producción
Para Referencia Futura
Si estás agregando cifrado KMS a tópicos SNS usados con AWS Chatbot:
Lista de verificación:
- Agregar
cloudwatch.amazonaws.coma la política KMS - Agregar
sns.amazonaws.coma la política KMS - Agregar rol del canal de Chatbot a la política KMS
- Agregar
AWSServiceRoleForAWSChatbota la política KMS - Otorgar solo
kms:Decrypta los roles de Chatbot - Crear rol IAM de Chatbot en Terraform
- Actualizar configuración de consola de Chatbot para usar nuevo rol
- Verificar que la app de Slack esté instalada en el workspace
- Verificar que el bot esté agregado a los canales de Slack
- Probar con alarma real de CloudWatch
- Verificar CloudWatch Logs para Chatbot
- Verificar que las métricas de SNS muestren entrega
- Confirmar que se recibieron notificaciones de Slack
Referencias
- Políticas de Clave KMS de AWS
- Roles IAM de AWS Chatbot
- Cifrado SNS con KMS
- Alarmas de CloudWatch con SNS Cifrado
Conclusión
Esta experiencia reforzó que agregar cifrado no es solo activar un interruptor: se trata de entender todo el flujo de mensajes y todos los servicios involucrados. La arquitectura de doble rol de AWS Chatbot es una particularidad que no está bien documentada.
Si estás gestionando notificaciones de Slack vía AWS Chatbot y planeas cifrar tus tópicos SNS, espero que este post te ahorre tiempo de depuración. Siempre prueba con alarmas reales de CloudWatch en un entorno no productivo, no solo publicaciones manuales a SNS.
¿Te resultó útil? Contáctame en LinkedIn.