aws-sdk-go, aws-sdk-go-v2 でMFAトークンによるAssumeRoleに対応する

PUBLISHED ON 2019-08-16 — AWS, AWS-SDK-GO, GO, GOLANG

Summary

  • aws-sdk-goで、標準入力からのMFAトークン入力によるAssumeRoleを実装する
  • aws-sdk-go-v2で、標準入力からのMFAトークン入力によるAssumeRoleを実装する

前提条件

  • aws-sdk-go - v1.21.x
  • aws-sdk-go-v2 - v0.10.0

aws-sdk-goで、標準入力からのMFAトークン入力によるAssumeRoleを実装する

AssumeRole便利ですよね。AssumeRole自体の詳細な説明は公式のチュートリアル 等の記事に任せるとして、aws-sdk-goもMFAによるAssumeRoleに対応しています。拙作のEC2インスタンス一覧取得ツールこれを実装する機会があったので、実装方法の備忘メモを残しておきます。

今回の要件は下記を想定しました。

  • 一時的セキュリティ認証情報(STS)を利用した実装 にはしない
  • 代わりに 切替用のIAMロールを設定ファイル経由で使用できるようにする 実装にする
    • ~/.aws/credentials~/.aws/config に、IAMユーザのprofileと切替先ロールのprofileを定義し、これを使用する
    • この実装にすることで、コマンドやライブラリを使う側が指定しなければならない情報(例えばRoleARNやSessionTokenなど)を削減できる
  • 実際に利用する際はMFAトークンの入力を(標準入力から)促し、普段使っている2段階認証アプリ等で表示されたトークンを入力することで、切替先ロールの各種リソースにアクセスできるようにする

~/.aws/credentials~/.aws/config の設定内容

公式の例 を参考に下記のような内容で作成しておきます。リンク先でも触れられていますが、aws-sdk-goだけに限らず、他言語のSDKやawscliでもこの設定は使い回す事が出来ます。

# ~/.aws/credentials
# IAMユーザーのアクセスキー情報が hoge という名前のprofileで定義されている

[hoge]
aws_access_key_id=ACCESS_KEY_ID
aws_secret_access_key=SECRET_ACCESS_KEY
# ~/.aws/config
# hoge profileのconfig(例ではregionのみ)と、
# hogeをsource_profileとするロール切替先のprofile=dev-hoge-role、
# の2つが定義されている

[profile hoge]
region=ap-northeast-1

[profile dev-hoge-role]
region=ap-northeast-1
source_profile=hoge
role_arn=arn:aws:iam::123456789012:role/DevHogeRole
mfa_serial=arn:aws:iam::987654321098:mfa/hoge.hugao

aws.sessionパッケージを活用したprofile読み込み処理の実装

今回の要件をaws-sdk-goで実装する方法は実は複数パターン提供されているのですが、 利用側が指定しなければならない情報を減らしたい(プロファイルさえ設定されていれば、その情報を使うようにしたい) という要件を達成する為に、aws/session パッケージ を活用した実装にします。下記に解説コメント付きでコード例を載せておきます。

// configを新規生成
config := aws.NewConfig()

// session.Options を使ってMFAによるAssumeRole設定を行う
sessOpts := session.Options{
  // 先程生成したconfigをセット
  Config:                  *config,

  // *切替先ロールの* profileをセット(source IAMユーザーのprofileではないので注意)
  Profile:                 profile,  // = dev-hoge-role

  // AssumeRole時のMFAトークン取得経路をセット。今回は標準入力を使う実装にしている
  // See: https://godoc.org/github.com/aws/aws-sdk-go/aws/session#hdr-Assume_Role_configuration
  AssumeRoleTokenProvider: stscreds.StdinTokenProvider,

  // MFA & AssumeRoleという文脈とは無関係だが、
  // 実行環境で AWS_SDK_LOAD_CONFIG 環境変数がセットされていなくても ~/.aws/config ファイルを自動で読み込んでくれるようにする為に、
  // このオプションを設定しておくと便利
  // See: https://godoc.org/github.com/aws/aws-sdk-go/aws/session#hdr-Shared_Config_Fields
  SharedConfigState: session.SharedConfigEnable,
}

// AssumeRole設定済のsession.Optionsを使ってsessionオブジェクトを生成
sess := session.Must(session.NewSessionWithOptions(sessOpts))

// sessionオブジェクトを使用する例: EC2クライアントの場合
ec2Client := ec2.New(sess)

先述の拙作ツール はEC2インスタンス一覧を取得するCLIなのですが、この実装でMFA & AssumeRoleに対応した結果、実行時に下記のようなMFAトークンの入力が促されるようになりました。

$ lsec2 --profile hoge
Assume Role MFA token code: [2段階認証アプリ等から取得したトークンを入力する]

この実装でAssumeRole不要の別profileも利用できるの?

できます。例えば下記例のように default というprofileが定義されている場合、先述のコードでprofileを指定している箇所に “default” を指定すればOKです。AssumeRoleとは無関係のprofileなので、当然MFAトークンの入力を求められる事もありません。

# ~/.aws/credentials

[default]
aws_access_key_id=ACCESS_KEY_ID
aws_secret_access_key=SECRET_ACCESS_KEY

[hoge]
:
:
# ~/.aws/config

[default]
region=ap-northeast-1

[profile hoge]
:

[profile dev-hoge-role]
:

aws-sdk-go-v2の場合

v2ではaws/session パッケージが削除されてしまった為(泣) 別の実装を行う必要があるのですが、結論を書くと aws/external パッケージの WithMFATokenFunc の利用例をほぼそのまま使って実装できます。

なお、configとcredentialsの両設定ファイルは、v1の実装例で紹介した設定内容をそのまま流用できます。

// external.LoadDefaultAWSConfig を2つのオプション付きで呼び出す
cfg, err = external.LoadDefaultAWSConfig(
  // external.WithMFATokenFunc でMFAトークンの入力方法を設定する
  // stscreds.StdinTokenProvider は標準入力を使用するProvider
  external.WithMFATokenFunc(stscreds.StdinTokenProvider),

  // external.WithSharedConfigProfile でprofile名を設定する
  external.WithSharedConfigProfile(profile),
)

if err != nil {
  // エラー処理
}

// configオブジェクトを使用する例: EC2クライアントの場合
ec2Client := ec2.New(cfg)

この件に限らずv2はconfigの取り扱い方が大幅に変更されているのですが、便利な変更の一例として ~/.aws/config ファイルを読み込む為に AWS_SDK_LOAD_CONFIG 環境変数の設定が不要になっています。(v1の実装時は session.SharedConfigEnable を設定することで AWS_SDK_LOAD_CONFIG 環境変数の設定漏れをカバーしていたのですが、この考慮も不要になりました)

v2はまだカジュアルに仕様変更が行われているフェーズなので、利用時はご注意を

v2はまだまだ活発に開発されている途上のフェーズで、docやexampleがv1の内容のまま残っていたりして、今回紹介する例と異なる実装例が公式のドキュメントで大量にHITします(そしてこれをそのまま流用しても動きません)。今回の例は本体のコード&テストを読んで実装を行い、一応の動作確認までは済ませてある例になります。が、将来的にこの実装では動かなくなる可能性は無くは無さそうという点だけご留意ください。