paivana

HTTP paywall reverse proxy
Log | Files | Refs | Submodules | README | LICENSE

paivana-httpd.h (10776B)


      1 /*
      2      This file is part of GNUnet.
      3      Copyright (C) 2026 Taler Systems SA
      4 
      5      Paivana is free software; you can redistribute it and/or
      6      modify it under the terms of the GNU Affero General Public License
      7      as published by the Free Software Foundation; either version
      8      3, or (at your option) any later version.
      9 
     10      Paivana is distributed in the hope that it will be useful,
     11      but WITHOUT ANY WARRANTY; without even the implied warranty
     12      of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13      the GNU Affero General Public License for more details.
     14 
     15      You should have received a copy of the GNU Affero General Public
     16      License along with Paivana; see the file COPYING.  If not,
     17      write to the Free Software Foundation, Inc., 51 Franklin
     18      Street, Fifth Floor, Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @author Christian Grothoff
     23  * @file paivana-httpd.h
     24  * @brief globals for the HTTPD reverse proxy
     25  */
     26 #ifndef PAIVANA_HTTPD_H
     27 #define PAIVANA_HTTPD_H
     28 
     29 #include <regex.h>
     30 #include <stdbool.h>
     31 
     32 /**
     33  * The de-facto forwarding headers.  libmicrohttpd only defines the
     34  * standardized #MHD_HTTP_HEADER_FORWARDED (RFC 7239), but these are
     35  * what nginx, Apache and everything else in front of us actually
     36  * emit.  They are consumed on the way in (client address, base URL)
     37  * and produced on the way out (proxying), in different files, so
     38  * they are spelled out once here.
     39  */
     40 #define PH_HEADER_X_FORWARDED_FOR "X-Forwarded-For"
     41 #define PH_HEADER_X_FORWARDED_PROTO "X-Forwarded-Proto"
     42 #define PH_HEADER_X_FORWARDED_HOST "X-Forwarded-Host"
     43 #define PH_HEADER_X_FORWARDED_PORT "X-Forwarded-Port"
     44 
     45 /**
     46  * Longest URL we are willing to run a regular expression over.
     47  *
     48  * Both the WHITELIST expression and the ones from the merchant's
     49  * templates are matched against client-controlled URLs on the
     50  * pre-payment path, and neither is validated beyond regcomp(3)
     51  * succeeding.  A careless expression can exhibit catastrophic
     52  * backtracking, whose cost then grows super-linearly in the length of
     53  * the subject; capping the subject length bounds that cost.  16 kb is
     54  * far above any legitimate URL and at (or above) the request line
     55  * limit of the usual front-end servers.
     56  */
     57 #define PH_MAX_URL_LENGTH (16 * 1024)
     58 
     59 #define PAIVANA_LOG_INFO(...)                                  \
     60         GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__)
     61 #define PAIVANA_LOG_DEBUG(...)                                  \
     62         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
     63 #define PAIVANA_LOG_WARNING(...)                                  \
     64         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__)
     65 #define PAIVANA_LOG_ERROR(...)                                  \
     66         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
     67 
     68 /**
     69  * Destination to which HTTP server we forward requests to.
     70  * Of the format "http://servername:PORT"
     71  */
     72 extern char *PH_target_server_base_url;
     73 
     74 /**
     75  * Replace the connection to target server.
     76  * File path to the unix socket
     77  */
     78 extern char *PH_target_server_unixpath;
     79 
     80 /**
     81  * Merchant backend base URL.
     82  */
     83 extern char *PH_merchant_base_url;
     84 
     85 /**
     86  * Base URL of this site as seen by the client. If not set,
     87  * we will try to determine it from "X-Forwarded-Host" and
     88  * "Host" and "X-Forwarded-Port" headers.
     89  */
     90 extern char *PH_base_url;
     91 
     92 /**
     93  * Curl context for talking to the merchant backend.  Carries the
     94  * `Authorization: Bearer $MERCHANT_ACCESS_TOKEN` header on every
     95  * request, so it must never be used for anything but the backend.
     96  * NULL in `-n` (no payment) mode, where there is no backend.
     97  */
     98 extern struct GNUNET_CURL_Context *PH_merchant_ctx;
     99 
    100 /**
    101  * Curl context for forwarding client requests to the origin server.
    102  * Deliberately separate from #PH_merchant_ctx: headers appended to a
    103  * context apply to every request made through it, and our credentials
    104  * for the merchant backend have no business being sent to the site we
    105  * proxy for.
    106  */
    107 extern struct GNUNET_CURL_Context *PH_proxy_ctx;
    108 
    109 /**
    110  * Pre-compiled regular expression for sites that are whitelisted
    111  * and never paywalled.
    112  */
    113 extern regex_t PH_whitelist_ex;
    114 
    115 /**
    116  * True if whitelist_ex was set.
    117  */
    118 extern bool PH_have_whitelist_ex;
    119 
    120 /**
    121  * Set to true if the cookie applies globally to all sites
    122  * and not per-page.
    123  */
    124 extern int PH_global_cookie;
    125 
    126 /**
    127  * Disable paywall check.
    128  */
    129 extern int PH_no_check;
    130 
    131 /**
    132  * If set, we are behind a reverse proxy: the socket peer is a proxy we
    133  * trust, and the forwarding headers ("Forwarded" and the "X-Forwarded-*"
    134  * family) are read to recover what the client actually did.  Unset, no
    135  * forwarding header is consulted at all and the peer *is* the client.
    136  * Only enable it when the server in front writes those headers itself,
    137  * either setting them or appending to them.  Appending is enough --
    138  * what it appends is the address it accepted the request from, so the
    139  * element we read is still one it vouches for.  What is not safe is a
    140  * front server that passes the client's own headers through unchanged,
    141  * which leaves the client writing the element we believe.
    142  *
    143  * This flag alone extends trust exactly one hop, to the peer, so with
    144  * no #PH_trusted_proxies4 / #PH_trusted_proxies6 the client is the
    145  * RIGHTMOST element of the chain -- the only one the peer vouches for.
    146  * Naming further networks there extends the walk leftwards, one hop per
    147  * trusted node; see PAIVANA_HTTPD_resolve_forwarding().
    148  *
    149  * It also decides whether `BASE_URL` is optional: the flag is the
    150  * assertion that the proxy enforced a correct "Host", which is what
    151  * makes reconstructing our own URL from the request safe.
    152  */
    153 extern int PH_respect_forwarded_headers;
    154 
    155 /**
    156  * Networks whose members are reverse proxies we trust to report the
    157  * client address truthfully, from the `TRUSTED_PROXIES` configuration
    158  * option.  NULL if unconfigured.  These are the hops BEYOND the socket
    159  * peer: #PH_respect_forwarded_headers already trusts the peer, and each
    160  * network named here lets the walk step one element further left, past
    161  * a node it matches.  The first node not covered is the client.
    162  */
    163 extern struct GNUNET_STRINGS_IPv4NetworkPolicy *PH_trusted_proxies4;
    164 
    165 /**
    166  * IPv6 counterpart of #PH_trusted_proxies4, from `TRUSTED_PROXIES6`.
    167  */
    168 extern struct GNUNET_STRINGS_IPv6NetworkPolicy *PH_trusted_proxies6;
    169 
    170 /**
    171  * True if either #PH_trusted_proxies4 or #PH_trusted_proxies6 was
    172  * configured.  Distinguishes "no hop beyond the peer is trusted" from
    173  * "these networks are, in addition to the peer".
    174  */
    175 extern bool PH_have_trusted_proxies;
    176 
    177 /**
    178  * Value to return from main()
    179  */
    180 extern int PH_global_ret;
    181 
    182 /**
    183  * Our configuration.
    184  */
    185 extern const struct GNUNET_CONFIGURATION_Handle *PH_cfg;
    186 
    187 /**
    188  * Total number of concurrent client connections we accept, from the
    189  * `CONNECTION_LIMIT` configuration option.  Divided evenly over the
    190  * listen sockets that come up, because MHD's own limit is per daemon
    191  * and TALER_MHD_listen_bind() starts one daemon per address.
    192  *
    193  * MHD's default (about 1018 per daemon) is wrong here in both
    194  * directions: it is not process-wide, and it does not know that we
    195  * spend file descriptors on outbound libcurl handles from the same
    196  * table -- exhausting them makes *paying* clients' requests fail.
    197  */
    198 extern unsigned int PH_connection_limit;
    199 
    200 /**
    201  * Number of concurrent connections we accept from any single client
    202  * address, from `PER_IP_CONNECTION_LIMIT`; 0 disables the check, which
    203  * is MHD's default.
    204  *
    205  * Must be 0 wherever the peer address is not the client's: under
    206  * `SERVE = unix` or `systemd` every client shares one peer, and behind
    207  * a reverse proxy or a NAT many clients do.  See the README.
    208  */
    209 extern unsigned int PH_per_ip_connection_limit;
    210 
    211 /**
    212  * How many bytes of a request body we hold in memory at once while
    213  * relaying it upstream, from `REQUEST_BUFFER_MAX` or the `-u` /
    214  * `--max-upload` command-line option; 256 KiB by default.
    215  *
    216  * A throughput knob, not a limit: the body is streamed, so this bounds
    217  * only how far the client may run ahead of the origin before we stop
    218  * reading from it.  What an upload is *allowed* to be is
    219  * #PH_max_request_size.  Larger means fewer suspend/resume round trips
    220  * on a fast link and more memory per request in flight; the worst case
    221  * is the product with #PH_connection_limit.
    222  *
    223  * Bounds the *proxied* path only.  Bodies sent to our own endpoints
    224  * never reach this buffer: `POST /.well-known/paivana` is read by
    225  * TALER_MHD_parse_post_json(), whose limit is
    226  * #TALER_MHD_REQUEST_BUFFER_MAX.
    227  */
    228 extern unsigned long long PH_request_buffer_max;
    229 
    230 /**
    231  * How many bytes of a response body we hold in memory at once while
    232  * relaying it to the client, from `RESPONSE_BUFFER_MAX`; 256 KiB by
    233  * default.  The mirror of #PH_request_buffer_max, and equally not a
    234  * limit — there is deliberately no ceiling on the size of a response.
    235  */
    236 extern unsigned long long PH_response_buffer_max;
    237 
    238 /**
    239  * Largest request body we accept, from `MAX_REQUEST_SIZE`; 1 MiB by
    240  * default.  Anything above it is answered 413, on the declared
    241  * `Content-Length` where there is one and otherwise once the body
    242  * actually exceeds it.
    243  *
    244  * Distinct from #PH_request_buffer_max, which used to do both jobs
    245  * because a body that could not be buffered could not be forwarded.
    246  * Streaming separates them: this one is policy, that one is memory.
    247  * For the sake of configurations written when they were one number,
    248  * an explicitly configured `REQUEST_BUFFER_MAX` with no
    249  * `MAX_REQUEST_SIZE` beside it still sets both.
    250  */
    251 extern unsigned long long PH_max_request_size;
    252 
    253 /**
    254  * How long the origin has to produce its response *headers* before we
    255  * give up on it and answer 504, from `UPSTREAM_TIMEOUT`; 60 s by
    256  * default.
    257  *
    258  * Deliberately not a bound on the whole request: a response is
    259  * relayed as it arrives, so a large one legitimately runs for as long
    260  * as it runs.  This clock is cancelled the moment the final header
    261  * section ends, and it is the only one of the three that can still
    262  * produce a status code — after that the status is already on the
    263  * wire.  A stalling origin is caught afterwards by
    264  * #PH_upstream_stall_timeout instead.
    265  */
    266 extern struct GNUNET_TIME_Relative PH_upstream_timeout;
    267 
    268 /**
    269  * How long the origin may make no progress at all — no byte moved in
    270  * either direction — before we give up on the request, from
    271  * `UPSTREAM_STALL_TIMEOUT`; 60 s by default.
    272  *
    273  * Not a bound on how long a request may take: a 500 MiB download is
    274  * expected to run for as long as it runs.  The clock is suspended
    275  * whenever *we* are the reason nothing is moving, i.e. while we hold
    276  * libcurl's receive side paused because the client has not drained
    277  * what we already have.
    278  */
    279 extern struct GNUNET_TIME_Relative PH_upstream_stall_timeout;
    280 
    281 
    282 #endif