Reproxy
Reproxy это простой edge HTTP(s) сервер / reverse proxy поддерживающий множество провайдеров (docker, static, file, consul catalog). Один или более провайдеров предоставляют информацию о запрашиваемых серверах, запрашиваемых URL, конечных URL и health check URL. Он распрастраняется в виде одиночного бинарного файла или как docker image
- Automatic SSL termination with Let’s Encrypt
- Support of user-provided SSL certificates
- Simple but flexible proxy rules
- Static, command-line proxy rules provider
- Dynamic, file-based proxy rules provider
- Docker provider with an automatic discovery
- Consul Catalog provider with discovery by service tags
- Support of multiple (virtual) hosts
- Optional traffic compression
- User-defined size limits and timeouts
- Single binary distribution
- Docker container distribution
- Built-in static assets server with optional “SPA friendly” mode
- Support for redirect rules
- Optional limiter for the overall activity as well as for user’s activity
- Live health check and fail-over/load-balancing
- Management server with routes info and prometheus metrics
- Plugins support via RPC to implement custom functionality
- Optional logging with both Apache Log Format, and simplified stdout reports.
Сервер (host) может быть указан как FQDN (s.example.com
, *
(catch all)) или как регулярное выражение. Приоритетнее будет полное совпадение, так, если у нас будет два правила с серверами example.com
и example\.(com|org)
, то запрос example.com/some/url
уйдет на первый сервер. Запрашиваемый URL тоже может быть указан как регулярное выражение, например ^api/(.*)
, а конечный URL может содержать группы из регулярного выражения запрашиваемого URL, например http://d.example.com:8080/$1
. В итоге http://s.example.com/api/something?foo=bar
будет отпроксирован в http://d.example.com:8080/something?foo=bar
Для удобства, запросы с /
на конце и без regex группы автоматически расширяются до /(.*)
, а конечные URL в таком случае расширяются до /$1
. То есть /api/
-> http://localhost/service
будет переведен в ^/api/(.*)
-> http://localhost/service/$1
Поддерживается как HTTP так и HTTPS. Для HTTPS могут быть использованы как статичные (пользовательские) сертификаты, так и автоматически выпущенные через ACME (Let's Encrypt). Опциональный сервер ассетов может быть использован для раздачи статических файлов. Чтобы запустить Reproxy нужен как минимум один провайдер. Остальные параметры опциональны и имеют разумные дефолтные значения
Примеры:
- with a static provider:
reproxy --static.enabled --static.rule="example.com/api/(.*),https://api.example.com/$1"
- with an automatic docker discovery:
reproxy --docker.enabled --docker.auto
- as a docker container:
docker up -p 80:8080 umputun/reproxy --docker.enabled --docker.auto
- with automatic SSL:
docker up -p 80:8080 -p 443:8443 umputun/reproxy --docker.enabled --docker.auto --ssl.type=auto --ssl.fqdn=example.com
Install
Reproxy распространяется как маленький сдержанный бинарный файл, а также как docker image. И бинарь и докер имадж поддерживают множество архитектур и множество ОС, также есть deb/rpm пакеты
- бинарь может быть скачан с https://github.com/umputun/reproxy/releases
- имадж доступен на https://hub.docker.com/r/umputun/reproxy и на https://github.com/users/umputun/packages/container/reproxy/versions
Providers
Правила проксирования предоставляются провайдерами. Сейчас в приложение включены - file
, docker
, static
и consul-catalog
. Каждый провайдер может предоставлять множество правил роутинга и для проксируемых запросов и для раздачи статики (assets). Пользователь может указывать множество провайдеров одновременно
Static provider
Это простейший провайдер который указывает правила прямо в командной строке (или через переменные окружения). Поддерживается возможность указания множества правил. Каждое правило это 3 или 4 разделенных запятой элемента server,sourceurl,destination[,ping-url]
. Например:
*,^/api/(.*),https://api.example.com/$1
- proxy all request to any host/server with/api
prefix tohttps://api.example.com
example.com,/foo/bar,https://api.example.com/zzz,https://api.example.com/ping
- proxy all requests toexample.com
and with/foo/bar
url tohttps://api.example.com/zzz
and it seeshttps://api.example.com/ping
for the health check
Последний (четвертый) элемент определяет опциональный ping url
File provider
Этот провайдер использует yaml файл с правилами роутинга
reproxy --file.enabled --file.name=config.yml
Пример конфига:
default: # the same as * (catch-all) server
- { route: "^/api/svc1/(.*)", dest: "http://127.0.0.1:8080/blah1/$1" }
- {
route: "/api/svc3/xyz",
dest: "http://127.0.0.3:8080/blah3/xyz",
ping: "http://127.0.0.3:8080/ping",
}
srv.example.com:
- { route: "^/api/svc2/(.*)", dest: "http://127.0.0.2:8080/blah2/$1/abc" }
- { route: "^/web/", dest: "/var/www", "assets": true }
Изменения в файле подтягиваются автоматически
Docker provider
https://reproxy.io/#docker-provider
Consul Catalog provider
https://reproxy.io/#consul-catalog-provider
Compose-specific details
https://reproxy.io/#compose-specific-details
SSL support
Режим работы по SSL может быть выставлен в auto
(ACME/LE сертификаты), static
(существующие сертификаты) или none
(по умолчанию none
). Если стоит auto
, то сертификаты будут выпущены автоматически для всех найденных server names. Пользователь может изменить это поведение явно указав нужные домены в --ssl.fqdn
Headers
Reproxy позволяет удалять входящие заголовки с помощью опции --drop-header
которая может быть указана множество раз. Этот параметр может быть полезен чтобы быть уверенным что некоторые заголовки установлены внутренними сервисами, а не подсунуты пользователем. Например, если какой-то из сервисов ответственный за аутентификацию подставляет заголовки X-Auth-User
и X-Auth-Token
, то может быть полезно удалять такие заголовки у входящих от пользователей запросов, это можно делать так --drop-header=X-Auth-User --drop-header=X-Auth-Token
или через переменную окружения DROP_HEADERS=X-Auth-User,X-Auth-Token
Также доступна противополжная функция подстановки заголовков в исходящие ответы. Это можно делать с помощью параметра --header
который можно указывать много раз или через переменную HEADER
Пример того как это выглядит в docker-compose:
environment:
- HEADER=
X-Frame-Options:SAMEORIGIN,
X-XSS-Protection:1; mode=block;,
Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline';
Logging
По умолчанию запросы не логируются, но это поведение можно изменить с помощью опции --logger.enabled
. Лог ротируется автоматически и имеет Apache Combined Log Format
С помощью опции --logger.stdout
можно сказать Reproxy выводить краткий лог зарпосов в stdout (это не влияет на логирование в файл, а просто добавляет краткий лог в stdout)
Assets Server
Пользователь может включить сервер ассетов для обслуживания запросов к статике (по умолчанию он выключен). Пока установлен параметр --assets.location
он будет пытаться обработать запросы по пути assets.root
как запросы к статике (assets.root - часть урла, assets.location - путь до файлов). Сервер ассетов может быть использован без каких-либо провайдеров, в этом режиме Reproxy работает как простой web-server для раздачи статического контента. Также есть режим SPA который можно включить параметром --assets.spa
, в таком случае каждый запрос к ненайденному файлу будет перенаправляться на index.html
В дополнение к основному серверу статики поддерживается множество кастомных. Каждый провайдер имеет собственный вариант указания его, а некоторые провайдеры его и вовсе не имеют
- static provider - if source element prefixed by
assets:
orspa:
it will be treated as file-server. For example*,assets:/web,/var/www,
will serve all/web/*
request with a file server on top of/var/www directory
- file provider - setting optional fields
assets: true
orspa: true
- docker provider -
reproxy.assets=web-root:location
, i.e.reproxy.assets=/web:/var/www
. Switching to spa mode done by settingreproxy.spa
toyes
ortrue
Caching
Сервер ассетов имеет контроль кэширования, который задается через опцию --assets.cache=<duration>
. 0s
в этой опции отключит кэширование. <duration>
это последовательность десятичных чисел, которые могут дробными и имеют суффикс обозначающий величину, например “300ms”, “1.5h” или “2h45m”. Валидные суффиксы: “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h” и “d”
Есть два варианта указания длительности кэширования:
- A single value for all static assets. This is as simple as
--assets.cache=48h
- Custom duration for different mime types. It should include two parts - the default value and the pairs of mime:duration. In command line this looks like multiple
--assets.cache
options, i.e.--assets.cache=48h --assets.cache=text/html:24h --assets.cache=image/png:2h
. Environment values should be comma-separated, i.e.ASSETS_CACHE=48h,text/html:24h,image/png:2h
Кастомная страница для ответа 404 может быть задана через опцию --assets.404=<path>
. Этот путь указывается относительно пути assets root
Using reproxy as a base image
https://reproxy.io/#using-reproxy-as-a-base-image
SPA-friendly mode
Некоторые SPA приложения полагаются на прокси для обработки 404 особым способом - перенаправляя на /index.html
. Это похоже на nginx'овую директиву try_files $uri $uri/ ...
и предположительно эта функция что-то важное для современных web приложений
По умолчанию в Reproxy эта функция выключена, но может быть включена с помощью --assets.spa
или ASSETS_SPA=true
Redirects
По умолчанию Reproxy пробует destination как proxy location, то есть делает внутренний http запрос и возвращает ответ клиенту
Однако это поведение можно изменить префиксируя destination url с помощью @code
. Таким образом если указать @301 https://example.com/something
, то получим редирект на Location: https://example.com/something
Поддерживаемые коды:
@301
,@perm
- permanent redirect@302
,@temp
,@tmp
- temporary redirect
More options
--gzip
enables gzip compression for responses.--max=N
allows to set the maximum size of request (default 64k). Setting it to0
disables the size check.--timeout.*
various timeouts for both server and proxy transport. Seetimeout
section in All Application Options. A zero or negative value means there will be no timeout.
Default ports
Чтобы избежать необходимости прописывать кастомные параметры/окружения, дефолтный --listen
- динамичный, он пытается быть разумным и полезным для типичных кейсов:
- If anything set by users to
--listen
all the logic below ignored and host:port passed in and used directly. - If nothing set by users to
--listen
and reproxy runs outside of the docker container, the default is127.0.0.1:80
for http mode (ssl.type=none
) and127.0.0.1:443
for ssl mode (ssl.type=auto
orssl.type=static
). - If nothing set by users to
--listen
and reproxy runs inside the docker, the default is0.0.0.0:8080
for http mode, and0.0.0.0:8443
for ssl mode.
Another default set in the similar dynamic way is --ssl.http-port
. For run inside of the docker container it set to 8080
and without to 80
.
Ping, health checks and fail-over
Reproxy предоставляет два эндпоинта для этих целей:
/ping
responds with pong and indicates what reproxy up and running/health
returns200 OK
status if all destination servers responded to their ping request with200
or417 Expectation Failed
if any of servers responded with non-200 code. It also returns json body with details about passed/failed services.
В дополнение к эндпоинтам выше, Reproxy поддерживает проверки в реальном времени. В таком случае (если они включены) каждый конечный url периодически проверяется на ping ответ и в случае ошибки исключается из роутинга. Это дает возможность возвращать множество идентичных конечных url из одного или множества провайдеров, и будут выбраны только прошедшие проверки. Если множество подходящих было обнаружено и проверки были пройдены - только один будет использоваться в соответствии со стратегией lb-type
(по умолчанию случайный выбор)
Чтобы включить проверки в реальном времени, пользователю нужно указать --health-check.enabled
(или через переменную HEALTH_CHECK_ENABLED=true
). Для кастомизации интервала - --health-check.interval=
Management API
Опционально с помощью флага --mgmt.enabled
можно включить два эндпоинта которые будут доступны на mgmt.listen
:
GET /routes
- list of all discovered routesGET /metrics
- returns prometheus metrics (http_requests_total
,response_status
andhttp_response_time_seconds
)
Errors reporting
Reproxy вернет 502 (Bad Gateway) в случае если запрос не матчится ни на один имеющийся роут или ассет. В случае неожиданных внутренних ошибок он вернет 500. По умолчанию он рендерит простейшую текстовую версию страницы ошибки - "Server error". Настройка --error.enabled
включит дефолтную html страницу, а с помощью --error.template
пользователь может установить любой кастомный html шаблон для ошибок. В таком темплейте будут доступны две переменные {{.ErrCode}}
и {{.ErrMessage}}
Например шаблон oh my! {{.ErrCode}} - {{.ErrMessage}}
будет отрендерен в oh my! 502 - Bad Gateway
Throttling
Reproxy позволяет задать ограничение в max req/sec как для всей системы, так и для пользователей. Значение 0
воспринимается как - неограничено
Ограничение на пользовательскую активность (запросы) распространяется как на сматченные, так и несматченные роуты. Все несматченные роуты рассматриваются как "single destination group" и ограничиваются в rate*3
. Это значит что если указано --throttle.user=10
то конечный пользователь сможет делать вплоть до 30 запросов в секунду на какие-то статические ассеты или несматченные роуты. Для сматченных роутов это ограничение выставляется per destination, таким образом запросы проксируемые на s1.example.com/api будут ограничены 10r/s, а запросы проксируемые на s2.example.com будут ограничены другими 10r/s
Basic auth
Reproxy поддерживает Basic Auth для всех запросов. Это полезно для защиты эндпоинтов которые находятся в разработке или тестировании до запуска их в публичный доступ. Эта функция выключена по умолчанию и не достаточно гранулярна для настройки ее на каждый роут отдельно
Таким образом включенный Basic Auth будет влиять на все запросы
Чтобы включить Basic Auth для всех запросов пользователю нужно указать типичный htpasswd файл в опции --basic-htpasswd=<file location>
или с помощью переменной BASIC_HTPASSWD=<file location>
Reproxy ожидает htpasswd файл в следующем формате:
username1:bcrypt(password2)
username2:bcrypt(password2)
...
Его можно сгенерировать с помощью htpasswd -nbB
Plugins support
https://reproxy.io/#plugins-support
Container security
https://reproxy.io/#container-security
Options
Каждая опция может быть указана двумя способами: опция командной строки и переменная окружения. Некоторые cli опции имеют короткую форму, как -l localhost:8080
и --listen localhost:8080
. Имя соответствующей переменной окружения указано в конце описания каждой опции
Все опции описывающие размеры поддерживают суффикс величины 10K (or 10k) for kilobytes, 16M (or 16m) for megabytes, 10G (or 10g) for gigabytes. Отсутствие суффикса воспринимается как байты
Некоторые опции могут быть указаны несколько раз, в таком случае пользователь может несколько раз указать cli опцию, либо через запятую перечислить значения в переменной окружения. Например опция --ssl.fqdn
одна из таких и может быть указана как --ssl.fqdn=a1.example.com --ssl.fqdn=a2.example.com
или как переменная SSL_ACME_FQDN=a1.example.com,a2.example.com
Ниже список всех таких опций:
ssl.fqdn (SSL_ACME_FQDN)
assets.cache (ASSETS_CACHE)
docker.exclude (DOCKER_EXCLUDE)
static.rule ($STATIC_RULES)
header ($HEADER)
drop-header ($DROP_HEADERS)
All Application Options
-l, --listen= listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without) [$LISTEN]
-m, --max= max request size (default: 64K) [$MAX_SIZE]
-g, --gzip enable gz compression [$GZIP]
-x, --header= outgoing proxy headers to add [$HEADER]
--drop-header= incoming headers to drop [$DROP_HEADERS]
--basic-htpasswd= htpasswd file for basic auth [$BASIC_HTPASSWD]
--lb-type=[random|failover] load balancer type (default: random) [$LB_TYPE]
--signature enable reproxy signature headers [$SIGNATURE]
--dbg debug mode [$DEBUG]
ssl:
--ssl.type=[none|static|auto] ssl (auto) support (default: none) [$SSL_TYPE]
--ssl.cert= path to cert.pem file [$SSL_CERT]
--ssl.key= path to key.pem file [$SSL_KEY]
--ssl.acme-location= dir where certificates will be stored by autocert manager (default: ./var/acme) [$SSL_ACME_LOCATION]
--ssl.acme-email= admin email for certificate notifications [$SSL_ACME_EMAIL]
--ssl.http-port= http port for redirect to https and acme challenge test (default: 8080 under docker, 80 without) [$SSL_HTTP_PORT]
--ssl.fqdn= FQDN(s) for ACME certificates [$SSL_ACME_FQDN]
assets:
-a, --assets.location= assets location [$ASSETS_LOCATION]
--assets.root= assets web root (default: /) [$ASSETS_ROOT]
--assets.spa spa treatment for assets [$ASSETS_SPA]
--assets.cache= cache duration for assets [$ASSETS_CACHE]
--assets.not-found= path to file to serve on 404, relative to location [$ASSETS_NOT_FOUND]
logger:
--logger.stdout enable stdout logging [$LOGGER_STDOUT]
--logger.enabled enable access and error rotated logs [$LOGGER_ENABLED]
--logger.file= location of access log (default: access.log) [$LOGGER_FILE]
--logger.max-size= maximum size before it gets rotated (default: 100M) [$LOGGER_MAX_SIZE]
--logger.max-backups= maximum number of old log files to retain (default: 10) [$LOGGER_MAX_BACKUPS]
docker:
--docker.enabled enable docker provider [$DOCKER_ENABLED]
--docker.host= docker host (default: unix:///var/run/docker.sock) [$DOCKER_HOST]
--docker.network= docker network [$DOCKER_NETWORK]
--docker.exclude= excluded containers [$DOCKER_EXCLUDE]
--docker.auto enable automatic routing (without labels) [$DOCKER_AUTO]
--docker.prefix= prefix for docker source routes [$DOCKER_PREFIX]
consul-catalog:
--consul-catalog.enabled enable consul catalog provider [$CONSUL_CATALOG_ENABLED]
--consul-catalog.address= consul address (default: http://127.0.0.1:8500) [$CONSUL_CATALOG_ADDRESS]
--consul-catalog.interval= consul catalog check interval (default: 1s) [$CONSUL_CATALOG_INTERVAL]
file:
--file.enabled enable file provider [$FILE_ENABLED]
--file.name= file name (default: reproxy.yml) [$FILE_NAME]
--file.interval= file check interval (default: 3s) [$FILE_INTERVAL]
--file.delay= file event delay (default: 500ms) [$FILE_DELAY]
static:
--static.enabled enable static provider [$STATIC_ENABLED]
--static.rule= routing rules [$STATIC_RULES]
timeout:
--timeout.read-header= read header server timeout (default: 5s) [$TIMEOUT_READ_HEADER]
--timeout.write= write server timeout (default: 30s) [$TIMEOUT_WRITE]
--timeout.idle= idle server timeout (default: 30s) [$TIMEOUT_IDLE]
--timeout.dial= dial transport timeout (default: 30s) [$TIMEOUT_DIAL]
--timeout.keep-alive= keep-alive transport timeout (default: 30s) [$TIMEOUT_KEEP_ALIVE]
--timeout.resp-header= response header transport timeout (default: 5s) [$TIMEOUT_RESP_HEADER]
--timeout.idle-conn= idle connection transport timeout (default: 90s) [$TIMEOUT_IDLE_CONN]
--timeout.tls= TLS hanshake transport timeout (default: 10s) [$TIMEOUT_TLS]
--timeout.continue= expect continue transport timeout (default: 1s) [$TIMEOUT_CONTINUE]
mgmt:
--mgmt.enabled enable management API [$MGMT_ENABLED]
--mgmt.listen= listen on host:port (default: 0.0.0.0:8081) [$MGMT_LISTEN]
error:
--error.enabled enable html errors reporting [$ERROR_ENABLED]
--error.template= error message template file [$ERROR_TEMPLATE]
health-check:
--health-check.enabled enable automatic health-check [$HEALTH_CHECK_ENABLED]
--health-check.interval= automatic health-check interval (default: 300s) [$HEALTH_CHECK_INTERVAL]
throttle:
--throttle.system= throttle overall activity' (default: 0) [$THROTTLE_SYSTEM]
--throttle.user= limit req/sec per user and per proxy destination (default: 0) [$THROTTLE_USER]
plugin:
--plugin.enabled enable plugin support [$PLUGIN_ENABLED]
--plugin.listen= registration listen on host:port (default: 127.0.0.1:8081) [$PLUGIN_LISTEN]
Help Options:
-h, --help Show this help message
No Comments