Suporte para IMDSv2

Estou escrevendo para perguntar sobre o suporte do IMDSv2 através de perfis de instância no Discourse. Estamos em processo de migração do nosso serviço para usar o IMDSv2, pois o IMDSv1 não é uma opção segura.

Gostaríamos de entender se o Discourse atualmente suporta o IMDSv2 através de perfis de instância e, se não, quais são os planos para suportá-lo em um futuro próximo. Além disso, existem soluções alternativas ou patches disponíveis que nos permitiriam usar o IMDSv2 com o Discourse?

É importante para nós garantir que nossos requisitos de segurança sejam atendidos, e acreditamos que o uso de credenciais temporárias através do IMDSv2 é um aspecto crítico disso.

O comportamento desejado para acessar credenciais de segurança fornecidas através do perfil de instância é:

  • Um aplicativo na instância recupera as credenciais de segurança fornecidas pela função do item de metadados da instância iam/security-credentials/ nome-da-função.
  • O aplicativo recebe as permissões para as ações e recursos que definimos para a função através das credenciais de segurança associadas à função. Essas credenciais de segurança são temporárias e são rotacionadas automaticamente. Disponibilizamos novas credenciais pelo menos cinco minutos antes da expiração das credenciais antigas.

Observamos que existem diferenças entre as chamadas IMDSv1 e IMDSv2.

Chamada IMDSv1:

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

Enquanto a chamada IMDSv2 requer o uso de um token de metadados, e pode ser feita usando os seguintes 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 qualquer informação que você possa fornecer sobre como podemos usar o IMDSv2 com o Discourse, ou se existem soluções alternativas ou patches disponíveis.

Referência:

Não estou ciente de nenhuma forma que o Discourse em si use o IMDS. Você instalou o Discourse na AWS de alguma forma? Você está usando o IMDSv1 com o Discourse de alguma forma?

Qual é o seu caso de uso?

Encontrei um código relacionado.

Isso está na especificação de teste.

O Discourse usa uma versão do AWS SDK (3.130.2) acima do mínimo necessário para suportar IMDSv2 e, pelo que posso ver, olhando para a métrica MetadataNoToken em nossas implantações da AWS, não temos chamadas para IMDSv1.

Pelo que posso ver, já estamos usando IMDSv2 em todos os lugares.

3 curtidas

Começamos a usar o Discourse em uma instância AWS EC2 há um ano. E esta semana atualizamos nossa instância para usar apenas o IMDSv2, isso quebrou nossos uploads do AWS S3 com a mensagem de erro “unable to sign request without credentials set”. Também utilizamos a configuração “s3 use iam profile”.

O serviço IMDS local é usado pelo Discourse para obter credenciais para fazer outras chamadas de API de serviço relacionadas à AWS. Isso é feito usando o Ruby aws-sdk-s3

Também estamos vendo esse problema de backup após desabilitar o IMDSv1 por motivos de segurança.

Podemos ver o uso do IMDSv1 (na versão 3.3.0.beta1-dev) através da métrica MetadataNoToken, então estamos nos perguntando em qual versão o Discourse mudou para usar o v2 em todos os lugares?

Nós também fomos afetados por isso hoje, depois de alterarmos nossa instância AWS para usar apenas o IMDSv2: nossos usuários não conseguiam mais fazer upload de imagens para o S3.

Provavelmente relevante aqui: também estamos usando a opção s3 use iam profile.

Por enquanto, mudamos para “Opcional”, o que basicamente significa que o IMDSv1 ainda está habilitado, o que não é o ideal em termos de segurança, mas isso fez com que os uploads funcionassem novamente.

1 curtida

Alguém tem uma solução/solução alternativa para fazer o Discourse funcionar com o IMDSv2?

Estamos fazendo alterações para permitir/esperar que mais configurações sejam possíveis através do arquivo .aws/config, o que pode coincidir com isso e torná-lo possível.

@supermathie a coisa mais estranha que não consigo entender é que configurei pessoalmente 2 instâncias do Discourse (dev/prod) que são configuradas de forma idêntica (uploads s3 para arquivos e backup usando perfil IAM) e atualizadas para uma versão idêntica (9436f5e3d4) e quando desativei o IMDSv1… no dev tudo continuou funcionando como esperado, enquanto no prod não funciona e continua lançando algo como “incapaz de assinar a solicitação sem credenciais definidas”… bastante intrigante

Se você tiver alguma ideia sobre testes/verificações que eu possa fazer, me avise

@ducks identificou que os timeouts no SDK para aquisição de credenciais IMDS são muito agressivos (1 segundo, sem novas tentativas), então é possível que esteja atingindo esse timeout.

Mas isso é apenas um palpite.

Se você acessar o ambiente de produção via console, consegue fazer isso interativamente, por exemplo:

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 curtida

Eu descobri o que estava errado e só tenho que me culpar, mas o problema era bem sutil.
O problema era com o “HttpPutResponseHopLimit” definido como 1, o que não permitia que o IMDSv2 fosse chamado de dentro do contêiner.

Ao emitir este comando, obtive esta resposta:

> 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 a configuração, a saída correta é

> 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”
}

…e finalmente o mistério está resolvido :sweat_smile:

Obrigado a todos pela ajuda

1 curtida

É ótimo saber disso!

E espero que isso se aplique a outros também.

Mas estou me perguntando por que isso não é um problema para nós. Temos isso definido como 1 e, ainda assim, 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

e essa instância tem esses metadados de acordo com o mesmo comando de consulta:

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

Estamos executando o Discourse em um contêiner docker no EC2, assim como outros, então… qual é a diferença?

Este tópico foi fechado automaticamente após 21 horas. Novas respostas não são mais permitidas.