Skip to content

Commit

Permalink
New Topic Page: Original Request Context (#2569) (#2602)
Browse files Browse the repository at this point in the history
* init original context doc

* copy edit

* init Service Account page

* update and expand user context article

* fix header name

* copy edit

* update response path through Pomerium

* clarify SA name is user in policy creation

Co-authored-by: Alex Fornuto <afornuto@pomerium.com>
  • Loading branch information
backport-actions-token[bot] and Alex Fornuto committed Sep 15, 2021
1 parent d88cdb6 commit a3005bf
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
"Pomerium is a beyond-corp inspired, zero trust, open source identity-aware access proxy.",
plugins: [
"vuepress-plugin-element-tabs",
'vuepress-plugin-mermaidjs',
[
"check-md",
{
Expand Down Expand Up @@ -136,6 +137,7 @@ module.exports = {
"topics/certificates",
"topics/data-storage",
"topics/getting-users-identity",
"topics/original-request-context",
"topics/kubernetes-integration",
"topics/production-deployment",
"topics/programmatic-access",
Expand Down Expand Up @@ -216,6 +218,7 @@ module.exports = {
],
},
"prometheus",
"service-accounts",
{
title: "Reference",
type: "group",
Expand Down
123 changes: 123 additions & 0 deletions docs/docs/topics/original-request-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
title: Original User Context
description: This article describes how the original user context is passed secondary requests.
sidebarDepth: 0
---

# Original User Context

::: tip
This article describes a use case available to [Pomerium Enterprise](/enterprise/about.md) customers.
:::

In enterprise environments where multiple services protected by Pomerium communicate with each other using a [service account](/enterprise/concepts.md#service-accounts), there are scenarios where the original user context must be preserved. This article describes how this is accomplished with the `X-Pomerium-Jwt-Assertion-For` header.

## Abstract

When a User communicates with a service downstream of Pomerium, the service can identify that user by the `X-Pomerium-JWT-Assertion` header, added by Pomerium, which provides as a value a Java Web Token (**JWT**) identifying the user.

Should that service need to communicate with another Pomerium-protected service to construct the response, that connection should be authorized through Pomerium with a [Service Account](/enterprise/concepts.md#service-accounts). Service accounts should be provided to Pomerium from the first service as a bearer token header, i.e. `Authorization: Bearer Pomerium-${service_acount_jwt}`. This header is how the secondary service authenticates the machine-to-machine interaction.

Should that second service need to know the original user context to return the proper response, it would have no way of knowing, as the first service authenticated as the service account and not the original user.

The solution is to have the first service forward the headers, including `X-Pomerium-JWT-Assertion`, in its request to the second service. When Pomerium receives a request that already includes this header, it passes the value to the second service in the `X-Pomerium-JWT-Assertion-For` header. This header can be read by the secondary service to identify the user context.

Should the second service need to communicate with a tertiary service (or more), it can also pass along the original headers it received. When Pomerium receives a request with the `X-Pomerium-JWT-Assertion-For` header, it preserves the value as provided to all additional upstream services.

## Example

Let's look at two example routes, App and API:

```yaml
routes:
- name: App
from: https://app.localhost.pomerium.io
to: https://appserver.local
pass_identity_headers: true
policy:
- allow:
or:
- domain:
is: companydomain.com
- name: API
from: https://api.localhost.pomerium.io
to: https://apiserver.local
pass_identity_headers: true
policy:
- allow:
or:
- user:
is: api-access
```

- **App** is a user-facing application. Users connect to it through Pomerium.
- **API** is also accessed through it's Pomerium Route, but is only accessible by the **App**, using a [service account](/enterprise/service-accounts.md) to authenticate.
- The **API** service needs to know the user making the request to **App** in order to formulate the correct response.

Both Routes include [`pass_identity_headers`](/reference.md#pass-identity-headers), which provides (at minimum) the `X-Pomerium-Jwt-Assertion` header to the downstream application.

When a user makes a request that requires data from the API service, the following happens:

```mermaid
sequenceDiagram
autonumber
participant Browser
participant Pomerium
participant App
participant API
participant API2
Browser->>Pomerium: Authenticated Request
rect rgba(197, 183, 221)
Pomerium-->>App: X-Pomerium-JWT-Assertion (User JWT)
%%rect rgba(0, 255, 255)
Note over App: "App" copies X-Pomerium-JWT-Assertion <br/>from the original request
App-->>Pomerium: X-Pomerium-JWT-Assertion (User JWT)<br/> Service Account Bearer Token
Note over Pomerium, App: "App" calls "API" through Pomerium.
Pomerium-->>API: X-Pomerium-JWT-Assertion (Service Account) <br/>X-Pomerium-JWT-Assertion-For (User JWT)
Note over Pomerium, API: Pomerium copies X-Pomerium-JWT-Assertion as X-Pomerium-JWT-Assertion-For
%% end
Note over API: The "API" service uses <br/>X-Pomerium-JWT-Assertion-For <br/>To identify context
API-->>Pomerium: Response
Pomerium-->>App: Response
App-->>Pomerium: Result Page
end
Pomerium->>Browser: Result Page
```

1. The user first authenticates with Pomerium.

1. As the traffic is passed to **App**, Pomerium adds the `X-Pomerium-Jwt-Assertion` header with the JWT for the user as a value.

1. The **App** software has been configured by the software developer to send a request to **API**, using a bearer token to authenticate as the service account, with the original `X-Pomerium-Jwt-Assertion` header forwarded as well.

1. Pomerium, seeing that the incoming request already includes `X-Pomerium-JTW-Assertion` copies the value to `X-Pomerium-Jwt-Assertion-For` and includes it in the request to **API**. `X-Pomerium-Jwt-Assertion` now contains the service account bearer token

1. Now the **API** can service can read `X-Pomerium-Jwt-Assertion-For` as needed to determine the proper response to send to **App** (through Pomerium), which can then construct the results page for the user.


### Secondary Requests

Suppose the **API** service needed to connect to another machine interface (which we'll call **API2**) for additional information, and this call also needs the original user context.

If **API** is configured to pass along the `X-Pomerium-JWT-Assertion-For` header, Pomerium will recognize and preserve this. All subsequent connections with this header forwarded will perpetuate the original user context.

```mermaid
sequenceDiagram
autonumber
participant Browser
participant Pomerium
participant App
participant API
participant API2
rect rgba(197, 183, 221)
opt Secondary Request
Note over API, Pomerium: "API" is configured to pass all headers from the original request.
API-->>Pomerium: X-Pomerium-JWT-Assertion (Service Account Bearer Token) <br/>X-Pomerium-JWT-Assertion-For (User JWT)
Note over Pomerium, API2: Pomerium preserves X-Pomerium-JWT-Assertion-For as provided by "API"
Pomerium-->> API2: X-Pomerium-JWT-Assertion (Service Account) <br/>X-Pomerium-JWT-Assertion-For (User JWT)
Note over API2: "API2" reads X-Pomerium-JWT-Assertion-For <br/>for user context
API2-->>Pomerium: Response
Pomerium-->>API: Response
end
end
```
Binary file added docs/enterprise/img/add-service-account.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/enterprise/img/create-service-account.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/enterprise/img/service-account-jwt.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/enterprise/img/service-account-policy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/enterprise/img/user-id.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions docs/enterprise/service-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: Service Accounts
sidebarDepth: 1
description: Protect internal machine resources with service accounts.
---

# Service Accounts

Service accounts offer a protected and standardized method of authenticating machine-to-machine communication between services protected by Pomerium.

## Create a Service Account

::: tip
Before you begin, confirm you are in the correct Namespace. A service account can only be used in the Namespace it was created in, including its children Namespaces.
:::

1. From the main menu, select **Service Accounts** under **CONFIGURE**. Click the **+ ADD SERVICE ACCOUNT** button:

![An empty Service Accounts page](./img/add-service-account.png)

1. Service accounts can be unique and exist only for Pomerium, or impersonate directory users from your IdP.

::::: tabs
:::: tab Unique
Give the user a unique ID. Consider referencing the Namespace you're creating it under, for easier reference later. Optionally set an expiration date:

![Adding a unique service account](./img/create-service-account.png)

The user ID set here corresponds to the `User` criteria when editing a policy.
::::
:::: tab Impersonated
You can find your User ID by going to the special endpoint `/.pomerium`, or selecting **Logout** under your user in the upper right hand corner (this will not immediately log you out):

![Session Details](./img/user-id.png)

Copy the User ID and paste it into the **User ID** field in the **Add Service Account** modal. The lookahead search should show you the user name You can also optionally set an expiration date:

![Adding an impersonated service account](./img/create-impersonated-service-account.png)
::::
:::::

1. After you click **Submit**, the modal presents the Java Web Token (**JWT**) for the service account. Temporarily save it somewhere secure, as you will not be able to view it again:

![Service Account Added](./img/service-account-jwt.png)

This JWT must be added to your application configuration to enable direct communication.

1. Edit or create policies to give the service account access to the internal service:

![An example policy for a service account](./img/service-account-policy.png)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"vuepress": "1.8.2",
"vuepress-plugin-check-md": "0.0.2",
"vuepress-plugin-element-tabs": "^0.2.8",
"vuepress-plugin-mermaidjs": "^1.8.1",
"vuepress-plugin-sitemap": "2.3.1"
},
"scripts": {
Expand Down

0 comments on commit a3005bf

Please sign in to comment.