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

NameDescriptionValue
global.network.enforce_httpEnforce http-type endpointsfalse
global.network.nodelayEnable TCP nodelay on packetsfalse
global.network.reuse_addressReuse sockets when establishing connectionsfalse
global.network.timeoutSet global connection timeoutfalse
global.security.config.hide_foldersReturn 404 for non-endpoints (folders)false
global.security.tls.accept_invalid_hostnamesAccept invalid hostnames when using httpsfalse
global.security.tls.insecureAccept incorrect certs when using httpsfalse
global.security.tls.import_certSpecify cert to import""
global.security.auth.clientSet default client auth (overridden at endpoint){}
global.security.auth.whitelist.networksSet default network whitelist[]
global.security.auth.whitelist.methodsSet 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

NameDescriptionValue
proxy.urlURL for remote server""
proxy.authenticationEnable the sending of credentials to remote server{}
proxy.timeoutEndpoint timeout after connection is establishedu64
proxy.security.clientEnable client authentication{}
proxy.security.whitelist.networksEnable network whitelisting[]
proxy.security.whitelist.methodsEnable method authentication[]
proxy.config.preserve_host_headerRetain 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

NameDescriptionValue
redirect.urlURL 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

NameDescriptionValue
static.bodyBody of response""
static.security.clientEnable client authentication{}
static.security.whitelist.networksEnable network whitelisting[]
static.security.whitelist.methodsEnable method authentication[]
static.headersAdd 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

NameDescriptionValue
vault.secretPath to secret in Vault""
vault.templateTemplate 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