sandcastle-ng

Scripts for the deployment of Sandcastle (GNU Taler)
Log | Files | Refs | README

commit f1ceb79ee3cea5ee53040c9d5b3ce3a3b674e211
parent 5628e6c244a0c8064b0676e325bf10432477bb23
Author: Florian Dold <dold@taler.net>
Date:   Thu, 27 Aug 2026 17:03:46 +0200

sandcastle: expose services over Unix sockets

Diffstat:
MREADME.md | 69+++++++++++++++++++++++++++++++++++++++++----------------------------
Mhost/container-taler-sandcastle-demo.service | 12------------
Mhost/container-taler-sandcastle-froscon.service | 11-----------
Mhost/container-taler-sandcastle-test.service | 11-----------
Msandcastle-run | 44--------------------------------------------
Mscripts/demo/setup-sandcastle.sh | 185++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtests/test_sandcastle_run.py | 19+++++++------------
Atests/test_socket_configuration.py | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 204 insertions(+), 209 deletions(-)

diff --git a/README.md b/README.md @@ -14,8 +14,8 @@ You need (on your host system): * bash * Python 3.10 or newer -The sandcastle-ng container exposes the merchant backend through a Unix domain -socket and exposes TCP ports for the other GNU Taler APIs / Web interfaces. +The sandcastle-ng container exposes the GNU Taler APIs and Web interfaces +through Unix domain sockets. Some of these services expect to be accessed via a certain hostname, so it is recommended that you set up the following domain names: @@ -29,7 +29,7 @@ so it is recommended that you set up the following domain names: * `paivana.$MYDOMAIN` for the demo Paivana paywall Your host system should reverse-proxy HTTP(s) traffic to the respective service -port for each of the services. +socket. # Upgrading Component Versions @@ -106,12 +106,8 @@ Currently there is not much configuration. The main adjustments to be made are: * scripts/demo/setup-sandcastle.sh has the currency on top of the file -* `sandcastle-run` has variables for the ports that'll be exposed ("published") - on the host. They can be overwritten with environment variables named - `SANDCASTLE_PORT_$COMPONENT`. -* The merchant backend uses the Unix socket - `sockets/merchant-backend.sock` by default. Set `SANDCASTLE_SOCKET_DIR` to - choose a different host directory for the socket. +* Services use Unix sockets below `sockets/` by default. Set + `SANDCASTLE_SOCKET_DIR` to choose a different host directory. To make adjustments for an environment without changing the ``setup-sandcastle.sh`` script, you can use an override file @@ -165,12 +161,32 @@ These will be passed on to ``podman run``. Example: ./sandcastle-run --log-level=debug -The running container publishes ports to the host as defined in -`./sandcastle-run`. You can manually verify these port mappings via -`podman port taler-sandcastle`. - -The merchant backend is instead available through a bind-mounted Unix socket: - +The running container exposes these services through bind-mounted Unix sockets: + +| Service | Socket | +| --- | --- | +| Bank | `bank.sock` | +| Exchange | `exchange.sock` | +| Merchant backend | `merchant-backend.sock` | +| Landing page | `landing.sock` | +| Blog shop | `blog.sock` | +| Donations | `donations.sock` | +| Challenger | `challenger.sock` | +| Auditor | `auditor.sock` | +| Donau | `donau.sock` | +| Drupal | `drupal.sock` | +| Paivana | `paivana.sock` | + +Exchange, Merchant, the Blog and Donations demos, Auditor, Donau, and Paivana +are exposed directly through their native listener or packaged systemd socket +activation. Caddy owns the bank socket to bridge libeufin-bank's internal TCP +listener, the landing socket to add the Sandcastle status endpoint, the +Challenger socket to serve the insecure demo challenges, and the Drupal socket +because it is Drupal's HTTP server. + +For example, query the Exchange and merchant configuration endpoints with: + + curl --unix-socket ./sockets/exchange.sock http://localhost/config curl --unix-socket ./sockets/merchant-backend.sock http://localhost/config The socket directory defaults to `./sockets`, relative to the repository, and @@ -178,19 +194,18 @@ can be changed when starting the deployment: SANDCASTLE_SOCKET_DIR=/run/taler-sandcastle ./sandcastle-run -Missing socket directories are created with mode `1777`, and the merchant -socket has mode `0666`, so a host reverse proxy running as a different user can -connect to it. A pre-existing custom directory must allow the container's -`caddy` user to create the socket. The bind mount uses Podman's shared SELinux -label; deployments with a confined host reverse proxy may additionally need an -appropriate local SELinux policy. - -To retain the previous dedicated TCP exposure for the merchant backend, set -its port explicitly: +Missing socket directories are created with mode `1777`, and the sockets have +mode `0666`, so a host reverse proxy running as a different user can connect to +them. A pre-existing custom directory must allow the relevant container +service users to create sockets. The bind mount uses Podman's shared SELinux +label; deployments with a confined host reverse proxy may additionally need +an appropriate local SELinux policy. - SANDCASTLE_PORT_MERCHANT=127.0.0.1:16000 ./sandcastle-run +libeufin-bank does not yet support Unix sockets, so it listens on localhost +inside the container and Caddy exposes it through `bank.sock`. -This keeps the Unix socket available as well. +Overrides that define `EXTERNAL_PORT`, such as `taler.localhost`, continue to +publish that single Caddy ingress port for local integration testing. When starting sandcastle like @@ -339,8 +354,6 @@ touch exported/import-request * Do self-tests of the deployment using the wallet CLI * Running the auditor * Running a currency conversion setup with multiple libeufin-bank instances -* Migrate the remaining HTTP port exposures to Unix domain sockets, avoiding - port collision problems. * Instead of requiring the reverse proxy to handle TLS, the sandcastle container itself could do TLS termination with caddy. * To improve performance, allow connecting to an external database diff --git a/host/container-taler-sandcastle-demo.service b/host/container-taler-sandcastle-demo.service @@ -8,18 +8,6 @@ After=network-online.target RequiresMountsFor=%t/containers [Service] -Environment=SANDCASTLE_PORT_MERCHANT=127.0.0.1:5556 -Environment=SANDCASTLE_PORT_EXCHANGE=127.0.0.1:5555 -Environment=SANDCASTLE_PORT_BLOG=127.0.0.1:5559 -Environment=SANDCASTLE_PORT_DONATIONS=127.0.0.1:5560 -Environment=SANDCASTLE_PORT_SURVEY=127.0.0.1:5561 -Environment=SANDCASTLE_PORT_LANDING=127.0.0.1:5562 -Environment=SANDCASTLE_PORT_LIBEUFIN_BANK=127.0.0.1:15000 -Environment=SANDCASTLE_PORT_CHALLENGER=127.0.0.1:15003 -Environment=SANDCASTLE_PORT_AUDITOR=127.0.0.1:15004 -Environment=SANDCASTLE_PORT_DONAU=127.0.0.1:15005 -Environment=SANDCASTLE_PORT_DRUPAL=127.0.0.1:15006 -Environment=SANDCASTLE_PORT_PAIVANA=127.0.0.1:15007 Restart=on-failure TimeoutStopSec=70 ExecStart=%h/sandcastle-ng/sandcastle-run diff --git a/host/container-taler-sandcastle-froscon.service b/host/container-taler-sandcastle-froscon.service @@ -9,17 +9,6 @@ RequiresMountsFor=%t/containers [Service] Environment=SANDCASTLE_OVERRIDE_NAME=froscon.taler.net -Environment=SANDCASTLE_PORT_MERCHANT=127.0.0.1:18000 -Environment=SANDCASTLE_PORT_EXCHANGE=127.0.0.1:18001 -Environment=SANDCASTLE_PORT_BLOG=127.0.0.1:18002 -Environment=SANDCASTLE_PORT_DONATIONS=127.0.0.1:18003 -Environment=SANDCASTLE_PORT_LANDING=127.0.0.1:18005 -Environment=SANDCASTLE_PORT_LIBEUFIN_BANK=127.0.0.1:18007 -Environment=SANDCASTLE_PORT_CHALLENGER=127.0.0.1:18010 -Environment=SANDCASTLE_PORT_AUDITOR=127.0.0.1:18011 -Environment=SANDCASTLE_PORT_DONAU=127.0.0.1:18012 -Environment=SANDCASTLE_PORT_DRUPAL=127.0.0.1:18013 -Environment=SANDCASTLE_PORT_PAIVANA=127.0.0.1:18014 Restart=on-failure TimeoutStopSec=70 ExecStart=%h/sandcastle-ng/sandcastle-run diff --git a/host/container-taler-sandcastle-test.service b/host/container-taler-sandcastle-test.service @@ -9,17 +9,6 @@ RequiresMountsFor=%t/containers [Service] Environment=SANDCASTLE_OVERRIDE_NAME=test.taler.net -Environment=SANDCASTLE_PORT_MERCHANT=127.0.0.1:16000 -Environment=SANDCASTLE_PORT_EXCHANGE=127.0.0.1:16001 -Environment=SANDCASTLE_PORT_BLOG=127.0.0.1:16002 -Environment=SANDCASTLE_PORT_DONATIONS=127.0.0.1:16003 -Environment=SANDCASTLE_PORT_LANDING=127.0.0.1:16005 -Environment=SANDCASTLE_PORT_LIBEUFIN_BANK=127.0.0.1:16007 -Environment=SANDCASTLE_PORT_CHALLENGER=127.0.0.1:16010 -Environment=SANDCASTLE_PORT_AUDITOR=127.0.0.1:16011 -Environment=SANDCASTLE_PORT_DONAU=127.0.0.1:16012 -Environment=SANDCASTLE_PORT_DRUPAL=127.0.0.1:16013 -Environment=SANDCASTLE_PORT_PAIVANA=127.0.0.1:16014 Restart=on-failure TimeoutStopSec=70 ExecStart=%h/sandcastle-ng/sandcastle-run diff --git a/sandcastle-run b/sandcastle-run @@ -4,34 +4,6 @@ set -exou -# Ports where individual services are published to the host. The merchant -# backend is exposed through a Unix socket by default, but can still be -# published on a TCP port by setting SANDCASTLE_PORT_MERCHANT. -SANDCASTLE_PORT_MERCHANT=${SANDCASTLE_PORT_MERCHANT:-} -SANDCASTLE_PORT_EXCHANGE=${SANDCASTLE_PORT_EXCHANGE:-127.0.0.1:16001} -SANDCASTLE_PORT_BLOG=${SANDCASTLE_PORT_BLOG:-127.0.0.1:16002} -SANDCASTLE_PORT_DONATIONS=${SANDCASTLE_PORT_DONATIONS:-127.0.0.1:16003} -SANDCASTLE_PORT_LANDING=${SANDCASTLE_PORT_LANDING:-127.0.0.1:16005} -SANDCASTLE_PORT_LIBEUFIN_BANK=${SANDCASTLE_PORT_LIBEUFIN_BANK:-127.0.0.1:16007} -SANDCASTLE_PORT_CHALLENGER=${SANDCASTLE_PORT_CHALLENGER:-127.0.0.1:16010} -SANDCASTLE_PORT_AUDITOR=${SANDCASTLE_PORT_AUDITOR:-127.0.0.1:16011} -SANDCASTLE_PORT_DONAU=${SANDCASTLE_PORT_DONAU:-127.0.0.1:16012} -SANDCASTLE_PORT_DRUPAL=${SANDCASTLE_PORT_DRUPAL:-127.0.0.1:16013} -SANDCASTLE_PORT_PAIVANA=${SANDCASTLE_PORT_PAIVANA:-127.0.0.1:16014} - -# Container-internal ports, should by synced with scripts/setup-sandcastle.sh -PORT_INTERNAL_EXCHANGE=8201 -PORT_INTERNAL_MERCHANT=8301 -PORT_INTERNAL_LIBEUFIN_BANK=8080 -PORT_INTERNAL_LANDING=8501 -PORT_INTERNAL_BLOG=8502 -PORT_INTERNAL_DONATIONS=8503 -PORT_INTERNAL_CHALLENGER=8506 -PORT_INTERNAL_AUDITOR=8507 -PORT_INTERNAL_DONAU=8508 -PORT_INTERNAL_DRUPAL=8509 -PORT_INTERNAL_PAIVANA=8510 - SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) cd "$SCRIPT_DIR" @@ -42,11 +14,6 @@ if [[ ! -d $SANDCASTLE_SOCKET_DIR ]]; then fi SANDCASTLE_SOCKET_DIR=$(realpath "$SANDCASTLE_SOCKET_DIR") -PUBLISH_MERCHANT_PORT=() -if [[ -n $SANDCASTLE_PORT_MERCHANT ]]; then - PUBLISH_MERCHANT_PORT=("-p=$SANDCASTLE_PORT_MERCHANT:$PORT_INTERNAL_MERCHANT") -fi - existing_id=$(podman ps -q -a -f=name=taler-sandcastle) if [[ -n $existing_id ]]; then @@ -92,17 +59,6 @@ NETWORK="--network slirp4netns" exec podman run \ -d \ - "${PUBLISH_MERCHANT_PORT[@]}" \ - -p=$SANDCASTLE_PORT_EXCHANGE:$PORT_INTERNAL_EXCHANGE \ - -p=$SANDCASTLE_PORT_LIBEUFIN_BANK:$PORT_INTERNAL_LIBEUFIN_BANK \ - -p=$SANDCASTLE_PORT_LANDING:$PORT_INTERNAL_LANDING \ - -p=$SANDCASTLE_PORT_BLOG:$PORT_INTERNAL_BLOG \ - -p=$SANDCASTLE_PORT_DONATIONS:$PORT_INTERNAL_DONATIONS \ - -p=$SANDCASTLE_PORT_CHALLENGER:$PORT_INTERNAL_CHALLENGER \ - -p=$SANDCASTLE_PORT_AUDITOR:$PORT_INTERNAL_AUDITOR \ - -p=$SANDCASTLE_PORT_DONAU:$PORT_INTERNAL_DONAU \ - -p=$SANDCASTLE_PORT_DRUPAL:$PORT_INTERNAL_DRUPAL \ - -p=$SANDCASTLE_PORT_PAIVANA:$PORT_INTERNAL_PAIVANA \ --privileged \ --name taler-sandcastle \ --systemd=always \ diff --git a/scripts/demo/setup-sandcastle.sh b/scripts/demo/setup-sandcastle.sh @@ -176,20 +176,9 @@ PAIVANA_DOMAIN=paivana.$MYDOMAIN # served by the sandcastle itself. PAIVANA_DESTINATION=${PAIVANA_DESTINATION:-https://docs.taler.net/} -# Ports of the services running inside the container. +# Port of libeufin-bank running inside the container. # Should be synchronized with the sandcastle-run script. -PORT_INTERNAL_EXCHANGE=8201 -PORT_INTERNAL_MERCHANT=8301 PORT_INTERNAL_LIBEUFIN_BANK=8080 -PORT_INTERNAL_LANDING=8501 -PORT_INTERNAL_LANDING_SERVICE=8511 -PORT_INTERNAL_BLOG=8502 -PORT_INTERNAL_DONATIONS=8503 -PORT_INTERNAL_CHALLENGER=8506 -PORT_INTERNAL_AUDITOR=8507 -PORT_INTERNAL_DONAU=8508 -PORT_INTERNAL_DRUPAL=8509 -PORT_INTERNAL_PAIVANA=8510 ENABLE_AUDITOR=0 @@ -197,15 +186,19 @@ ENABLE_AUDITOR=0 # Just make sure the services are stopped systemctl stop postgresql.service systemctl stop taler-auditor.target +systemctl stop taler-auditor-httpd.socket systemctl stop taler-exchange.target +systemctl stop taler-exchange-httpd.socket systemctl stop taler-exchange-offline.timer systemctl stop taler-merchant-httpd.service +systemctl stop taler-merchant-httpd.socket systemctl stop taler-merchant.target systemctl stop taler-demo-landing.service systemctl stop taler-demo-blog.service systemctl stop taler-demo-donations.service systemctl stop libeufin-bank.service systemctl stop donau-httpd.service +systemctl stop donau-httpd.socket systemctl stop paivana-httpd.service systemctl stop paivana-httpd.socket @@ -311,6 +304,29 @@ AUTH_COMMAND = /data/sandcastle-challenger-auth ADDRESS_RESTRICTIONS = {"email":{"hint":"not an e-mail address","regex":"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$"}} EOF +# Move systemd-activated HTTP listeners into the bind-mounted directory so the +# host can use them directly without an extra reverse-proxy hop. +configure_public_socket() { + local unit=$1 + local socket_path=$2 + local drop_in_dir=/etc/systemd/system/$unit.d + + mkdir -p "$drop_in_dir" + cat <<EOF >"$drop_in_dir/sandcastle.conf" +[Socket] +ListenStream= +ListenStream=$socket_path +SocketMode=0666 +RemoveOnStop=yes +EOF +} + +configure_public_socket taler-exchange-httpd.socket /sockets/exchange.sock +configure_public_socket taler-merchant-httpd.socket /sockets/merchant-backend.sock +configure_public_socket taler-auditor-httpd.socket /sockets/auditor.sock +configure_public_socket donau-httpd.socket /sockets/donau.sock +configure_public_socket paivana-httpd.socket /sockets/paivana.sock + mkdir -p /etc/paivana cat <<EOF >/etc/paivana/paivana.conf [paivana] @@ -340,7 +356,7 @@ EOF chown paivana-httpd:root /etc/paivana/paivana.conf chmod 640 /etc/paivana/paivana.conf -# The paywall runs behind the caddy reverse proxy, so we need +# The paywall runs behind a reverse proxy, so we need # '-f' to get the client address from X-Forwarded-For. With '-g' # a single payment unlocks the entire site instead of just the # URL that was paid for. @@ -369,10 +385,10 @@ SPA_CAPTCHA_URL = $PROTO://$BANK_DOMAIN$PORT_SUFFIX/webui/#/operation/{woid} SUGGESTED_WITHDRAWAL_EXCHANGE = $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/ ALLOW_REGISTRATION = yes SERVE = tcp -PORT = 8080 +PORT = $PORT_INTERNAL_LIBEUFIN_BANK # Bind address. # Option soon to be deprecated! -ADDRESS = 0.0.0.0 +ADDRESS = 127.0.0.1 WIRE_TYPE = $WIRE_METHOD # Compat mode for now @@ -525,6 +541,7 @@ EOF [[ $(type -t hook_merchant_config) == function ]] && hook_merchant_config mkdir -p /etc/taler-merchant-demos +install -d -o taler-merchant-demos -g taler-merchant-demos /run/taler-merchant-demos cat <<EOF >/etc/taler-merchant-demos/taler-merchant-demos.conf [taler] @@ -537,20 +554,23 @@ BLOG_URL = $PROTO://$BLOG_DOMAIN$PORT_SUFFIX/ DONATIONS_URL = $PROTO://$DONATIONS_DOMAIN$PORT_SUFFIX/ [frontend-demo-landing] -HTTP_SERVE = tcp -HTTP_PORT = $PORT_INTERNAL_LANDING_SERVICE +HTTP_SERVE = unix +HTTP_UNIXPATH = /run/taler-merchant-demos/landing.sock +HTTP_UNIXPATH_MODE = 666 [frontend-demo-blog] -HTTP_SERVE = tcp -HTTP_PORT = $PORT_INTERNAL_BLOG +HTTP_SERVE = unix +HTTP_UNIXPATH = /sockets/blog.sock +HTTP_UNIXPATH_MODE = 666 BACKEND_URL = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/blog/ BACKEND_APIKEY = secret-token:$(get_credential_pw merchant/blog) ENABLE_TOKENS = ${ENABLE_SUBSCRIPTIONS:-no} [frontend-demo-donations] DONAU_URL = $PROTO://$DONAU_DOMAIN$PORT_SUFFIX/ -HTTP_SERVE = tcp -HTTP_PORT = $PORT_INTERNAL_DONATIONS +HTTP_SERVE = unix +HTTP_UNIXPATH = /sockets/donations.sock +HTTP_UNIXPATH_MODE = 666 BACKEND_URL_TOR = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/tor/ BACKEND_APIKEY_TOR = secret-token:$(get_credential_pw merchant/tor) BACKEND_URL_TALER = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/taler/ @@ -705,6 +725,14 @@ cat <<EOF >/etc/caddy/Caddyfile } } +(sandcastle_bank) { + reverse_proxy 127.0.0.1:$PORT_INTERNAL_LIBEUFIN_BANK { + # libeufin-bank should eventually not require this anymore, + # but currently doesn't work without this header. + header_up X-Forwarded-Prefix "" + } +} + (sandcastle_landing) { handle /metrics/sandcastle-status { root * /var/www/sandcastle @@ -712,60 +740,19 @@ cat <<EOF >/etc/caddy/Caddyfile file_server } handle { - reverse_proxy :$PORT_INTERNAL_LANDING_SERVICE - } -} - -(sandcastle_merchant) { - reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock { - # Set this, or otherwise wrong taler://pay URIs will be generated. - header_up X-Forwarded-Proto "https" - } -} - -# Services that only listen on unix domain sockets -# are reverse-proxied to public listeners. - -:$PORT_INTERNAL_EXCHANGE { - reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock -} - -:$PORT_INTERNAL_MERCHANT { - import sandcastle_merchant -} - -http:// { - bind unix//sockets/merchant-backend.sock|0666 - import sandcastle_merchant -} - -:$PORT_INTERNAL_DONAU { - reverse_proxy unix//run/donau/httpd/http.sock { - header_up X-Forwarded-Proto "https" + reverse_proxy unix//run/taler-merchant-demos/landing.sock } } -:$PORT_INTERNAL_LANDING { - import sandcastle_landing -} - -:$PORT_INTERNAL_DRUPAL { +(sandcastle_drupal) { root * /talerdata/sandcastle-drupal/web/ php_fastcgi unix/var/run/php/php8.4-fpm.sock file_server } -:$PORT_INTERNAL_AUDITOR { - reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock -} - -:$PORT_INTERNAL_PAIVANA { - reverse_proxy unix//run/paivana/httpd/paivana-http.sock -} - -:$PORT_INTERNAL_CHALLENGER { +(sandcastle_challenger) { handle { - reverse_proxy unix//run/challenger/httpd/challenger.http { + reverse_proxy unix//run/challenger/httpd/challenger-http.sock { # Set this, or otherwise wrong taler://pay URIs will be generated. header_up X-Forwarded-Proto "https" } @@ -781,6 +768,30 @@ http:// { } } } + +# Caddy owns sockets only where it bridges a TCP-only service, composes +# additional routing, or serves the application itself. Native services or +# their socket units own the others. + +http:// { + bind unix//sockets/bank.sock|0666 + import sandcastle_bank +} + +http:// { + bind unix//sockets/landing.sock|0666 + import sandcastle_landing +} + +http:// { + bind unix//sockets/challenger.sock|0666 + import sandcastle_challenger +} + +http:// { + bind unix//sockets/drupal.sock|0666 + import sandcastle_drupal +} EOF if [[ $PROTO = https ]]; then @@ -792,11 +803,7 @@ if [[ $PROTO = https ]]; then https://$BANK_DOMAIN { tls internal - reverse_proxy :8080 { - # libeufin-bank should eventually not require this anymore, - # but currently doesn't work without this header. - header_up X-Forwarded-Prefix "" - } + import sandcastle_bank } https://$LANDING_DOMAIN { @@ -806,12 +813,12 @@ https://$LANDING_DOMAIN { https://$EXCHANGE_DOMAIN { tls internal - reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock + reverse_proxy unix//sockets/exchange.sock } https://$MERCHANT_DOMAIN { tls internal - reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock { + reverse_proxy unix//sockets/merchant-backend.sock { # Set this, or otherwise wrong taler://pay URIs will be generated. header_up X-Forwarded-Proto "https" } @@ -819,24 +826,24 @@ https://$MERCHANT_DOMAIN { https://$DONAU_DOMAIN { tls internal - reverse_proxy unix//run/donau/httpd/http.sock { + reverse_proxy unix//sockets/donau.sock { header_up X-Forwarded-Proto "https" } } https://$AUDITOR_DOMAIN { tls internal - reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock + reverse_proxy unix//sockets/auditor.sock } https://$CHALLENGER_DOMAIN { tls internal - reverse_proxy unix//run/challenger/httpd/challenger.http + reverse_proxy unix//run/challenger/httpd/challenger-http.sock } https://$PAIVANA_DOMAIN { tls internal - reverse_proxy unix//run/paivana/httpd/paivana-http.sock + reverse_proxy unix//sockets/paivana.sock } EOF @@ -847,31 +854,27 @@ else cat <<EOF >>/etc/caddy/Caddyfile http://$BANK_DOMAIN$PORT_SUFFIX { - reverse_proxy :8080 { - # libeufin-bank should eventually not require this anymore, - # but currently doesn't work without this header. - header_up X-Forwarded-Prefix "" - } + import sandcastle_bank } http://$EXCHANGE_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock + reverse_proxy unix//sockets/exchange.sock } http://$MERCHANT_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock + reverse_proxy unix//sockets/merchant-backend.sock } http://$DONAU_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/donau/httpd/http.sock + reverse_proxy unix//sockets/donau.sock } http://$AUDITOR_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock + reverse_proxy unix//sockets/auditor.sock } http://$CHALLENGER_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/challenger/httpd/challenger.http + reverse_proxy unix//run/challenger/httpd/challenger-http.sock } http://$LANDING_DOMAIN$PORT_SUFFIX { @@ -879,19 +882,19 @@ http://$LANDING_DOMAIN$PORT_SUFFIX { } http://$BLOG_DOMAIN$PORT_SUFFIX { - reverse_proxy :$PORT_INTERNAL_BLOG + reverse_proxy unix//sockets/blog.sock } http://$DONATIONS_DOMAIN$PORT_SUFFIX { - reverse_proxy :$PORT_INTERNAL_DONATIONS + reverse_proxy unix//sockets/donations.sock } http://$DRUPAL_DOMAIN$PORT_SUFFIX { - reverse_proxy :$PORT_INTERNAL_DRUPAL + import sandcastle_drupal } http://$PAIVANA_DOMAIN$PORT_SUFFIX { - reverse_proxy unix//run/paivana/httpd/paivana-http.sock + reverse_proxy unix//sockets/paivana.sock } EOF diff --git a/tests/test_sandcastle_run.py b/tests/test_sandcastle_run.py @@ -59,12 +59,13 @@ class SandcastleRunTests(unittest.TestCase): ) for variable in ( "SANDCASTLE_OVERRIDE_NAME", - "SANDCASTLE_PORT_MERCHANT", "SANDCASTLE_SOCKET_DIR", ): environment.pop(variable, None) environment.update(settings) + self.log.unlink(missing_ok=True) + result = subprocess.run( [str(self.repository / "sandcastle-run")], env=environment, @@ -82,15 +83,17 @@ class SandcastleRunTests(unittest.TestCase): self.assertEqual("run", calls[1][0]) return calls[1] - def test_default_uses_socket_without_publishing_merchant_port(self): + def test_default_uses_sockets_without_publishing_ports(self): arguments = self.run_sandcastle() socket_dir = self.repository / "sockets" self.assertTrue(socket_dir.is_dir()) self.assertEqual(0o1777, stat.S_IMODE(socket_dir.stat().st_mode)) self.assertIn(f"{socket_dir}:/sockets:z", arguments) - self.assertNotIn("-p=127.0.0.1:16000:8301", arguments) - self.assertIn("-p=127.0.0.1:16001:8201", arguments) + published_ports = [ + argument for argument in arguments if argument.startswith("-p=") + ] + self.assertEqual([], published_ports) def test_custom_socket_directory_is_mounted_as_one_argument(self): socket_dir = self.root / "custom socket directory" @@ -100,13 +103,5 @@ class SandcastleRunTests(unittest.TestCase): self.assertEqual(0o1777, stat.S_IMODE(socket_dir.stat().st_mode)) self.assertIn(f"{socket_dir}:/sockets:z", arguments) - def test_explicit_merchant_port_is_published(self): - arguments = self.run_sandcastle( - SANDCASTLE_PORT_MERCHANT="127.0.0.1:26000" - ) - - self.assertIn("-p=127.0.0.1:26000:8301", arguments) - - if __name__ == "__main__": unittest.main() diff --git a/tests/test_socket_configuration.py b/tests/test_socket_configuration.py @@ -0,0 +1,62 @@ +# This file is in the public domain. + +import re +import unittest +from pathlib import Path + + +REPOSITORY = Path(__file__).resolve().parents[1] +SETUP_SCRIPT = (REPOSITORY / "scripts/demo/setup-sandcastle.sh").read_text( + encoding="utf-8" +) + + +class SocketConfigurationTests(unittest.TestCase): + def test_systemd_activated_services_use_public_sockets(self): + sockets = { + "taler-exchange-httpd.socket": "exchange.sock", + "taler-merchant-httpd.socket": "merchant-backend.sock", + "taler-auditor-httpd.socket": "auditor.sock", + "donau-httpd.socket": "donau.sock", + "paivana-httpd.socket": "paivana.sock", + } + + for unit, socket in sockets.items(): + with self.subTest(unit=unit): + self.assertIn( + f"configure_public_socket {unit} /sockets/{socket}", + SETUP_SCRIPT, + ) + + def test_demo_applications_use_unix_sockets(self): + sockets = { + "landing": "/run/taler-merchant-demos/landing.sock", + "blog": "/sockets/blog.sock", + "donations": "/sockets/donations.sock", + } + + for application, socket in sockets.items(): + with self.subTest(application=application): + section = re.search( + rf"\[frontend-demo-{application}\]\n(.*?)(?=\n\[)", + SETUP_SCRIPT, + re.DOTALL, + ) + self.assertIsNotNone(section) + self.assertIn("HTTP_SERVE = unix", section.group(1)) + self.assertIn(f"HTTP_UNIXPATH = {socket}", section.group(1)) + + def test_caddy_only_owns_bridge_or_composed_service_sockets(self): + sockets = re.findall( + r"bind unix//sockets/([^|]+)\|0666", + SETUP_SCRIPT, + ) + + self.assertEqual( + ["bank.sock", "landing.sock", "challenger.sock", "drupal.sock"], + sockets, + ) + + +if __name__ == "__main__": + unittest.main()