はじめに
Datadog で Mysql の接続監視をする際、
設定ファイルに DB パスワードを直書きしたくないなーと思っていたところ、どうやら以下の「Secrets Management」機能でシークレット値を取得できるとのことなのでやってみました。
公式ドキュメントはこちら
記事目安...15分
「Secrets Management」機能
「Secrets Management」機能をざっくり説明すると、Datadog 起動時に設定ファイル外から値を取得してくれる機能です。
メリットは、設定ファイルに値を直書きすることを防げるので機密情報をセキュアに管理できます。
仕組みとしては、datadog-agent の起動時に「dd-agent」ユーザがスクリプトを実行して値をメモリに格納してるらしいです。
スクリプトについて
#!/bin/python # coding: utf-8 import json import os.path import sys import boto3 import ast region_name = "ap-northeast-1" session = boto3.session.Session() secrets_manager = session.client( service_name='secretsmanager', region_name=region_name ) def create_output_from(secret_names): """Datadog に渡すシークレット情報を作成する。 Args: secret_names (list): シークレットキーの配列 Returns: dict: Datadog に渡すシークレット情報 """ output = {} for s in secret_names: try: secret_value = get_secrets(s) output[s] = {"value": secret_value} except ValueError as e: output[s] = {"error": str(e)} return output def list_secret_names(input_json): """シークレットキーを取得する Args: input_json (json): Datadog からの入力 JSON Raises: ValueError: プロトコルバージョンエラー ValueError: シークレットキーが配列でない Returns: list: シークレットキーの配列 """ query = json.loads(input_json) version = query["version"].split(".") if version[0] != "1": raise ValueError("incompatible protocol version {}".format(query["version"])) names = query["secrets"] if type(names) is not list: raise ValueError("the secrets field should be an array: {}".format(names)) return names def get_secrets(secret_key): """AWS Secrets Manager から シークレット値取得する。 Args: secret_key (string): シークレットキー Returns: [string]: シークレットキーに対応する値 """ res = secrets_manager.get_secret_value( SecretId=secret_key ) SecretString = res.get('SecretString') secret_value = ast.literal_eval(SecretString).get(secret_key) return secret_value def main(input_json): """メイン関数 Args: input_json (json): Datadog からの入力 JSON Returns: dict: Datadog に渡すシークレット情報 """ try: secret_names = list_secret_names(input_json) except ValueError as e: sys.exit('Cannot parse input: ' + str(e)) output = create_output_from(secret_names) return output if __name__ == '__main__': input_json = sys.stdin.read() output = main(input_json) print json.dumps(output)
概要
Datadog から受け取ったシークレットキーを基に、シークレット値を標準出力するスクリプトです。
シークレット値は AWS Secrets Manager から取得します。
Datadog の「シークレット管理」については、以下ドキュメントを参照ください。
動作環境
# python -V Python 2.7.18
使い方
シークレットキーの登録
設定ファイルに ENC[hogehoge]
で登録します。
Example:
instances: - server: db_prod # two valid secret handles user: "ENC[db_prod_user]" password: "ENC[db_prod_password]" # The `ENC[]` handle must be the entire YAML value, which means that # the following is NOT detected as a secret handle: password2: "db-ENC[prod_password]"
AWS Secrets Manager との連携
- AWS Secrets Manager にシークレット値を格納します。登録時のシークレットキーは、設定ファイルのシークレットキーと同じにしてください。
ex) "ENC[hogehoge]" の場合、AWS Secrets Manager のシークレットキーは "hogehoge" - スクリプトを設置するサーバーが、AWS Secrets Manager から シークレット値を取得できる権限を持ってることを確認してください
pythonモジュールのインストール
$ pip install boto3
スクリプトの配置
- スクリプト用のフォルダを作成します。
※ ここでは/usr/loca/bin/
に設置
$ cd /usr/local/bin $ mkdir get-datadog-secrets
- スクリプトを設置してください。設置後は権限も修正します。
$ cd get-datadog-secrets $ wget https://raw.githubusercontent.com/sugaya0204/blog/Public/Tips/Datadog/get-datadog-secrets/src/main.py $ chmod 700 main.py $ chown dd-agent:dd-agent main.py
- スクリプト内のリージョンが正しいかを確認してください
region_name = "ap-northeast-1"
実行スクリプトの登録
/etc/datadog-agent/datadog.yaml
内の secret_backend_command
に実行スクリプトのファイルパスを入力します。
$ vi /etc/datadog-agent/datadog.yaml
■ 修正内容
# secret_backend_command: <COMMAND_PATH> → secret_backend_command: /usr/local/bin/get-datadog-secrets/main.py
引用元: Datadog-SecretsManagement
Datadog Agent の再起動
スクリプトを読み込むために再起動します。
$ sudo systemctl restart datadog-agent $ sudo systemctl status datadog-agent
動作確認
以下コマンドで
- 実行スクリプトの権限が正しいか
- シークレット値が正しく取得で来ているか
確認できます。
$ sudo -u dd-agent -- datadog-agent secret
(Tips)トラブルシューティング
スクリプトの実行エラーは /var/log/datadog/agent.log
に吐き出されます。
まとめ
このスクリプトを使えばセキュアな運用が出来るので、参考にしてみてください~
参考
* https://github.com/Sho2010/datadog-env-secret
菅谷 歩 (記事一覧)