IMDSv2 지원

I am writing to inquire about the support of IMDSv2 through instance profiles in Discourse. We are in the process of migrating our service to use IMDSv2, as IMDSv1 is not an secure option.

We would like to understand if Discourse currently supports IMDSv2 through instance profiles and if not, what are the plans to support it in the near future. Additionally, are there any workarounds or patches available that would allow us to use IMDSv2 with Discourse?

It is important for us to ensure that our security requirements are met, and we believe that using temporary credentials through IMDSv2 is a critical aspect of that.

Desired behavior for accessing security credentials provided through the instance profile is

  • An application on the instance retrieves the security credentials provided by the role from the instance metadata item iam/security-credentials/ role-name.
  • The application is granted the permissions for the actions and resources that we have defined for the role through the security credentials associated with the role. These security credentials are temporary and are rotated automatically. We make new credentials available at least five minutes before the expiration of the old credentials.

We have noted that there are differences between the IMDSv1 and IMDSv2 calls.

IMDSv1 call:

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

While the IMDSv2 call requires the use of a metadata token, and can be made using the following commands:

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>

We would appreciate any information you can provide on how we can use IMDSv2 with Discourse, or if there are any workarounds or patches available.

Reference:

I’m not aware of any way that Discourse itself uses IMDS. Have you installed Discourse on AWS somehow? Are you somehow using IMDSv1 with Discourse already?

What is your use case?

I found one related code.

This is in the testing spec.

Discourse uses a version of the AWS SDK (3.130.2) above the minimum required to support IMDSv2 and from what I can tell looking at the MetadataNoToken metric in our AWS deployments, we have no calls to IMDSv1.

From what I can tell we’re already using IMDSv2 everywhere.

We started using Discourse on an AWS EC2 instance a year ago. And this week we updated our instance to only use IMDSv2, this broke our AWS S3 uploads with the error message “unable to sign request without credentials set”. We also utilise the “s3 use iam profile” setting.

The local IMDS service is used by Discourse to get credentials for doing other AWS related service API calls. This is done using Ruby aws-sdk-s3

We are also seeing this backup issue after disabling IMDSv1 due to security reasons.

We can see the use of IMDSv1 (in 3.3.0.beta1-dev) via the MetadataNoToken metric, so we are wondering what version of Discourse switched to using v2 everywhere?

We too were bitten by this today once we changed our AWS instance to use IMDSv2 only: our users could no longer upload images to S3.

Probably relevant here: we are also using the s3 use iam profile option.

For now we switched it to “Optional” which basically means IMDSv1 is still enabled which isn’t the best security wise, but that made uploads work again.

IMDSv2와 함께 Discourse를 작동시키는 해결책이나 우회 방법이 있는 분이 계신가요?

이 기능과 중복될 수 있는 .aws/config 파일을 통해 더 많은 구성을 가능하도록 하는 변경 작업을 진행 중입니다.

@supermathie 제가 아무리 생각해봐도 이해가 안 되는 가장 이상한 점은, 제가 직접 dev/prod 두 개의 discourse 인스턴스를 동일하게 구성(파일 업로드 및 백업용 S3, IAM 프로파일 사용)하고 동일한 버전(9436f5e3d4)으로 업데이트했는데, IMDSv1을 비활성화했을 때 dev 환경에서는 모든 것이 정상적으로 작동한 반면 prod 환경에서는 작동하지 않고 “unable to sign request without credentials set” 같은 오류가 계속 발생한다는 것입니다. 정말 난해한 문제입니다.

테스트나 확인해볼 수 있는 방법이 떠오르시면 알려주세요.

@ducks가 SDK에서 IMDS 자격 증명을 가져오는 데 사용되는 타임아웃이 매우 공격적(1초, 재시도 없음)이라는 점을 식별했습니다. 따라서 해당 타임아웃에 걸리고 있을 가능성이 있습니다.

하지만 그것은 그저 추측일 뿐입니다.

프로덕션 환경에 콘솔로 접속할 수 있다면, 다음과 같이 인터랙티브로 실행해 볼 수 있나요:

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

문제가 무엇이었는지 파악했고, 결국 내 탓이라고 할 수밖에 없었지만, 문제는 꽤 미묘했습니다.
문제는 "HttpPutResponseHopLimit"이 1로 설정되어 있어 컨테이너 내부에서 IMDSv2를 호출할 수 없었던 것이었습니다.

이 명령어를 실행하면 다음과 같은 응답을 받았습니다:

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

설정을 조정하면 올바른 출력은 다음과 같습니다.

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

…그리고 마침내 미스터리는 풀렸습니다 :sweat_smile:

모두에게 도움을 주셔서 감사합니다

이 정보를 알게 되어 좋습니다!

그리고 이것이 다른 사람들에게도 적용되기를 바랍니다.

하지만 왜 우리에게서는 문제가 되지 않는지 궁금합니다. 우리는 이 값을 1로 설정해 두었는데도 작동하거든요?

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

그리고 해당 인스턴스는 동일한 쿼리 명령에 따라 다음과 같은 메타데이터를 가지고 있습니다:

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

우리는 다른 사람들과 마찬가지로 EC2에서 Docker 컨테이너로 Discourse를 실행하고 있으므로… 차이는 무엇일까요?