Overview
Proxima is a simple L7 proxy, redirect engine, and static site server, commonly used as an API gateway, acting as a single entry point for your microservices. Proxima supports connecting to backend endpoints over http/https, and can source dynamic endpoints through http calls, or through Hashicorp Vault.
Docker
You can pull down Proxima images at docker.
Compile
You can compile Proxima by running cargo build --release
after pulling down the repo.
Alternatively, you may run cargo install --git https://github.com/findelabs/proxima.git
.
Proxima Usage
Proxima is, at heart, a layer seven proxy, and is configured using a yaml config file, and also allows for multi-level routing structures.
For example, a simple configuration could look like the following. With this config, with proxima running on localhost on port 8080, a GET to localhost:8080/google
will get redirected to https://www.google.com.
routes:
google:
url: https://www.google.com
yahoo:
url: https://yahoo.com
A more complex configuration could also proxy in the following. With this config, with a GET to /user1/search
, the call will get sent to google, whereas a GET to /user2/search
will get forwarded to yahoo.
routes:
user1:
search:
url: https://www.google.com
user2:
search:
url: https://yahoo.com
Proxying Requests
The main use case for Proxima is to handle clients hitting various endpoints specified in the config. General usage is below:
List Sub Endpoints
View all endpoints under a path
URL : /:endpoint
Method : GET
Sample Response
{
"endpoint_four": {
"url": "http://localhost:8080/"
},
"endpoint_three": {
"url": "http://localhost:8080/"
},
"level_three": {
"endpoint_five": {
"url": "http://localhost:8080/"
}
}
}
Endpoint Forward
Endpoints can forward, redirect, and display static content:
URL : /:endpoint/*path
Method : ANY
Docker
Findelabs pushes new images to docker.com.
You will need to provide Proxima with a basic configuration file in order to start. Below is a bare-bones config:
cat << EOF > config.yaml
routes:
endpoint:
url: https://google.com
EOF
You can then start a Proxima container on a server running docker with something like:
docker run -p 8080:8080 \
--mount type=bind,source="$(pwd)"/config.yaml,target=/config.yaml\
findelabs/proxima:latest --config /config.yaml
Command Line Arguments
Quick Start
Basic usage to start Proxima is
proxima --config config.yaml
Flags
Flags are optional to start Proxima.
--enforce_http
Using --enforce_http
causes Proxima to error if any endpoints specify non-http urls.
--help
Print out help information with either --help
or -h
.
--nodelay
Enable socket nodelay with --nodelay
, read about this here.
--reuse_address
Proxima will reuse socket if possible when --reuse_address
is specified.
--accept_invalid_hostnames
Ignore hostnames that do not match the request
--accept_invalid_certs
Accept certs that are not valid on remote servers
--root_cert
Import root cert, overriding system default cert
--version
Print Proxima version with --version
or -v
Options
--config [env: PROXIMA_CONFIG]
Specify a yaml config file with --config
or -c
. This configuration can either be a file, or an http endpoint.
--config_username [env: PROXIMA_AUTH_USERNAME]
If you config file is an http endpoint that requires authentication, specify a username with --config_username
.
--config_password [env: PROXIMA_AUTH_PASSWORD]
If you config file is an http endpoint that requires authentication, specify a password with --config_password
.
--port [env: PROXIMA_LISTEN_PORT]
Set the port on which to listen with --port
or -p
, default of 8080.
--username [env: PROXIMA_CLIENT_USERNAME]
Force all clients hitting Proxima to authenticate with Basic creds, with --username
or -u
specifying the username.
-password [env: PROXIMA_CLIENT_PASSWORD]
Force all clients hitting Proxima to authenticate with Basic creds, with --password
or -p
specifying the password.
--timeout [env: PROXIMA_TIMEOUT]
Set a global connection timeout with --timeout
, default is 60 seconds.
Configuration
Proxima is configured via a yaml config file, which currently specifies just the subfolders, endpoints, and various endpoint options which Proxima will serve.
In the future, we will be including many more configuration options within this config.
Configuration Syntax
The configuration file of Proxima always starts with a routes
block, under which all remote endpoints reside. Proxima can handle multi-level endpoints as well.
For example, to have Proxima send any clients requesting /host/yahoo
to yahoo.com, and /host/google
to google.com, your config.yaml would look something like:
routes:
host:
proxy:
yahoo:
url: http://yahoo.com
google:
url: http:://google.com
Alternatively, Proxima can also serve redirects for various sites, as shown in the example below:
routes:
links:
google:
redirect:
url: https://google.com
craigslist:
redirect:
url: https://craigslist.com
Optional Endpoint Fields
Proxy and static endpoint variants can contain a series of optional fields:
- Endpoint timeouts (timeout)
- Security (security)
- Client Authentication (security.client)
- Method Whitelisting (security.whitelist.method)
- Network Whitelisting (security.whitelist.method)
- Remote URL Authentication (authentication)
Dynamic Loading
Proxima will check for changes to the config file every 30 seconds by default. If the newest changes are unparsable, Proxima will continue to operate using the previous working configuration.
Global Config
Example global config is shown below:
global:
network:
enforce_http: Bool
nodelay: Bool
reuse_address: Bool
timeout: u64
security:
config:
hide_folders: Bool
tls:
accept_invalid_hostnames: Bool
insecure: Bool
import_cert: String
auth:
client:
api_key:
basic:
bearer:
digest:
jwks:
whitelist:
networks: Vec<CIDR>
methods: Vec<Methods>
Config Item Details
Name | Description | Value |
---|---|---|
global.network.enforce_http | Enforce http-type endpoints | false |
global.network.nodelay | Enable TCP nodelay on packets | false |
global.network.reuse_address | Reuse sockets when establishing connections | false |
global.network.timeout | Set global connection timeout | false |
global.security.config.hide_folders | Return 404 for non-endpoints (folders) | false |
global.security.tls.accept_invalid_hostnames | Accept invalid hostnames when using https | false |
global.security.tls.insecure | Accept incorrect certs when using https | false |
global.security.tls.import_cert | Specify cert to import | "" |
global.security.auth.client | Set default client auth (overridden at endpoint) | {} |
global.security.auth.whitelist.networks | Set default network whitelist | [] |
global.security.auth.whitelist.methods | Set default method whitelist | [] |
Endpoints
Below is a list of the endpoint variations that Proxima supports, along with a brief description:
Remote Config
This variant will attempt to pull all sub folders from the url specified.
Proxy
This variant will forward request payload, method, and headers, along with sub folders, to the specified url.
Redirect
This variant will simply redirect all requests to the specified url.
Static
Specify a static body to serve.
Vault
Connect to a HashiCorp Vault instance, in order to pull dynamic secrets, optionally templated to match any matching variant using handlebars.
Remote Config
This variant will pull the sub-config from a remote url. This can be used to stitch multiple Proximas together, in order to share a config.
An example of this is shown below:
routes:
proxima:
http_config:
url: http://localhost:8081/config
Proxy
This endpoint variant will forward all requests that terminate the config entry. For example, with the following config:
routes:
api:
proxy:
url: http://localhost:8081
A user request to localhost:8080/api/health will return back the response from http://localhost:8081/health.
Proxy Endpoint Details
Name | Description | Value |
---|---|---|
proxy.url | URL for remote server | "" |
proxy.authentication | Enable the sending of credentials to remote server | {} |
proxy.timeout | Endpoint timeout after connection is established | u64 |
proxy.security.client | Enable client authentication | {} |
proxy.security.whitelist.networks | Enable network whitelisting | [] |
proxy.security.whitelist.methods | Enable method authentication | [] |
proxy.config.preserve_host_header | Retain original client HOST header | {} |
Redirect
With this variant, hitting this endpoint will cause proxima to return a 308 Permanent Redirect to the client, along with the next hop location in the headers.
Example of such config below:
routes:
google:
redirect:
url: https://google.com
Redirect Endpoint Details
Name | Description | Value |
---|---|---|
redirect.url | URL for remote server | "" |
Static
This endpoint variant simply returns back a body to the client. This can easily be used to override the default root page of Proxima.
An example override of /
shown below:
routes:
/:
static:
body: "hello world"
Static Endpoint Details
Name | Description | Value |
---|---|---|
static.body | Body of response | "" |
static.security.client | Enable client authentication | {} |
static.security.whitelist.networks | Enable network whitelisting | [] |
static.security.whitelist.methods | Enable method authentication | [] |
static.headers | Add headers to response | {} |
Hashicorp Vault Integration
Proxima can be directed to pull secrets from a single Vault endpoint, and can be configured to authenticate via approle, or JWT. Proxima is able to source either an entire secret directory from Vault, or a single secret.
Proxima is able to pull dynamic secrets from Hashicorp Vault, and can cache these remote endpoints locally in the endpoint cache. This reduces the necassary calls to Vault to just a single GET, with subsequent proxy requests being served from memory.
Examples on how to configure Proxima to connect to Vault is shown below:
Vault AppRole Authentication
./proxima --config $CONFIG \
--vault_role_id $ROLE_ID \
--vault_secret_id $SECRET_ID \
--vault_url https://vault.local:8200 \
--vault_mount kv2
Vault JWT Authentication (within pod)
./proxima --vault_kubernetes_role kubernetes \
--vault_login_path auth/kubernetes \
--vault_url https://vault.local:8200 \
--vault_mount kv2
Endpoint Configuration
Proxima will attempt to authenticate to the Vault upon startup. Proxima will only begin to serve endpoints once it is able to connect to Vault succesfully. Next, we will need to ensure that the endpoints are pointed at a secret or folder that exists. This is done by specifying both the secret path, and an optional template, in case of a complex secrets. Below is an example of how one could setup an endpoint designed to use Digest authentication to connect to the MongoDB Atlas REST API.
routes:
atlas:
vault:
template: eyJ1cmwiOiJ7eyB1cmwgfX0iLCJhdXRoZW50aWNhdGlvbiI6eyJkaWdlc3QiOnsidXNlcm5hbWUiOiJ7eyB1c2VybmFtZSB9fSIsInBhc3N3b3JkIjoie3sgcGFzc3dvcmQgfX0ifX19Cg==
secret: atlas/apis/rest
When we unpack the example template, we find: {"url":"{{ url }}","authentication":{"digest":{"username":"{{ username }}","password":"{{ password }}"}}}
The Vault secret found at kv2/atlas/apis/rest
can contain any number of fields, as long as they contain a minimum number of fields specified in the base64-encoded template: url, username, and password.
Vault Secret Folder
Proxima is also able to grab secrets from a folder within Vault. Proxima differentiate a folder vs a single secret by whether or not the secret provided has a trailing forward slash.
For example, if a running Proxima were configured with an endpoint named atlas, with a Vault secret pointing at /atlas/apis/
, a GET request to http://localhost:8080/atlas/rest
will cause Proxima to attempt to pull a secret in Vault at kv2/atlas/apis/rest
, and will attempt to apply the template against the contents. If the resulting json can be successfully parsed as an endpoint, Proxima will route the client to the remote url with digest authentication. If the secret does not contain the fields specified within the template, Proxima will log an error, and will return a 404 status code.
Additionally, a GET request to http://localhost:8080/atlas
will cause Proxima to attempt to apply the template against any secrets found within kv2/atlas/apis
, returning back a json payload with any endpoints it found within the folder.
Vault Endpoint Details
Name | Description | Value |
---|---|---|
vault.secret | Path to secret in Vault | "" |
vault.template | Template to use when rendering secret | "" |
Endpoint Options
Extra endpoint options are available for specific endpoint variants.
Endpoint Timeouts
Proxima has a default connection timeout of 60 seconds that can be overridden with a command line argument or environmental variable. However, there is also a request timeout that is set at the individual endpoint level, also with a default of 60 seconds.
If 60 seconds is considered too long for your remote endpoint, you can override the setting like the following (in ms):
routes:
my_endpoint:
proxy:
url: http://google.com
timeout: 5000
URL Failover
Proxima allow for URL's to be failed over when a remote URL socket is not open, or when the url times out.
Configure an endpoint like below in order to have the URL failover. In this example, if google.com fails to respond, then duckduckgo.com will be promoted as the primary URL for this endpoint.
routes:
search:
proxy:
url:
failover:
- http://google.com
- http://duckduckgo.com
Remote Server Authentication
Proxima can both connect to remote URL's using the following authentication methods:
- Basic
- Bearer
- Digest
- API Key
- JWT
If a remote endpoint requires authentication, for example, Basic authentication, simply specify a new authentication field within the endpoint yaml:
routes:
endpoint_requiring_basic:
proxy:
url: http://myurl.net
authentication:
basic:
username: myusername
password: mypassword
Here are some examples on how to specify each of these authentication types, for an endpoint. Keep in mind that the authentication block only supports one type of authentication for an endpoint:
Basic
routes:
endpoint_test:
proxy:
url: http://myurl.net
authentication:
basic:
username: client
password: mypassword
Digest
routes:
endpoint_test:
proxy:
url: http://myurl.net
authentication:
digest:
username: client
password: mypassword
Bearer
routes:
endpoint_test:
proxy:
url: http://myurl.net
authentication:
bearer:
token: s.Y2Rhc2Nkc2NkYXNjc2QK
API Key
routes:
endpoint_test:
proxy:
url: http://myurl.net
authentication:
api_key:
token: s.Y2Rhc2Nkc2NkYXNjc2QK
key: api-key
JWT
routes:
endpoint_test:
proxy:
url: http://myurl.net
authentication:
jwt:
url: https://dev-8177876213.okta.com/oauth2/default/v1/token
audience: 0oa4cdaknkn866cdacd
client_id: njcda8981cds
client_secret: s.Y2Rhc2Nkc2NkYXNjc2QK
grant_type: client_credentials
scopes:
- default
Endpoint Security
Proxima allows for the whitelisting of both methods and networks. An example of an endpoint that allows GET and POST requests, from private networks, is shown below:
routes:
endpoint_basic:
proxy:
url: http://myurl.net
security:
whitelist:
methods:
- GET
- POST
networks:
- 10.0.0.0/8
- 127.0.0.0/16
- 192.168.0.0/24
Method Whitelisting
You can whitelist specific methods for all clients for an endpoint.
routes:
endpoint_basic:
proxy:
url: http://myurl.net
security:
whitelist:
methods:
- GET
- POST
The following methods can currently be whitelisted:
- Options
- Get
- Post
- Put
- Delete
- Head
- Trace
- Connect
- Patch
Client Whitelists
You can also whitelist specific methods for specific clients under security.clients[].whitelist, as shown below. Keep in mind that clients will only be able to use methods included in the global endpoint configuration.
routes:
endpoint_basic:
proxy:
url: http://myurl.net
security:
whitelist:
methods:
- GET
- POST
client:
basic:
# This will work
- username: myuser_one
password: mypassword
whitelist:
methods:
- GET
# This will fail since PUT is not a globally whitelisted endpoint method
- username: myuser_two
password: mypassword
whitelist:
methods:
- PUT
Network Whitelisting
Proxima is able to whitelist CIDR networks. You may configure both global whitelisted endpoint networks, as well per-client whitelisted networks. The global network whitelist trumps client whitelists, in that if a client network is not included in the global whitelisted networks, connections from the client network will be refused.
The global endpoint network whitelist is not a requirement. You keep network whitelisted solely at the client whitelist level.
An example of a series of whitelisted networks with a global whitelist is shown below:
routes:
single:
proxy:
url: http://localhost:3000
security:
whitelist:
networks:
- 192.168.1.0/24
- 192.168.0.0/24
client:
basic:
# This admin user will only be allowed to authenticate from a single network
- username: admin1
password: adminpassword1
whitelist:
networks:
- 192.168.0.0/24
# This admin user will only be allowed to authenticate from a single network
- username: admin2
password: adminpassword2
whitelist:
networks:
- 192.168.1.0/24
Here is an example of an endpoint that does not specify a global network whitelist:
routes:
single:
proxy:
url: http://localhost:3000
security:
client:
basic:
- username: admin1
password: adminpassword1
whitelist:
networks:
- 192.168.0.0/24
Client Authentication
Proxima is able to authenticate clients using any of the following protocols:
- Basic
- Bearer
- Digest
- JWKS
Basic and Digest Client Authentication
If you would like to require that one user logs in with Basic, and another with Digest, simply create a new security.client map that contains both basic and digest, within the endpoint yaml.
routes:
locked:
proxy:
url: http://myurl.net
security:
client:
basic:
- username: client_one
password: mypasswordone
digest:
- username: client_digest
password: mypasswordtwo
Client JWKS Configuration
Proxima can authenticate users' JWT's by caching the JWKS from other authentication providers, like Okta.
Here is an example on how to configure an endpoint with jwks client authentication:
routes:
endpoint_test:
proxy:
url: http://myurl.net
security:
client:
jwks:
- url: https://dev-17129172.okta.com/oauth2/default/v1/keys
audience: api://default
scopes:
- findelabs.test
With this configuration, after a user generates a token via the Okta /token endpoint, include said token field in the Authorization header of the request to Proxima.
Client Bearer Configuration
Proxima can authenticate user's token as well, by using the token client security type.
Here is an example on how to configure an endpoint with bearer client authentication:
routes:
endpoint_test:
proxy:
url: http://myurl.net
security:
client:
bearer:
- token: s.YWQ5MWY2N2RiMTE1ZjNhZDdkOTFiOGZl
Client API Key Configuration
Proxima can also authenticate users based on a specified header's value. The default header name is set to x-api-key
, but this name can be set to any arbitrary value.
Here is an example on how to configure an endpoint with api_key client authentication:
routes:
endpoint_test:
proxy:
url: http://myurl.net
security:
client:
api_key:
- token: s.YWQ5MWY2N2RiMTE1ZjNhZDdkOTFiOGZl
- token: s.NDQ4YzIzNzA0OWI4YmU1MjVkN2M4ZDZi
key: api-key
Administration
Proxima exposes a series of administrative API endpoints that can used to gain insight into endpoint mappings, caching, metrics, and more.
Endpoint Caching
Proxima employs a two-stage cache; the first which maps a Proxima config folder path to config endpoint, and the second which maps unique client request paths to the first stage cache.
First Stage
The first stage cache can be viewed by hitting the /cache
endpoint on the admin port, and may show contents like:
{
"archivelabs": "https://api.archivelab.org/v1",
"inshortsapi": "https://inshortsapi.vercel.app/news"
}
What this shows is that there are at least two endpoints listed within Proxima's config, one for archivelabs, and the other for inshortsapi. The value for both of these keys is simply displayed as the remote URL, but within the cache is the entire endpoint, which includes any authentication, or security parameters.
Second Stage
The second stage cache can be accessed by hitting the /mappings
endpoint, also served on the admin port. Example contents corresponding to the first stage cache listed above may look something like:
{
"/archivelabs": "archivelabs",
"/inshortsapi": "inshortsapi",
"/inshortsapi/foobar": "inshortsapi"
}
What this means is that at least one client has hit Proxima at the paths /archivelabs, /inshortsapi, and /inshortsapi/foobar. If another client were to request another unique path from Proxima which matches a known endpoint, that too would be added to the second stage cache.
Relearning Endpoints
If the endpoints for any of the entries in the cache were learned from a remote source, such as Vault, and then had their Vault secret modified, the first stage cache will have invalid data. This is acknowledged as a potential risk, and we plan on introducing a cache timeout in the future.
For now, if a Vault secret is modified that has already been cached, you may remove the cached entry by using a delete method against /cache?key=inshortsapi
, which in this case will remove the entry for inshortsapi from the first stage cache. There is no delete option available for the second stage cache, since the likelyhood of a mapping going bad is exceedingly low.
REST API
Proxima exposes a series of admin API paths on a secondary port, by default 8081.
Show Config
Get Proxima's current configuration
URL : /config
Method : GET
Success Response:
Code : 200 OK
Sample Response
{
"network": {
"enforce_http": false,
"nodelay": false,
"reuse_address": false,
"timeout": 5000
},
"security": {
"config": {
"hide_folders": false
},
"tls": {
"accept_invalid_hostnames": false,
"insecure": false
},
"whitelist": {
"networks": [
"10.0.0.0/8"
]
}
}
}
Show Routes
Get Proxima's current routes
URL : /routes
Method : GET
Success Response:
Code : 200 OK
Sample Response
{
"routes": {
"archivelabs": {
"proxy": {
"timeout": 5000,
"url": "https://api.archivelab.org/v1"
}
},
"inshortsapi": {
"proxy": {
"url": "https://inshortsapi.vercel.app/news"
}
},
"local": {
"proxy": {
"url": "http://localhost:8082/"
}
}
}
}
Show Health
Get Proxima's current health
URL : /health
Method : GET
Success Response:
Code : 200 OK
Sample Response
{
"msg": "Healthy"
}
Reload Config
Reload Proxima Config
URL : /reload
Method : POST
Success Response:
Code : 200 OK
Get Mappings Cache
Get the current mappings cache.
URL : /mappings
Method : GET
Success Response:
Code : 200 OK
Sample Response
{
"/archivelabs": "archivelabs",
"/inshortsapi": "inshortsapi"
}
Get Cache
Get the current url cache.
URL : /cache
Method : GET
Success Response:
Code : 200 OK
Sample Response
{
"archivelabs": "https://api.archivelab.org/v1",
"inshortsapi": "https://inshortsapi.vercel.app/news"
}
Delete Cache
Delete Proxima's cache
URL : /cache
Method : DELETE
Success Response:
Code : 200 OK
Sample Response
{
"msg": "cache has been cleared"
}
Delete Cache Entry
Get the current url cache hashmap.
URL : /cache?key=[key]
Method : DELETE
Success Response:
Code : 200 OK
Sample Response
{
"entry": "archivelabs",
"msg": "entry remove from cache"
}
Metrics
Proxima exposes a /-/metrics
path that can be scraped by Prometheus.
The following metric types are currently enabled:
- TYPE proxima_cache_attempt_total counter
- TYPE proxima_cache_keys gauge
- TYPE proxima_cache_miss_total counter
- TYPE proxima_config_renew_attempts_total counter
- TYPE proxima_config_renew_failures_total counter
- TYPE proxima_jwts_renew_attempts_total counter
- TYPE proxima_jwts_renew_failures_total counter
- TYPE proxima_requests_duration_seconds histogram
- TYPE proxima_requests_total counter
- TYPE proxima_response_errors_total counter
- TYPE proxima_security_client_authentication_total counter
- TYPE proxima_security_method_attempts_total counter
- TYPE proxima_security_method_blocked_total counter