Skip to content

Commit

Permalink
docs: add a document to put an app behind gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
f-necas committed Dec 2, 2024
1 parent 40a8b0b commit 4b32d7d
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions docs/gatewify-an-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Gateway

![gateway](https://github.com/georchestra/georchestra-gateway/actions/workflows/docker.yml/badge.svg)

The gateway belongs to geOrchestra core, since it is the component which :

* handles user sessions
* routes requests to webapps

The behavior is controlled by the files from the `<datadir_root>/security-proxy` folder, which can be found [here](https://github.com/georchestra/datadir/tree/master/security-proxy)

## How-to integrate a new application in geOrchestra ?

The goal here is to benefit from the [SSO](https://en.wikipedia.org/wiki/Single_sign-on) feature for the new application without having to use an external application.

### Gateway configuration

1. **Put application behind the gateway**

The new application has to be proxified by the gateway first.

It can be done in the [routes.yaml](https://github.com/georchestra/datadir/blob/master/gateway/routes.yaml) file, which can be found in geOrchestra datadir. Remember: changes in this file requires to restart the gateway.
This file maps public URLs to internal (private) URLs.

To do so, add a new line in the file under `georchestra.gateway.services` in order to map the internal service:

```yaml
georchestra.gateway.services:
myapp.target: http://localhost:8280/app_path/
```
Then, in order to map the public URL to the internal service, add a new config under `georchestra.cloud.gateway.routes`:

```yaml
spring:
cloud:
gateway:
routes:
- id: myappname
uri: ${georchestra.gateway.services.myapp.target}
predicates:
- Path=/app_path,/app_path/**
```

- `id` is the name of the route.
- `uri` must point to internal target.
- `predicates` is mostly used with Path predicate, but we can use [other predicates](https://cloud.spring.io/spring-cloud-gateway/multi/multi_gateway-request-predicates-factories.html) like Host, Method, etc.

For instance, both combined, the `app_path` path on gateway's host will be routed to http://localhost:8280/app_path".

2. Set security rules

Now, imagine your application has a public frontend (`/newapp/frontend`) and a private backend (`/newapp/backend`).
You probably would like to restrict backend access to administrators, or people having a specific role.

This can be done very easily with the [gateway.yaml](https://github.com/georchestra/datadir/blob/master/gateway/gateway.yaml) file, also from the geOrchestra datadir.
Here, rules are defined under `georchestra.gateway.services`.

```yaml
georchestra:
gateway:
services:
nawappbackend:
target: http://localhost:8080/newapp/frontend/
access-rules:
- intercept-url: /newapp/backend/admin*
allowed-roles: ADMINISTRATOR
- intercept-url: /newapp/backend/public*
anonymous: true
newappfrontend:
target: http://localhost:80/newapp/frontend
access-rules:
- intercept-url: /newapp/**
anonymous: true
headers:
proxy: true
username: false
roles: false
org: false
orgname: true
json-user: true
```

### Application configuration

#### Headers

As you can see above, the `newappfrontend` service has a header section, which is used to override the default headers that will be sent to the frontend application.

In `gateway.yaml` as the start of the file, you can modify the default headers sent to apps :

```yaml
georchestra:
gateway:
default-headers:
proxy: true
...
json-user: true
```


It is also possible to create a specific role in the console app which grants access to the backend, eg with role `NEWAPP_ADMIN`.

#### How it works

With every request, the proxy adds specific HTTP headers, allowing the application to know:

* if the request comes from a registered user, or an anonymous one - this is `sec-username` (not provided if anonymous).
* which roles the user holds - `sec-roles` is a semi-colon separated list of roles (not provided if anonymous).
* which organisation the user belongs to - `sec-orgname` provides the human-readable organisation title while `sec-org` is mapped onto the organisation id (LDAP's `cn`).

Several other user properties are also provided as headers:

* `sec-proxy` tells that request comes from the proxy
* `sec-email` is the user email
* `sec-firstname` is the first name (LDAP `givenName`)
* `sec-lastname` is the second name (LDAP `sn`)
* `sec-tel` is the user phone number (LDAP `telephoneNumber`)

You can find full configuration in [HeaderMappings.java](https://github.com/georchestra/georchestra-gateway/blob/main/gateway/src/main/java/org/georchestra/gateway/model/HeaderMappings.java) file. You just need to rename fields from camelCase (in java file) to kebab-case (in yaml file).

The application handles requests appropriately thanks to the headers received.
Some applications will require a direct connection to the LDAP (where users, roles and organisations objects are stored), for instance to list all organisations.

#### Entrypoints

The login entrypoint is `/login` but more generally, one uses the `login` GET parameter in any querystring to force login into a given application.
As a result, the new application may generate links like these: `/newapp/frontend/?login`, for instance if some features in the frontend are only available when authenticated.

Logout entrypoint is `/logout`.
Password recovery form is available from `/console/account/passwordRecovery`.
Account creation form can be found at `/console/account/new`.


#### Cookie Affinity Mapping

Sometimes, cookies sent by one backing service need to be readable by another.
The Gateway will set a cookie path to all backend service cookies to match the service base path (for example,
all cookies sent by the `console` application will have their path set to `/console`.
This makes it impossible for other applications to read them.
A clear case is when the `datahub` application, under the `/datahub` context path, needs access to the
GeoNetwork `XSRF-TOKEN` issued cookie.

**Cookie Affinity Mapping** allows to duplicate cookies set to one path with another path. For the example above,
we need to make it so the GeoNetwork `XSRF-TOKEN` cookie is sent twice to the client, once with `Path=/geonetwork`
and once with `Path=/datahub`.

`gateway.yaml` can be used to configure such cookie affinity. It shall contain an array of objects like the following:

```yaml
spring:
cloud:
gateway:
routes:
- id: geonetwork
uri: ${georchestra.gateway.services.geonetwork.target}
predicates:
- Path=/geonetwork/**
filters:
- name: CookieAffinity
args:
name: XSRF-TOKEN
from: /geonetwork
to: /datahub
```

The `name` property indicates the cookie name, the `from` property indicates from which original path the cookie
will be duplicated, and the `to` property which path to duplicate the cookie with.

0 comments on commit 4b32d7d

Please sign in to comment.