4 minute read

πŸ‡«πŸ‡· FR πŸ‡¬πŸ‡§ EN

First article in the Otoroshi + Clever Cloud series. We start with the foundational use case: a single backend application, exposed three different ways β€” without touching the code.


Aux Alentours par MAIF β€” context

Aux Alentours par MAIF is an application that lets users consult natural and technological risks from a given address, and get tailored prevention advice and solutions. The infrastructure consists of two applications deployed on Clever Cloud:

  • The web frontend β€” auxalentours.maif.fr, the user interface
  • The API β€” the backend that powers the frontend and exposes several types of data

Otoroshi is deployed on a Clever Cloud JVM scaler β€” a custom installation, because when the platform was first set up, the Otoroshi add-on did not exist yet. It sits in front of the entire platform: both the web frontend and the API.

The applications are deployed on internal domains (*.innovation.maif) and are never directly accessible. To enforce this, Otoroshi uses the exchange protocol: a mechanism that allows backends to verify that incoming requests have actually passed through Otoroshi, and reject them otherwise. Worth noting: Clever Cloud now offers Request Flow, a native feature that serves the same purpose without having to implement the challenge on the application side.

graph LR
    Users["πŸ‘€ Users"]
    Partners["🀝 Partners"]
    Oto["βš™οΈ Otoroshi
Clever Cloud JVM scaler"] Site["🌐 Web frontend
Clever Cloud"] API["πŸ–₯️ API
Clever Cloud"] Users --> Oto Partners -->|"dedicated API key"| Oto Oto -->|"exchange protocol"| Site Oto -->|"exchange protocol"| API

This article focuses on the API. It exposes three very different profiles: a secured REST API, public documentation, and a map tile service. Three reasons not to expose them the same way.


Three routes, one application

In Otoroshi, each route defines a frontend (incoming domain + path) and a backend (target). There is no need to create a separate backend object: the target is declared directly in the route. All three routes point to the same Clever Cloud application (app-xxxxxxxx.innovation.maif), but with different configurations.

graph TD
    R1["API route
api.auxalentours.innovation.maif
πŸ”‘ API key required"] R2["Doc route
api.auxalentours.innovation.maif/doc
🌍 Public access"] R3["Tiles route
tiles.auxalentours.innovation.maif
🌍 Public access"] Backend["πŸ–₯️ app-xxxxxxxx.innovation.maif
Aux Alentours par MAIF API"] R1 --> Backend R2 --> Backend R3 --> Backend

Plugins common to all routes

Four plugins are present on each route:

  • OverrideHost β€” replaces the Host header of the outgoing request with the backend hostname. Required for the application to receive the correct host.
  • ForceHttpsTraffic β€” automatically redirects HTTP requests to HTTPS.
  • DisableHttp10 β€” rejects HTTP/1.0 requests, which are obsolete and ill-suited to modern traffic volumes.
  • Robots β€” blocks search engine indexing (we’ll cover this in detail in article 2).

Route 1 β€” API secured by API keys

"frontend": {
  "domains": ["api.auxalentours.innovation.maif"],
  "strip_path": true
},
"backend": {
  "targets": [{ "hostname": "app-xxxxxxxx.innovation.maif" }],
  "root": "/api/"
}

Requests come in on api.auxalentours.innovation.maif. With strip_path: true and root: /api/, Otoroshi forwards to /api/<path> on the backend. The route does not filter by path β€” it covers the entire domain.

The ApikeyCalls plugin is added with mandatory: true β€” any request without a valid key receives a 401.

How API keys work in Otoroshi

An API key consists of a client ID and a client secret. Otoroshi supports several ways to transmit them; here we use the bearer token, via the standard HTTP header:

Authorization: Bearer otoapk_<api-key-id>_<hash>

The token is a dedicated token generated by Otoroshi, used directly in requests. It is validated on the Otoroshi side by the oto_bearer extractor of the ApikeyCalls plugin.

Quotas (requests per day, per month) can be configured, as well as IP or domain restrictions. The Aux Alentours par MAIF frontend has its own key to consume the API β€” as do partners, each with a dedicated key and adjusted limits. If a key is compromised, it can be revoked without impacting other clients.


Route 2 β€” Public documentation

"frontend": {
  "domains": ["api.auxalentours.innovation.maif/doc"],
  "strip_path": false
},
"backend": {
  "targets": [{ "hostname": "app-xxxxxxxx.innovation.maif" }],
  "root": "/"
}

The documentation is exposed on the same domain as the API (api.auxalentours.innovation.maif), but on the /doc path. With strip_path: false, the path is forwarded as-is to the backend.

No authentication plugin is added: the documentation is public. Otoroshi routes requests to /doc unconditionally, even though the main API route requires a key.


Route 3 β€” Tile API

"frontend": {
  "domains": ["tiles.auxalentours.innovation.maif"],
  "strip_path": true
},
"backend": {
  "targets": [{ "hostname": "app-xxxxxxxx.innovation.maif" }],
  "root": "/tiles/"
}

Map tiles are exposed on a dedicated subdomain (tiles.auxalentours.innovation.maif). With strip_path: true and root: /tiles/, Otoroshi forwards to /tiles/<z>/<x>/<y>.png on the backend.

Tiles are public β€” they are consumed directly by the browser, and a single map view can trigger dozens of parallel requests. Exposing tiles on a separate subdomain brings immediate architectural clarity and makes it easier to apply differentiated traffic policies β€” including caching, which we’ll cover in article 3.


Overview

Route Frontend Auth Notes
API api.auxalentours.innovation.maif API key ApikeyCalls mandatory
Documentation api.auxalentours.innovation.maif/doc None Public access
Tiles tiles.auxalentours.innovation.maif None β€”

Key takeaways

Otoroshi makes it possible to decouple an application’s exposure policy from its implementation. A single application can be accessed in radically different ways depending on the context, with no code changes and no additional deployments.

The granularity operates at two levels: the domain (dedicated subdomain for tiles) and the path (same domain, different path for documentation). In both cases, each route has its own plugin pipeline, completely independent from the others.

In the next article, we stay with Aux Alentours par MAIF for two concrete HTTP use cases (CORS and robots.txt), before moving on to independent scenarios (HTTP redirects, iframe).