paivana-httpd-manual.rst (21476B)
1 .. 2 This file is part of GNU TALER. 3 4 Copyright (C) 2026 Taler Systems SA 5 6 TALER is free software; you can redistribute it and/or modify it under the 7 terms of the GNU Affero General Public License as published by the Free Software 8 Foundation; either version 2.1, or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 13 14 You should have received a copy of the GNU Affero General Public License along with 15 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 16 17 @author Christian Grothoff 18 19 .. _Paivana-httpd: 20 21 Paivana-httpd 22 ============= 23 24 This chapter documents the installation and operation of the Paivana 25 reverse proxy ``paivana-httpd``. The reverse proxy sits between the 26 public Internet and an upstream Web service, intercepting requests 27 that have not yet been paid for and presenting the client with a 28 GNU Taler paywall. Once a payment has been confirmed by the 29 configured GNU Taler merchant backend, ``paivana-httpd`` forwards 30 subsequent requests of that client to the upstream service. 31 32 The full list of command-line options is documented in 33 :manpage:`paivana-httpd(1)`; the configuration file is 34 documented in :manpage:`paivana.conf(5)`. 35 36 37 Architecture overview 38 --------------------- 39 40 ``paivana-httpd`` does not implement any payment logic of its own. 41 Instead, every Paivana deployment combines three components: 42 43 1. **The upstream web service.** This is the existing HTTP service 44 whose content should be sold (a static website, a cgit service, 45 a REST API, …). It does not need to be modified to 46 work with Paivana. 47 2. **A GNU Taler merchant backend** (``taler-merchant-httpd``). The 48 merchant backend manages templates, creates orders, talks to one 49 or more Taler exchanges, and ultimately reports back whether a 50 given order has been paid. See the 51 :ref:`Taler Merchant Backend Operator Manual 52 <taler-merchant-backend-operator-manual>` for full details. 53 3. **``paivana-httpd`` itself.** This is the reverse proxy that 54 gates the upstream service. It reads a single 55 :ref:`paivana.conf <Paivana-Configuration>` configuration file 56 that points at both the merchant backend and the upstream 57 service. 58 59 Typically a TLS-terminating reverse proxy (Nginx or Apache) is 60 deployed in front of ``paivana-httpd`` to handle HTTPS and to route 61 multiple virtual hosts; see :ref:`Paivana-ReverseProxy` below. 62 63 In normal operation the request flow is: 64 65 :: 66 67 client ──▶ Nginx/Apache (TLS) ──▶ paivana-httpd ──▶ upstream 68 │ 69 ▼ 70 taler-merchant-httpd 71 │ 72 ▼ 73 Taler exchange 74 75 76 Installation 77 ------------ 78 79 Installing from source 80 ^^^^^^^^^^^^^^^^^^^^^^ 81 82 The package sources can be found in our 83 `download directory <http://ftpmirror.gnu.org/taler/>`__. 84 85 GNU Taler components follow the ``MAJOR.MINOR.MICRO`` version 86 scheme. The general rule for compatibility is that ``MAJOR`` and 87 ``MINOR`` must match across components; exceptions are noted in the 88 release notes. For example, ``paivana-httpd`` 1.6.x is expected to 89 work with ``taler-merchant-httpd`` 1.6.x. A ``MAJOR`` version of 0 90 indicates experimental development; in that case you should always 91 run the *latest* releases of every component together. 92 93 The following packages must be installed before compiling 94 ``paivana-httpd``: 95 96 - GNUnet (``libgnunetutil``) matching the Taler release 97 - GNU Taler exchange libraries (``libtalerexchange``, 98 ``libtalerutil``) 99 - GNU Taler merchant client library (``libtalermerchant``) 100 - GNU Taler HTTP daemon helpers (``libtalermhd``, 101 ``libtalertemplating``) 102 - libmicrohttpd, libcurl, libjansson, libgcrypt, zlib 103 104 Build and install with: 105 106 .. code-block:: shell-session 107 108 $ ./bootstrap 109 $ ./configure --prefix=$PREFIX 110 $ make 111 $ sudo make install 112 113 114 Installing the binary packages on Debian 115 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 116 117 .. include:: frags/installing-debian.rst 118 119 To install ``paivana-httpd`` you can now simply run: 120 121 .. code-block:: shell-session 122 123 # apt install paivana-httpd 124 125 The package does not perform any deployment-specific configuration 126 work; it only sets up the ``paivana-httpd`` system user, the systemd 127 service and socket units, and installs example configuration 128 snippets for Nginx and Apache under ``/etc/nginx/sites-available/`` 129 and ``/etc/apache2/sites-available/``. You still must configure the 130 HTTP request routing and the Paivana templates as described below. 131 132 133 Installing the binary packages on Ubuntu 134 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 135 136 .. include:: frags/installing-ubuntu.rst 137 138 To install ``paivana-httpd``, run: 139 140 .. code-block:: shell-session 141 142 # apt install paivana-httpd 143 144 As on Debian, the package does not perform any deployment-specific 145 configuration work. 146 147 148 .. _Paivana-Configuration: 149 150 Configuring paivana-httpd 151 ------------------------- 152 153 The main configuration file is ``/etc/paivana/paivana.conf``. Its 154 syntax follows the standard GNUnet configuration file format and is 155 documented in full in :manpage:`paivana.conf(5)`. Default values 156 shipped with the package live under 157 ``/usr/share/paivana/config.d/``; values in ``paivana.conf`` 158 override those defaults. 159 160 All Paivana-specific keys live in the ``[paivana]`` section. At a 161 minimum, the file must specify three things: 162 163 - where ``paivana-httpd`` should listen for incoming requests 164 (``SERVE``, ``UNIXPATH`` / ``PORT``); 165 - where it should forward paid requests to 166 (``DESTINATION_BASE_URL``); 167 - how it should reach the merchant backend 168 (``MERCHANT_BACKEND_URL`` and ``MERCHANT_ACCESS_TOKEN``). 169 170 A typical configuration that listens on a UNIX domain socket 171 managed by systemd and forwards to a local upstream server looks 172 like this: 173 174 .. code-block:: ini 175 176 [paivana] 177 # Listen on the socket provided by paivana-httpd.socket. 178 SERVE = unix 179 UNIXPATH = /run/paivana/httpd/paivana-http.sock 180 UNIXPATH_MODE = 660 181 182 # Public base URL of this paywall as seen by clients. 183 # Used when the Host/X-Forwarded-Host headers are unavailable. 184 BASE_URL = https://paywall.example.com/ 185 186 # Upstream service that gets proxied after payment. 187 DESTINATION_BASE_URL = http://127.0.0.1:8080/ 188 189 # Merchant backend used to create and verify orders. 190 MERCHANT_BACKEND_URL = http://localhost:9966/ 191 MERCHANT_ACCESS_TOKEN = secret-token:CHANGE-ME 192 193 # Stable secret used to MAC the access cookie. 194 # If unset, a random value is generated at every startup, 195 # invalidating all previously issued cookies. 196 SECRET = please-change-this-to-a-long-random-value 197 198 # Resources that should never trigger the paywall, e.g. 199 # logos, stylesheets or favicons. 200 WHITELIST = ^/(favicon\.ico|assets/.*|robots\.txt)$ 201 202 The exhaustive list of supported keys (``SERVE``, ``PORT``, 203 ``BIND_TO``, ``UNIXPATH``, ``UNIXPATH_MODE``, ``BASE_URL``, 204 ``DESTINATION_BASE_URL``, ``MERCHANT_BACKEND_URL``, 205 ``MERCHANT_BACKEND_UNIX_PATH``, ``MERCHANT_ACCESS_TOKEN``, 206 ``SECRET``, ``WHITELIST``) is documented in 207 :manpage:`paivana.conf(5)`. 208 209 If you reach the merchant backend over a UNIX domain socket on the 210 same host (recommended for a single-machine deployment), replace 211 the ``MERCHANT_BACKEND_URL`` block with: 212 213 .. code-block:: ini 214 215 MERCHANT_BACKEND_URL = http://localhost/ 216 MERCHANT_BACKEND_UNIX_PATH = /run/taler-merchant/merchant.sock 217 218 .. note:: 219 220 ``MERCHANT_ACCESS_TOKEN`` and ``SECRET`` are sensitive values. 221 Make sure ``paivana.conf`` is only readable by the 222 ``paivana-httpd`` user. The Debian package installs the file 223 accordingly. 224 225 When ``paivana-httpd`` runs behind a trusted reverse proxy 226 (Nginx/Apache), pass ``-f`` / ``--respect-forwarded-headers`` in the 227 systemd unit's ``ExecStart=`` so the real client address is taken 228 from ``X-Forwarded-For``. See :manpage:`paivana-httpd(1)` for the 229 remaining command-line flags (in particular ``-g`` to require only 230 a single payment per site and ``-n`` to disable the paywall for 231 debugging). 232 233 234 Starting and stopping the service 235 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 236 237 The Debian/Ubuntu package ships a socket-activated systemd unit. 238 After editing ``/etc/paivana/paivana.conf`` enable and start it: 239 240 .. code-block:: shell-session 241 242 # systemctl enable --now paivana-httpd.socket 243 # systemctl status paivana-httpd 244 245 The socket listens on ``/run/paivana/httpd/paivana-http.sock`` with 246 group ``www-data``, which lets a co-located Nginx or Apache talk to 247 the daemon without granting it broader filesystem access. Logs are 248 sent to the journal: 249 250 .. code-block:: shell-session 251 252 # journalctl -u paivana-httpd -f 253 254 255 .. _Paivana-Templates: 256 257 Configuring Paivana templates 258 ----------------------------- 259 260 ``paivana-httpd`` does not store any per-site pricing or URL-matching rules 261 itself. Instead, all rules are expressed as :ref:`merchant templates 262 <template>` of type ``paivana`` in the merchant backend. When 263 ``paivana-httpd`` starts up it asks the merchant backend for every template 264 configured for the instance identified by ``MERCHANT_BACKEND_URL`` and uses 265 the ``website_regex`` field of each template to decide which template (and 266 therefore which payment options) applies to an incoming request URL. 267 268 The corresponding REST API is documented in detail in the 269 :ref:`Merchant Backend HTTP API <merchant-api>`; see in particular 270 the 271 `POST /private/templates 272 <https://docs.taler.net/core/api-merchant.html#post--private-templates>`__ 273 endpoint and the 274 :ts:type:`TemplateContractPaivana` definition. 275 276 Prerequisites 277 ^^^^^^^^^^^^^ 278 279 Before creating a template you need: 280 281 - a running ``taler-merchant-httpd`` (see the 282 :ref:`Launching-the-backend` section of the merchant manual); 283 - a merchant :ref:`instance <Instance-setup>` with at least one 284 configured :ref:`bank account <instance-bank-account>`; 285 - the access token of that instance (used as 286 ``MERCHANT_ACCESS_TOKEN`` in ``paivana.conf``). 287 288 In the examples below we assume the merchant backend is reachable 289 at ``http://localhost:9966/``, the default instance is ``default``, 290 its access token is ``secret-token:sandbox`` and the currency is 291 ``KUDOS``. Adjust the URLs, tokens and amounts to match your 292 deployment. The 293 `src/backend/test.sh 294 <https://git.taler.net/paivana.git/tree/src/backend/test.sh>`__ 295 script that ships with Paivana sets up exactly this minimal 296 configuration and is a good starting point for experimentation. 297 298 Creating a single global template 299 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 300 301 The simplest Paivana setup uses one template that matches every 302 URL on the site and charges a fixed price. This is the 303 configuration created by ``src/backend/test.sh``: 304 305 .. code-block:: bash 306 307 $ curl -X POST http://localhost:9966/private/templates \ 308 -H 'Authorization: Bearer secret-token:sandbox' \ 309 -H 'Content-Type: application/json' \ 310 -d '{ 311 "template_id": "paivana", 312 "template_description": "A Paivana template", 313 "template_contract": { 314 "template_type": "paivana", 315 "summary": "Access to example.com", 316 "website_regex": ".*", 317 "choices": [ { "amount": "KUDOS:1" } ] 318 } 319 }' 320 321 The ``template_type`` must be ``"paivana"``: this allows 322 ``paivana-httpd`` to pick the template up at startup and 323 also enables some required logic in the merchant backend. The 324 ``website_regex`` is a POSIX extended regular expression that is 325 matched against the request URL; ``.*`` covers everything. Each 326 entry in ``choices`` describes one way the client may pay and is an 327 :ts:type:`OrderChoice` object (so the paywall can also support 328 the use of subscription tokens, discount coupons, etc.). 329 330 A successful create returns HTTP ``204 No Content``. After 331 creating the template, (re)start ``paivana-httpd`` so that it 332 re-reads the template list: 333 334 .. code-block:: shell-session 335 336 # systemctl restart paivana-httpd 337 338 Multiple templates with URL-specific pricing 339 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 340 341 When a single site contains content with different prices, define one template 342 per price bucket and use ``website_regex`` to scope each template to the 343 matching URLs. When several templates match the same URL ``paivana-httpd`` 344 picks the first one if finds that matches. Be careful: if multiple templates 345 match a URL, the result is non-deterministic! 346 347 For example, a news site might charge 2 KUDOS for premium articles 348 and 50 cents (``KUDOS:0.5``) for standard articles: 349 350 .. code-block:: bash 351 352 $ curl -X POST http://localhost:9966/private/templates \ 353 -H 'Authorization: Bearer secret-token:sandbox' \ 354 -H 'Content-Type: application/json' \ 355 -d '{ 356 "template_id": "premium", 357 "template_description": "Premium long-form articles", 358 "template_contract": { 359 "template_type": "paivana", 360 "summary": "Premium article on example.com", 361 "website_regex": "^/premium/.*", 362 "choices": [ { "amount": "KUDOS:2" } ] 363 } 364 }' 365 366 $ curl -X POST http://localhost:9966/private/templates \ 367 -H 'Authorization: Bearer secret-token:sandbox' \ 368 -H 'Content-Type: application/json' \ 369 -d '{ 370 "template_id": "default", 371 "template_description": "Standard articles", 372 "template_contract": { 373 "template_type": "paivana", 374 "summary": "Standard article on example.com", 375 "website_regex": "^/standard/.*", 376 "choices": [ { "amount": "KUDOS:0.5" } ] 377 } 378 }' 379 380 Offering multiple payment options 381 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 382 383 The ``choices`` array lets a single template offer several mutually exclusive 384 ways to pay. A common pattern is to accept either a cash payment or to sell a 385 subscription; the wallet shows both options and the customer picks one. The 386 third option, where the customer already has a subscription, will be used 387 automatically by the wallet for subscribers and the customer will not even 388 have to click to bypass the paywall as a subscriber. See the merchant manual 389 for the details of :ref:`OrderChoice <template-choice>` objects. 390 391 .. code-block:: bash 392 393 $ curl -X POST http://localhost:9966/private/templates \ 394 -H 'Authorization: Bearer secret-token:sandbox' \ 395 -H 'Content-Type: application/json' \ 396 -d '{ 397 "template_id": "article", 398 "template_description": "Single article, paid or via subscription", 399 "template_contract": { 400 "template_type": "paivana", 401 "summary": "Article on example.com", 402 "website_regex": ".*", 403 "choices": [ 404 { "amount": "KUDOS:1", 405 "description": "Pay per article" }, 406 { "amount": "KUDOS:100", 407 "description": "Buy subscription", 408 "outputs": [ { "token": "monthly-subscription" } ] }, 409 { "amount": "KUDOS:0", 410 "description": "Use my subscription", 411 "inputs": [ { "token": "monthly-subscription" } ], 412 "outputs": [ { "token": "monthly-subscription" } ] } 413 ] 414 } 415 }' 416 417 Managing templates 418 ^^^^^^^^^^^^^^^^^^ 419 420 Templates can be listed, updated and deleted through the merchant 421 backend's REST API or through the merchant backend SPA at 422 ``$MERCHANT_BACKEND_URL/``. See the merchant manual section on 423 :ref:`templates <template>` for details, and the API reference for 424 the relevant endpoints: 425 426 - `GET /private/templates 427 <https://docs.taler.net/core/api-merchant.html#get--private-templates>`__ — 428 list all templates of the instance; 429 - `PATCH /private/templates/$TEMPLATE_ID 430 <https://docs.taler.net/core/api-merchant.html#patch--private-templates-$TEMPLATE_ID>`__ — 431 update a template; 432 - `DELETE /private/templates/$TEMPLATE_ID 433 <https://docs.taler.net/core/api-merchant.html#delete--private-templates-$TEMPLATE_ID>`__ — 434 remove a template. 435 436 After any change, restart ``paivana-httpd`` so the new template 437 list takes effect. 438 439 440 .. _Paivana-ReverseProxy: 441 442 Reverse proxy configuration 443 --------------------------- 444 445 ``paivana-httpd`` itself speaks plain HTTP on a UNIX socket (or a 446 local TCP port). In production it is often run behind an Internet-facing 447 reverse proxy that terminates TLS and forwards requests to the 448 Paivana socket. This section gives minimal working examples for 449 both Nginx and Apache. The same approach is used for the merchant 450 backend; see the merchant manual's 451 :ref:`reverse-proxy-configuration` section for additional 452 discussion. 453 454 The examples assume the public domain is ``example.com``, 455 that ``paivana-httpd`` is socket-activated by the shipped 456 ``paivana-httpd.socket`` unit (so its listening socket lives at 457 ``/run/paivana/httpd/paivana-http.sock``) and that TLS termination 458 happens at the reverse proxy. 459 460 .. tab-set:: 461 462 .. tab-item:: Nginx 463 464 Place the snippet below in 465 ``/etc/nginx/sites-available/example.com`` (the 466 Debian package installs a starter template under 467 ``/etc/nginx/sites-available/paivana``), then enable it via 468 ``ln -s ../sites-available/example.com 469 /etc/nginx/sites-enabled/`` and reload Nginx 470 (``systemctl reload nginx``). 471 472 .. code-block:: nginx 473 474 server { 475 listen 443 ssl http2; 476 listen [::]:443 ssl http2; 477 server_name example.com; 478 479 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 480 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; 481 482 location / { 483 proxy_pass http://unix:/run/paivana/httpd/paivana-http.sock; 484 proxy_redirect off; 485 proxy_set_header Host $host; 486 proxy_set_header X-Forwarded-For $remote_addr; 487 proxy_set_header X-Forwarded-Host $host; 488 proxy_set_header X-Forwarded-Proto https; 489 } 490 } 491 492 server { 493 listen 80; 494 listen [::]:80; 495 server_name example.com; 496 return 301 https://$host$request_uri; 497 } 498 499 Make sure ``paivana-httpd`` is started with 500 ``--respect-forwarded-headers`` (see 501 :manpage:`paivana-httpd(1)`) so the ``X-Forwarded-For`` 502 header set above is honoured. 503 504 .. tab-item:: Apache 505 506 Enable the required modules once: 507 508 .. code-block:: shell-session 509 510 # a2enmod proxy proxy_http headers ssl 511 # systemctl reload apache2 512 513 Then drop the following into 514 ``/etc/apache2/sites-available/example.com.conf`` 515 (the Debian package installs a starter template at 516 ``/etc/apache2/sites-available/paivana.conf``), enable it 517 with ``a2ensite example.com`` and reload Apache. 518 519 .. code-block:: apacheconf 520 521 <VirtualHost *:80> 522 ServerName example.com 523 Redirect permanent / https://example.com/ 524 </VirtualHost> 525 526 <VirtualHost *:443> 527 ServerName example.com 528 529 SSLEngine on 530 SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem 531 SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem 532 533 <Location "/"> 534 ProxyPass "unix:/run/paivana/httpd/paivana-http.sock|http://example.com/" 535 ProxyPassReverse "unix:/run/paivana/httpd/paivana-http.sock|http://example.com/" 536 RequestHeader set X-Forwarded-Proto "https" 537 RequestHeader set X-Forwarded-Host "example.com" 538 </Location> 539 </VirtualHost> 540 541 As with Nginx, run ``paivana-httpd`` with 542 ``--respect-forwarded-headers`` so that the client IP is 543 taken from ``X-Forwarded-For``. 544 545 If you operate both Paivana and the merchant backend on the same 546 host, you typically expose them under two different hostnames (e.g. 547 ``example.com`` and ``backend.example.com``); the merchant 548 backend must *never* be proxied through ``paivana-httpd``, only 549 the upstream content service should be. 550 551 552 Verifying the setup 553 ------------------- 554 555 After completing the steps above, a quick smoke test is to request 556 a paywalled URL with ``curl``: 557 558 .. code-block:: shell-session 559 560 $ curl -i https://example.com/some-article 561 562 An unpaid request should return ``HTTP/1.1 402 Payment Required`` together 563 with a Taler-formatted paywall body containing the ``taler://pay/...`` URI of 564 the freshly created order. Paying that order with any GNU Taler wallet (see 565 the `Wallet documentation <https://docs.taler.net/wallet/>`__) and 566 re-requesting the URL from the same client should then yield the upstream 567 content unchanged. If the page is run in a browser, the client-side 568 JavaScript should automatically trigger the required reload of the page after 569 the wallet made the payment. 570 571 For interactive debugging, ``paivana-httpd -n`` disables the 572 paywall and turns the daemon into a transparent reverse proxy; 573 this is useful to confirm that the network plumbing to the 574 upstream service works before involving the merchant backend. 575 See :manpage:`paivana-httpd(1)` for the other runtime flags.