Ansibleのpostgresql_userモジュールを使ってRDS PostgreSQLのユーザ管理を行なっています。 それをGitHub ActionsでCDしたいとなりましたが、Ansibleの標準出力にパスワードが含まれてしまうためマスクした話です。
別の方法もあるよ(2021-11-12追記)
アウトプットするとインプットが加速すると言いますが、 @naa0yama さんから素晴らしい情報をいただきました。 sed
は万能ですがシンプルではないので、Ansibleの機能でコントロールする方が良いですね。
情報ありがとうございます!!!
これでやってることできる気がする。https://t.co/Pk1CaugTFu
— Naoki Aoyama (@naa0yama) November 11, 2021
マスク前後の例
具体的な例を以下に挙げます。
'password': 'SuperSecrets'
のようにパスワードが生で出力されてしまっていたのが 'password': '<MASKED>'
となります。
マスク前の出力
TASK [postgres : Add user to morihaya.local] **************** changed: [localhost] => (item={'name': 'morihaya_dev', 'password': 'SuperSecrets', 'db': 'morihaya', 'groups': 'readwrite', 'state': 'present'}) => {"ansible_loop_var": "item", "changed": true, "item": {"db": "morihaya", "groups": "readwrite", "name": "morihaya_dev", "password": "SugoiHimitsu", "state": "present"}, "queries": [], "user": "morihaya_dev", "warnings": ["Module did not set no_log for no_password_changes"]}
マスク後の出力
TASK [postgres : Add user to morihaya.local] **************** changed: [localhost] => (item={'name': 'morihaya_dev', 'password': '<MASKED>', 'db': 'morihaya', 'groups': 'readwrite', 'state': 'present'}) => {"ansible_loop_var": "item", "changed": true, "item": {"db": "morihaya", "groups": "readwrite", "name": "morihaya_dev", "password": "<MASKED>", "state": "present"}, "queries": [], "user": "morihaya_dev", "warnings": ["Module did not set no_log for no_password_changes"]}
どうしたのか
sed
を使いました。GitHub Actionsのコードを以下に示します。
たくさん書いてありますが、本記事のテーマとしては最終行の ansible-playbook ${{ matrix.ANSIBLE_PLAYBOOK }} --check --diff -v | sed -E "s/password': '[^']+/password': '<MASKED>/g" | sed -E 's/password": "[^"]+/password": "<MASKED>/g'
がポイントです。
name: Deploy-Check on: workflow_dispatch: push: paths: - ".github/workflows/deploy-check.yml" - "*.yml" - "group_vars/*.yml" - "host_vars/*.yml" - "secret_vars/*.yml" - "roles/*/*/*.yml" branches-ignore: - master pull_request: paths: - "*.yml" - "group_vars/*.yml" - "host_vars/*.yml" - "secret_vars/*.yml" - "roles/*/*/*.yml" jobs: deploy: runs-on: [self-hosted, morihaya] strategy: fail-fast: true # どれかか失敗しても完走させる matrix: ANSIBLE_PLAYBOOK: - morihaya-db-prod.yml - morihaya-deb-dev.yml steps: - uses: actions/checkout@v2 - name: Ansible setting run: | # ansbile のインストール&設定 apt-get install -y ansible ansible-galaxy collection install community.general # postgresql モジュールのための準備 apt-get install -y python3 python3-psycopg2 echo "[defaults]" > ~/.ansible.cfg echo "interpreter_python = /usr/bin/python3" >> ~/.ansible.cfg # Ansible vault用のパスワードを作成 echo "${{secrets.ANSIBLE_VAULT_PASSWORD}}" > ~/.vault_password ansible --version - name: Deploy run: | type python3 # "module_stderr": "/bin/sh: 1: /usr/bin/python: not found\n" 対応 ln -sf /usr/bin/python3 /usr/bin/python # Ansible playbook を実行 ansible-playbook ${{ matrix.ANSIBLE_PLAYBOOK }} --check --diff -v | sed -E "s/password': '[^']+/password': '<MASKED>/g" | sed -E 's/password": "[^"]+/password": "<MASKED>/g'
マスクするまでの雑な経緯
実装直後は ansible-playbook ${{ matrix.ANSIBLE_PLAYBOOK }} --check --diff -v
だけでしたが、ログにパスワードが記録されてしまうことに気づいて慌ててマスクすることにしました。
GitHub Actionsのドキュメントや、Ansibleのドキュメントを軽くみましたが、それらの機能で特定の文字列を正規表現でマッチさせてマスクすることは現状できないと判断しました。
難しく考えかけたところを単純に sed
コマンドでマスクし、期待した結果を得ることができました。この結果は自分にとっては大きくて、今後どのような秘密情報を出力する処理であってもマスクすることができる自信がつきました。
トラップとして、Ansibleの出力がシングルクォートの場合とダブルクォートの場合がありどちらもマスクする必要があったので sed
を2回行う必要がありました。
'password': 'SuperSecrets'
->sed -E "s/password': '[^']+/password': '<MASKED>/g"
で処理"password": "SugoiHimitsu"
->sed -E 's/password": "[^"]+/password": "<MASKED>/g'
で処理
以下のツイートが率直な感想です。
GitHub ActionsでAnsibleを実行するやつを作ったのですが、思いっきりクレデンシャルが標準出力に出てしまう処理だったので最後に
— もりはや (@morihaya55) 2021年11月11日
| sed -E 's/xxx/<MASKED>/g'
でマスクしました
2021年でも"シェル芸は身を助く"のです😇