# JWT Security

## What does it do?

On incoming requests, the `gm-jwt-security` filter creates a [jwt token](http://jwt.io/) from the [gm-jwt-security service](https://github.com/greymatter-io/gm-jwt-security) "/policies" endpoint using the incoming `USER_DN`. It passes the token along with the request in a `jwt` header.

## Gotchas

### Ordering

When using TLS and, in most instances, this filter should be applied _after_ inheaders and impersonation. This is because the `userpolicyid` sent to the gm-jwt-security is taken from the `USER_DN` header. If the gm-jwt-security filter is applied _before_ those headers, it could retrieve a token for the wrong `USER_DN` associated with the request, or you will receive a 407 response and see the error

```
gm-proxy_1_3c03fda44591 | {"level":"error","filter":"gm-jwt-security","error":"USER_DN must be present to retrieve jwt token from gm-jwt-security","time":"2020-01-30T17:58:37Z","message":"DecodeHeaders() - retrieving USER_DN header"}
```

### Header Length

Something to be careful of is the maximum size of headers allowed, as this can differ substantially between deployments. By default, the maximum header size for incoming requests is `60kb`.\* In production, we suggest a header size of no more than `30kb`, as gm-proxy dynamically adds, updates, and removes header values while processing filters. If a request header is too large for the proxy to handle, it will return the request with a [431 response code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431).

`30kb` should support most `jwt` token sizes since the average jwt token from gm-jwt-security is about 500 bytes. If this is still too small for your header, you can consider:

* Set the parameter `max_request_headers_kb` to `96` in the `envoy.http_connection_manager` section of the proxy.\*\*
* Using an alternative approach to jwt authentication such as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) or [access tokens](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/identity).
* Process access control information within the sidecar.

Note that for outgoing requests from the gm-proxy, the maximum request header size is about `1mb`. This means that in some scenarios it's possible for a large `jwt` token to be added to outgoing headers but fail as an upstream request to another sidecar. Alternatively, the request could also succeed as an upstream request to external service, depending on the implementation.

_\*The maximum header size is the size of all headers added together. This was tested and verified locally. see \[Envoy docs for more info]\(_[_https://www.envoyproxy.io/docs/envoy/v1.10.0/api-v2/config/filter/network/http\_connection\_manager/v2/http\_connection\_manager.proto.html?highlight=header%20size_](https://www.envoyproxy.io/docs/envoy/v1.10.0/api-v2/config/filter/network/http_connection_manager/v2/http_connection_manager.proto.html?highlight=header%20size)_)._

_\*_ In testing, http/1.1 requests increase nicely to 96kb (98,100 chars in header) but grpc and http/2 requests start failing after a max of 74kb. This could be because different protocols use different header [compression algorithms](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding)\*

## Filter Configuration Options

| Name                 | Type   | Default                    | Example                                                      | Description                                                                                                                                                                      |
| -------------------- | ------ | -------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`             | string | \[empty]                   | MTIzCg==                                                     | Api key used in request to gm-jwt-security                                                                                                                                       |
| `endpoint`           | string | \[empty]                   | [https://gm-jwt-security:9443](https://gm-jwt-security:9443) | Endpoint of gm-jwt-security                                                                                                                                                      |
| `jwtHeaderName`      | string | "jwt"                      | "custom-header-name"                                         | Header in which the jwt token is put                                                                                                                                             |
| `useTls`             | bool   | false                      | --                                                           | Should the filter use certs in connecting to gm-jwt-security?                                                                                                                    |
| `certPath`           | string | "./certs/server.crt"       | --                                                           | Certificate path                                                                                                                                                                 |
| `keyPath`            | string | "./certs/server.key"       | --                                                           | Keyfile path                                                                                                                                                                     |
| `caPath`             | string | "./certs/intermediate.crt" | --                                                           | Certificate authority or intermediate certificate path                                                                                                                           |
| `insecureSkipVerify` | bool   | false                      | --                                                           | Should calls to gm-jwt-security require hostname verification in certs? Should be used only for testing. See [go docs](https://golang.org/pkg/crypto/tls/) for more information. |
| `timeoutMs`          | int    | 1000                       | --                                                           | Timeout in milliseconds for the connection between gm-proxy and gm-jwt-security service                                                                                          |
| `maxRetries`         | int    | 0                          | --                                                           | Number of retries after failed connection between gm-proxy and gm-jwt-security service                                                                                           |
| `retryDelayMs`       | int    | 200                        | --                                                           | Amount of time in milliseconds between each unsuccessful retry                                                                                                                   |

## Full Request Workflow

* Makes a request to `$GM_SENSE_ENDPOINT/policies` to get a jwt token for the user.
  * In the request, `userpolicyid` will be the `USER_DN` from the incoming request.
  * `api-key` is be a preconfigured api token for each sidecar to interact with gm-jwt-security.
* Process the response from gm-jwt-security.
  * If unsuccessful, return a `401` error.
  * If successful, set the `jwt` header to the returned token value.

![image](https://user-images.githubusercontent.com/5482080/71980381-e0fe6500-31ed-11ea-8992-71adbfdae184.png)
