もりはやメモφ(・ω・ )

ITとか読書感想文とか

GitHub ActionsでAnsibleを実行した時に秘密情報が記録されるのをsedでマスクする

Ansibleのpostgresql_userモジュールを使ってRDS PostgreSQLのユーザ管理を行なっています。 それをGitHub ActionsでCDしたいとなりましたが、Ansibleの標準出力にパスワードが含まれてしまうためマスクした話です。

docs.ansible.com

別の方法もあるよ(2021-11-12追記)

アウトプットするとインプットが加速すると言いますが、 @naa0yama さんから素晴らしい情報をいただきました。 sed は万能ですがシンプルではないので、Ansibleの機能でコントロールする方が良いですね。 情報ありがとうございます!!!

docs.ansible.com

マスク前後の例

具体的な例を以下に挙げます。 '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' で処理

以下のツイートが率直な感想です。