Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ AWS STS を図解するサイトです。英語と日本語に対応したイン
| 2 | エンドポイント | `sts.amazonaws.com` やリージョナルのホスト名を呼んだとき、どこで処理され、どこに記録され、どの枠でスロットリングされるかを 2025 年 4 月の変更前後で比べるシミュレーター。全パーティションのホスト名ビルダー、SDK の既定値エクスプローラー、商用 34 リージョンの一覧ボード |
| 3 | 全 API | 「何を持っているか」から 11 個の操作を引くピッカー、CLI 例付きの詳細、期間ルーラー、比較表、アクセスキーのプレフィックス判定 |
| 4 | 取得経路 | EC2、Lambda、ECS、IRSA、Pod Identity、Roles Anywhere、GitHub Actions、Identity Center、`aws login`、SAML、Cognito、AssumeRoot、アウトバウンドフェデレーションのシーケンス図をステップ実行。認証情報プロバイダーチェーンのラボ |
| 5 | セッション | 実効権限のゲート、ロールチェーンのビルダー (1 時間上限、推移タグ、ソースアイデンティティ)、セッショントークンのサイズメーター、セッション失効のタイムライン |
| 6 | 信頼ポリシー | 2 つのゲートのラボ、GitHub OIDC の信頼ポリシー評価、混乱した代理問題のウォークスルー、条件キーのカタログ |
| 7 | ログと上限 | 注釈付き CloudTrail レコード、600 RPS クォータのラボ、脅威カードと強化チェックリスト |
| 8 | エラー | エラーメッセージを貼ると原因と対処を表示 |
| 9–10 | 年表、用語集 | 2011〜2026 年の STS の歴史、EN/JA の用語 43 個 |
| 5 | 署名 | 本物の SigV4 署名処理 (長期キー、AssumeRole の呼び出しそのもの、一時認証情報) で正規リクエスト・署名対象文字列・キー導出・署名を表示し、値を書き換えて壊せるラボ。presigned URL のビルダー、SigV4a の鍵ペア導出と複数リージョン署名、Bedrock API キーの bearer 呼び出し |
| 6 | セッション | 実効権限のゲート、ロールチェーンのビルダー (1 時間上限、推移タグ、ソースアイデンティティ)、セッショントークンのサイズメーター、セッション失効のタイムライン |
| 7 | 信頼ポリシー | 2 つのゲートのラボ、GitHub OIDC の信頼ポリシー評価、混乱した代理問題のウォークスルー、条件キーのカタログ |
| 8 | ログと上限 | 注釈付き CloudTrail レコード、600 RPS クォータのラボ、脅威カードと強化チェックリスト |
| 9 | エラー | エラーメッセージを貼ると原因と対処を表示 |
| 10–11 | 年表、用語集 | 2011〜2026 年の STS の歴史、EN/JA の用語 43 個 |

数値の裏付けは [`docs/`](docs/README.md) に出典付きでまとめています。2026-10-10 時点の AWS ドキュメント、What's New、AWS SDK のエンドポイントデータ、GitHub の changelog で確認しました。AWS の情報源同士で食い違う箇所は、その旨を書いています。

SigV4 署名そのものは姉妹プロジェクト [caller-identity](https://github.com/0-draft/caller-identity) で図解しています。こちらは STS が主役です。
署名の章では、本物の SigV4 / SigV4a 実装 (WebCrypto と `@noble/curves`) をブラウザ内で動かします。テストでは AWS が公開している署名の計算例とバイト単位で照合しています。その確かめ方と、途中で見つけた AWS ドキュメントの誤りは [`docs/signing/`](docs/signing/README.ja.md) にあります。この章とノートは、2026-11-08 に統合した旧 `caller-identity` プロジェクトから移したものです。

## 開発

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ AWS STS, drawn. An interactive, bilingual (English / 日本語) explainer of the
| 2 | Endpoints | Request router: where a call to `sts.amazonaws.com` or a regional hostname is served, logged and throttled, before and after the April 2025 change; hostname builder for every partition and family; SDK default explorer; board of all 34 commercial Regions |
| 3 | Every API | "What are you holding?" picker over all 11 operations, detail passes with CLI examples, duration ruler, comparison matrix, access-key prefix decoder |
| 4 | Credential paths | Step-through sequence diagrams for EC2, Lambda, ECS, IRSA, Pod Identity, Roles Anywhere, GitHub Actions, Identity Center, `aws login`, SAML, Cognito, AssumeRoot, outbound federation; credential provider chain lab |
| 5 | Sessions | Effective-permissions gates, role-chaining builder (1 h cap, transitive tags, source identity), session-token size meter, revoke-sessions timeline |
| 6 | Trust policies | Two-gates lab, GitHub OIDC trust policy evaluator, confused-deputy walkthrough, condition-key catalogue |
| 7 | Logs & limits | Annotated CloudTrail records, 600 RPS quota lab, threat cards and hardening checklist |
| 8 | Errors | Paste an error message and get the cause and the fix |
| 9–10 | Timeline, glossary | STS history 2011–2026, 43 terms in EN/JA |
| 5 | Signing | Live SigV4 signer (long-term key, the AssumeRole call itself, temporary credentials) showing the canonical request, string to sign, key derivation and signature, with a "break it" panel; presigned URL builder; SigV4a keypair derivation and multi-Region signature; Bedrock API key bearer call |
| 6 | Sessions | Effective-permissions gates, role-chaining builder (1 h cap, transitive tags, source identity), session-token size meter, revoke-sessions timeline |
| 7 | Trust policies | Two-gates lab, GitHub OIDC trust policy evaluator, confused-deputy walkthrough, condition-key catalogue |
| 8 | Logs & limits | Annotated CloudTrail records, 600 RPS quota lab, threat cards and hardening checklist |
| 9 | Errors | Paste an error message and get the cause and the fix |
| 10–11 | Timeline, glossary | STS history 2011–2026, 43 terms in EN/JA |

The research behind every number lives in [`docs/`](docs/README.md), with sources. Facts were checked against AWS documentation, What's New posts, the AWS SDK endpoint data and the GitHub changelog on 2026-10-10. Where AWS sources disagree, the docs say so.

The SigV4 signature itself is drawn step by step in the sibling project [caller-identity](https://github.com/0-draft/caller-identity); this one is about STS.
The Signing chapter runs a real SigV4 and SigV4a implementation in the browser (WebCrypto plus `@noble/curves`). Its tests assert it byte for byte against the signature examples AWS publishes; how that was checked, and an error found in the AWS documentation along the way, is in [`docs/signing/`](docs/signing/README.md). That chapter and its notes came from the former `caller-identity` project, merged in on 2026-11-08.

## Development

Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ The research behind the site, one topic per file. Every non-obvious claim links
| [05-credential-paths.md](05-credential-paths.md) | How EC2, Lambda, ECS, EKS, Roles Anywhere, CI/CD OIDC, Identity Center, `aws login`, SAML, brokers, Cognito, AssumeRoot and outbound federation get credentials; SDK provider chains |
| [06-operations-and-security.md](06-operations-and-security.md) | CloudTrail, quotas and throttling, pricing, error catalogue, threats, best practices |
| [07-timeline.md](07-timeline.md) | STS history from 2011 to 2026 |
| [signing/](signing/README.md) | SigV4 in full, presigned URLs and SigV4a, the credential map, federation notes, and how the signer was verified against AWS's published examples (EN and JA) |

Where official AWS sources contradict each other (for example the SDK defaults table versus the 2025-07-31 release announcements), the notes say which source was followed and why.
101 changes: 101 additions & 0 deletions docs/signing/01-credential-map.ja.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
[English](01-credential-map.md) | **日本語**

# クレデンシャルの地図

[ノートに戻る](README.ja.md)

## 何がクレデンシャルなのか

AWS の API を呼ぶという目的に限れば、形は2つだけ。それに最近増えた、どちらにも当てはまらない3つ目
が加わる。

| 形 | 構成要素 | 発行元 | 期限 |
| --- | --- | --- | --- |
| 長期キー | `AKIA...` + secret | `iam:CreateAccessKey` | 無し |
| 一時クレデンシャル | `ASIA...` + secret + session token | STS | 必ずある |
| bearer token | 不透明な文字列1本 | Bedrock の API キー | 種類による |

SigV4 のキーペアの中で、期限を持たないのは長期キーだけ (長期の Bedrock API キーは作成時に決めた期限まで有効で、専用の IAM user に紐づく)。このページに書く残り全部は、その1点から
派生している。

## 入り口の問題

STS も普通の AWS サービスなので、呼ぶには署名が要り、署名にはクレデンシャルが要る。これは循環して
いて、フェデレーションの仕組みは全部この循環を断つために存在する。

```mermaid
flowchart LR
akia["AKIA... 長期"] -->|"SigV4 署名付き<br/>sts:AssumeRole"| asia["ASIA... 一時"]
asia -->|"SigV4 + session token"| api["AWS API"]

oidc["OIDC ID token"] -->|"未署名<br/>AssumeRoleWithWebIdentity"| asia
saml["SAML assertion"] -->|"未署名<br/>AssumeRoleWithSAML"| asia
x509["X.509 証明書"] -->|"rolesanywhere:CreateSession"| asia
meta["IMDS / ECS エンドポイント"] -->|"素の HTTP、リンクローカル"| asia

style akia stroke:#ff6b6b
style oidc stroke:#7ee787
style saml stroke:#7ee787
style meta stroke:#7ee787
```

`AssumeRoleWithWebIdentity` と `AssumeRoleWithSAML` は、AWS クレデンシャルを一切持たずに呼べる唯一の
STS API。未署名の HTTP リクエストで、ボディに「他人が署名した assertion」を載せる。仕掛けはそれだけ
で、CI パイプラインに `AKIA` が1本も要らなくなる理由もこれ。

IMDS は別のやり方で循環を断つ。プラットフォームが既に AssumeRole を済ませ、その結果をリンクローカル
アドレスに置いている。

## 各経路の役割

- **長期キー。** 基準線であり、消す対象。IAM user が同時に持てるキーは Active/Inactive を問わず2本
まで。3本目は `LimitExceeded`。2本あることがローテーションを成立させている。作る、切り替える、
消す。
- **AssumeRole。** クロスアカウントと権限分離。既にクレデンシャルが必要なので、入り口ではなく連鎖の
途中の1ステップ。
- **OIDC フェデレーション。** CI から長期キーを消す。仕事をしているのは trust policy で、そこを
間違える話は [フェデレーション](03-federation.ja.md) で扱う。
- **IMDS。** EC2 のディスクに何も置かなくてよい理由 (Lambda は同じ 3 つの値を環境変数で、ECS や EKS Pod Identity はコンテナ用エンドポイントで受け取る)。IMDSv2 の `PUT` は趣味ではない。SSRF
は脆弱なアプリから `GET` を引き出せても、カスタムヘッダ付きの `PUT` はまず引き出せない。
- **bearer token。** API キーを期待していて署名を教え込めないツール向け。AWS 自身の案内も「STS が
不可能な場合に限れ」。

## 3つの層

混乱のほとんどは、SigV4 と STS をセットだと思うところから来る。セットではなく、別の層にいる。

```mermaid
flowchart TD
L1["第1層: クレデンシャルの出所<br/>IAM · STS · IMDS · 外部 IdP"]
L2["第2層: リクエストの認証方法<br/>SigV4 · SigV4a · bearer"]
L3["第3層: 呼び出し元に何が許されるか<br/>identity policy · resource policy · SCP · session policy"]

L1 --> L2 --> L3
```

長期キーも一時クレデンシャルも、同じアルゴリズムの SigV4 で署名する。ワイヤー上の違いは
`X-Amz-Security-Token` ヘッダが増えることだけで、そのヘッダは `SignedHeaders` にも入れる必要がある。
自前実装で `SignatureDoesNotMatch` が出る最頻出の原因がこの入れ忘れ。

## エラーの読み方

層に順番があるので、エラーがどの層まで到達したかを教えてくれる。

| エラー | 到達した層 | 意味 |
| --- | --- | --- |
| `InvalidClientTokenId` | 2 | `ASIA...` を session token 無しで送った |
| `SignatureDoesNotMatch` | 2 | 署名を再計算したら違った |
| `RequestTimeTooSkewed` | 2 | タイムスタンプが許容窓の外 |
| `ExpiredToken` | 2 | `Expiration` を過ぎている |
| `AccessDenied` | 3 | 署名は通り、ポリシーが拒否した |

`AccessDenied` は第2層については良い知らせ。クレデンシャルが問題ならそこまで到達していない。

## API 用ではないクレデンシャルもある

ここまでは全部「AWS の API を呼ぶ」話。データプレーンに対する認証は別の一族で、上のルールに従わない。
RDS のマスターパスワード、IAM database authentication、Redshift の `GetClusterCredentials`、secret key
から導出する SES の SMTP credential、CodeCommit と Keyspaces の service-specific credential、IoT の
X.509 クライアント証明書、MSK の SASL/SCRAM secret、Cognito の user pool token、API Gateway の API
キー、CloudFront の signed URL。ここでは扱わないが、「クレデンシャルなら上の3つの形のどれかだ」と
結論する前に、存在だけは知っておく価値がある。
109 changes: 109 additions & 0 deletions docs/signing/01-credential-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
**English** | [日本語](01-credential-map.ja.md)

# The credential map

[Back to notes](README.md)

## What counts as a credential

For calling an AWS API there are exactly two shapes, plus a third that arrived recently
and does not fit either.

| Shape | Components | Issued by | Expires |
| --- | --- | --- | --- |
| Long-term key | `AKIA...` + secret | `iam:CreateAccessKey` | Never |
| Temporary credential | `ASIA...` + secret + session token | STS | Yes, always |
| Bearer token | One opaque string | Bedrock API keys | Depends |

Among the SigV4 key pairs, the long-term key is the only one with no expiry. That single
property is the reason for everything else on this page. (A long-term Bedrock API key lasts
until the expiry configured at creation, and is tied to an IAM user of its own.)

## The entry problem

STS is an ordinary AWS service, so calling it requires a signature, which requires a
credential. That is circular, and every federation mechanism exists to break the circle.

```mermaid
flowchart LR
akia["AKIA... long-term"] -->|"SigV4-signed<br/>sts:AssumeRole"| asia["ASIA... temporary"]
asia -->|"SigV4 + session token"| api["AWS API"]

oidc["OIDC ID token"] -->|"unsigned<br/>AssumeRoleWithWebIdentity"| asia
saml["SAML assertion"] -->|"unsigned<br/>AssumeRoleWithSAML"| asia
x509["X.509 certificate"] -->|"rolesanywhere:CreateSession"| asia
meta["IMDS / ECS endpoint"] -->|"plain HTTP, link-local"| asia

style akia stroke:#ff6b6b
style oidc stroke:#7ee787
style saml stroke:#7ee787
style meta stroke:#7ee787
```

`AssumeRoleWithWebIdentity` and `AssumeRoleWithSAML` are the only STS APIs callable with
no AWS credential at all. They take an unsigned HTTP request whose body carries an
assertion signed by somebody else. That is the whole trick, and it is why a CI pipeline
needs no `AKIA` anywhere.

IMDS breaks the circle differently: the platform has already done the AssumeRole and
leaves the result on a link-local address.

## What each path is actually for

- **Long-term key.** The baseline, and the thing to remove. An IAM user may hold two keys
at a time, Active or Inactive, and a third request returns `LimitExceeded`. Two is what
makes rotation possible at all: create, cut over, delete.
- **AssumeRole.** Cross-account access and privilege separation. Needs credentials
already, so it is a step in a chain rather than an entry point.
- **OIDC federation.** Ends long-term keys in CI. The trust policy does the work, and
getting it wrong is the subject of [Federation](03-federation.md).
- **IMDS.** Why an EC2 instance needs nothing on disk. (Lambda gets the same three values
as environment variables instead, and ECS and EKS Pod Identity from a container endpoint.) The `PUT` in IMDSv2 is
not stylistic: server-side request forgery can usually coerce a `GET` out of a
vulnerable application, rarely a `PUT` carrying a custom header.
- **Bearer tokens.** For tools that expect an API key and cannot be taught to sign. AWS's
own guidance is to use them only where STS is impossible.

## The three layers

Most confusion comes from treating SigV4 and STS as a pair. They are not; they sit in
different layers.

```mermaid
flowchart TD
L1["Layer 1: where the credential comes from<br/>IAM · STS · IMDS · external IdP"]
L2["Layer 2: how the request is authenticated<br/>SigV4 · SigV4a · bearer"]
L3["Layer 3: what the caller may do<br/>identity policy · resource policy · SCP · session policy"]

L1 --> L2 --> L3
```

A long-term key and a temporary credential both sign with SigV4, using the identical
algorithm. The only difference on the wire is the extra `X-Amz-Security-Token` header,
which must also appear in `SignedHeaders`. Omitting it is the most common cause of
`SignatureDoesNotMatch` in hand-rolled clients.

## Reading the error

Because the layers are ordered, the error names the layer.

| Error | Layer reached | What it means |
| --- | --- | --- |
| `InvalidClientTokenId` | 2 | An `ASIA...` key sent without its session token |
| `SignatureDoesNotMatch` | 2 | Signature recomputed and differed |
| `RequestTimeTooSkewed` | 2 | Timestamp outside the accepted window |
| `ExpiredToken` | 2 | Past `Expiration` |
| `AccessDenied` | 3 | Signature verified, policy said no |

An `AccessDenied` is good news about layer 2. If credentials were the problem you would
never have got that far.

## Not all credentials are for the API

Everything above is about calling AWS APIs. A separate family authenticates to data
planes and follows none of these rules: RDS master passwords, IAM database
authentication, Redshift `GetClusterCredentials`, SES SMTP credentials derived from the
secret key, CodeCommit and Keyspaces service-specific credentials, IoT X.509 client
certificates, MSK SASL/SCRAM secrets, Cognito user-pool tokens, API Gateway API keys,
CloudFront signed URLs. They are out of scope here, but worth knowing they exist before
concluding that a credential must be one of the three shapes above.
Loading
Loading