Galleon может отдавать HTTPS сам (TLS), поэтому обратный прокси перед ним не обязателен. Если прокси всё же стоит перед сервером, настройте его по требованиям ниже: с настройками по умолчанию часть запросов к репозиториям ломается.

Требования

Любой обратный прокси должен:

  • проксировать запросы на порт Galleon; терминировать TLS на прокси или передавать трафик дальше по HTTPS, если TLS настроен и в Galleon;
  • не декодировать процентные последовательности в путях: раскрытие %2F в / меняет путь запроса, и артефакт по нему не находится;
  • пропускать большие тела запросов: разрешённый прокси размер согласуйте с server.max_upload_size в конфигурации Galleon;
  • задать тайм-ауты чтения и записи с запасом под медленные загрузки больших артефактов;
  • передавать заголовки Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto.

Доверие заголовкам

По умолчанию Galleon не доверяет заголовкам X-Forwarded-*: прямой клиент может указать в них произвольный адрес, и тот попадёт в лог как client_ip. Доверие включают два независимых условия:

server:
  # Условие 1: адреса прокси в нотации CIDR.
  trusted_proxies: ["172.18.0.0/16"]
  # Условие 2: общий секрет в заголовке.
  proxy_auth:
    header: X-Origin-Token
    secret_env: GALLEON_ORIGIN_TOKEN

Как это работает:

  • задан только trusted_proxies — проверяется адрес, с которого пришёл запрос;
  • задан только proxy_auth — проверяется секрет в заголовке (по умолчанию X-Origin-Token), адрес не важен;
  • заданы оба — требуются оба одновременно;
  • не задано ничего — forwarded-заголовки игнорируются у всех запросов.

Запрос, не прошедший проверку, обрабатывается как запрос прямого клиента: forwarded-заголовки в нём игнорируются.

Абсолютные адреса, которые Galleon сообщает клиентам (поле url в ответах API, адреса пакетов в метаданных npm), собираются из заголовка Host и схемы, по которой работает сам Galleon. Когда TLS терминирует прокси, такие адреса уходят клиентам со схемой http. Чтобы хост и схему брали из X-Forwarded-Host и X-Forwarded-Proto, включите server.trust_forwarded_headers. Заголовки учитываются только у запросов, прошедших проверку выше, поэтому без условий доверия этот параметр ничего не меняет.

Параметр server.public_base_url задаёт адрес жёстко, в обход заголовков:

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

Он нужен, когда клиенты должны получать один и тот же адрес независимо от того, по какому имени пришёл запрос. Например, инстанс доступен ещё и по внутреннему имени: клиент, пришедший по нему, получит в метаданных npm внутренние адреса пакетов, и они попадут в package-lock.json.

Пример nginx

Конфигурация ниже терминирует TLS, проксирует запросы на Galleon и передаёт заголовки, по которым сервер определяет исходного клиента:

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

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

  # Лимит загрузок задаёт прокси: без этой строки nginx режет тела на 1 МБ.
  client_max_body_size 0;

  # Тайм-ауты под большие артефакты.
  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 без URI-части: с ней nginx декодирует %2F в путях.
    proxy_pass http://galleon;
  }
}

Директива proxy_set_header не наследуется с предыдущего уровня конфигурации, если на текущем объявлена хотя бы одна такая директива. При добавлении отдельных location повторяйте в каждом весь набор заголовков, включая секрет.

Входящий заголовок X-Request-ID Galleon игнорирует и всегда возвращает собственный идентификатор запроса. Чтобы сопоставлять записи прокси с записями Galleon, логируйте его в nginx через переменную $upstream_http_x_request_id.