AWS SDK v3 における認証
対象読者
- AWS SDK v3 の適切な credential provider の選び方を知りたい方、公式ドキュメントだけではわからなかった方
- credential providerの使用方法・仕組みを知りたい方
- AWS の認証について、使用目的をベースに学びたい方
AWS SDK v3 credential provider のインストール
npm install @aws-sdk/credential-providers
- すべての定義が
@aws-sdk/credential-providers
に含まれています。個別のパッケージが見つかりますが、個別にインストールすることは非推奨となっています。 - TS の型定義は含まれています。
各種サービスでServiceRoleの認証情報を取得する方法
EC2
fromInstanceMetadata()
view code
- 引数なしで動きます。
-
http://169.254.169.254
(Instance Metadata Endpoint) からトークンを取得します。 - 詳しい処理については#reading fromInstanceMetadata()
AWS Lambda
- 引数なしで動きます。
-
注意: Dockerイメージを指定する場合も、
fromContainerMetadata()
は使用しません。 - LambdaにAWSがセットする環境変数を使用します。
-
詳細: 実際に使われる環境変数は以下です。
-
AWS_ACCESS_KEY_ID
(必須) -
AWS_SECRET_ACCESS_KEY
(必須) -
Temporary security credentials in IAM
AWS_SESSION_TOKEN
AWS_CREDENTIAL_EXPIRATION
- 注意: 自分でセットして使うというようなことは、基本的にないはずで、避けるべきです。
-
ECS
fromContainerMetadata()
view code
引数なしで動きます。
詳しい処理については#reading fromContainerMetadata()
INIファイル
INIファイルはプロフィールやShared Configとも呼ばれます。aws configure
経由で生成される、~/.aws/credentials
にあるやつです。
例えば、ローカル環境であることを分岐して、 fromIni({ profile: process.env.AWS_PROFILE })
のようにします。
注意: プロダクション環境では使用を避けてください。
また、#Cloud9(EC2)や、#EC2へのSSH経由によってのみ使えるようにするのがセキュリティ的にベターかと思います。
Cloud9(EC2)
AWS managed temporary credentialsを使う場合はfromIni()
を使用します。一部の操作に制限があるので注意してください。基本的にはこちらが推奨されます。~/.aws/credentials
が見当たらない場合は、Preferences > AWS Settings > Credentials > AWS managed temporary credentials がセットされているか確認してください。
注意: fromTemporaryCredentials()
ではありません。STSとの処理はAWS側が自動でやってくれるのがAWS managed temporary credentialsです。その結果はINIファイルに入っています。
もしくは、EC2 Instance Profile を設定して#EC2と同様にfromInstanceMetadata()
を使用します。
Cloud9(SSH)
SSHを使用している場合は#INIファイルによる方法をとるしかないようです。(source)
fromIni()
を使います。EC2タイプのCloud9環境を使用することが推奨されます。
Web Identity Federation (Web、モバイル、他のクラウド環境、GitHub Actions、その他OIDC)
Amazon Cognitoのidentity pools(federated identities)の場合は、fromCognitoIdentity()
とfromCognitoIdentityPool()
を使用します。
BrowserとNode.js環境両方で使えます。GitHub ActionsはOpenID Providerを提供してくれるようになったので、この方法が使えるはずです。引数は公式ドキュメントを参照してください。
sts:AssumeRoleWithWebIdentity
を使う方法の場合は、fromWebToken()
を使用します。Google、Amazon、Facebookにしか対応されておらず、特にモバイルなどではCognitoを使用する方法が推奨されています。引数は公式ドキュメントを参照してください。
オンプレミス
#Cloud9(SSH)と同様の事情かと思います。他のクラウドというだけであったり、OIDC Providerを独自で提供できるのであればWeb Identity Federationが使えると思います。
EKS
#オンプレミスの事情と同じだと思っています。使ったことないので、cluster authenticationに使われたAWSやOIDCの情報が使えるのかもしれませんが、パッと見たところ、k8sへのログイン認証して終わりなのかな。(あってたらあってるよ!ってコメントもらえると助かります)。
sts:AssumeRole
fromTemporaryCredentials({ /* sts:AssumeRole params, etc... */ })
view code
- 内部では
@aws-sdk/client-sts
の、STSClient
を経由してsts:AssumeRole
を呼び出しています。つまり、先にSTSClient
のための認証をfromEnv()
などにより適切に渡すことになります。 - 使い機会は少ないかと思います。
SSO
使ったことありません。引数を与えなければINIファイルを読みに行きます。
外部プロセス
おそらくマニアックな方式だと思われます。認証情報を外部プロセスからもらう方式です。(AWSがそういったツールを用意しているというわけではなさそうです)
重要なインターフェース
以下の2つのインターフェースが、AWS SDK v3 の認証において重要です。
-
Credentials
: 静的な認証情報を表す。 -
CredentialsProvider
:Credentials
を非同期(Promise
)で返す関数
Credentials
の実装は以下です。
export interface Credentials {
/**
* AWS access key ID
*/
readonly accessKeyId: string;
/**
* AWS secret access key
*/
readonly secretAccessKey: string;
/**
* A security or session token to use with these credentials. Usually
* present for temporary credentials.
*/
readonly sessionToken?: string;
/**
* A {Date} when these credentials will no longer be accepted.
*/
readonly expiration?: Date;
}
各種のfrom...()
はCredentials
かCredentialsProvider
を返します。
Credentialsの使い方
Credentialsを各種AWS SDK v3クライアントのコンストラクタに渡します。例えば、fromEnv()
とS3クライアントを組み合わせる場合は以下のようになります。
import { S3 } from '@aws-sdk/client-s3';
import { fromInstanceMetadata } from '@aws-sdk/credential-providers';
const credentials = fromInstanceMetadata();
const s3 = new S3({ credentials /* ... */ });
s3.listBuckets({}).then((result) => console.log(result));
これは例えば、適切なRoleを設定したEC2などで使えます。学習目的の場合は、Nodeをセットアップして実際に試してみると良いかもしれません。
すべてのクライアントは、S3
に対して、S3Client
のようにうしろにClient
をつけたものが存在します。使用方法は以下のようになります。
import { S3Client, ListBucketsCommand } from '@aws-sdk/client-s3';
import { fromInstanceMetadata } from '@aws-sdk/credential-providers';
const credentials = fromInstanceMetadata();
const s3Client = new S3Client({
credentials,
})
s3Client.send(new ListBucketsCommand({})).then((result) => console.log(result))
やっていることは同一です。
例: どのCredentialを使うかを環境変数によって決める
たとえば、EC2で開発して、本番環境であるLambdaにデプロイするような例を考えます。
import { fromInstanceMetadata, fromEnv } from '@aws-sdk/credential-providers';
export const createAwsCredentials = () => {
if (process.env.NODE_ENV === 'production') {
return fromEnv();
} else {
return fromInstanceMetadata();
}
};
環境変数 NODE_ENV
が production
かどうかで分岐して異なる AWS Credentials を作成する関数を用意しておきます。
他にもINIファイルを指定できたり、直接トークンを指定できるようにしたいというような要望に合わせて変えていくといいと思います。
以下はコードリーディングしたものです。バージョンを固定したリンクを置いています。そのバージョンの時の情報です。内容としては少し細かいかもしれません。また、ドキュメントにもこの辺は書いてあります。
fromInstanceMetadata()
詳細: reading
- インスタンスメタデータのエンドポイントを決定する。
- a. 環境変数
AWS_EC2_METADATA_SERVICE_ENDPOINT
の値 - b. INIファイル内の
ec2_metadata_service_endpoint
の値 - c. これで決まらなければ、まず IPv4 と IPv6 どちらを使うか決める。
- i. 環境変数
AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE
の値 (IPv4
/IPv6
) - ii. INIファイル内の
ec2_metadata_service_endpoint_mode
の値 - iii. これで決まらなければ
IPv4
- i. 環境変数
- d. 続いて、
IPv4
ならhttp://169.254.169.254
、IPv6
ならhttp://[fd00:ec2::254]
を使用
-
PROFILE="$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/)"
にてロール名取得(同等のcurl
+bash
文を書いているだけです、エンドポイントhttp://169.254.169.254
は上記で決定した値) -
MTOKEN="$(curl http://169.254.169.254/latest/api/token -H x-aws-ec2-metadata-token-ttl-seconds:21600 -X PUT)"
でmetadata API用のトークンを取得 -
CRED="$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${PROFILE} -H x-aws-ec2-metadata-token:${MTOKEN})"
でTempoary Credential取得
後半は以下を実装した形になっています。
ただし、リトライ処理などが途中にあります。
fromContainerMetadata()
詳細: reading
以下を実装した形になっています。
curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
だけでほぼ終わります。一部、AWS_CONTAINER_CREDENTIALS_FULL_URI
やAWS_CONTAINER_AUTHORIZATION_TOKEN
によって挙動を変える部分がありますが、詳細な利用目的はわかりません。
ECSでしか使わず、ECSで使う場合は迷わずにfromContainerMetadata()
とすればよいので、あまり気になる部分もなさそうです。
fromEnv()
詳細: reading
環境変数から読んでセットするだけなのでかんたんです。fromEnv()
を読んだタイミングではなく、そのCredentialsを使うタイミングでのprocess.env
が使用されるので、動的にprocess.env
を更新するようなことも、できなくはないです。
fromTemporaryCredentials()
詳細: reading
@aws-sdk/client-sts
のSTSClient
を利用してsts:AssumeRole
しているだけのようです。MFAにも対応しています。
追加でまた別の認証方式についても読んだり、使ったことないものを試したりするかもしれません。その時はまた追記します。
Discussion