- Introduction
- Quickstart
- Configure
- Plugins
- Authentication
- OAuth 2 with JWT access token
- OAuth 2 with opaque token introspection
- Request transformation
- CORS
- Brute-force protection
- ACP authorizer
- Authentication
- How to
Pyron is a lightweight, developers, and DevOps friendly API Gateway with advanced authentication and authorization capabilities. It's build using Scala on top of the Vertx.io framework enabling high performance and non-blocking execution. Plugin based architecture allows further extensions of capabilities and features as well as seamless integrations. Native support of the HashiCorp Consul service catalog enables Pyron to bridge the traditional network with service discovery based routing. Support for declarative configuration model allows Pyron to be easily integrated into the CI/CD process.
Pyron can be deployed and run as:
- Standalone JVM process
- Docker Container
- Kubernetes service deployed using Helm
Pyron provides many tools to publish and manage your API endpoints:
- L7 Routing - API calls can be routed to different targets based on the URI path patterns
- URI Rewrite - ability to drop a prefix or modify the URI before the request is proxied to the upstream service
- TCP/IP Proxy Headers - Support for X-Forwarded-For, X-TrueClient-IP, Proxy VIA headers
- Load Balancing - Client-side load balancing support
- Static up-stream service discovery - ability to configure a static set of upstream services to be used in load balancing pool
- Catalog based up-stream service discovery - ability to utilize Consul service catalog to discover instances of the up-stream services
- Static TLS support - ability to enable TLS based on provided certificate and key, ability to configure desired ciphers and TLS versions
- Vault based TLS support - ability to use Vault as a source for the TLS certificates
Cloudentity Pyron can normalize your API by transforming and managing requests to the services it protects.
- HTTP Request Header transformation - ability to inject/modify headers
- HTTP Request path parameter transformation - ability to inject/modify path parameters
- HTTP Request body transformation - ability to modify the request body (currently only available for JSON based payload)
- Cross-Domain Support - CORS headers publishing and support
- Correlation ID integration - Injection as well as utilization of the external correlation id to relate transactions for full tracing
- Open Tracing support - provides visibility into cross-service communication and instrumentation, enabling the distributed tracing.
- Rich Access Logs - the ability to stream logs to file, socket or Kafka
As an enforcement point, Pyron integrates with a wide range of protocols and tools to ensure the request is not only authorized but also secure in the context of a wide range of risk and business rules.
- JWT OAuth Access Token based - authenticate and authorize the request using OAuth Token
- Opaque OAuth Access Token based - authenticate and authorize the request using Opaque OAuth Token with Introspection
- Fallback Authentication - the ability to chain multiple authentication methods together and define the fallback scenarios Request authentication.
Pyron offers support for declarative configuration model out of the box with support for various sources of configuration.
- File-based Declarative configuration - the ability to load configuration from JSON/YAML based configuration files
- Consul & Vault based declarative configuration - the ability to load configuration from Consul Key-Value Store as well as secrets and certificates from Vault
- HTTP based declarative configuration - the ability to load configuration from an external HTTP endpoint
- API Request retries - ability to configure failure conditions and count of the API request retries strategy
- Request failover - the ability to failover the request to next health instance of the upstream microservice
- Circuit Breaker support - support for the circuit breaker pattern, works in concert with request retries and request failover
- OpenAPI Specification publishing and transformation - ability to transform and published the OpenAPI specs provided by the upstream services
Cloudentity Pyron also provides broad API protection with many standard features.
- Brute Force Protection (in-memory implementation) - Ability to protect APIs against brute force attacks or perform simple rate spike arrests - in-memory implementation
- Brute Force Protection (IMDG based) - Enterprise Feature - Ability to protect APIs against brute force attacks or make simple rate spike arrests - implementation using IMDG enabling shared state between all instances of the API Gateway.
Pyron also allows for custom plugins, which can be used to integrate legacy or proprietary systems as part of the standard data flow and enforcement. This could include custom callouts, complex business logic, or custom protocol/security management.
- Plugins - ability to add third-party plugins in Java or Scala
- Plugins Reload - ability to add plugins in runtime
NOTE
You needdocker
anddocker-compose
installed with access todocker.artifactory.syntegrity.com
artifactory.
Please contact Cloudentity team to get artifactory access.
This section walks you through the steps required to expose an API using Pyron.
We will expose http://example.com
API at /example
path.
Checkout this repository, go into install
directory in your terminal and execute docker-compose up -d
.
Set content of /configs/rules.json
to:
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "GET",
"pathPattern": "/example",
"rewritePath": "/"
}
]
}
]
}
After saving rules.json
, wait 5 seconds for Pyron to automatically reload the routing rules.
Executing curl -v localhost:8080/example
should return 200 status code with body of example.com
web page.
NOTE
Pyron's alive endpoint is at/alive
path.curl -v localhost:8080/alive
should return 200 status code.
You can configure Pyron using environment variables defined in install/configs/.pyron_env
file.
After changing a variable, recreate Pyron container using docker-compose up -d
command.
- Meta config
- Routing rules
- API groups
- Service discovery
- HTTP server
- HTTP clients
- Default retries and timeout
- Circuit breaker
- Open tracing
- Access log
- Authentication context and request headers in access log
- Proxy headers
At startup Pyron needs meta-config.json
file describing where to read configuration from.
{
"scanPeriod": 5000,
"stores": [
{
"type": "classpath",
"format": "json",
"config": {
"path": "config.json"
}
},
{
"type": "file",
"format": "json",
"config": {
"path": "rules.json"
}
}
]
}
Above meta-config.json
defines two configuration stores: config.json
from JAR classpath and rules.json
from the file system.
config.json
defines minimal configuration required to run Pyron. Routing rules are provided in rules.json
.
You will find meta-config.json
in install/configs
folder.
Learn how to read configuration from Consul and secrets from Vault.
A rule defines routing to a target endpoint. Rules are grouped in blocks that share common attributes in the default
object.
If an endpoint attribute is missing then it is taken from default
.
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
...
]
}
]
}
Attribute | Description |
---|---|
targetHost | host of target service (upstream) |
targetPort | port of target service |
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user"
}
]
}
]
}
Attribute | Description |
---|---|
method | HTTP method |
pathPattern | regular expression extended with support of path-param placeholders, e.g. /user/{id} |
Example: client's call POST /user
is proxied to target POST /user
.
Expose multiple endpoints using the same path prefix.
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80,
"pathPrefix": "/example",
"dropPrefix": true
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user"
},
{
"method": "GET",
"pathPattern": "/user/{id}"
}
]
}
]
}
Attribute | Description |
---|---|
pathPrefix | prefix appended to pathPattern (optional) |
dropPrefix | drop path prefix when calling target service (default true) |
By default, the prefix is dropped when calling target service. To preserve the prefix set dropPrefix
to false.
Example: client's call POST /example/user
is proxied to target POST /user
.
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "GET",
"pathPattern": "/user/{id}",
"rewritePath": "/entities/user/{id}"
}
]
}
]
}
Attribute | Description |
---|---|
rewritePath | path that Pyron calls target service at (optional, pathPattern used if this not set) |
Example: client's call GET /user/123
is proxied to target GET /entities/user/123
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"rewriteMethod": "PUT",
"pathPattern": "/user"
}
]
}
]
}
Attribute | Description |
---|---|
rewriteMethod | method that Pyron calls target service with (optional, method used if this not set) |
Example: client's call POST /user
is proxied to target PUT /user
.
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user",
"call": {
"responseTimeout": 3000
}
}
]
}
]
}
Attribute | Description |
---|---|
call.responseTimeout | target service response timeout in milliseconds |
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user",
"call": {
"retries": 1,
"failureHttpCodes": [500],
"retryFailedResponse": true,
"retryOnException": true
}
}
]
}
]
}
Attribute | Description |
---|---|
call.retries | maximum number of retries |
call.failureHttpCodes | response codes that Pyron retries if returned by target service |
call.retryFailedResponse | retry call if target service returned code in failureHttpCodes (default true) |
call.retryOnException | retry call on HTTP client exception, e.g. response timeout (default true) |
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user",
"preserveHostHeader": true
}
]
}
]
}
Attribute | Description |
---|---|
preserveHostHeader | should send to target service Host header received from the client (default false) |
By default, Pyron sends target host in Host header to target service, set preserveHostHeader
to true to send Host header sent by the client instead.
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user",
"requestBody": "buffer"
}
]
}
]
}
Attribute | Description |
---|---|
requestBody | body handling strategy (buffer , stream or drop , default buffer ) |
buffer
- load entire body into memory, required by some plugins (e.g.transform-request
)stream
- stream the body directly to target service (after applying request plugins)drop
- ignore the body, do not transfer it to target service (Content-Length
header of target request is set to 0)
{
"rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 80
},
"endpoints": [
{
"method": "POST",
"pathPattern": "/user",
"requestBodyMaxSize": 100
}
]
}
]
}
Attribute | Description |
---|---|
requestBodyMaxSize | max number of kilobytes transferred to target service (optional) |
NOTE
If maximum body size is reached then Pyron responds to the client with413
status code.
If the request body is usingchunked
Transfer-Encoding (content length is not known upfront) andrequestBody
isstream
then the body streaming to target service stops whenrequestBodyMaxSize
kilobytes has been streamed. Otherwise no data is sent to target service ifrequestBodyMaxSize
limit would be reached.
Set DEFAULT_REQUEST_BODY_MAX_SIZE env variable with defaultrequestBodyMaxSize
for all routing rules.
API Groups allow separating routing rule sets. You can define a set of rules and expose it on a domain and/or base-path. An incoming request is initially matched against domain and base-path and then dispatched to appropriate set for further processing.
{
"apiGroups": {
"example": {
"_group": {
"domains": ["demo.com"],
"basePath": "/apis"
},
"_rules": [
{
"default": {
"targetHost": "example.com",
"targetPort": 8080
},
"endpoints": [
{
"method": "GET",
"pathPattern": "/user"
}
]
}
]
}
}
}
Attribute | Description |
---|---|
_group.domains | Host headers Pyron matches the API group for |
_group.basePath | base path Pyron matches the API group at (optional) |
Note _
(underscore) in _rules
and _group
.
API Groups configuration details.
Pyron Gateway provides support for service discovery utilizing Consul client or configuration object.
{
"rules": [
{
"default": {
"targetService": "example-service"
},
"endpoints": [
...
]
}
]
}
Attribute | Description |
---|---|
targetService | service-name of target nodes from service-discovery |
Pyron calls nodes with targetService
service-name using a round-robin load balancer.
Below you will find instructions on how to enable service discovery providers.
Add sd-provider/consul
to MODULES
environment variable, i.e. MODULES=["sd-provider/consul"]
.
Env variable | Description |
---|---|
CONSUL_HOST | host |
CONSUL_PORT | port (default 8500) |
CONSUL_SSL | SSL enabled (default false) |
CONSUL_ACL_TOKEN | ACL token (optional) |
CONSUL_DC | data center (optional) |
CONSUL_TIMEOUT | connection timeout (optional) |
CONSUL_SD_SCAN_PERIOD | nodes refresh period in milliseconds (default 2000) |
Note: nodes registered in Consul need to have http-endpoint
tag and ssl
tag if exposed over SSL.
Add sd-provider/static
to MODULES
environment variable, i.e. MODULES=["sd-provider/static"]
.
Add sd-records
configuration attribute (e.g. in system.json
file).
{
"sd-records": [
{
"name": "example-service",
"location": {
"host": "example.com",
"port": 80,
"ssl": false,
"root": "/v1"
}
}
]
}
Attribute | Description |
---|---|
name | service-name of target node |
location.host | host of target node |
location.port | port of target node |
location.ssl | SSL of target node |
location.root | root path of target node (optional) |
Pyron node can register itself in Consul for service discovery.
Add sd-registrar/consul
to MODULES
environment variable, i.e. MODULES=["sd-registrar/consul"]
.
Env variable | Description |
---|---|
CONSUL_HOST | host |
CONSUL_PORT | port (default 8500) |
CONSUL_SSL | SSL enabled (default false) |
CONSUL_ACL_TOKEN | ACL token (optional) |
CONSUL_DC | data center (optional) |
CONSUL_TIMEOUT | connection timeout (optional) |
REGISTER_SD_SERVICE_NAME | Pyron service name |
REGISTER_SD_HOST | host of Pyron node |
REGISTER_SD_PORT | port of Pyron node |
REGISTER_SD_SSL | ssl of Pyron node (default false) |
REGISTER_SD_HEALTHCHECK_HOST | host of Pyron health-check (default SELF_SD_SERVICE_NAME) |
REGISTER_SD_HEALTHCHECK_PORT | port of Pyron health-check (default SELF_SD_HEALTHCHECK_PORT) |
REGISTER_SD_HEALTHCHECK_PATH | path of Pyron health-check (default /alive) |
REGISTER_SD_HEALTHCHECK_INTERVAL | health-check interval (default 3s) |
REGISTER_SD_DEREGISTER_AFTER | node de-register period when health-check fails (default 600s) |
REGISTER_SD_TAGS | extra node tags (default []) |
Pyron uses Vertx HTTP server implementation. Use environment variables to configure io.vertx.core.http.HttpServerOptions
.
Environment variables map to HttpServerOptions
(see docs) attributes in following way:
- All variable names start with
HTTP_SERVER_
prefix, - HttpServerOptions attribute name is capitalized and camel-case is replaced with underscore
_
. - If an attribute has object value then it's sub-attribute env names are prefixed with
HTTP_SERVER_{parent-env-name}__
(note double underscore).
Examples:
Name | HttpServerOptions attribute |
---|---|
HTTP_SERVER_PORT | port |
HTTP_SERVER_ACCEPT_BACKLOG | acceptBacklog |
HTTP_SERVER_PEM_TRUST_OPTIONS__CERT_PATHS | pemTrustOptions.certPaths |
In order to set HttpServerOptions
attribute with collection value use JSON syntax, e.g. HTTP_SERVER_PEM_TRUST_OPTIONS__CERT_PATHS=["/etc/ssl/cert.pem"]
.
Pyron uses Vertx implementation of HTTP clients. Use environment variables to configure default io.vertx.core.http.HttpClientOptions
.
Environment variables map to HttpClientOptions
(see docs) attributes the same way they map to HttpServerOptions
.
Name | HttpClientOptions attribute |
---|---|
HTTP_CLIENT_MAX_POOL_SIZE | maxPoolSize |
HTTP_CLIENT_KEEP_ALIVE | keepAlive |
HTTP_CLIENT_TRUST_ALL | trustAll |
You can configure HTTP client for each target service separately (note that default attributes from environment variables are ignored in this case):
{
"smart-http-target-clients": {
"example-service": {
"http": {
"maxPoolSize": 50
}
}
}
}
{
"smart-http-target-clients": {
"example-service": {
"responseTimeout": 3000,
"retries": 5,
"failureHttpCodes": [500]
}
}
}
Target client retry and timeout default attributes are overridden by values set in routing rule.
Configure io.vertx.circuitbreaker.CircuitBreakerOptions
in circuitBreaker
object to enable circuit breaker functionality per target service.
{
"smart-http-target-clients": {
"example-service": {
"circuitBreaker": {
"maxFailures": 3
}
}
}
}
Add tracing/jaeger
to MODULES
environment variable, i.e. MODULES=["tracing/jaeger"]
.
Env variable | Description |
---|---|
TRACING_SERVICE_NAME | Pyron name in Jaeger |
JAEGER_AGENT_HOST | Jaeger agent host (optional) |
JAEGER_AGENT_PORT | Jaeger agent port (optional) |
JAEGER_SAMPLER_MANAGER_HOST_PORT | Jaeger sampler host:port (optional) |
TRACING_FORMAT | tracing format: cloudentity, jaeger, zipkin (default cloudentity ) |
cloudentity
tracing format allows configuration of span context key and baggage item prefix:
Env variable | Description |
---|---|
TRACING_TRACE_ID | span context key (default x-trace-id ) |
TRACING_BAGGAGE_PREFIX | baggage item prefix (default x-ctx- ) |
{
"timestamp": "2018-04-06T15:14:33.929Z",
"trueClientIp": "192.168.0.13",
"remoteClient": "192.168.0.127",
"http": {
"httpVersion": "HTTP/1.1",
"method": "GET",
"uri": "/service-a/user/123",
"status": "200"
},
"gateway": {
"method": "GET",
"path": "/user/{userId}",
"pathPrefix": "/service-a",
"aborted": false,
"targetService": "service-a"
},
"request": {
"headers": {
"Host": ["example.com"]
}
},
"authnCtx": {
"method": "oauth2",
"user":"4b1b17f8-a934-458f-3c08-cc01d9f9b917",
"uid":"admin@cloudentity.com"
}
},
"timeMs": "3"
}
Attribute | Description |
---|---|
timestamp | request time in ISO 8601 format |
trueClientIp | IP address of original client, either X-Real-IP header or first IP from X-Forwarded-For or remote address |
remoteIp | IP of the direct client |
authnCtx | authentication context |
httpVersion | HTTP version |
method | HTTP method |
uri | URI without host |
status | HTTP status code |
gateway.method | method of matching rule |
gateway.path | path pattern of matching rule |
gateway.pathPrefix | path prefix of matching rule |
gateway.aborted | true if Pyron aborted the call without proxying to target service; false otherwise |
gateway.interrupted | true if the call was interrupted by the client; false otherwise |
gateway.failed | true if an exception occurred on target call or plugin application; not set otherwise |
gateway.targetService | target service of matching rule |
request.headers | request headers |
timeMs | time from receiving the request body till writing full response body |
Env variable | Description | Example |
---|---|---|
ACCESS_LOG_SLF4J_DISABLED | disable SLF4J access logging (default false) | true |
ACCESS_LOG_AUTHN_CTX | authentication context set in access log (optional) | {"method":"authnMethod","user":"sub","uid":"email"} |
ACCESS_LOG_REQUEST_HEADERS_ALL | log all headers flag (default false) | true |
ACCESS_LOG_REQUEST_HEADERS_WHITELIST | log selected headers (optional) | ["Host","User-Agent"] |
ACCESS_LOG_REQUEST_HEADERS_MASK_WHOLE | mask whole logged header (optional) | ["Authorization"] |
ACCESS_LOG_REQUEST_HEADERS_MASK_PARTIAL | mask logged header partially (optional) | ["Token"] |
Pyron applies following request headers modification (unless disabled):
- Add
remote-address.host
toX-Forwarded-For
headers - Add
remote-address.protocol
toX-Forwarded-Proto
headers - If
Host
header is set then add it toX-Forwarded-Host
headers - If True Client IP header is missing then set it to first
X-Forwarded-For
value - Set True Client IP header to upstream service
Env variable | Description |
---|---|
PROXY_HEADERS_ENABLED | enable proxy headers (default true) |
INPUT_TRUE_CLIENT_IP_HEADER | True Client IP header name (default X-Real-IP) |
OUTPUT_TRUE_CLIENT_IP_HEADER | Outgoing True Client IP header name (default X-Real-IP) |
Plugins extend request-response flow, e.g. can enforce authorization rules, modify request or response, enhance access or audit logs, etc.
Read about plugins application in routing rules.