1. 目的
- AWSのセキュリティ関連サービスの復習をしている。パスワードなどの保護すべき値を安全に保存するための仕組みであるAWS Secrets Manager と AWS Systems Manager Parameter Store について、それぞれを試して使い勝手などを確認する。
- 機能の差異や使い分けについては、Qiita記事「AWSのParameter StoreとSecrets Manager、結局どちらを使えばいいのか?比較」に詳しく整理、網羅されており、それを読むだけで一通り理解できるが、一応自分でどんなものか試してみる。
2. やったこと
- RDSと、RDSへ接続し操作する用のEC2インスタンスを作成する。
- RDSに接続する際のパスワードをそれぞれAWS Secrets Manager と AWS Systems Manager Parameter Storeに保存し、EC2インスタンスからの接続時に、値を呼び出して利用する。
3. 構成図
4. 設定手順
4.1 環境準備
- VPC、EC2インスタンス(Amazon Linux 2)、RDS DB(MySQL 8.0.20)を作成する。
- RDSに接続するための認証情報を「user: admin, password: PASSWORD」に設定しておく。
- EC2インスタンス(Amazon Linux 2)で、Python3, MySQLクライアントが利用できるように各種パッケージのインストールを行う。
- 参考手順(Python mysqlclientのインストール): 「mysqlclient Python3でMySQLに接続する」
- 参考手順(mysql クライアントコマンドのインストール): 「【AWS EC2】Amazon Linux2にMySQLのclientだけをインストールしてRDSに接続する方法」
[ec2-user@ip-10-0-0-196 ~]$ sudo yum install python3
[ec2-user@ip-10-0-0-196 ~]$ python3 -V
Python 3.7.9
[ec2-user@ip-10-0-0-196 ~]$ sudo pip3 install boto3
[ec2-user@ip-10-0-0-196 ~]$ sudo yum install python-devel mysql-devel
[ec2-user@ip-10-0-0-196 ~]$ sudo yum install python3-devel
[ec2-user@ip-10-0-0-128 ~]$ sudo yum install gcc
[ec2-user@ip-10-0-0-128 ~]$ sudo pip3 install mysqlclient
[ec2-user@ip-10-0-0-128 ~]$ sudo yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
[ec2-user@ip-10-0-0-128 ~]$ sudo yum install -y mysql-community-client
- RDS DBに接続し、テスト用のテーブルを作っておく。
[ec2-user@ip-10-0-0-128 ~]$ mysql -h mksamba-testdb-202103.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
mysql> create database mksambadb;
mysql> use mksambadb;
mysql> create table mksambadb.mytable(id int, stamp varchar(30));
mysql> insert into mytable (id, stamp)
-> values('1', '1000');
4.2 EC2インスタンス(Python3)からの接続確認
- RDSに接続するためのID/Passwordをコードにそのまま埋め込んで、RDSに接続できることを確認する。
dbconnect01.py
import MySQLdb
connection = MySQLdb.connect(
host='mksamba-testdb-202103.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com',
user='admin',
passwd='PASSWORD',
db='mksambadb')
cursor = connection.cursor()
cursor.execute("SELECT * FROM mksambadb.mytable")
for row in cursor:
print(row)
connection.commit()
connection.close()
[ec2-user@ip-10-0-0-128 ~]$ python3 dbconnect01.py
(1, '1000')
4.3 Secret Manager の設定
- Secret Manager にパスワードを保存する設定を行う。単純にパスワードのみを保存することもできるが、今回は「RDSデータベース認証情報」形式での保存を行う。
- Secret Managerの画面にて、「新しいシークレットを保存する」を選択し、必要な項目を入力する。
- シークレットの種類として、「RDSデータベースの認証情報」を選択する。
- ユーザ名は「admin」、パスワードは設定したパスワード「PASSWORD」を入力する。
- 暗号化キーとして、あらかじめ作成済のKMSカスタマーマスターキーを指定する。
- このID/Passwordを使用して接続するRDS DBを選択する。
- このsecretの名前を「mksamba-secret-for-rds」とする。
- 今回はローテーションは無効のままにしておく。
- 最後に値を取得するためのサンプルコードが表示される。これをコピペして利用する。
4.4 Secret Manager からの値の取得
- EC2インスタンスからRDS DBに接続する際に、パスワードをコードに埋め込むのではなく、Secret Managerに保存した値を取得して使用するように設定する。
- EC2インスタンスに、Secret Manager及びKMSの権限があるIAM Roleを付与する。(今回は検証のため、Poweruser相当の権限を付与)
- コードは以下のように修正する。注意点として、Secret(ID/Passwordの値を保存)が文字列型のため、辞書型に変更した上で値を取得する。DevelopersIOの記事「PythonでAWS Secrets ManagerからAPIキーを取得するときのちょっとしたポイント」のやり方を利用させて頂いた。
dbconnect02.py
import MySQLdb
import boto3
import base64
from botocore.exceptions import ClientError
import ast
secret_name = "mksamba-secret-for-rds"
region_name = "ap-northeast-1"
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
if e.response['Error']['Code'] == 'DecryptionFailureException':
raise e
elif e.response['Error']['Code'] == 'InternalServiceErrorException':
raise e
elif e.response['Error']['Code'] == 'InvalidParameterException':
raise e
elif e.response['Error']['Code'] == 'InvalidRequestException':
raise e
elif e.response['Error']['Code'] == 'ResourceNotFoundException':
raise e
else:
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
else:
decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
secret = ast.literal_eval(secret)
connection = MySQLdb.connect(
host='mksamba-testdb-202103.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com',
user=secret['username'],
passwd=secret['password'],
db='mksambadb')
cursor = connection.cursor()
cursor.execute("SELECT * FROM mksambadb.mytable")
for row in cursor:
print(row)
connection.commit()
connection.close()
4.5 Parameter Store の設定
- 今度はパスワードの値をParameter Storeに保存してみる。
- AWS Systems Managerの画面にて、「パラメータストア」- 「パラメータを作成」を選択する。
- タイプは「安全な文字列」を選択し、暗号化を行う。
- あらかじめ作成済のKMS カスタマーマスターキーを指定する。
- 値として、パスワードの文字列「PASSWORD」を入力する。
4.6 Parameter Store からの値の取得
- EC2インスタンスからRDS DBに接続する際に、パスワードをコードに埋め込むのではなく、Parameter Storeに保存した値を取得して使用するように設定する。
- EC2インスタンスに、Systems Manager及びKMSの権限があるIAM Roleを付与する。(今回は検証のため、Poweruser相当の権限を付与)
- コードは以下のように修正する。手順はDevelopersIO記事「AWS SDK for Python (Boto3) で AWS Systems Manager パラメータストアから情報を取得する」を参照。
dbconnect03.py
import MySQLdb
import boto3
import json
ssm = boto3.client('ssm', 'ap-northeast-1')
response = ssm.get_parameters(
Names=[
'mksamba-password-rds',
],
WithDecryption=True
)
connection = MySQLdb.connect(
host='mksamba-testdb-202103.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com',
user='admin',
passwd=response['Parameters'][0]['Value'],
db='mksambadb')
cursor = connection.cursor()
cursor.execute("SELECT * FROM mksambadb.mytable")
for row in cursor:
print(row)
connection.commit()
connection.close()
5. 所感
- どちらも隠したい値を暗号化して保存しておき、必要な時に取り出して使用する、という点ではそんなに変わりなく使えるかなと思った。費用や、細かい機能の差異(Secret Managerの値の自動更新機能など)の必要性に応じて判断できるようにしたい。