Ansible のシークレットマスキング
Ansible の Playbook では、サーバーへの接続やアプリケーションのデプロイにパスワードや API トークンを使うことがよくあります。こうした値はタスクの出力に含まれることもあります。たとえば、コマンドがパスワードを含むエラーを返す場合です。
この問題に対処するため、Ansible にシークレットマスキングが追加されます。出力に含まれる既知のシークレット値を $REDACTED$ に置き換え、メッセージの残りは読める状態に保ちます。タスクは引き続き元の値を使います。
この機能は、2026 年 9 月 10 日にマージされた Secret Masking API のプルリクエストで追加されました。この記事の執筆時点では、ansible-core 2.22 の開発ビルドで利用できます。安定版にはまだ含まれていません。
まず簡単な Playbook を見てから、Semaphore UI でこの機能をどのように使う予定なのかを紹介します。
まずシークレットを登録する
Ansible は、どの値をマスキングするかを知る必要があります。新しい API は、Vault で暗号化された変数や no_log が指定されたモジュールパラメーターなど、既知のシークレットソースから値を登録します。
通常の変数には register_secret フィルターを使えます。--extra-vars で渡した変数も登録が必要です。名前を db_password にするだけでは不十分です。
この例では架空のデータベースパスワードを使います。masking.yml という Playbook を作成してください。
- name: Try secret masking
hosts: localhost
gather_facts: false
vars:
db_password: "example-db-password-123456"
tasks:
- name: Register the password before using it
ansible.builtin.set_fact:
db_password: "{{ db_password | ansible.builtin.register_secret }}"
- name: Show a message containing the password
ansible.builtin.debug:
msg: "Connecting to db.internal with password={{ db_password }}"
最初のタスクがパスワードを登録します。フィルターは元の値を返すため、Playbook は引き続きその値をデータベースへの接続に使えます。どのタスクもその値を出力する前に、登録を済ませる必要があります。
ansible-core 2.22 の開発ビルドで Playbook を実行します。
ansible-playbook -i localhost, -c local masking.yml
2 番目のタスクには、次のメッセージが表示されます。
Connecting to db.internal with password=$REDACTED$
ここではマスキングを確認するためだけにパスワードを出力しています。実際の Playbook ではシークレットの管理元から取得し、認証情報を意図的に出力しないようにしてください。
Semaphore UI との連携
Semaphore UI は、どの変数がシークレットとして設定されているかをすでに把握しています。今後のバージョンでは、この情報を使って値を Ansible に自動登録する予定です。
そのために、Ansible の _SECRETS_INPUT_FILES オプションを使います。このオプションは環境変数 _ANSIBLE_SECRETS_INPUT_FILES で設定し、Playbook の開始前に Ansible がシークレットの一覧を読み込めるようにします。
入力には YAML または JSON を使えます。先ほどのパスワードを含むドキュメントは、次のようになります。
version: 1
secrets:
- example-db-password-123456
この一覧は、マスキングする値を Ansible に伝えます。変数自体は、これまでどおり別途 Playbook に渡します。
予定している連携では、次のシークレット値を対象にします。
- 変数グループ(Variable Groups) — タスクで使うために保存されたシークレット。
- サーベイ(Surveys) — タスクの開始時に入力されるシークレット回答。
これらの値については、各 Playbook に register_secret を使うタスクを追加する必要がなくなります。
この方法を ansible-core 2.22 の開発ビルドで検証しました。登録した値は、debug メッセージ、コマンドの結果、エラーメッセージ、詳細出力、Ansible のログファイルでマスキングされました。パイプ経由での一覧の受け渡しも機能し、シークレット専用のファイルをディスクに書き込まずに済みました。
このオプションは現在内部用であり、変更される可能性があります。Semaphore UI での対応は今後のリリースを予定しており、互換性のある Ansible のバージョンが必要になります。
例:アプリケーションのデプロイ
アプリケーションをデプロイするタスクテンプレート(Task Template)があるとします。デプロイスクリプトは、変数グループにシークレットとして保存された API トークンを使います。
予定している連携では、タスクは次のように動作します。
- Semaphore UI がトークンを Playbook に渡します。
- 実行前に、
_SECRETS_INPUT_FILESを使って Ansible のマスキング一覧にもトークンを渡します。 - Playbook は実際のトークンを使ってデプロイスクリプトを実行します。
- スクリプトが出力にトークンを含めた場合、Ansible は表示する結果でその値をマスキングします。
たとえば、トークンの有効期限が切れていると、タスクログに次のようなメッセージが表示される場合があります。
Deployment rejected: service=payments environment=staging token=$REDACTED$ reason=token expired
ログを読む人全員にトークンを見せずに、どのデプロイがなぜ失敗したのかを確認できます。サーベイのシークレットフィールドから入力した一時的なトークンにも、同じ流れが適用されます。
制限事項
マスキングの対象になるのは、登録済みの値です。開発版の実装では 4 文字未満のシークレットは対象外となり、エンコードなどの変換を行った値は別途登録が必要になる場合があります。
また、プロセス引数、Playbook が書き込んだファイル、Ansible の外部で生成されたログ内の認証情報は保護しません。出力全体を非公開にする必要があるタスクには、引き続き no_log: true を使ってください。
この連携により、Semaphore UI で指定したシークレットを Ansible のマスキングシステムも認識するようになります。タスクログに認証情報が残るのを抑えつつ、トラブルシューティングに役立つ情報を保持できます。

