継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

踏み台用 ECS Fargate から Aurora へのローカル接続を実現する

タダです.

AWSコンテナ設計・構築[本格]入門」を読んでて,ECS Fargate で踏み台を作るハンズオンがあります.この踏み台コンテナを使って Eureca さんの記事で紹介されてたローカルの DB クライアントからプライベートサブネットの Aurora へ socat で TCP リレーを行い接続する検証をしてみたのでやった作業をこの記事に書いていきます.

検証概要

自分の管理している環境では AWS SSO を採用して AWS アカウントにログインしています.また各システムアカウントには踏み台用 AWS アカウントを経由して更にスイッチロールでログインしており,開発者ごとに必要な権限を渡す運用にしています.

検証では踏み台コンテナに AWS SSO 経由で許可された開発者だけアクセスさせるようにしたいので,スイッチロールを行った後 Session Manager の AWS-StartPortForwardingSession を使ってローカルからプライベートサブネットの Aurora へ接続を試みます.

検証イメージ

f:id:sadayoshi_tada:20220203220318p:plain

必要なセットアップ

今回の検証を行うために次のツールのセットアップを行います.

AWS CLI v2 の導入後の確認

 % aws --version
aws-cli/2.2.11 Python/3.8.8 Darwin/19.6.0 exe/x86_64 prompt/off

Session Manager Plugin の導入後の確認

% session-manager-plugin

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.

事前準備

事前準備として SSO 初期設定と AWS CLI の初期設定を行います.

AWS CLI 初期設定

1, ~/.aws/credentials のファイルに AWS SSO に表示されている踏み台 AWS アカウントのクレデンシャル情報を追記する

[踏み台 AWS アカウントの任意のプロファイル名]
aws_access_key_id=hoge
aws_secret_access_key=hoge
aws_session_token=hoge

[システム AWS アカウントの任意のプロファイル名]
role_arn = arn:aws:iam::12345678910:role/[スイッチロールのロール名]
source_profile = [踏み台 AWS アカウントの任意のプロファイル名]

2, ~/.aws/config のファイルに下記のように追記する

[profile システム AWS アカウントの任意のプロファイル名]
region = ap-northeast-1
output = json

SSO 初期設定

1, aws configure sso --profile [踏み台 AWS アカウントのプロファイル名] を実行してプロンプトに必要な情報を埋める

% aws configure sso --profile [踏み台 AWS アカウントのプロファイル名]
SSO start URL [None]: https://hoge.awsapps.com/start#/                                                         
SSO Region [None]: ap-northeast-1                                                                                         
Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:

https://device.sso.ap-northeast-1.amazonaws.com/

Then enter the code:

hoge-hoge(自動で表示される)
There are 5 AWS accounts available to you.
Using the account ID 12345678910(踏み台 AWS アカウント)
There are 5 roles available to you.
Using the role name "hoge-role"(踏み台 AWS アカウントで使っているロール名)
CLI default client Region [None]: ap-northeast-1                                                                          
CLI default output format [None]: json                                                                                    

To use this profile, specify the profile name using --profile, as shown:

aws s3 ls --profile 12345678910_bastion-switch-role <=ここまで出れば終了

2, 以下のコマンドを実行し、システム AWS アカウントのスイッチロール先 IAM ロールが表示されたら完了

% aws --profile [システム AWS アカウントのプロファイル名]sts get-caller-identity
{
    "UserId": "hoge:botocore-session-123456789",
    "Account": "12345678910",
    "Arn": "arn:aws:sts::12345678910:assumed-role/switch-role/botocore-session-123456789"
}

クライアント側から接続のためのスクリプト

DB クライアント側から接続するために開発者が実行する共通処理をスクリプトにしてみました.

#!/bin/sh
# RDS のホスト名
TARGET_HOST=hoge.ap-northeast-1.rds.amazonaws.com
# ターゲットのポート番号
PORT_NUBMER=3306
# ローカルのポート番号
LOCAL_PORT_NUMBER=3306
# 踏み台 AWS アカウントのプロファイル名
SOURCE_PROFILE=[踏み台 AWS アカウントのプロファイルを記載]
# システム AWS アカウントのプロファイル名
SYSTEM_PROFILE=[システム AWS アカウントのプロファイルを記載]

# init
aws sso login --profile ${SOURCE_PROFILE}

# オンラインステータスのコンテナIDを取得
BASTION_CONTAINER_ID=`aws --profile ${SYSTEM_PROFILE} ssm describe-instance-information --filters "Key=PingStatus,Values=Online" | jq -r '.InstanceInformationList[] | select ( .InstanceId| startswith("mi-")) | .InstanceId '`

# コマンドID取得
COMMAND_ID=`aws --profile ${SYSTEM_PROFILE} ssm send-command \
  --instance-ids ${BASTION_CONTAINER_ID} \
  --document-name [socat 実行するためのドキュメント名] \
  --parameters targetHost=${TARGET_HOST} | jq -r .Command.CommandId`

# ポートフォワード
aws --profile ${SYSTEM_PROFILE} ssm start-session \
    --target ${BASTION_CONTAINER_ID} \
    --document-name AWS-StartPortForwardingSession \
    --parameters portNumber=${PORT_NUBMER},localPortNumber=${LOCAL_PORT_NUMBER}

# RunCommand のキャンセル
aws --profile ${SYSTEM_PROFILE} ssm cancel-command --command-id ${COMMAND_ID}  

接続確認

クライアント側から接続のためのスクリプトを実行して,SSO の認証が通れば IAM の権限で Session Manager や RunCommand の権限が許可されていてかつネットワーク周りの経路が確保されていた状態であれば,Aurora への接続が成功しました.

まとめ

検証で DB クライアントからの接続が成功したので,開発メンバーにも展開しました.これで SSH 鍵の管理がなくなり,踏み台サーバーのセキュリティグループの管理からも開放されます.加えて SSO や IAM で許可した開発者だけが DB につなげるためセキュリティ向上にも資するような仕組みしていけるなと感じました.

参考にさせていただいた記事

dev.classmethod.jp

dev.classmethod.jp

medium.com

CloudWatch Events の定数を使って Lambda の処理を分岐させる

タダです.

定期イベントを CloudWatch Events で設定し,定型処理を実行する Lambda をキックすることはよくあることだと思います.今回 CloudWatch Events の定数を使って Lambda の処理を分岐させるのをやってみたのでこの記事にまとめていきます.今回は ECS タスクの数を調整する処理を作ってみました.

ECS のタスク数を調整する Lambda

Lambda のコードは以下のようなコードを書いてます.CloudWatch Events から desired_count をパラメーターとして渡し,desired_countが0ならコンテナをなくして,desired_countを1ならコンテナを1台起動させます.

import json
import boto3
import logging
from botocore.exceptions import ClientError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info("Update ECS Task Count")
    CLUSTER_NAME = 'cluster name'
    SERVICE_NAME =  'service name'
    desired_count = event['desired_count'] 
    try:
        client = boto3.client('ecs')
        
        if desired_count == 0:
            result = client.update_service(
                    cluster = 'cluster name',
                    service = 'service name',
                    desiredCount = desired_count
                )
            return "Stop Complete."
            
        elif desired_count == 1:
            result = client.update_service(
                    cluster = 'cluster name',
                    service = 'service name',
                    desiredCount = desired_count
                )
            return "Start Complete."
            
            
    except ClientError as e:
        logger.exception("Exception.")

CloudWatch Events の定数

Lambda のコードで設定している desired_count を定数で定義していきます.イベントのターゲットを指定する箇所で定数を {"desired_count": 0(1)}と設定します.

コンテナを0台にするイベント f:id:sadayoshi_tada:20220130180916p:plain

コンテナを1台にするイベント f:id:sadayoshi_tada:20220130180902p:plain

関連情報

docs.aws.amazon.com

動作テスト

Lambda のテストイベントで次のような設定をして実行してみます.下記のテストでコンテナを起動できるか確認します.1台のコンテナ起動できました.

{
  "desired_count": 1
}

f:id:sadayoshi_tada:20220130181755p:plain

f:id:sadayoshi_tada:20220130181902p:plain

次にコンテナをなくしてみます.意図通りコンテナを消せました.

{
  "desired_count": 0
}

f:id:sadayoshi_tada:20220130182019p:plain

f:id:sadayoshi_tada:20220130182035p:plain

まとめ

CloudWatch Events の定数を使って ECS タスク数を Lambda 側で処理を分岐させました.この機能を知るまでは Lambda を処理ごとに作ってしまっていたので,管理する関数の数が少なくなるので良かったです.

Session Manager のログ出力形式をまとめる

タダです.

Session Manager のログ周りを触る機会があり,S3 と CloudWatch Logs へ Session Manager のログを出力できるのですが,それぞれの設定方法をまとめていこうと思います.

S3 に Session Manager のログを出力する場合

S3 にログを出力する場合は, セッションマネージャー > 設定よりS3 logging のセクションで設定していきます.今回は下記のように設定を行います.

f:id:sadayoshi_tada:20220126062750p:plain

上記の設定を保存後,Session Manager でターミナル作業を終了したら S3 にログが格納されます.ユーザー名-乱数.logというファイルになっており,中身はログの中身以下に記載のようなログとして記録されます.

f:id:sadayoshi_tada:20220126063133p:plain

ログの中身

Script started on 2022-01-25 06:49:24+0000
[?1034hsh-4.2$ 
[Ksh-4.2$ 
sh-4.2$ 

関連情報

docs.aws.amazon.com

CloudWatch Logs に Session Manager のログを出力する場合

CloudWatch Logs にログを出力する場合も セッションマネージャー > 設定よりCloudWatch logging のセクションで設定していきます.今回は下記のように設定を行います.

  • CloudWatch loggingEnable チェックボックスをオン
  • Choose your preferred logging optionStream session logs (Recommended) を選択
  • Enforce encryptionAllow only encrypted CloudWatch log groups チェックボックスをオン
  • リストからロググループを選択する から作成済みの暗号化済みのロググループを選択

f:id:sadayoshi_tada:20220126092931p:plain

上記の設定を保存後,Session Manager でターミナル作業中の操作が CloudWatch Logs にログが格納されます.ユーザー名-乱数というストリームになっており,中身は以下に記載のような JSON ログとして記録されます.

f:id:sadayoshi_tada:20220126093332p:plain

ログの中身

{
    "eventVersion": "1.0",
    "eventTime": "2022-01-25T12:08:12Z",
    "awsRegion": "ap-northeast-1",
    "target": {
        "id": "i-123456789101112"
    },
    "userIdentity": {
        "arn": "arn:aws:iam::123456789101112:user/ssm-test"
    },
    "runAsUser": "ssm-user",
    "sessionId": "ssm-test-123456789101112",
    "sessionData": [
        "sh-4.2$ sh-4.2$ "
    ]
}

関連情報

docs.aws.amazon.com

まとめ

Session Manager のログ保存方法について2種類あり,設定方法とログの中身を確認したのでまとめました.こういったログは監査の際に必要になったりすると思いますが,保存方法,保存期間や閲覧など要件に応じた選択をしていきたいと思います.

Slack のスラッシュコマンドを使って AWS リソースを操作する

タダです.

過去に AWS Chatbot から Lambda に AWS リソースを操作する記事を書いたのですが,AWS Chatbot を呼び出すコマンドはパラメータなど覚えていないといけず普段詰まることがよくあり,開発者に提供するツールとしてはスラッシュコマンドで出すようにしています.スラッシュコマンドは API Gateway と Lambda の組み合わせが一般的で記事もたくさんあるのですが,スラッシュコマンド で AWS リソースを操作したい時の設定事項をこの記事に備忘録として残しておきます.

sadayoshi-tada.hatenablog.com

AWS 側の設定

API Gateway 側の設定としてまずは REST API を構築します.次に設定するのがメソッドとパスごとに呼び出したい Lambda 関数の設定です.

f:id:sadayoshi_tada:20220118164727p:plain

最後にマッピングテンプレートです.Content-Type を application/x-www-form-urlencoded にして,テンプレートの生成の箇所を メソッドリクエストのパススルー に設定します.

f:id:sadayoshi_tada:20220118164420p:plain

以上で完了です.

Slack 側の設定

下記のページから Create New Appを押して新しいアプリケーションを作っていきます.From scratch > App Name にアプリケーション名と Pick a workspace to develop your app in にインストールする Slack のワークスペースを選びます.

api.slack.com

加えて Add features and functionality > Slash Commands に実行したいコマンドと API Gateway のエンドポイントを追記します.その後,Install your appワークスペースにインストールすればOKです.

まとめ

スラッシュコマンド で AWS リソースを操作する時の設定をまとめました.今後も AWS Chatbot と使い分けをしていきたいと思います.

参考記事

dev.classmethod.jp

qiita.com

GitHub Actions で AWS の認証に使っている OIDC provider の thmbprint が更新された場合の対処

タダです.

以前の記事で GitHub Actions の AWS 認証を OIDC provider に変更したことを書いたのですが,1/13 にこれまでうまくいっていたリリースが失敗するようになりました.そのエラーに対する対応をこの記事にまとめます.

sadayoshi-tada.hatenablog.com

発生した事象概要

aws-actions/configure-aws-credentials のセクションでこれまで成功していた認証が通らなくなり Error: OpenIDConnect provider's HTTPS certificate doesn't match configured thumbprint というメッセージが表示されていました.メッセージにも thumbprint が合致してないことが表示されたので,thumbprint が怪しそうと思って調査をしてみました.

事象の原因

Twitter で下記のツイートを見つけ関連の issue も貼っていただいていたため,解決に素早く到達できました.感謝です...やはり,thumbprint が更新されたため既存のものを変更する必要がありました.

参考情報

github.blog

事象の解決方法

事象の解決方法は非常にシンプルです.元々の thumbprint が下記の値になっているので,6938fd4d98bab03faadb97b34396831e3780aea1という更新した値に差し替えました.この後,リリースを回したら成功しました.

f:id:sadayoshi_tada:20220116141552p:plain

f:id:sadayoshi_tada:20220116141834p:plain

f:id:sadayoshi_tada:20220116141844p:plain

まとめ

OIDC provider の thumbprint 更新がされた場合の対応についてまとめました.GitHub は再発しないように調査を進めるとコメントがありましたが,issue の中で Terraform で管理する場合の例を共有してくださっていたのですが,今後も付き合っていく上ではやり方を検討していく必要がありそうです.

github.com