AWS PHPからSSMパラメータストアの値を取得する


SSMパラメータストアとは

パスワード、データベース文字列、Amazon Machine Image (AMI) ID、ライセンスコードなどのデータをパラメータ値として保存することができます。

例えば、プログラムからデータベースにアクセスする際に、データベース接続文字列をどこに保管するかが問題になりますが、その保管場所としてパラメータストアはうってつけではないでしょうか。

保存したパラメータの暗号化キーの自動ローテーションの機能が必要な時はSecretManagerが候補に挙がるかと思います。

今回のゴール

パラメータストアは階層(パス)で管理する事ができます。

今回は、

  • /dev/db
  • /prod/db

の階層を作成して、/dev/のパスにのみ読み取り権限を与えるようにします。

また、データベースの接続情報は重要な情報ですので、SSMのCMK(カスタマーマスターキー)で暗号化してパラメータストアに保存するようにします。

KMSのCMK(カスタマーマスターキー)の作成

データベース文字列を格納しますので、暗号化して保存するために、CMK(カスタマーマスターキー)を作成します。

[KMSサービス]-[キーの作成]とクリックして、デフォルトの状態で「次へ」をクリックします。

[エイリアス]:cmk-parameter-store-test

あとは全てデフォルトの状態で作成を完了しました。

パラメータストアの作成

[SSMサービス]-[パラメータストア]-[パラメータの作成]をクリックします。

[名前]:/dev/db
[タイプ]:安全な文字列
[KMS キー ID]:先ほど作成したCMKを選択
[値]:connection_string_XXXXXXXXXX

同様に、 /prod/dbも作成します。

EC2の設定

IAMロールにポリシーのアタッチ

CMKで暗号化しているので、下記の復号するためのポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt",
                "kms:GenerateDataKey*"
            ],
            "Resource": "(your KMS key ARN)"
        }
    ]
}

パラメータストアのdevのパスのみ読み取り可のポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeParameters"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": "arn:aws:ssm:ap-northeast-1:(your account ID):parameter/dev*"
        }
    ]
}

PHPのインストール

手順は省略しますが、
必要であれば過去の記事をご参照ください。

PHPファイルの作成

今回は下記のファイルを作成しました。

ハイライトされている行は後で変更する箇所です。

<?php
require 'vendor/autoload.php';

use Aws\Ssm\SsmClient;
use Aws\Exception\AwsException;

function getParameters(array $names)
{
    $client = new SsmClient([
        'region' => 'ap-northeast-1',
        'version' => '2014-11-06',
    ]);

    $result = $client->getParameters(
        [
            'Names' => $names,
            'WithDecryption' => true
        ]
    );

    echo $result;

    $params = [];
    foreach ($result['Parameters'] as $v) {
        $name = $v['Name'];
        $params[$name] = $v['Value'];
    }

    return $params;
}

try {
    $params = getParameters(['/dev/db']);

    var_dump($params);
} catch (AwsException $e) {
    var_dump('---AwsException---');
    error_log($e->getMessage());
} catch (Exception $e) {
    var_dump('---Exception---');
    error_log($e->getMessage());
}

確認

get-parameters.phpを実行すると、パラメータが取得できました。

php get-parameters.php

{
    "Parameters": [
        {
            "Name": "\/dev\/db",
            "Type": "SecureString",
            "Value": "connection_string_XXXXXXXXXX",
            "Version": 3,
            "LastModifiedDate": "2021-11-13T09:40:09+00:00",
            "ARN": "arn:aws:ssm:ap-northeast-1:000000000000:parameter\/dev\/db",
            "DataType": "text"
        }
    ],
    "InvalidParameters": [],
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/ssm.ap-northeast-1.amazonaws.com",
        "headers": {
            "x-amzn-requestid": "635e49cf-8f2d-4440-89af-85a2f5272ef0",
            "content-type": "application\/x-amz-json-1.1",
            "content-length": "249",
            "date": "Sat, 13 Nov 2021 03:37:59 GMT"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    }
}

array(1) {
  ["/dev/db"]=>
  string(28) "connection_string_XXXXXXXXXX"
}


次に”/dev/xxx”のように、dev内の存在しないパスを get-parameters.phpを変更して実行します。

$params = getParameters(['/dev/db', '/dev/xxx']);

すると、InvalidParametersに “/dev/xxx”がセットされてました。

{
    "Parameters": [
        {
            "Name": "\/dev\/db",
            "Type": "SecureString",
            "Value": "connection_string_XXXXXXXXXX",
            "Version": 3,
            "LastModifiedDate": "2021-11-13T09:40:09+00:00",
            "ARN": "arn:aws:ssm:ap-northeast-1:000000000000:parameter\/dev\/db",
            "DataType": "text"
        }
    ],
    "InvalidParameters": [
        "\/dev\/xxx"
    ],
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/ssm.ap-northeast-1.amazonaws.com",
        "headers": {
            "x-amzn-requestid": "a15c3baa-b1a9-49de-8acc-904f33a04fd2",
            "content-type": "application\/x-amz-json-1.1",
            "content-length": "259",
            "date": "Sat, 13 Nov 2021 03:50:40 GMT"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    }
}

array(1) {
  ["/dev/db"]=>
  string(28) "connection_string_XXXXXXXXXX"
}


最後に権限のない’/prod/db’を追加して実行します。

$params = getParameters(['/dev/db', '/dev/xxx', '/prod/db']);

すると、AwsExceptionの例外が発生して、AccessDeniedExceptionとなっておりました。

string(18) "---AwsException---"
Error executing "GetParameters" on "https://ssm.ap-northeast-1.amazonaws.com"; AWS HTTP error: Client error: `POST https://ssm.ap-northeast-1.amazonaws.com` resulted in a `400 Bad Request` response:
{"__type":"AccessDeniedException","Message":"User: arn:aws:sts::000000000000:assumed-role/ec2_sqs_test/i-000000000000 (truncated...)
 AccessDeniedException (client): User: arn:aws:sts::000000000000:assumed-role/ec2_sqs_test/i-000000000000 is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:ap-northeast-1:000000000000:parameter/prod/db because no identity-based policy allows the ssm:GetParameters action - {"__type":"AccessDeniedException","Message":"User: arn:aws:sts::000000000000:assumed-role/ec2_sqs_test/i-000000000000 is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:ap-northeast-1:000000000000:parameter/prod/db because no identity-based policy allows the ssm:GetParameters action"}


コメントを残す

メールアドレスが公開されることはありません。