IAM認証を使っているAWSのAPI Gatewayは、APIリクエスト時にSigV4署名が必要です。
以前に同様の記事を書きましたが、EC2のインスタンスプロファイルからIAMロールにスイッチしてからリクエスト署名する処理になっていました。
スイッチせずにインスタンスプロファイルで直接署名すればいいことがわかりましたので、そのコードをここに残しておきます。
前提
IAMロールのアタッチされているEC2インスタンスでC#のコードを実行します。 ~/.aws/config
は不要です。
※前回の記事では、EC2にIAMロールがアタッチされているだけではなく、インスタンスプロファイルからIAMロールにスイッチする権限が必要でした。このような権限が必要なケースが前回の記事のコード以外の場面であるのかよくわからず、おそらく前回の記事はミスリードでした。
API GatewayのリソースポリシーにはこのIAMロールからのAPIアクセスを許可してあるものとします。
動作確認した環境はUbuntu 20.04です。
C#の環境は以下の通り。
$ dotnet --version
3.1.404
本記事でのライブラリ等は2020/12/21時点のものです。
サンプルコードダウンロード
SigV4署名するC#のサンプルコードはAWS公式サイトにありますので、それをダウンロードし、必要なディレクトリのみ残します。
この手順の詳細は前々回の記事を参照。
$ mkdir sample
$ cd sample
$ mkdir tmp
$ cd tmp
$ wget https://docs.aws.amazon.com/AmazonS3/latest/API/samples/AmazonS3SigV4_Samples_CSharp.zip
$ unzip AmazonS3SigV4_Samples_CSharp.zip
$ cd ..
$ mv tmp/AWSSignatureV4-S3-Sample/Signers ./
$ mv tmp/AWSSignatureV4-S3-Sample/Util ./
$ rm -r tmp
$ grep -rl AWSSignatureV4_S3_Sample Signers | xargs sed -i 's/AWSSignatureV4_S3_Sample/Sample/g'
$ grep -rl AWSSignatureV4_S3_Sample Util | xargs sed -i 's/AWSSignatureV4_S3_Sample/Sample/g'
C#のプロジェクト作成
dotnet
コマンドでプロジェクトを作成します。
$ dotnet new console
以下のようなディレクトリ構成になります。
$ tree
.
├── obj
│ ├── project.assets.json
│ ├── project.nuget.cache
│ ├── sample.csproj.nuget.dgspec.json
│ ├── sample.csproj.nuget.g.props
│ └── sample.csproj.nuget.g.targets
├── Program.cs
├── sample.csproj
├── Signers
│ ├── AWS4SignerBase.cs
│ ├── AWS4SignerForAuthorizationHeader.cs
│ ├── AWS4SignerForChunkedUpload.cs
│ ├── AWS4SignerForPOST.cs
│ └── AWS4SignerForQueryParameterAuth.cs
└── Util
└── HttpHelpers.cs
3 directories, 13 files
sample.csproj
に以下のように RootNamespace
の項目を追加します。サンプルダウンロード後に全置換したnamespaceを指定します。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Sample</RootNamespace>
</PropertyGroup>
</Project>
必要なパッケージをダウンロードします。
$ dotnet add package AWSSDK.SecurityToken
C#のソースコード
Program.cs
は以下です。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;
using Sample.Signers;
using Sample.Util;
namespace Sample
{
class Program
{
private static async Task Run()
{
InstanceProfileAWSCredentials instanceCredentials = new InstanceProfileAWSCredentials();
var credentials = await instanceCredentials.GetCredentialsAsync();
var uri = new Uri("https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello");
// 署名するためのソースとなるヘッダ情報
var headers = new Dictionary<string, string>
{
{AWS4SignerBase.X_Amz_Content_SHA256, AWS4SignerBase.EMPTY_BODY_SHA256},
{"content-type", "text/plain"},
{"x-amz-security-token", credentials.Token}, // IAMロールではこれが必要
};
// 署名を作成
var signer = new AWS4SignerForAuthorizationHeader
{
EndpointUri = uri,
HttpMethod = "GET",
Service = "execute-api",
Region = "ap-northeast-1"
};
var authorization = signer.ComputeSignature(headers,
"", // no query parameters
AWS4SignerBase.EMPTY_BODY_SHA256,
credentials.AccessKey,
credentials.SecretKey);
// リクエストヘッダに署名を追加
headers.Add("Authorization", authorization);
// リクエスト実行
// HttpHelpers はUtilで定義
HttpHelpers.InvokeHttpRequest(uri, "GET", headers, null);
}
static void Main(string[] args)
{
Run().Wait();
}
}
}
uri
はAPI GatewayのAPIのURLを入れます。
実行
以下のコマンドで実行できます。
$ dotnet run
ダウンロードしたサンプルコードのSigners
とUtil
にデバッグ用出力があるので、いろいろ表示されますが、最後にAPI Gatewayからのレスポンスが表示されます。
前回の記事との違い
前回は InstanceProfileAWSCredentials
からassumeRoleしていたのが、今回は InstanceProfileAWSCredentials
をそのまま使っている点です。
diffを見たほうが早いか。
@@ -16,21 +16,8 @@
{
private static async Task Run()
{
- // ~/.aws/credentials からRoleArnを読み取る
- SharedCredentialsFile sharedFile = new SharedCredentialsFile();
- sharedFile.TryGetProfile("default", out CredentialProfile credentialProfile);
- string roleArn = credentialProfile.Options.RoleArn;
-
- // IAMロールにassumeする
InstanceProfileAWSCredentials instanceCredentials = new InstanceProfileAWSCredentials();
- AmazonSecurityTokenServiceClient stsClient = new AmazonSecurityTokenServiceClient(instanceCredentials);
- AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest
- {
- RoleArn = roleArn,
- RoleSessionName = "test_session",
- };
- var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
- var credentials = assumeRoleResponse.Credentials;
+ var credentials = await instanceCredentials.GetCredentialsAsync();
var uri = new Uri("https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello");
@@ -39,7 +26,7 @@
{
{AWS4SignerBase.X_Amz_Content_SHA256, AWS4SignerBase.EMPTY_BODY_SHA256},
{"content-type", "text/plain"},
- {"x-amz-security-token", credentials.SessionToken}, // IAMロールではこれが必要
+ {"x-amz-security-token", credentials.Token}, // IAMロールではこれが必要
};
// 署名を作成
@@ -53,8 +40,8 @@
var authorization = signer.ComputeSignature(headers,
"", // no query parameters
AWS4SignerBase.EMPTY_BODY_SHA256,
- credentials.AccessKeyId,
- credentials.SecretAccessKey);
+ credentials.AccessKey,
+ credentials.SecretKey);
// リクエストヘッダに署名を追加
headers.Add("Authorization", authorization);
関連記事
SigV4署名に関する私の記事
- AWS RDSのログファイルを高速にダウンロードするスクリプト (11/06)
- IAM認証のAWS API GatewayにC#からIAMロールでSigV4署名してアクセスするには (2020/10/19)
- IAM認証のAWS API GatewayにC#からIAMユーザでSigV4署名してアクセスするには (2020/10/14)
- IAM認証のAWS API GatewayにPythonからSigV4署名してアクセスするには (2020/07/22)
最近API Gatewayの記事ばかり続いています。