Files
computer-playbook/roles/web-app-mattermost/tasks/01_setup.yml
kevinveenbirkenbachandClaude Opus 5 b3ce43a596 fix(security): require no_log on every task that carries a credential
At -vvv Ansible prints each module argument and result, so a password in
a command line, a request body, a header or a fact lands in the deploy
log. Nothing enforced masking outside the MCP task files, and no check
looked at parents at all.

A new lint, test_credential_tasks_are_masked, finds tasks whose printed
parts (module arguments, environment, loop) reference an identifier ending
in password, secret, token, api_key or private_key, or
secrets.credentials. A task passes when it or an enclosing block sets
no_log, when every file that pulls it in does so through include_tasks
with apply.no_log or import_tasks with no_log (transitively), when the
secret only reaches a parameter the module masks itself, or when the
module masks its values. no_log on an include_tasks task itself does not
count, because it does not reach the included tasks. The rule can be
suppressed per task with `# nocheck: credential-masking`, now listed in
the suppression catalog, and the lint tests its parent and suppression
logic on synthetic cases.

It found 39 unmasked tasks in 19 roles, all real: join tokens, client
secrets, API tokens in Cloudflare headers, database passwords in
command lines, the ChatGPT bot login body and more. Each now sets
no_log from MASK_CREDENTIALS_IN_LOGS. The sys-dns-hetzner-rdns alias
hetzner_no_log, which only restated that switch, is gone.

Not covered yet: template and copy tasks whose rendered file carries a
secret print it as a diff under --diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 21:52:18 +02:00

76 lines
3.9 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
- name: "🔍 Resolve mattermost hosting node + container id"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: "{{ entity_name }}"
resolve_output_prefix: MATTERMOST
resolve_compose_cid: "{{ MATTERMOST_CONTAINER_ADDRESS }}"
resolve_converge_retries: 120
resolve_converge_delay: 5
- name: "⏳ Mattermost | Wait until mmctl local mode is ready"
ansible.builtin.shell: | # nocheck: container-exec-resolver MATTERMOST_LOCAL_CID resolved at delegate_to runtime; container_address would return a manager-only subshell
container exec -i {{ MATTERMOST_LOCAL_CID | quote }} \
/mattermost/bin/mmctl --local system status
register: mm_ready_wait
retries: 60
delay: 5
until: mm_ready_wait.rc == 0
changed_when: false
failed_when: mm_ready_wait.rc != 0 and mm_ready_wait.attempts == 61
delegate_to: "{{ MATTERMOST_HOST_NODE }}"
- name: "📁 Mattermost | Create system administrator user"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
ansible.builtin.shell: | # nocheck: container-exec-resolver MATTERMOST_LOCAL_CID resolved at delegate_to runtime; container_address would return a manager-only subshell
container exec -i {{ MATTERMOST_LOCAL_CID | quote }} \
/mattermost/bin/mmctl --local user create \
--email "{{ lookup('users', 'administrator').email }}" \
--username "{{ lookup('users', 'administrator').username }}" \
--password {{ lookup('users', 'administrator').password | quote }} \
--system-admin
register: mm_create_admin
changed_when: mm_create_admin.rc == 0
failed_when: mm_create_admin.rc != 0 and 'already exists' not in (mm_create_admin.stdout + mm_create_admin.stderr)
delegate_to: "{{ MATTERMOST_HOST_NODE }}"
- name: "📁 Mattermost | Create default team"
ansible.builtin.shell: | # nocheck: container-exec-resolver MATTERMOST_LOCAL_CID resolved at delegate_to runtime; container_address would return a manager-only subshell
container exec -i {{ MATTERMOST_LOCAL_CID | quote }} \
/mattermost/bin/mmctl --local team create \
--name "main" \
--display-name "{{ ORGANIZATION }}"
register: mm_create_team
changed_when: mm_create_team.rc == 0
failed_when: mm_create_team.rc != 0 and 'already exists' not in (mm_create_team.stdout + mm_create_team.stderr) and 'existing team' not in
(mm_create_team.stdout + mm_create_team.stderr)
delegate_to: "{{ MATTERMOST_HOST_NODE }}"
- name: "🟢 Mattermost | Enable open invite on default team"
database_query:
config: "{{ lookup('database', application_id) }}"
query_file: "{{ role_path }}/files/sql/update/default-team-open-invite.sql"
mask_values: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
changed_when: false
- name: "🔎 Mattermost | Check which users are already in default team"
ansible.builtin.shell: | # nocheck: container-exec-resolver MATTERMOST_LOCAL_CID resolved at delegate_to runtime; container_address would return a manager-only subshell
container exec -i {{ MATTERMOST_LOCAL_CID | quote }} \
/mattermost/bin/mmctl --local user list --team main
register: mm_team_members
changed_when: false
delegate_to: "{{ MATTERMOST_HOST_NODE }}"
- name: "➕ Mattermost | Add administrator to default team"
ansible.builtin.shell: | # nocheck: container-exec-resolver MATTERMOST_LOCAL_CID resolved at delegate_to runtime; container_address would return a manager-only subshell
container exec -i {{ MATTERMOST_LOCAL_CID | quote }} \
/mattermost/bin/mmctl --local team users add \
main \
"{{ lookup('users', 'administrator').email }}"
when: lookup('users', 'administrator').email not in mm_team_members.stdout
delegate_to: "{{ MATTERMOST_HOST_NODE }}"
- name: "🗂️ Mattermost | Wire administrator OIDC auth"
ansible.builtin.include_tasks: 03_oidc_admin.yml
when: MATTERMOST_OIDC_ENABLED | bool