Galleon can serve HTTPS itself (TLS), so a reverse proxy in front of it is not required. If a proxy does stand in front of the server, set it up to the requirements below: with default settings part of the repository traffic breaks.

Requirements

Any reverse proxy must:

  • proxy requests to the Galleon port; terminate TLS at the proxy, or pass the traffic on over HTTPS when TLS is configured in Galleon as well;
  • not decode percent-encoded sequences in paths: expanding %2F into / changes the request path, and the artifact is not found under it;
  • pass large request bodies through: keep the size the proxy allows in sync with server.max_upload_size in the Galleon configuration;
  • set read and write timeouts with a margin for slow uploads of large artifacts;
  • forward the Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto headers.

Header trust

By default Galleon does not trust the X-Forwarded-* headers: a direct client can put any address in them, and it would reach the log as client_ip. Trust is enabled by two independent conditions:

server:
  # Condition 1: proxy addresses in CIDR notation.
  trusted_proxies: ["172.18.0.0/16"]
  # Condition 2: a shared secret in a header.
  proxy_auth:
    header: X-Origin-Token
    secret_env: GALLEON_ORIGIN_TOKEN

How it works:

  • only trusted_proxies is set — the address the request came from is checked;
  • only proxy_auth is set — the secret in the header (by default X-Origin-Token) is checked; the address does not matter;
  • both are set — both are required at once;
  • nothing is set — the forwarded headers of every request are ignored.

A request that fails the check is handled as a direct client’s request: its forwarded headers are ignored.

The absolute addresses Galleon reports to clients (the url field in API responses, the package addresses in npm metadata) are built from the Host header and the scheme Galleon itself serves. When TLS is terminated at the proxy, those addresses reach clients with the http scheme. To have the host and the scheme taken from X-Forwarded-Host and X-Forwarded-Proto, turn on server.trust_forwarded_headers. The headers are honoured only for requests that pass the check above, so without the trust conditions this parameter changes nothing.

The server.public_base_url parameter fixes the address and bypasses the headers:

server:
  public_base_url: https://galleon.example.com

It is needed when clients have to be told the same address no matter which name the request arrived at. For example, the instance is also reachable under an internal name: a client that comes in through it receives internal package addresses in the npm metadata, and those end up in package-lock.json.

nginx example

The configuration below terminates TLS, proxies requests to Galleon, and passes the headers the server uses to identify the originating client:

upstream galleon {
  server 127.0.0.1:8080;
  keepalive 32;
}

server {
  listen 443 ssl;
  server_name galleon.example.com;

  # The upload limit is set by the proxy: without this line nginx cuts bodies at 1 MB.
  client_max_body_size 0;

  # Timeouts for large artifacts.
  proxy_read_timeout 1h;
  proxy_send_timeout 1h;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Origin-Token "<GALLEON_ORIGIN_TOKEN>";
    # proxy_pass without a URI part: with one, nginx decodes %2F in paths.
    proxy_pass http://galleon;
  }
}

A proxy_set_header directive is not inherited from the previous configuration level if at least one such directive is declared on the current level. When adding separate locations, repeat the whole header set in each of them, including the secret.

Galleon ignores an inbound X-Request-ID header and always returns a request identifier of its own. To match the proxy’s log records with Galleon’s, log it in nginx through the $upstream_http_x_request_id variable.