朝日ネット 技術者ブログ

朝日ネットのエンジニアによるリレーブログ。今、自分が一番気になるテーマで書きます。

アクセスキー不要!AWS IAM IDプロバイダーのハンズオン

こんにちは。株式会社朝日ネット サービス基盤部のxfuzzyです。 Amazon Web Services(以下AWS)外のサーバーのバッチ処理でAWSを使う1場合などで、「IAMユーザーのアクセスキー」を使うことがあると思います。

アクセスキーは有効期限のない認証情報なので、定期的なローテーションが推奨されるなど、運用に気を使う必要があります。そこで、アクセスキーを使わない方法について考えていこうと思います。なお、アクセスキーを使わないからといって、「鍵となる情報」をサーバー(あるいは専用のハードウェア)が保管することは変わりません。定期的なローテーションなどは必要ですが、頻度を下げたり、自動化を組んだりしやすくなるのがメリットです。

この記事では、OIDCについて簡単に説明し、AWS Identity and Access Management(以下IAM)の「OIDC IDプロバイダー」機能(以下IAM IDプロバイダー)を体験します。IAM IDプロバイダーは、ソフトウェアのビルドやデプロイに便利なGitHub ActionsからAWSにアクセスする際に、よく使われる機能でもあります。

AWSのユーザー認証

AWS外からAWSを使う場合の認証は、どのように行われるでしょうか。 AWS CLIboto3など、AWSを使うための複数の方法がありますが、認証方式としては共通で、以下の2種類のどちらかの認証情報を使うことになります。

  • 長期の認証情報(アクセスキー)。認証情報として「アクセスキーID、シークレットアクセスキー」を使う。「IAMユーザー」に対してCreateAccessKeyの操作をすると得られる。有効期間はなく、無効化操作等をしなければ数年は使える2
  • 短期の認証情報。認証情報として「アクセスキーID、シークレットアクセスキー、セッショントークン」を使う。「IAMロール」に対して「AssumeRole」系の操作をすると得られる。有効期間は十数分~12時間

上記の認証情報は、AWSに対するAPIリクエストをする際に、AWS Signature Version 4という方式で署名するために使われます。アクセスキーIDとセッショントークンはそのまま(TLS経由で)送られますが、シークレットアクセスキーは送られません。シークレットアクセスキーをもとに計算された「署名」がAPIリクエストに付加されて送られます。

短期の認証情報の方が長期の認証情報よりもリスクが低いため、「可能ならば長期の認証情報は使いたくない(用意したくない)」ということになります。

ベストプラクティス

AWSのベストプラクティスでも、長期の認証情報の使用は推奨されていません。

関係のある部分を私なりに抽出すると、次のことが書かれています。

  • 短期の認証情報の使用を必須にするのがベストプラクティス3
  • オンプレミスサーバー等、AWS環境外で実行されるワークロードでは、IAM Roles AnywhereAssumeRoleWithWebIdentityを使って長期の認証情報を不要にできる
  • 長期の認証情報を使う場合はアクセスキー更新などの運用を決める必要がある

AssumeRoleWithWebIdentityというのはIAM IDプロバイダーと組み合わせて使う機能です。この記事の後半ではIAM IDプロバイダーなど最低限のリソースを構築し、AssumeRoleWithWebIdentityの動作を体験します。

OIDCの説明

AWSの機能の紹介の前に、簡単にOIDC(OpenID Connect)について説明します。

OIDCは、あるサービスが別のサービスにユーザー認証を委任するためのプロトコルです。

OIDCの登場人物にはEntity、OpenID Provider(OP)、Relying Party(RP)等があります。 Entityは操作を行いたいユーザーです。OPは、ユーザー認証をする手段を持っているサービスです。 RPはOPにユーザー認証を委任するサービスです。

OIDCの登場人物

OIDCの詳しい解説はこの記事では省略しますが、代表的な動作の概要を2枚の図で示します。

OIDCの初期設定

OIDCを使ったSSOログイン

上記のうち、Discoveryエンドポイントと、IDトークンが、この後のIAM IDプロバイダーの解説に登場します。

AWS IAM OIDC IDプロバイダーを使った構成例

オンプレミスのバッチサーバー(この記事ではワークロードと呼ぶことにします)からAWSを使う想定での、構成例は次のようになります。 初期設定として、次を行います。

  1. ワークロードを認証できる機材をOPとし、鍵ペアを作成
  2. 公開鍵の情報等をOIDC Discoveryに沿って公開
  3. IAM IDプロバイダーに上記のURLを設定

実際にワークロードからAWSを操作する際は、次の動作になります。

  1. ワークロードはOPに対してIDトークンの発行を依頼し、取得
  2. ワークロードはAssumeRoleWithWebIdentityでIDトークンをAWSに送信
  3. AWSはIDトークンを検証する
  4. AWSはAssumeRoleWithWebIdentityへの応答として、ワークロードに短期の認証情報を送信
  5. ワークロードは短期の認証情報を用いてAWSを操作

OIDCの用語との対応としては、ワークロードがEntity、AWSがRPとなり、AWSがOPに対して、ワークロードの認証を委任する形になります。 なぜOPとワークロードを分けるのか疑問に思われるかもしれませんが、「1つのHSMが1つの機材に接続されていて、その機材のみが秘密鍵を管理し、それぞれのサーバーはそれを利用する」という状況を想定していただければと思います。

こうして見てみると、「OIDC規格に準拠したOPがあれば、それをIAM IDプロバイダーでも使える」という仕組みになっています。 OIDC規格ではIDトークンの形式が規定されている、つまり、IDトークンの形式はOPによらないので、AWSはどのOPが発行したIDトークンでも検証できることになります。

IAM IDプロバイダーのハンズオン

ここからはIAM IDプロバイダーを使って、長期の認証情報を使わずにAWSを操作する、ということを体験していきます。 具体的には次を体験します。

  • IAM IDプロバイダーを設定(AWSに対して認証機能の委任を設定)・・・Terraformで実施
  • IDトークンを発行
  • IDトークンを送り、AWSの操作ができることを確認

簡単のため、ハンズオンでは、ワークロードはOPを兼ねる構成とし、1つのCloudShell環境で、OPの役割(鍵ペアの生成・保管やトークン発行)とAWSへのアクセスを行います。 ソースコードは以下の場所にあります。

github.com

CloudShellを使って動かせるようにしてあり、CloudShellの場合の動かし方はREADME.mdに書きました。ローカルにTerraformの環境がある方は、ローカルにチェックアウトしても動かせるはずです。

以下、README.mdのコマンドを実行しながら、IAM IDプロバイダーの動作を確認してみましょう。

サーバー側の確認

「2.環境構築」までを実行すると、この時点で「OPの構成要素のうちIAM IDプロバイダーを動かすのに必要な要素」(鍵ペアと公開URL)は作れています。

公開URLを見るには、次のコマンドを実行します。

# 設定ドキュメント(Discoveryエンドポイント)を表示
$ curl -s ${TF_url_issuer}/.well-known/openid-configuration | jq
{
  "claims_supported": [],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "issuer": "https://***.execute-api.us-west-2.amazonaws.com/stage1/oidc",
  "jwks_uri": "https://***.execute-api.us-west-2.amazonaws.com/stage1/oidc/.well-known/jwks",
  "response_types_supported": [
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ]
}
# 公開鍵を表示
$ curl -s ${TF_url_issuer}/.well-known/jwks | jq
{
  "keys": [
    {
      "e": "AQAB",
      "kid": "k1",
      "kty": "RSA",
      "n": "***"
    }
  ]
}

次に、秘密鍵を表示してみましょう。

# OPが持つ秘密鍵を表示
$ openssl pkey -text -in tmp/keypair.pem -noout
Private-Key: (2048 bit, 2 primes)
・・・

OPの構成を図で示すと次の通りです。

ハンズオン環境のOPの構成

IDトークンの確認

IDトークンを生成してみます。「3.JWT生成」のコマンドを実行してみてください。 この時点で、jwtという変数にIDトークンが格納されます。IDトークンは「ヘッダ」「ペイロード」「署名」の3つの部分を.でつないだ形式になっています。 次のコマンドを実行すると、IDトークンの各部分の内容を確認し署名を検証することができます。4

$ echo ${jwt}
eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImsxIn0=.eyJzdWIiOiAic3V・・・b20ifQ==.nDADvk+MC9c・・・fCDcYWa7Kw==
$ echo $jwt | cut -d. -f1 | base64 -d | jq
{
  "alg": "RS256",
  "kid": "k1"
}
$ echo $jwt | cut -d. -f2 | base64 -d | jq
{
  "sub": "sub1",
  "exp": 1754473596,
  "iss": "https://****",
  "iat": 1754469996,
  "aud": "sts.amazonaws.com"
}
$ echo $jwt | cut -d. -f3 | base64 -d > tmp/jwt_signature
$ echo -n $text | openssl dgst -verify tmp/publickey.pem -signature tmp/jwt_signature
Verified OK

AWSの認証情報の取得

「4.短期の認証情報の取得」と同じ内容ですが、AssumeRoleWithWebIdentityを呼び、IDトークンに問題がなければ短期の認証情報が表示されます。

$ aws sts assume-role-with-web-identity --duration-seconds 900 --role-session-name webid --role-arn $TF_role_arn --web-identity-token $jwt | jq
{
  "Credentials": {
    "AccessKeyId": "...",
    "SecretAccessKey": "...",
    "SessionToken": "...",
    "Expiration": "2025-..."
  },
(以下略)

このステップでは、Terraformを使って構築した、次のリソースを使いました。

ハンズオン環境のAWS側の構成

認証情報の検証

この認証情報を使ってAWSの操作をしてみます。

$ export AWS_ACCESS_KEY_ID="(上記の結果から貼り付け)"
$ export AWS_SECRET_ACCESS_KEY="(上記の結果から貼り付け)"
$ export AWS_SESSION_TOKEN="(上記の結果から貼り付け)"
$ aws sts get-caller-identity | jq
{
  "UserId": "...:webid",
  "Account": "...",
  "Arn": "arn:aws:sts::...:assumed-role/oidc-sample-.../webid"
}

上記ではSTS GetCallerIdentityを実行し、短期の認証情報が正しいことを確認しています。

最後にリソースを削除します。AWS_ACCESS_KEY_ID等の環境変数を元に戻してから、「6.リソース削除」のコマンドを実行します。

以上で、AWSから認証を委任する設定をしたり、発行したIDトークンを使ってAWSの操作ができることが確認できました。 長期の認証情報を使わずに、AWSの操作ができたことに注目してください。

実際の使用に向けて

ハンズオンでは、ワークロードはOPを兼ねる構成としましたが、実際に使う場合は、信頼できるOPを用意し、トークン発行を集中管理するのがよさそうです。その際、OPがSPOFにならないように構成しておく必要もあります。

公開URLを用意することが少し大変ですが、ハンズオンで見たように、API Gatewayを使って比較的簡単に用意することもできます。

また、秘密鍵は、OPの中で漏洩しないように保管する必要があります。鍵となる情報を保管する際の注意点は、秘密鍵でも長期の認証情報でも同じですが、ローテーションの頻度は、より低くてよくなります。

まとめ

一般論として、長期の認証情報(共有シークレット)を使う方式よりも秘密鍵(電子署名)を使う方式の方が優れている理由として、次の点が挙げられます。

  • 秘密鍵の生成および保管が一つの機材内で完結する。漏洩の可能性が低く、設定や更新の手間が少ない
  • OSやハードウェアの鍵保持機構を使える5

AWSのベストプラクティスでなぜ、長期の認証情報が推奨されていないかを考えると、漏洩の可能性や、攻撃者にとっての攻撃のしやすさ6などの要因があると思います。

長期の認証情報を使わなくしていくことは意味があると思いますが、まだまだ長期の認証情報を使っている現場は多いです。 長期の認証情報を使うにあたり、代替手段の検討をしたけど、結局、長期の認証情報が残ってしまった、ということもあるかもしれません。 長期の認証情報がなくならない理由として、IDプロバイダーなどの代替手段の存在や使い方があまり知られていないことが挙げられます。IDプロバイダーについて知ろうとしている誰かの役に立ちたい、との思いでこの記事を書きました。

引き続き、長期の認証情報の代替手段について考えて、ブログにまとめていけたらよいな、と考えています。特に、今回紹介したOIDC IDプロバイダーではOPの存在が文字通り「鍵」となっているので、OPを用意できない環境でも使えるIAM Roles Anywhereの紹介もしたいと思っています。

採用情報

朝日ネットでは新卒採用・キャリア採用を行っております。

新卒採用 キャリア採用|株式会社朝日ネット


  1. 1日に1回、オンプレミスのサーバーからAmazon S3にファイルをアップロードする、というようなことを想定しています。
  2. 2026年3月現在。AWSの機能は頻繁にアップデートされるため、将来、有効期間が設定可能になるかもしれません。現在でも、EventBridge SchedulerとAPIターゲットを組み合わせるなどすれば、できるかもしれません。
  3. 「長期の認証情報を使ってはいけない」と言っているわけではないはずです。「長期の認証情報しか使えない場面では使ってもよい」とのことです。また、「長期の認証情報のみ」という構成はベストプラクティスから外れていて、「長期と短期の認証情報を両方使う」という構成にするべきのようです。
  4. JWTの仕様ではbase64url形式とされていますが、AWSのAssumeRoleWithWebIdentityは普通のbase64も受け付けているようなので、今回のハンズオンでは普通のbase64にしています。
  5. Windowsには「OSが秘密鍵を管理し、秘密鍵は取り出せないけど署名や暗号化はできる」という仕組みがあるようです。また、OSが乗っ取られても秘密鍵が取り出せない、TPMやHSMといった特別なハードウェアもあります。
  6. サーバーの中で、AWSの長期の認証情報がどこに保存されていそうか、当たりがつけられる。