Soporte de IMDSv2

Escribo para consultar sobre la compatibilidad de IMDSv2 a través de perfiles de instancia en Discourse. Estamos en proceso de migrar nuestro servicio para usar IMDSv2, ya que IMDSv1 no es una opción segura.

Nos gustaría entender si Discourse actualmente admite IMDSv2 a través de perfiles de instancia y, si no es así, cuáles son los planes para admitirlo en un futuro próximo. Además, ¿existen soluciones alternativas o parches disponibles que nos permitan usar IMDSv2 con Discourse?

Es importante para nosotros asegurarnos de que se cumplan nuestros requisitos de seguridad, y creemos que el uso de credenciales temporales a través de IMDSv2 es un aspecto crítico de ello.

El comportamiento deseado para acceder a las credenciales de seguridad proporcionadas a través del perfil de instancia es:

  • Una aplicación en la instancia recupera las credenciales de seguridad proporcionadas por el rol del elemento de metadatos de la instancia iam/security-credentials/ nombre-del-rol.
  • La aplicación recibe los permisos para las acciones y recursos que hemos definido para el rol a través de las credenciales de seguridad asociadas con el rol. Estas credenciales de seguridad son temporales y se rotan automáticamente. Ponemos nuevas credenciales a disposición al menos cinco minutos antes de la expiración de las credenciales antiguas.

Hemos notado que existen diferencias entre las llamadas IMDSv1 e IMDSv2.

Llamada IMDSv1:

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access

Mientras que la llamada IMDSv2 requiere el uso de un token de metadatos, y se puede realizar utilizando los siguientes comandos:

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \\\\\\\\\\\\\\\\
&& curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access

Agradeceríamos cualquier información que pueda proporcionar sobre cómo podemos usar IMDSv2 con Discourse, o si existen soluciones alternativas o parches disponibles.

Referencia:

No tengo conocimiento de ninguna forma en que Discourse en sí mismo utilice IMDS. ¿Has instalado Discourse en AWS de alguna manera? ¿Estás utilizando IMDSv1 con Discourse de alguna manera?

¿Cuál es tu caso de uso?

Encontré un código relacionado.

Esto está en la especificación de pruebas.

Discourse utiliza una versión del SDK de AWS (3.130.2) superior a la mínima requerida para admitir IMDSv2 y, por lo que puedo ver al observar la métrica MetadataNoToken en nuestras implementaciones de AWS, no tenemos llamadas a IMDSv1.

Por lo que puedo ver, ya estamos utilizando IMDSv2 en todas partes.

3 Me gusta

Empezamos a usar Discourse en una instancia AWS EC2 hace un año. Y esta semana actualizamos nuestra instancia para usar solo IMDSv2, esto rompió nuestras cargas de AWS S3 con el mensaje de error “unable to sign request without credentials set”. También utilizamos la configuración “s3 use iam profile”.

El servicio IMDS local es utilizado por Discourse para obtener credenciales para realizar otras llamadas a la API de servicios relacionados con AWS. Esto se hace usando Ruby aws-sdk-s3

También estamos experimentando este problema de respaldo después de deshabilitar IMDSv1 por razones de seguridad.

Podemos ver el uso de IMDSv1 (en 3.3.0.beta1-dev) a través de la métrica MetadataNoToken, por lo que nos preguntamos en qué versión de Discourse se pasó a usar v2 en todas partes.

Nosotros también fuimos afectados por esto hoy, una vez que cambiamos nuestra instancia de AWS para usar solo IMDSv2: nuestros usuarios ya no podían subir imágenes a S3.

Probablemente sea relevante aquí: también estamos usando la opción s3 use iam profile.

Por ahora, lo cambiamos a “Opcional”, lo que básicamente significa que IMDSv1 todavía está habilitado, lo cual no es lo mejor en cuanto a seguridad, pero eso hizo que las cargas funcionaran de nuevo.

1 me gusta

¿Alguien tiene una solución/solución alternativa para que Discourse funcione con IMDSv2?

Estamos haciendo cambios para permitir/esperar que se pueda configurar más a través del archivo .aws/config, lo que podría coincidir con esto y hacerlo posible.

@supermathie lo más extraño que no puedo descifrar es que configuré personalmente 2 instancias de Discourse (desarrollo/producción) que están configuradas de manera idéntica (carga de S3 para archivos y copia de seguridad usando perfil IAM) y actualizadas a una versión idéntica (9436f5e3d4) y cuando deshabilité IMDSv1… en desarrollo todo siguió funcionando como se esperaba, mientras que en producción no lo hace y sigue lanzando algo como “imposible firmar la solicitud sin credenciales establecidas”… bastante desconcertante.

Si tienes alguna idea sobre pruebas/verificaciones que pueda hacer, házmelo saber.

@ducks identificó que los tiempos de espera en el SDK para adquirir credenciales de IMDS son muy agresivos (1 segundo, sin reintentos), por lo que es posible que esté alcanzando ese tiempo de espera.

Pero eso es solo una suposición.

Si te conectas a producción por consola, ¿puedes hacerlo interactivamente, por ejemplo?:

discourse(prod)> c = Aws::S3::Client.new(region: ENV['DISCOURSE_S3_REGION'])
=> #<Aws::S3::Client>

discourse(prod)> c.list_objects_v2(bucket: ENV['DISCOURSE_S3_BUCKET']).contents.count
=> 1000
1 me gusta

Descubrí cuál era el problema y solo tengo que culparme a mí mismo, pero el problema era bastante sutil.
El problema era con “HttpPutResponseHopLimit” establecido en 1, lo que no permitía llamar a IMDSv2 desde dentro del contenedor.

Al emitir este comando obtuve esta respuesta:

> aws ec2 describe-instances --instance-ids i-00000000000000000 --query “Reservations[0].Instances[0].MetadataOptions”`
{
“State”: “applied”,
“HttpTokens”: “optional”,
“HttpPutResponseHopLimit”: 1,
“HttpEndpoint”: “enabled”,
“HttpProtocolIpv6”: “disabled”,
“InstanceMetadataTags”: “disabled”
}

Ajustando la configuración, la salida correcta es

> aws ec2 describe-instances --instance-ids i-00000000000000000 --query “Reservations[0].Instances[0].MetadataOptions”`
{
“State”: “applied”,
“HttpTokens”: “required”,
“HttpPutResponseHopLimit”: 2,
“HttpEndpoint”: “enabled”,
“HttpProtocolIpv6”: “disabled”,
“InstanceMetadataTags”: “disabled”
}

…y finalmente el misterio está resuelto :sweat_smile:

Gracias a todos por su ayuda

1 me gusta

¡Esto es genial saberlo!

Y ojalá esto se aplique a otros también.

Pero me pregunto por qué esto no es un problema para nosotros. Lo tenemos configurado en 1 y aun así funciona?

discourse(prod)> ENV['AWS_EC2_METADATA_V1_DISABLED'] = 'true'
=> "true"
discourse(prod)> c = Aws::S3::Client.new(region: ENV['DISCOURSE_S3_REGION'])
=> #<Aws::S3::Client>
discourse(prod)> c.config.credentials.disable_imds_v1
=> true
discourse(prod)> c.list_objects_v2(bucket: ENV['DISCOURSE_S3_BUCKET']).contents.count
=> 1000

y esa instancia tiene estos metadatos según el mismo comando de consulta:

{
    "State": "applied",
    "HttpTokens": "optional",
    "HttpPutResponseHopLimit": 1,
    "HttpEndpoint": "enabled",
    "HttpProtocolIpv6": "disabled",
    "InstanceMetadataTags": "disabled"
}

Estamos ejecutando Discourse en un contenedor docker en EC2, al igual que otros, así que… ¿cuál es la diferencia?

Este tema se cerró automáticamente después de 21 horas. Ya no se permiten nuevas respuestas.