2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

IAM認証のAWS API GatewayにC#からIAMロールでSigV4署名してアクセスするには

Last updated at Posted at 2020-10-19

IAM認証を使っているAWSのAPI Gatewayは、APIリクエスト時にSigV4署名が必要です。

前回の記事では、IAMユーザで認証するAPI Gateway呼び出しをC#で書きました。

今回は、EC2インスタンスにアタッチされているIAMロールでの認証です。

2020/12/23追記
EC2からはもっとシンプルに書いたほうがよさそうなので、改訂版の記事を書きました。
IAM認証のAWS API GatewayにEC2インスタンスからSigV4署名してアクセスするには

前提

IAMロールのアタッチされているEC2インスタンスでC#のコードを実行します。~/.aws/config に以下のようにIAMロールが指定されているものとします。

[profile default]
role_arn = arn:aws:iam::999999999999:role/ROLENAME
credential_source = Ec2InstanceMetadata

API GatewayのリソースポリシーにはこのIAMロールからのAPIアクセスを許可してあるものとします。

動作確認した環境はUbuntu 20.04です。

C#の環境は以下の通り。

$ dotnet --version
3.1.402

以下は私の記事でして、このとおりC#をほとんど始めて触っています。C#の流儀と違うところがあったらごめんなさい。

本記事でのライブラリ等は2020/10/19時点のものです。

サンプルコードダウンロード

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

サンプルソースコードのnamespaceを全置換しておきます。(このコマンドの説明は sedコマンドでディレクトリ内の全ファイルをテキスト全置換するには)

$ 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 は以下です。

2020/12/23追記:もう少しシンプルなコード → IAM認証のAWS API GatewayにEC2インスタンスからSigV4署名してアクセスするには

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()
        {
            // ~/.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 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.SessionToken}, // 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.AccessKeyId,
                                                        credentials.SecretAccessKey);

            // リクエストヘッダに署名を追加
            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からのレスポンスが表示されます。

情報源

C#でIAMロールにAssumeする方法がわからず、今回のテーマは難儀でした。わかってしまえば大したことないのですが。

参考にした情報は、前回の記事に加えて、AWS SDK for .NETのAPIレファレンスとPython boto3のソースコードと、参考としてawscurlというコマンドのソースコードです。

AWS SDK for .NETのドキュメントは、説明が少なく、メソッドのシグニチャはわかってもどう使ったらいいのかがわかりませんでした。IAMロールでの署名をしているはずのawscurlのソースコードを読み、awscurlが呼び出しているboto3のソースコードも読むことで、AWSのAPIをどう呼び出しているのかを把握し、同じことをC#で書くことで実装できました。

boto3はAWSのAPIを呼び出しているだけだと思っていましたが、使いやすいように多くの機能をPythonで実装していることを実感しました。boto3とは違って、AWS SDK for .NETは単にAWS API呼び出しをそのままメソッドにしているだけのように見えます。

2
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?