LoginSignup
1
1

More than 3 years have passed since last update.

IAM認証のAWS API GatewayにEC2インスタンスからSigV4署名してアクセスするには

Last updated at Posted at 2020-12-23

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

ダウンロードしたサンプルコードのSignersUtilにデバッグ用出力があるので、いろいろ表示されますが、最後に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署名に関する私の記事

最近API Gatewayの記事ばかり続いています。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1