nostr-rs-relay/docs/reverse-proxy.md
github-actions[bot] 8bf52646c7
Fork Sync: Update from parent repository (#3)
* improvement: use appropriate paths for systemd example

* improvement: add a configurable postgres write conn string

This adds a new configurable connection string for postgres writes.

* improvement: document pg connection_write config

* build: upgrade checkout action for github ci

* perf: use standard allocator, limit sqlite mmap to 4GB

This is an experimental change to see if we can reduce memory usage
with large SQLite databases.  If successful, we'll do this again and
further reduce the database mmap size.

This will cause greater use of the page cache, but that is more easily
reclaimed by the kernel, and should reduce memory pressure, as well as
making it clearer how much memory the application is actually using
for connections, subscriptions, etc.

* docs: reformatting

* docs: allow host header prefix matching, required for Damus compatibility

* perf: disable sqlite mmap to reduce memory pressure

* perf: switch to jemalloc allocator

* docs: helpful ubuntu packages for building

* perf: reduce SQLite connection count and idle lifetime

On lightly loaded relays, we free up memory faster by letting idle
connections be reclaimed in 10 seconds instead of the default 10
minutes.  This also sets the minimum to zero connections, instead of
always trying to hold one open.

---------

Co-authored-by: Petr Kracik <petrkr@petrkr.net>
Co-authored-by: Kieran <kieran@harkin.me>
Co-authored-by: Greg Heartsfield <scsibug@imap.cc>
2023-05-10 10:30:18 +02:00

7.1 KiB

Reverse Proxy Setup Guide

It is recommended to run nostr-rs-relay behind a reverse proxy such as haproxy, nginx or traefik to provide TLS termination. Simple examples for haproxy, nginx and traefik configurations are documented here.

Minimal HAProxy Configuration

Assumptions:

  • HAProxy version is 2.4.10 or greater (older versions not tested).
  • Hostname for the relay is relay.example.com.
  • Your relay should be available over wss://relay.example.com
  • Your (NIP-11) relay info page should be available on https://relay.example.com
  • SSL certificate is located in /etc/certs/example.com.pem.
  • Relay is running on port 8080.
  • Limit connections to 400 concurrent.
  • HSTS (HTTP Strict Transport Security) is desired.
  • Only TLS 1.2 or greater is allowed.
global
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

frontend fe_prod
    mode    http
    bind    :443 ssl crt /etc/certs/example.com.pem alpn h2,http/1.1
    bind    :80
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    redirect scheme https code 301 if !{ ssl_fc }
    acl host_relay hdr(host) -i -m beg relay.example.com
    use_backend relay if host_relay
    # HSTS (1 year)
    http-response set-header Strict-Transport-Security max-age=31536000

backend relay
    mode http
    timeout connect 5s
    timeout client 50s
    timeout server 50s
    timeout tunnel 1h
    timeout client-fin 30s
    option tcp-check
    default-server maxconn 400 check inter 20s fastinter 1s
    server relay 127.0.0.1:8080

HAProxy Notes

You may experience WebSocket connection problems with Firefox if HTTP/2 is enabled, for older versions of HAProxy (2.3.x). Either disable HTTP/2 (h2), or upgrade HAProxy.

Bare-bones Nginx Configuration

Assumptions:

  • Nginx version is 1.18.0 (other versions not tested).
  • Hostname for the relay is relay.example.com.
  • SSL certificate and key are located at /etc/letsencrypt/live/relay.example.com/.
  • Relay is running on port 8080.
http {
    server {
        listen 443 ssl;
        server_name relay.example.com;
        ssl_certificate /etc/letsencrypt/live/relay.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/relay.example.com/privkey.pem;
        ssl_protocols TLSv1.3 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ecdh_curve secp521r1:secp384r1;
        ssl_ciphers EECDH+AESGCM:EECDH+AES256;

        # Optional Diffie-Helmann parameters
        # Generate with openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
        #ssl_dhparam /etc/ssl/certs/dhparam.pem;

        ssl_session_cache shared:TLS:2m;
        ssl_buffer_size 4k;

        # OCSP stapling
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]; # Cloudflare

        # Set HSTS to 365 days
        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
        keepalive_timeout 70;

        location / {
            proxy_pass http://localhost:8080;
            proxy_http_version 1.1;
            proxy_read_timeout 1d;
            proxy_send_timeout 1d;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
        }
    }
}

Nginx Notes

The above configuration was tested on nginx 1.18.0 on Ubuntu 20.04 and 22.04

For help installing nginx on Ubuntu, see this guide.

For guidance on using letsencrypt to obtain a cert on Ubuntu, including an nginx plugin, see this post.

Example Traefik Configuration

Assumptions:

  • Traefik version is 2.9 (other versions not tested).
  • Traefik is used for provisioning of Let's Encrypt certificates.
  • Traefik is running in Docker, using docker compose and labels for the static configuration. An equivalent setup useing a Traefik config file is possible too (but not covered here).
  • Strict Transport Security is enabled.
  • Hostname for the relay is relay.example.com, email adres for ACME certificates provider is name@example.com.
  • ipv6 is enabled, a viable private ipv6 subnet is specified in the example below.
  • Relay is running on port 8080.
version: '3'

networks:
  nostr:
    enable_ipv6: true
    ipam:
      config:
        - subnet: fd00:db8:a::/64
          gateway: fd00:db8:a::1

services:
  traefik:
    image: traefik:v2.9
    networks:
      nostr:
    command:
      - "--log.level=ERROR"
      # letsencrypt configuration
      - "--certificatesResolvers.http.acme.email==name@example.com"
      - "--certificatesResolvers.http.acme.storage=/certs/acme.json"
      - "--certificatesResolvers.http.acme.httpChallenge.entryPoint=http"
      # define entrypoints
      - "--entryPoints.http.address=:80"
      - "--entryPoints.http.http.redirections.entryPoint.to=https"
      - "--entryPoints.http.http.redirections.entryPoint.scheme=https"
      - "--entryPoints.https.address=:443"
      - "--entryPoints.https.forwardedHeaders.insecure=true"
      - "--entryPoints.https.proxyProtocol.insecure=true"
      # docker provider (get configuration from container labels)
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedByDefault=false"
      - "--providers.file.directory=/config"
      - "--providers.file.watch=true"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "$(pwd)/traefik/certs:/certs"
      - "$(pwd)/traefik/config:/config"
    logging:
      driver: "local"
    restart: always

  # example nostr config. only labels: section is relevant for Traefik config
  nostr:
   image: nostr-rs-relay:latest
   container_name: nostr-relay
   networks:
     nostr:
   restart: always
   user: 100:100
   volumes:
   - '$(pwd)/nostr/data:/usr/src/app/db:Z'
   - '$(pwd)/nostr/config/config.toml:/usr/src/app/config.toml:ro,Z'
   labels:
     - "traefik.enable=true"
     - "traefik.http.routers.nostr.entrypoints=https"
     - "traefik.http.routers.nostr.rule=Host(`relay.example.com`)"
     - "traefik.http.routers.nostr.tls.certresolver=http"
     - "traefik.http.routers.nostr.service=nostr"
     - "traefik.http.services.nostr.loadbalancer.server.port=8080"
     - "traefik.http.services.nostr.loadbalancer.passHostHeader=true"
     - "traefik.http.middlewares.nostr.headers.sslredirect=true"
     - "traefik.http.middlewares.nostr.headers.stsincludesubdomains=true"
     - "traefik.http.middlewares.nostr.headers.stspreload=true"
     - "traefik.http.middlewares.nostr.headers.stsseconds=63072000"
     - "traefik.http.routers.nostr.middlewares=nostr"

Traefik Notes

Traefik will take care of the provisioning and renewal of certificates. In case of an ipv4-only relay, simply detele the enable_ipv6: and ipam: entries in the networks: section of the docker-compose file.