commit a54bf78ca03d3f81dae33e5fcca0b77e6ff033a2 parent 09d91cb418e92ed4592e393e6b0e99caea8cd008 Author: Florian Dold <dold@taler.net> Date: Sat, 5 Sep 2026 20:12:17 +0200 Secrets: restrict configuration access and suppress task output Preserve the KYC attribute encryption key in a restricted include and move auditor authentication into a root-only nginx include. Suppress secret-bearing task output, configuration diffs and gathered local facts. Diffstat:
21 files changed, 124 insertions(+), 8 deletions(-)
diff --git a/README b/README @@ -332,6 +332,12 @@ Also run by the CI job in "contrib/ci/jobs/001-build". ## Deployment safety and recovery +The KYC attribute encryption key is preserved in a restricted secret include. +Do not rotate this key as a substitute for moving it: changing it without a data +migration would prevent decryption of existing attributes. Auditor authentication +is stored in a root-only nginx include. Secret tasks suppress output and diffs, +including when removing secrets from previously public configuration files. + Backup and restore share borg_host and borg_repo inventory defaults, retaining ssh://borg@pixel.taler-systems.com/~/spec-backup for existing installations. Set borg_repo in host variables to use another already provisioned repository. diff --git a/playbooks/backup.yml b/playbooks/backup.yml @@ -1,6 +1,12 @@ --- - name: Backup GNU Taler Databases hosts: all + gather_facts: false any_errors_fatal: true + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - role: backup diff --git a/playbooks/borg-ssh-export.yml b/playbooks/borg-ssh-export.yml @@ -1,5 +1,11 @@ --- - name: Export SSH public key used for backups hosts: all + gather_facts: false + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - borg-ssh-export diff --git a/playbooks/borg-start.yml b/playbooks/borg-start.yml @@ -1,6 +1,12 @@ --- - name: Start backups with borg hosts: all + gather_facts: false any_errors_fatal: true + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - borg-start diff --git a/playbooks/pixel-borg.yml b/playbooks/pixel-borg.yml @@ -1,6 +1,12 @@ --- - name: Setup Borg repository on spec to receive backups from pixel hosts: all + gather_facts: false any_errors_fatal: true + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - pixel_borg diff --git a/playbooks/reboot.yml b/playbooks/reboot.yml @@ -1,6 +1,12 @@ --- - name: Reboot the system after stopping all services and backing up the databases hosts: all + gather_facts: false + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - role: stop_services - role: backup diff --git a/playbooks/remove-monitoring.yml b/playbooks/remove-monitoring.yml @@ -1,7 +1,13 @@ --- - name: Remove the legacy monitoring infrastructure hosts: all + gather_facts: false any_errors_fatal: true + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + tasks: - name: Remove the legacy monitoring infrastructure ansible.builtin.include_role: diff --git a/playbooks/restore.yml b/playbooks/restore.yml @@ -1,6 +1,7 @@ --- - name: Restore a database backup onto a fresh PostgreSQL host hosts: all + gather_facts: false any_errors_fatal: true vars: database_restore_archive: >- @@ -10,6 +11,10 @@ database_restore_requested_target: >- {{ lookup('ansible.builtin.env', 'TALER_RESTORE_TARGET') }} pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + - name: Reject check mode for a restore ansible.builtin.assert: that: not ansible_check_mode diff --git a/playbooks/sanctionlist-check.yml b/playbooks/sanctionlist-check.yml @@ -1,6 +1,12 @@ --- - name: Import a sanction list an run all records against it hosts: all + gather_facts: false any_errors_fatal: true + pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + roles: - exchange-sanctionlist-import diff --git a/playbooks/setup.yml b/playbooks/setup.yml @@ -1,8 +1,13 @@ --- - name: Deploy gnu Taler hosts: all + gather_facts: false any_errors_fatal: true pre_tasks: + - name: Gather host facts without logging stored local secrets + ansible.builtin.setup: + no_log: true + - name: Reject the removed in-deployment restore switch ansible.builtin.assert: that: not (enable_restore_backup | default(false) | bool) diff --git a/roles/auditor/tasks/main.yml b/roles/auditor/tasks/main.yml @@ -22,6 +22,17 @@ register: st failed_when: not (st.stat.exists is defined and st.stat.exists) +- name: Place restricted auditor authentication configuration + ansible.builtin.template: + src: etc/nginx/auditor-auth.conf.inc.j2 + dest: /etc/nginx/auditor-auth.conf.inc + owner: root + group: root + mode: "0600" + no_log: true + diff: false + notify: Restart nginx + - name: Ensure Taler auditor virtualhost configuration file exists template: src: templates/etc/nginx/sites-available/auditor-nginx.conf.j2 @@ -30,6 +41,8 @@ group: root mode: "0644" notify: Restart nginx + no_log: true + diff: false - name: Ensure Taler auditor HTTP virtualhost configuration file exists template: @@ -82,6 +95,8 @@ owner: taler-auditor-httpd group: root mode: "0400" + no_log: true + diff: false - name: Grant usage to exchange and _v schema community.postgresql.postgresql_privs: diff --git a/roles/auditor/templates/etc/nginx/auditor-auth.conf.inc.j2 b/roles/auditor/templates/etc/nginx/auditor-auth.conf.inc.j2 @@ -0,0 +1,4 @@ +# Included only in the protected auditor location. +if ($http_authorization != "Bearer {{ auditor_access_token }}") { + return 401; +} diff --git a/roles/auditor/templates/etc/nginx/sites-available/auditor-nginx.conf.j2 b/roles/auditor/templates/etc/nginx/sites-available/auditor-nginx.conf.j2 @@ -19,9 +19,7 @@ server { access_log /var/log/nginx/auditor.{{ domain_name }}.tal taler if=$log_perf; location / { # Most of the API we will put behind simple access control for now. - if ($http_authorization != "Bearer {{ auditor_access_token }}") { - return 401; - } + include /etc/nginx/auditor-auth.conf.inc; proxy_pass http://unix:/var/run/taler-auditor/httpd/auditor-http.sock; } diff --git a/roles/challenger/tasks/post-exchange.yml b/roles/challenger/tasks/post-exchange.yml @@ -6,6 +6,8 @@ owner: taler-exchange-httpd group: taler-exchange-kyc mode: "0440" + no_log: true + diff: false - name: Place email challenger exchange config ansible.builtin.template: @@ -14,6 +16,8 @@ owner: taler-exchange-httpd group: taler-exchange-kyc mode: "0440" + no_log: true + diff: false - name: Place postal challenger exchange config ansible.builtin.template: @@ -22,6 +26,8 @@ owner: taler-exchange-httpd group: taler-exchange-kyc mode: "0440" + no_log: true + diff: false - name: Place general challenger exchange config copy: diff --git a/roles/challenger/tasks/pre-exchange.yml b/roles/challenger/tasks/pre-exchange.yml @@ -162,6 +162,7 @@ - /etc/ansible/facts.d/sms-challenger-client-secret.fact - "secret-token:" creates: /etc/ansible/facts.d/sms-challenger-client-secret.fact + no_log: true - name: Secret setup for email-challenger ansible.builtin.command: @@ -170,6 +171,7 @@ - /etc/ansible/facts.d/email-challenger-client-secret.fact - "secret-token:" creates: /etc/ansible/facts.d/email-challenger-client-secret.fact + no_log: true - name: Secret setup for postal-challenger ansible.builtin.command: @@ -178,9 +180,11 @@ - /etc/ansible/facts.d/postal-challenger-client-secret.fact - "secret-token:" creates: /etc/ansible/facts.d/postal-challenger-client-secret.fact + no_log: true - name: Force ansible to regather just created fact(s) about challenger ansible.builtin.setup: + no_log: true - name: Place SMS challenger config ansible.builtin.template: @@ -213,6 +217,8 @@ owner: root group: challenger-sms mode: "0640" + no_log: true + diff: false - name: Place postal challenger environment data ansible.builtin.template: @@ -221,6 +227,8 @@ owner: root group: challenger-postal mode: "0640" + no_log: true + diff: false - name: Setup SMS Challenger database ansible.builtin.command: @@ -247,6 +255,7 @@ - "{{ ansible_local['sms-challenger-client-secret'] }}" - "{{ exchange_base_url }}kyc-proof/sms-challenger" creates: /etc/ansible/facts.d/sms-challenger-client-id.fact + no_log: true - name: Setup Email Challenger exchange account ansible.builtin.command: @@ -258,6 +267,7 @@ - "{{ ansible_local['email-challenger-client-secret'] }}" - "{{ exchange_base_url }}kyc-proof/email-challenger" creates: /etc/ansible/facts.d/email-challenger-client-id.fact + no_log: true - name: Setup Postal Challenger exchange account ansible.builtin.command: @@ -269,9 +279,11 @@ - "{{ ansible_local['postal-challenger-client-secret'] }}" - "{{ exchange_base_url }}kyc-proof/postal-challenger" creates: /etc/ansible/facts.d/postal-challenger-client-id.fact + no_log: true - name: Force ansible to regather the challenger client IDs ansible.builtin.setup: + no_log: true - name: Place sms-challenger systemd service file copy: diff --git a/roles/devtesting/tasks/main.yml b/roles/devtesting/tasks/main.yml @@ -1,5 +1,6 @@ - name: Recollect facts setup: + no_log: true - name: Install devtesting dependencies apt: diff --git a/roles/exchange/tasks/main.yml b/roles/exchange/tasks/main.yml @@ -174,6 +174,18 @@ owner: taler-exchange-wire group: root mode: "0400" + no_log: true + diff: false + +- name: Place the existing KYC attribute encryption key in a restricted include + ansible.builtin.template: + src: etc/taler-exchange/secrets/exchange-attributes.secret.conf.j2 + dest: /etc/taler-exchange/secrets/exchange-attributes.secret.conf + owner: root + group: taler-exchange-kyc + mode: "0440" + no_log: true + diff: false - name: Place taler-exchange business config ansible.builtin.template: @@ -182,6 +194,8 @@ owner: root group: root mode: "0644" + no_log: true + diff: false - name: Setup or upgrade Taler Exchange database ansible.builtin.command: diff --git a/roles/exchange/templates/etc/taler-exchange/conf.d/exchange-business.conf.j2 b/roles/exchange/templates/etc/taler-exchange/conf.d/exchange-business.conf.j2 @@ -22,7 +22,7 @@ AML_SPA_DIALECT = {{ exchange_spa_dialect }} # Attribute encryption key for storing attributes encrypted # in the database. Should be a high-entropy nonce. -ATTRIBUTE_ENCRYPTION_KEY = {{ exchange_attribute_encryption_key }} +@inline-secret@ exchange ../secrets/exchange-attributes.secret.conf # For your terms of service and privacy policy, you should specify # an Etag that must be updated whenever there are significant diff --git a/roles/exchange/templates/etc/taler-exchange/secrets/exchange-attributes.secret.conf.j2 b/roles/exchange/templates/etc/taler-exchange/secrets/exchange-attributes.secret.conf.j2 @@ -0,0 +1,2 @@ +[exchange] +ATTRIBUTE_ENCRYPTION_KEY = {{ exchange_attribute_encryption_key }} diff --git a/roles/exchange_tops/tasks/main.yml b/roles/exchange_tops/tasks/main.yml @@ -6,6 +6,8 @@ owner: taler-exchange-httpd group: taler-exchange-kyc mode: "0440" + no_log: true + diff: false - name: Place taler-exchange external KYC provider configuration ansible.builtin.template: @@ -14,6 +16,8 @@ owner: taler-exchange-httpd group: taler-exchange-kyc mode: "0440" + no_log: true + diff: false - name: Place taler-exchange AML program environment ansible.builtin.template: diff --git a/roles/libeufin-nexus/tasks/main.yml b/roles/libeufin-nexus/tasks/main.yml @@ -88,13 +88,11 @@ - /etc/ansible/facts.d/libeufin-nexus-access-token.fact - "secret-token:" creates: /etc/ansible/facts.d/libeufin-nexus-access-token.fact + no_log: true - name: Libeufin-nexus force ansible to regather just created fact(s) ansible.builtin.setup: - -# FIXME: -# Once the libeufin package has the proper config structure, -# use a secrets include instead. + no_log: true - name: Place libeufin-nexus config ansible.builtin.template: @@ -112,6 +110,8 @@ group: libeufin-nexus mode: "0400" when: use_ebics or configure_ebics + no_log: true + diff: false - name: Place wire gateway secret config ansible.builtin.template: @@ -120,6 +120,8 @@ owner: libeufin-nexus group: libeufin-nexus mode: "0400" + no_log: true + diff: false - name: Setup libeufin database ansible.builtin.command: