🔏

LocalStackでローカル用のAWS Secrets Managerをさくっと用意する

2023/12/31に公開

みなさんAWS Secrets Managerは使っていますか?
秘匿情報を簡単に管理できて便利ですよね。
ただ、ローカルでの開発中はちょっと面倒だなぁと思ったことはありませんか?私はあります。
ということで、以前から気になっていたLocalStackを試したみたところ、ローカル用のAWS Secrets Managerを簡単に用意することができたので、備忘録も兼ねて記事にしておきたいと思います(今更感はありますが)。

環境、バージョン

Docker Desktop: 4.26.1 (131620)

動作確認用

https://github.com/negibouze/localstack-secretsmanager

本題

1. まずはコンテナを作ってみる

今回はDocker Composeを使います。
公式ページに書いてあるのでコピペしましょう。

version: "3.8"

services:
  localstack:
    image: localstack/localstack
    container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
    ports:
      - "127.0.0.1:4566:4566" # LocalStack Gateway
    environment:
      - SERVICES=secretsmanager # Write the AWS services you want to use, separated by commas
      - DEBUG=${DEBUG:-0}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

ほぼそのままですが、今回はSecrets Managerしか使わないのでSERVICESsecretsmanagerのみを指定しています。
コンテナを起動したら、Secrets Managerにアクセスできるかを確認しましょう。
※AWS CLIはインストールされているものとします。

aws --endpoint-url=http://localhost:4566 secretsmanager list-secrets

上のコマンドを実行して下の結果が返って来れば成功です。

{
    "SecretList": []
}

2. コンテナの起動時に値が登録されるようにする

このままでも良いのですが、いちいち値を登録するのが面倒なので、コンテナの起動時に登録されるようにしたいです。
以下に方法を3つ記載します(現状だとうまくいかないものも含めています)。

方法1. LocalStackのhookを使う

LocalStackはいくつかInitialization Hooksを用意してくれています。
今回のケースではREADYを使うのが良さそうです。

!注意
最終的にはこの方法が最適解になるんじゃないかなと思いますが、記事作成時点ではシェルスクリプトの実行時にパーミッションエラーになってしまい実行できませんでした。

関連issue: Problem with init hooks

方法2. 初期化処理用のイメージを用意する

前述のissueの最後のコメントに書いてある方法です。
localstackの公式イメージから新しいイメージを作ります。

FROM localstack/localstack
COPY --chown=localstack /path/to/init.sh /etc/localstack/init/ready.d/init.sh
RUN chmod u+x /etc/localstack/init/ready.d/init.sh
version: "3.8"

services:
  localstack:
    build: ./docker/aws <= ここを変更する
    container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
    ports:
      - "127.0.0.1:4566:4566" # LocalStack Gateway
    environment:
      - SERVICES=secretsmanager # Write the AWS services you want to use, separated by commas
      - DEBUG=${DEBUG:-0}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
方法3. 初期化処理用のコンテナを用意する

別のコンテナを起動して、そこで初期化処理を実行する方法です。
動作確認用リポジトリではこちらの方法を採用しています。
docker-compose.ymlに以下を追加します。

  localstacksetup:
    image: amazon/aws-cli <= AWSコマンドが使えれば他のイメージでも良いと思います
    depends_on:
      - localstack
    restart: "no"
    volumes:
      - ./init:/var/tmp
    entrypoint: ["sh", "-c", "sh /var/tmp/init.sh"]

3. ちゃんと動いているか確認する

コンテナを起動した後にlist-secretsを実行します。
コンテナ作成時にregionを指定した場合は同じregionを指定してください(サンプルではus-west-2を指定しています)。

aws --endpoint-url=http://localhost:4566 secretsmanager list-secrets --region us-west-2

上のコマンドを実行して下のような結果が返って来れば成功です。

{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:us-west-2:000000000000:secret:local/secret/example-sgsyiZ",
            "Name": "local/secret/example",
            "LastChangedDate": "2023-12-31T18:56:03.020410+09:00",
            "SecretVersionsToStages": {
                "cf84944c-fb19-4af0-8d44-941d552af4c2": [
                    "AWSCURRENT"
                ]
            },
            "CreatedDate": "2023-12-31T18:56:03.020410+09:00"
        }
    ]
}

もう少し詳細が見たい場合はget-secret-valueを実行します。

aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value \
 --secret-id local/secret/example \
 --region us-west-2

下のような結果が返って来れば成功です。

{
    "ARN": "arn:aws:secretsmanager:us-west-2:000000000000:secret:local/secret/example-sgsyiZ",
    "Name": "local/secret/example",
    "VersionId": "cf84944c-fb19-4af0-8d44-941d552af4c2",
    "SecretString": "{\n  \"continent\": \"Asia\",\n  \"country\": \"Japan\",\n  \"city\": \"Tokyo\"\n}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-12-31T18:56:03+09:00"
}

おまけ. シークレットの作成コマンドについて

secret-stringには"{\"user\":\"diegor\",\"password\":\"EXAMPLE-PASSWORD\"}" のように書く以外にJSONファイルを指定することもできるので、サンプルでは後者を使用しています 。
複数人で使う時などはsecrets.json.gitignoreに追加しておくのが良いんじゃないかなと思っています。

aws --endpoint-url=$endpoint_url secretsmanager create-secret \
    --name $secret_id \
    --region $region \
    --secret-string file:///var/tmp/secrets.json
{
  "continent": "Asia",
  "country": "Japan",
  "city": "Tokyo"
}

終わりに

ここが違うよ、こうすればもっと簡単にできるよ、などありましたら、コメントをしていただけるととてもありがたいです。

Discussion