ansible-taler-exchange

Ansible playbook to deploy a production Taler Exchange
Log | Files | Refs | Submodules | README | LICENSE

commit ecdba35e0c97ac924037fba7706f4429b089ac68
parent 613cd9be4a8a28539f754a1f83a13f204e26cfac
Author: Florian Dold <florian@dold.me>
Date:   Mon,  2 Jun 2025 16:03:30 +0200

add devtesting role

Diffstat:
Minventories/group_vars/all/defaults.yml | 9+++++++--
Minventories/host_vars/fdold-guava-glsint/test-public.yml | 9+++++++++
Minventories/host_vars/rusty/test-public.yml | 9+++++++++
Mplaybooks/setup.yml | 2++
Aroles/devtesting/tasks/files/etc/sudoers.d/devtesting | 1+
Aroles/devtesting/tasks/files/taler-devtesting | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroles/devtesting/tasks/main.yml | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroles/devtesting/tasks/templates/authorized_keys | 4++++
8 files changed, 182 insertions(+), 2 deletions(-)

diff --git a/inventories/group_vars/all/defaults.yml b/inventories/group_vars/all/defaults.yml @@ -10,4 +10,10 @@ deploy_challenger: false deploy_monitoring: true # If true, use EBICS keys from that were externally created. -ebics_keys_external: false -\ No newline at end of file +ebics_keys_external: false + +# If set to true, set up an additional user to allow faking wire transfers and +# inspecting challenger auth codes. +# This setting MUST NOT be enabled in production +# deployments under any circumstance. +dangerously_enable_devtesting: false diff --git a/inventories/host_vars/fdold-guava-glsint/test-public.yml b/inventories/host_vars/fdold-guava-glsint/test-public.yml @@ -61,3 +61,12 @@ EXCHANGE_OPERATOR_LEGAL_NAME: "GLS Taler Internal" KYC_THANK_YOU_URL: https://taler.gls.de/thank-you-kyc # Tool to use for sanction list checking EXCHANGE_SANCTION_HELPER: taler-exchange-helper-sanctions-dummy + +# If set to true, set up an additional user to allow faking wire transfers and +# inspecting challenger auth codes. +# This setting MUST NOT be enabled in production +# deployments under any circumstance. +dangerously_enable_devtesting: true + +devtesting_ssh_keys: + - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINHSjJ/zPwQnqBrKp0qK+OdsZYfQ8DHY2dyJakNozBi7 fdold-work@sapota" diff --git a/inventories/host_vars/rusty/test-public.yml b/inventories/host_vars/rusty/test-public.yml @@ -66,3 +66,12 @@ EXCHANGE_AML_PROGRAM_TOPS_POSTAL_COUNTRY_HINT: "Swiss address required" EXCHANGE_AML_PROGRAM_TOPS_POSTAL_COUNTRY_REGEX: "CH|Ch|ch" # Tool to use for sanction list checking EXCHANGE_SANCTION_HELPER: taler-exchange-helper-sanctions-dummy + +# If set to true, set up an additional user to allow faking wire transfers and +# inspecting challenger auth codes. +# This setting MUST NOT be enabled in production +# deployments under any circumstance. +dangerously_enable_devtesting: true + +devtesting_ssh_keys: + - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINHSjJ/zPwQnqBrKp0qK+OdsZYfQ8DHY2dyJakNozBi7 fdold-work@sapota" diff --git a/playbooks/setup.yml b/playbooks/setup.yml @@ -24,3 +24,5 @@ when: deploy_auditor | bool - role: monitoring when: deploy_monitoring | bool + - role: devtesting + when: dangerously_enable_devtesting | bool diff --git a/roles/devtesting/tasks/files/etc/sudoers.d/devtesting b/roles/devtesting/tasks/files/etc/sudoers.d/devtesting @@ -0,0 +1 @@ +devtesting ALL=(libeufin-nexus:libeufin-nexus) CWD=/tmp/ NOPASSWD: /usr/bin/libeufin-nexus testing * diff --git a/roles/devtesting/tasks/files/taler-devtesting b/roles/devtesting/tasks/files/taler-devtesting @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +import click +import shlex +import os +import sys +import subprocess +import random + + +def calculate_iban_check_digits(country_code, bban): + numeric_iban_str = "" + for char in bban + country_code + "00": + if char.isdigit(): + numeric_iban_str += char + elif "A" <= char.upper() <= "Z": + numeric_iban_str += str(ord(char.upper()) - ord("A") + 10) + else: + raise ValueError( + f"Invalid character '{char}' in IBAN for checksum calculation." + ) + remainder = int(numeric_iban_str) % 97 + check_digits_int = 98 - remainder + return str(check_digits_int).zfill(2) + + +def generate_random_swiss_iban(): + country_code = "CH" + bban = "".join([str(random.randint(0, 9)) for _ in range(12 + 5)]) + check_digits = calculate_iban_check_digits(country_code, bban) + return f"{country_code}{check_digits}{bban}" + + +def generate_random_german_iban(): + country_code = "DE" + bban = "".join([str(random.randint(0, 9)) for _ in range(18)]) + check_digits = calculate_iban_check_digits(country_code, bban) + return f"{country_code}{check_digits}{bban}" + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.option("--amount", required=True) +@click.option("--subject", required=True) +@click.option("--debitor-payto") +def fake_incoming(amount, subject, debitor_payto): + + currency_out = subprocess.check_output( + "libeufin-nexus config get nexus-ebics currency", + shell=True, + encoding="utf-8", + stderr=subprocess.DEVNULL, + ) + currency = currency_out.strip().upper() + + if debitor_payto is None: + if currency == "CHF": + iban = generate_random_swiss_iban() + elif currency == "EUR": + iban = generate_random_german_iban() + else: + raise Exception(f"unsupported currency {repr(currency)}") + debitor_payto = f"payto://iban/{iban}?receiver-name=Tammy%20Tester" + + print(f"Faking incoming {currency} transaction", file=sys.stderr) + print(f"Debitor Payto: ", debitor_payto, file=sys.stderr) + subprocess.run( + [ + "sudo", + "-u", + "libeufin-nexus", + "libeufin-nexus", + "testing", + "fake-incoming", + "--amount", + amount, + "--subject", + subject, + debitor_payto, + ], + check=True, + ) + + + +if __name__ == "__main__": + orig_cmd = os.environ.get("SSH_ORIGINAL_COMMAND") + if orig_cmd: + print("Running command", repr(orig_cmd), file=sys.stderr) + cmd = shlex.split(orig_cmd) + else: + cmd = sys.argv[1:] + cli(cmd) diff --git a/roles/devtesting/tasks/main.yml b/roles/devtesting/tasks/main.yml @@ -0,0 +1,53 @@ +- name: Recollect facts + setup: + +- name: Install devtesting dependencies + apt: + name: + - python3-click + - taler-harness + state: latest + when: ansible_os_family == 'Debian' + +- name: Ensure group for devtesting exists + group: + name: devtesting + +- name: Ensure technical user for libeufin-nexus import exists + user: + name: devtesting + group: devtesting + shell: /bin/bash + password: "!" + +- name: Place devtesting helper + ansible.builtin.copy: + src: files/taler-devtesting + dest: /usr/local/bin/taler-devtesting + owner: root + group: root + mode: "0755" + +- name: Grant sudo rights to devtesting user + ansible.builtin.copy: + src: etc/sudoers.d/devtesting + dest: /etc/sudoers.d/devtesting + owner: root + group: root + mode: "0644" + +- name: Ensure .ssh dir exists for devtesting user + file: + path: "/home/devtesting/.ssh/" + state: directory + owner: devtesting + group: devtesting + mode: "0755" + +- name: Allow devtesting users access via SSH + ansible.builtin.template: + src: templates/authorized_keys + dest: "/home/devtesting/.ssh/authorized_keys" + owner: devtesting + group: devtesting + mode: "0644" diff --git a/roles/devtesting/tasks/templates/authorized_keys b/roles/devtesting/tasks/templates/authorized_keys @@ -0,0 +1,3 @@ +{% for k in devtesting_ssh_keys %} +command="/usr/local/bin/taler-devtesting",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty {{ k }} +{% endfor %} +\ No newline at end of file