📝

AWS SDK for JavaScript for Node.jsでスイッチロールする方法

2021/09/14に公開

AWSでは、IAMロールを切り替えられるスイッチロールという方法がありますね。
20190130 AWS Black Belt Online Seminar AWS Identity and Access Management (AWS IAM) Part2

以前、AWS CLIでのスイッチロールという記事を書きましたが、今回は、AWS SDK for JavaScript for Node.jsでスイッチロールする方法を紹介します。

ちなみに、公式ではPythonでのサンプルが紹介されており、そちらも参考にしています。
IAM ロールの切り替え (AWS API)

事前準備

以前の記事で紹介しているように、以下を行います。
詳細は、AWS CLIでのスイッチロールをご覧ください。

  • スイッチ先のアカウントでロールを作成する
  • CLIでスイッチの設定をする
  • スイッチ元ユーザーにロールを引き受けるための権限を付与する

サンプルコード

まずはコードを紹介します。

switch.js
const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
AWS.config.apiVersions = {
  ec2: '2016-11-15',
  sts: '2011-06-15',
};
const sts = new AWS.STS();

const credentials = new AWS.SharedIniFileCredentials({
  profile: 'configファイルで設定した名前',
});
AWS.config.credentials = credentials;

const main = async () => {
  try {
    const credentials = await assumeRole();
    const options = {
      accessKeyId: credentials.AccessKeyId,
      secretAccessKey: credentials.SecretAccessKey,
      sessionToken: credentials.SessionToken,
    };
    const ec2 = new AWS.EC2(options);
    const params = {};
    const result = await ec2.describeInstances(params).promise();
    console.log(result);
  } catch (error) {
    console.log(error);
  }
};
main();

async function assumeRole() {
  const params = {
    RoleArn: "スイッチ先のIAMロールARN" /* required */,
    RoleSessionName: "任意の名前(configファイルのプロファイル名等)" /* required */,
    // DurationSeconds: 'NUMBER_VALUE',
    // ExternalId: 'STRING_VALUE',
    // Policy: 'STRING_VALUE',
    // PolicyArns: [
    //   {
    //     arn: 'STRING_VALUE',
    //   },
    //   /* more items */
    // ],
    // SerialNumber: 'STRING_VALUE',
    // Tags: [
    //   {
    //     Key: 'STRING_VALUE' /* required */,
    //     Value: 'STRING_VALUE' /* required */,
    //   },
    //   /* more items */
    // ],
    // TokenCode: 'STRING_VALUE',
    // TransitiveTagKeys: [
    //   'STRING_VALUE',
    //   /* more items */
    // ],
  };

  try {
    const result = await sts.assumeRole(params).promise();
    return result.Credentials;
  } catch (error) {
    console.log(error);
  }
}

SDKのロード

まずはaws-sdkをロードします。
このへんは鉄板処理だと思います。

const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
AWS.config.apiVersions = {
  ec2: '2016-11-15',
  sts: '2011-06-15',
};
const sts = new AWS.STS();

今回はスイッチロールのためにstsのAPIと、動作確認のためにec2のAPIを使用するので、それぞれのapiバージョンを固定しています。
各APIのバージョンは公式リファレンスをご覧ください。

共有認証情報ファイルから Node.js に認証情報をロードする

Node.jsで、configファイルから認証情報をロードします。

const credentials = new AWS.SharedIniFileCredentials({
  profile: 'configファイルで設定した名前',
});
AWS.config.credentials = credentials;

詳細は以下のドキュメントに記載されています。
共有認証情報ファイルから Node.js に認証情報をロードする

JavaScript 用 SDK は、ロード時に共有認証情報ファイルを検索します。これは「credentials」という名前です。共有認証情報ファイルを保存する場所は、オペレーティングシステムによって異なります。
・Linux、Unix、および macOS の共有認証情報ファイル: ~/.aws/credentials
・Windows の共有認証情報ファイル: C:\Users\USER_NAME.aws\credentials

aws configureでIAMユーザーを切り替えて、その情報をSDKで使用する方法もありますが、上記では、credentialファイルのプロファイル情報をロードし、その情報をSDKで使用するよう設定するという認識です。

SDK をロードする前に process.env.AWS_PROFILE を設定するか、次の例に示すように認証情報プロバイダーを選択することによって、SDK で使用されるプロファイルを明示的に選択することもできます。

var credentials = new AWS.SharedIniFileCredentials({profile: 'work-account'});
AWS.config.credentials = credentials;

assumeRoleでロールを引き受ける

assumeRole()関数で、`sts.assumeRole()APIを呼び出します。

async function assumeRole() {
  const params = {
    RoleArn: "スイッチ先のIAMロールARN" /* required */,
    RoleSessionName: "任意の名前(configファイルのプロファイル名等)" /* required */,
    // DurationSeconds: 'NUMBER_VALUE',
    // ExternalId: 'STRING_VALUE',
    // Policy: 'STRING_VALUE',
    // PolicyArns: [
    //   {
    //     arn: 'STRING_VALUE',
    //   },
    //   /* more items */
    // ],
    // SerialNumber: 'STRING_VALUE',
    // Tags: [
    //   {
    //     Key: 'STRING_VALUE' /* required */,
    //     Value: 'STRING_VALUE' /* required */,
    //   },
    //   /* more items */
    // ],
    // TokenCode: 'STRING_VALUE',
    // TransitiveTagKeys: [
    //   'STRING_VALUE',
    //   /* more items */
    // ],
  };

  try {
    const result = await sts.assumeRole(params).promise();
    return result.Credentials;
  } catch (error) {
    console.log(error);
  }
}

これでスイッチ先のIAMロールの一時的なアクセスキーやシークレットアクセスキーを取得できます。
paramsはリファレンスから引っ張ってきたので、たくさんオプションがありますが、ほとんど不要だったのでコメントアウトしています。必要に応じてコメントアウトを解除してください。

API呼び出しに使用する認証情報をセット

assumeRoleで取得したスイッチ先のIAMロールの認証情報を、ec2のAPIに渡すために、所定の形式にセットします。

 const credentials = await assumeRole();
    const options = {
      accessKeyId: credentials.AccessKeyId,
      secretAccessKey: credentials.SecretAccessKey,
      sessionToken: credentials.SessionToken,
    };

この辺の処理は、Pythonの例をNode.js用に変換したかたちです。

s3_resource=boto3.resource(
    's3',
    aws_access_key_id=credentials['AccessKeyId'],
    aws_secret_access_key=credentials['SecretAccessKey'],
    aws_session_token=credentials['SessionToken'],
)

認証情報を渡してAPIを呼び出す

ここまでくれば通常のAPI呼び出しとほぼ同じです。

const ec2 = new AWS.EC2(options);
const params = {};
const result = await ec2.describeInstances(params).promise();
console.log(result);

通常と異なるのは、new AWS.サービス名()の際に、引数としてセットした認証情報(opthons)を渡している点です。
これを渡さないとスイッチ先のIAMロールで呼び出しができません。
サンプルコードでは、認証情報を渡した後に、ec2.describeInstancesでEC2インスタンスの一覧を取得しています。

実行してみる

実際にEC2インスタンスを1台起動して実行してみましょう。

出力をすべて見たいので、
console.log(result)
console.log(JSON.stringify(result, null, 2));に変更し、
node switch.jsを実行します。

起動したインスタンスの情報を取得できることが確認できました。

認証情報を渡さずに実行してみる

const ec2 = new AWS.EC2(options);
optionsを渡さずに実行してみます。

CredentialsErrorが発生しました。

これで、スイッチ先のIAMロールの認証情報が、API呼び出しに使用されていることが確認できました。

(余談)フロントエンドから直接リクエストする方法

スイッチロールから逸脱してしまいますが、以前
Amplify Vue.jsアプリのフロントエンドからAWSリソースに直接リクエストしてみた
という記事で、フロントエンドからのリクエスト方法も紹介しています。
Veu.jsでの方法なので、用途は限られますが、こちらも参考になれば幸いです。

まとめ

今回は、AWS SDK for JavaScript for Node.jsでスイッチロールする方法を紹介しました。
EC2インスタンスやLambdaにはIAMロールをアタッチすればよいですが、開発段階でローカルでコードのテストをする際、環境ごとに1人1人にIAMユーザーを作って、毎回aws configureして・・・だと、面倒だし、IAMユーザーが増えるのはセキュリティ的にもよろしくないと思います。
そんな時に、CLIやSDKでスイッチロールできるようにしておけば、余計なリスクを増やすことはなくなります。
スイッチロールをうまく使って、安全に開発していきたいですね。
今回の内容が参考になれば幸いです。

Discussion