From 952d27c7639a80b5daddb702a6f790e855b1422b Mon Sep 17 00:00:00 2001 From: Aeneas Date: Sat, 18 Nov 2017 14:01:03 +0100 Subject: [PATCH] docs: Wrote basic developer guide --- book.json | 5 + cmd/serve.go | 10 +- docs/README.md | 13 + docs/SUMMARY.md | 11 + docs/concepts.md | 50 +++ docs/deployment.md | 46 +++ docs/diagrams/api_router_layout.mermaid | 8 + docs/diagrams/id_token.mermaid | 8 + docs/diagrams/invalid_access_request.mermaid | 15 + docs/diagrams/sidecar_layout.mermaid | 18 + docs/diagrams/valid_access_request.mermaid | 21 ++ docs/images/api_router_layout.svg | 360 +++++++++++++++++++ docs/images/gateway_deployment.svg | 360 +++++++++++++++++++ docs/images/id_token.svg | 360 +++++++++++++++++++ docs/images/invalid_access_request.svg | 360 +++++++++++++++++++ docs/images/sidecar_deployment.svg | 360 +++++++++++++++++++ docs/images/valid_access_request.svg | 360 +++++++++++++++++++ docs/quickstart.md | 74 ++++ docs/security.md | 78 ++++ 19 files changed, 2508 insertions(+), 9 deletions(-) create mode 100644 book.json create mode 100644 docs/README.md create mode 100644 docs/SUMMARY.md create mode 100644 docs/concepts.md create mode 100644 docs/deployment.md create mode 100644 docs/diagrams/api_router_layout.mermaid create mode 100644 docs/diagrams/id_token.mermaid create mode 100644 docs/diagrams/invalid_access_request.mermaid create mode 100644 docs/diagrams/sidecar_layout.mermaid create mode 100644 docs/diagrams/valid_access_request.mermaid create mode 100644 docs/images/api_router_layout.svg create mode 100644 docs/images/gateway_deployment.svg create mode 100644 docs/images/id_token.svg create mode 100644 docs/images/invalid_access_request.svg create mode 100644 docs/images/sidecar_deployment.svg create mode 100644 docs/images/valid_access_request.svg create mode 100644 docs/quickstart.md create mode 100644 docs/security.md diff --git a/book.json b/book.json new file mode 100644 index 0000000000..dea4c05929 --- /dev/null +++ b/book.json @@ -0,0 +1,5 @@ +{ + "root": "./docs", + "author": "Aeneas Rekkas", + "gitbook": ">=3.2.0" +} \ No newline at end of file diff --git a/cmd/serve.go b/cmd/serve.go index ef6f41b2c4..81b33c8320 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -23,16 +23,8 @@ import ( // serveCmd represents the serve command var serveCmd = &cobra.Command{ Use: "serve", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println("serve called") + fmt.Println(cmd.UsageString()) }, } diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..632cf212a5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +# ORY Oathkeeper + +## Introduction + +Welcome to the ORY Oathkeeper documentation! + +ORY Oathkeeper is a reverse proxy which evaluates incoming HTTP requests based on a set of rules. This capability +is referred to as an Identity and Access Proxy (IAP) in the context of [BeyondCorp and ZeroTrust](https://www.beyondcorp.com). + +In principal, ORY Oathkeeper inspects the `Authorization` header and the full request url (e.g. `https://mydomain.com/api/foo`) +of incoming HTTP requests, applies a given rule, and either grants access to the requested url or denies access. The +decision of whether to allow or deny the access request is made using ORY Hydra. Please keep in mind that ORY Oathkeeper +is a supplement to ORY Hydra. It is thus imperative to be familiar with the core concepts of ORY Hydra. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 0000000000..d71b3bffac --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,11 @@ +- [Concepts](concepts.md#concepts) + - [Terminology](concepts.md#terminology) + - [Rules](concepts.md#rules) + - [Rules REST API](concepts.md#rules-rest-api) + - [Rules CLI API](#rules-cli-api) +- [Deployment](deployment.md#deployment) + - [Gateway](deployment.md#gateway) + - [Sidecar](deployment.md#sidecar) +- [Security](security.md#security) + - [Stateless Authorization](security.md#stateless-authorization) + - [The ID Token](security.md#the-id-token) diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 0000000000..774ec0e0b2 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,50 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Concepts](#concepts) + - [Terminology](#terminology) + - [Rules](#rules) + - [Rules REST API](#rules-rest-api) + - [Rules CLI API](#rules-cli-api) + + + +# Concepts + +This section covers core concepts of ORY Oathekeeper. + +## Terminology + +* Access credentials: The credentials used to access an endpoint. This is typically an OAuth 2.0 access token supplied + by ORY Hydra. +* Backend URL: The URL (backend) where requests will be forwarded to, if access is granted. Typically an API Gateway + such as Mashape Kong. +* Scopes: An OAuth 2.0 Scope. +* Access Control Policies: These are JSON documents similar to AWS and Google Cloud Policies. Read more about them [here](https://ory.gitbooks.io/hydra/content/security.html#how-does-access-control-work-with-hydra). + +## Rules + +ORY Oathkeeper has a configurable set of rules. Rules are applied to all incoming requests and based on the rule definition, +an action is taken. There are four types of rules: + +1. **Passthrough**: Forwards the original request to the backend url without any modification to its headers. +2. **Anonymous**: Tries to extract user information from the given access credentials. If that fails, or no access + credentials have been provided, the request is forwarded and the user is marked as "anonymous". +3. **Basic Authorization**: Requires valid access credentials and optionally checks for a set of OAuth 2.0 Scopes. If + the supplied access credentials are invalid (expired, malformed, revoked) or do not fulfill the requested scopes, + access is denied. +4. **Policy Authorization**: Requires valid access credentials as defined in 3. and additionally validates if the user + is authorized to make the request, based on access control policies. + +The exact payloads are explained in detail in the REST API (see next section). + +### Rules REST API + +For more information on available fields and exemplary payloads of rules, as well as rule management using HTTP +please refer to the [REST API docs](https://oathkeeper.docs.apiary.io/#) + +### Rules CLI API + +Management of rules is not only possible through the REST API, but additionally using the ORY Oathkeeper CLI. +For help on how to manage the CLI, type `oathkeeper help rules`. diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000000..8198646a00 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,46 @@ + + +**Table of Contents** + +- [Deployment](#deployment) + - [Gateway](#gateway) + - [Sidecar](#sidecar) + + + +# Deployment + +ORY Oathkeeper is developed as a native cloud application. As such, it is completely stateless, does not require +configuration files and does not persist anything to disk. It is possible to restart ORY Oathkeeper without any side-effects +as ORY Oathkeeper supports graceful HTTP shutdown. + +ORY Oathkeeper works natively with Load Balancers and auto-scaling groups. It is configured using environment variables +and relies on a PostgreSQL / MySQL database instance. To answer access requests quickly, rules are cached in-memory +and refreshed within a given interval (e.g. 30 seconds). + +ORY Oathkeeper has a tiny footprint as it is compiled to native bytecode. The docker image is a mere 5 MB, the memory +footprint similarly low and the CPU usage is, on average, close to zero. + +Thus, two possible deployment layouts are possible for ORY Oathkeeper, which are covered in the next two sections. + +## Gateway + +In the gateway deployment scenario, ORY Oathkeeper is the single point of entry for all API endpoints. Oathkeeper might +sit behind a load balancer, but is generally deployed in front of the API router. Any incoming requests first +pass ORY Oathkeeper, then the API router, and finally reach the API endpoint service. + +![Gateway Oathkeeper Deployment Layout](images/gateway_deployment.svg) + +The advantage of this deployment layout is that there is only one deployment of ORY Oathkeeper that needs to be maintained. +The disadvantage is that it is not possible to talk to API endpoints directly without going through the full +reverse proxy chain. + +## Sidecar + +In the sidecar deployment scenario, ORY Oathkeeper is deployed alongside each API endpoint service, probably even +within the same logical unit (e.g. Docker Container or VM) as the service itself. + +![Gateway Oathkeeper Deployment Layout](images/sidecar_layout.svg) + +The advantage of this deployment layout is that requests to the API endpoints are possible without passing through +the API router. The disadvantage is that multiple instances of ORY Oathkeeper have to be maintained. diff --git a/docs/diagrams/api_router_layout.mermaid b/docs/diagrams/api_router_layout.mermaid new file mode 100644 index 0000000000..5e8988123d --- /dev/null +++ b/docs/diagrams/api_router_layout.mermaid @@ -0,0 +1,8 @@ +graph LR + + UA[User Agent] --- FW[ORY Oathkeeper] + + FW --- APIR[API Router] + APIR --- API1[API Endpoint 1] + APIR --- API2[API Endpoint 2] + APIR --- API3[API Endpoint 3] diff --git a/docs/diagrams/id_token.mermaid b/docs/diagrams/id_token.mermaid new file mode 100644 index 0000000000..5161b45be0 --- /dev/null +++ b/docs/diagrams/id_token.mermaid @@ -0,0 +1,8 @@ +sequenceDiagram + participant UA as User Agent + participant FW as ORY Oathkeeper + participant API as API Endpoint + + UA->>FW: Authorization: bearer Opaque.Token + FW->>API: Authorization: bearer JWT.ID.Token + API->>API: Decodes & validates JWT.ID.Token using Oathkeeper's public key diff --git a/docs/diagrams/invalid_access_request.mermaid b/docs/diagrams/invalid_access_request.mermaid new file mode 100644 index 0000000000..230d3ef0cd --- /dev/null +++ b/docs/diagrams/invalid_access_request.mermaid @@ -0,0 +1,15 @@ +sequenceDiagram + participant UA as User Agent + participant FW as ORY Oathkeeper + participant OA2 as ORY Hydra + participant API as API Endpoint + + UA->>FW: HTTP Request with opaque access token + activate FW + FW->>OA2: Ask for access request validation + activate OA2 + OA2->>OA2: Process access request + OA2->>FW: Forbid access request + deactivate OA2 + FW->>UA: HTTP 403 Forbidden + deactivate FW diff --git a/docs/diagrams/sidecar_layout.mermaid b/docs/diagrams/sidecar_layout.mermaid new file mode 100644 index 0000000000..28d7715cf9 --- /dev/null +++ b/docs/diagrams/sidecar_layout.mermaid @@ -0,0 +1,18 @@ +graph LR + + UA[User Agent] --- APIR[API Router] + + APIR --- FW1[ORY Oathkeeper] + subgraph Service 1 + FW1 --- API1[API Endpoint 1] + end + + APIR --- FW2[ORY Oathkeeper] + subgraph Service 2 + FW2 --- API2[API Endpoint 2] + end + + APIR --- FW3[ORY Oathkeeper] + subgraph Service 3 + FW3 --- API3[API Endpoint 3] + end \ No newline at end of file diff --git a/docs/diagrams/valid_access_request.mermaid b/docs/diagrams/valid_access_request.mermaid new file mode 100644 index 0000000000..392c852f01 --- /dev/null +++ b/docs/diagrams/valid_access_request.mermaid @@ -0,0 +1,21 @@ +sequenceDiagram + participant UA as User Agent + participant FW as ORY Oathkeeper + participant OA2 as ORY Hydra + participant API as API Endpoint + + UA->>FW: HTTP Request with opaque access token + activate FW + FW->>OA2: Ask for access request validation + activate OA2 + OA2->>OA2: Process access request + OA2->>FW: Approve request and return identity information + deactivate OA2 + FW->>FW: Generate JWT ID Token + FW->>API: Forward request with ID Token + activate API + API->>API: Process request with user info from ID token + API->>FW: Return response + deactivate API + FW->>UA: Forward response + deactivate FW diff --git a/docs/images/api_router_layout.svg b/docs/images/api_router_layout.svg new file mode 100644 index 0000000000..fcdd0d08aa --- /dev/null +++ b/docs/images/api_router_layout.svg @@ -0,0 +1,360 @@ +
ORY Gatekeeper
Access request validation
ORY Oathkeeper
ORY Hydra
User Agent
API Endpoint
\ No newline at end of file diff --git a/docs/images/gateway_deployment.svg b/docs/images/gateway_deployment.svg new file mode 100644 index 0000000000..30f82446c6 --- /dev/null +++ b/docs/images/gateway_deployment.svg @@ -0,0 +1,360 @@ +
User Agent
ORY Oathkeeper
API Router
API Endpoint 1
API Endpoint 2
API Endpoint 3
\ No newline at end of file diff --git a/docs/images/id_token.svg b/docs/images/id_token.svg new file mode 100644 index 0000000000..571cf01f71 --- /dev/null +++ b/docs/images/id_token.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperAPI EndpointAuthorization: bearer Opaque.TokenAuthorization: bearer JWT.ID.TokenDecodes & validates JWT.ID.Token using Oathkeeper's public keyUser AgentORY OathkeeperAPI Endpoint \ No newline at end of file diff --git a/docs/images/invalid_access_request.svg b/docs/images/invalid_access_request.svg new file mode 100644 index 0000000000..f6d2f3e96d --- /dev/null +++ b/docs/images/invalid_access_request.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperORY HydraAPI EndpointHTTP Request with opaque access tokenAsk for access request validationProcess access requestForbid access requestHTTP 403 ForbiddenUser AgentORY OathkeeperORY HydraAPI Endpoint \ No newline at end of file diff --git a/docs/images/sidecar_deployment.svg b/docs/images/sidecar_deployment.svg new file mode 100644 index 0000000000..31c2b07c06 --- /dev/null +++ b/docs/images/sidecar_deployment.svg @@ -0,0 +1,360 @@ +
Service 3
Service 2
Service 1
ORY Oathkeeper
API Endpoint 3
ORY Oathkeeper
API Endpoint 2
ORY Oathkeeper
API Endpoint 1
User Agent
API Router
\ No newline at end of file diff --git a/docs/images/valid_access_request.svg b/docs/images/valid_access_request.svg new file mode 100644 index 0000000000..fb4c54afec --- /dev/null +++ b/docs/images/valid_access_request.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperORY HydraAPI EndpointHTTP Request with opaque access tokenAsk for access request validationProcess access requestApprove request and return identity informationGenerate JWT ID TokenForward request with ID TokenProcess request with user info from ID tokenReturn responseForward responseUser AgentORY OathkeeperORY HydraAPI Endpoint \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000000..8cab3cdeff --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,74 @@ +# Quickstart + +This section covers how to get started using ORY Oathkeeper. ORY Oathkeeper has two servers that run on separate ports: + +* The management server: This server is responsible for exposing the management REST API. +* The proxy server: This server is responsible for evaluating access requests and forwarding them to the backend. + +For detailed documentation on the two servers, run `oathkeeper help serve management` and `oathkeeper help serve proxy`. + +ORY Oathkeeper supports two types of storage backends: In-memory and MySQL/PostgreSQL. The former is well suited +for testing while the latter should be used in production. For brevity, we will use the in-memory adapters for this +quickstart tutorial. + +First, we need to set up an ORY Hydra instance as instructed [here](https://ory.gitbooks.io/hydra/content/install.html). +We also need to create an OAuth 2.0 client capable of making requests to ORY Hydra's Warden API. For this purpose we +create two files: + +**client.json** +```json +{ + "id": "oathkeeper-client", + "client_secret": "something-secure", + "scope": "hydra.warden", + "grant_types": ["client_credentials"], + "response_types": ["token"] +} +``` + +**policy.json** +```json +{ + "id": "oathkeeper-policy", + "subjects": [ + "oathkeeper-client" + ], + "effect": "allow", + "resources": [ + "rn:hydra:warden:allowed", + "rn:hydra:warden:token:allowed" + ], + "actions": [ + "decide" + ] +} +``` + +and import them using ORY Hydra's CLI: + +``` +$ hydra clients import client.json +$ hydra policies create -f policy.json +``` + +Now we are all set to boot up ORY Oathkeeper. Because we are using the in-memory database backend, we have to use +`oathkeeper serve all`, otherwise the proxy and management process would not be able to talk to each other: + +``` +# We assume that ORY Hydra is running on localhost:4444 +$ export HYDRA_URL=http://localhost:4444/ + +# These are the values from the client.json file +$ export HYDRA_CLIENT_ID=oathkeeper-client +$ export HYDRA_CLIENT_SECRET=something-secure + +# This needs to be a URL of your HTTP endpoint. +$ export BACKEND_URL=http://my-api-endpoint-servers.com/ + +# This should be a proper database URL in production, see `hydra help serve proxy` +$ export DATABASE_URL=memory + +$ oathkeeper serve all +``` + +Now, we have a proxy listening on port 4455 and the management REST API at port 4456. \ No newline at end of file diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000000..f11dc6f790 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,78 @@ + + +**Table of Contents** + +- [Security](#security) + - [Stateless Authorization](#stateless-authorization) + - [The ID Token](#the-id-token) + + + +# Security + +ORY Oathkeeper, as all ORY products, takes security extremely serious. To reduce risk, ORY Hydra issues opaque access +tokens which are impossible to decode by third parties. Internal services however often rely on stateless authorization +to process requests quickly. The next section covers how ORY Oathkeeper achieves the best of both worlds. + +ORY Oathkeeper makes authentication and authorization data consumable to backends by transforming the original +opaque access token to a consumable ID Token (a signed JWT using the RS256 algorithm) as specified +by the OpenID Connect specification and [BeyondCorp](https://www.beyondcorp.com). + +## Stateless Authorization + +Given an HTTP request to a resource that is protected by ORY Oathkeeper: + +``` +GET /api/resource HTTP/1.1 +Host: www.example.com +Authorization: bearer +``` + +Assumed that ORY Oathkeeper is granting the access request, the `` will be replaced with a +JSON Web Token that is signed using the asymmetric RS256 key: + +``` +GET /api/resource HTTP/1.1 +Host: internal-api-endpoint-dns +Authorization: bearer +``` + +Now, the protected resource is capable of decoding and validating the JSON Web Token using the public key supplied +by ORY Oathkeeper's API. + +![ID Token Transformation](images/id_token.svg) + +## The ID Token + +The ID Token contains the following payload keys: + +* `iss`: Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme + that contains scheme, host, and optionally, port number and path components and no query or fragment components. + Typically, this is the URL of ORY Oathkeeper, for example: `https://oathkeeper.myapi.com`. +* `sub`: Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which + is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It must not + exceed 255 ASCII characters in length. The sub value is a case sensitive string. The End-User might also + be an OAuth 2.0 Client, given that the access token was granted using the OAuth 2.0 Client Credentials flow. +* `aud`: Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party + as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an + array of case sensitive strings. +* `exp`: Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this + parameter requires that the current date/time MUST be before the expiration date/time listed in the value. + Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the + date/time. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular. +* `iat`: Time at which the JWT was issued. Its value is a JSON number representing the number of seconds + from 1970-01-01T0:0:0Z as measured in UTC until the date/time. +* `jti`: A cryptographically strong random identifier to ensure the ID Token's uniqueness. + +Example: + +``` +{ + "iss": "https://server.example.com", + "sub": "24400320", + "aud": "s6BhdRkqt3", + "jti": "n-0S6_WzA2Mj", + "exp": 1311281970, + "iat": 1311280970, +} +```