2. Architecture and Network

2.1. Architecture

To facilitate connections between federated backends, two new components are added to each backend: Ingress and Federator. The ingress is, as the name suggests the ingress point for incoming connections, where requests from other backends are then forwarded to the federator, which further processes the requests. In addition, the federator also acts as egress point for requests from internal backend components to other, remote backends.

../../_images/federated-backend-architecture.png

2.1.1. Backend domains

A backend is identified by its infrastructure domain (or infra domain for short). This is the domain that the backend uses to authenticate towards other backends.

Similarly, there is the backend domain, which is used to qualify the names and identifiers of users local to an individual backend in the context of federation. See Qualified Identifiers and Names for more information. The owner of the backend domain has to specify an infra domain under which the Wire backend representing their domain is reachable.

2.1.2. Backend components

In addition to the regular components of a Wire backend, two additional components are added to enable federation with other backends: The Ingress and the Federator. Other Wire components use these two components to contact other backends and respond to queries originating from remote backends.

The following subsections briefly introduce the individual components, their state and their functionality. The semantics of backend-to-backend communication will be explained in more detail in Section 2.2.

2.1.2.1. Ingress

The ingress is a kubernetes ingress and uses nginx as its underlying software.

It is configured with a set of X.509 certificates, which acts as root of trust for the authentication of the infra domain of remote backends, as well as with a certificate, which it uses to authenticate itself toward other backends.

Its functions are:

  • terminate TLS connections

    • perform mutual Authentication as part of the TLS connection establishment

  • forward requests to the local Federator instance

2.1.2.2. Federator

Warning

As of July 2021, authentication is not fully implemented. See the section on Authentication for more details.

The federator acts as egress point for other backend components. It can be configured to use an allow list to authorize incoming and outgoing connections, and it keeps an X.509 client certificate for the backend’s infra domain to authenticate itself towards other backends. Additionally, it requires a connection to a DNS resolver to discover other backends.

When receiving a request from an internal component, the federator will:

  1. If enabled, ensure the target domain is in the allow list

  2. discover the other backend,

  3. establish a mutually authenticated channel to the other backend using its client certificate,

  4. send the request to the other backend and

  5. forward the response back to the originating component (and eventually to the originating Wire client).

The federator also implements the authorization logic for incoming requests and acts as intermediary between the Ingress and the internal components. The ‘federator’ will, for incoming requests from other backends (forwarded via the local Ingress):

  1. Discover the backend domain claimed by the other backend,

  2. if enabled, ensure that the backend domain of the other backend is in the allow list,

  3. normalize and sanitize the path component of the incoming request to ensure it’s recognizable as a federated request and

  4. forward requests to other wire-server components.

2.1.2.3. Other wire-server components

Components such as ‘brig’, ‘galley’, or ‘gundeck’ are responsible for actual business logic and interfacing with databases and non-federation related external services. See source code documentation. In the context of federation, their functions include:

For more information of the functionalities provided to remote backends through their federator, see the federated API documentation.

2.2. Backend to backend communication

We require communication between the federator of one (sending) backend another (receiving) backend to be both mutually authenticated and authorized. More specifically, both backends need to ensure the following:

Authentication

Determine the identity (infra domain name) of the other backend.

Discovery

Ensure that the other backend is authorized to represent the backend domain claimed by the other backend.

Authorization

Ensure that this backend is authorized to federate with the other backend.

2.2.1. Authentication

Warning

As of July 2021, the implementation of mutual backend-to-backend authentication is still work in progress. The behaviour described in this section should be considered a draft specification only.

Authentication between Wire backends is achieved using the mutual authentication feature of TLS as defined in RFC 8556.

In particular, this means that the ingress of each backend needs to be provisioned with one or more certificates which it trusts to authenticate certificates provided by other backends when accepting incoming connections from other backends.

Conversely, every federator needs to be provisioned with a (client) certificate which it uses to authenticate itself towards other backends.

Note that the client certificate is expected to be issued with the backend’s infra domain as the subject alternative name (SAN), which is defined in RFC 5280.

If a receiving backend fails to authenticate the client certificate, it should reply with an authentication error.

2.2.2. Discovery

The discovery process allows a backend to determine the infra domain of a given backend domain.

This step is necessary in two scenarios:

  • A backend would like to establish a connection to another backend that they only know the backend domain of. This is the case, for example, when a user of a local backend searchers for a qualified username, which only includes that user’s backends’s backend domain.

  • When receiving a message from another backend that authenticates with a given infra domain and claims to represent a given backend domain, a backend would like to ensure the backend domain owner authorized the owner of the infra domain to run their Wire backend.

To make discovery possible, any party hosting a Wire backend has to announce the the infra domain via a DNS SRV record as defined in RFC 2782 with service = wire-server-federator, proto = tcp and with name pointing to the backend’s domain and target to the backend’s infra domain.

For example, Company A with domain company-a.com and infra domain wire.company-a.com could publish

_wire-server-federator._tcp.company-a.com. 600  IN  SRV 10 5 443 federator.wire.company-a.com.

A backend can then be discovered, given its domain, by issueing a DNS query for the SRV record specifying the wire-server-federator service.

2.2.2.1. Caching

After retrieving the SRV record for a given domain, it caches the backend domain <–> infra domain mapping for the duration indicated in the TTL field of the record.

2.2.3. Authorization

After an incoming connection is authenticated, a second step is required to ensure that the sending backend is authorized to connect to the receiving backend. As the backend authenticates using its infra domain, but the allow list contains backend domains (which is not necessarily the same) the sending backend also needs to provide its backend domain.

To make this possible, requests to remote backends are required to contain a Wire-Domain header, which contains the remote backend’s domain.

While the receiving backend has authenticated the sending backend as the infra domain, it is not clear that the sending backend is indeed authorized by the owner of the backend domain to host the Wire backend of that particular domain.

To perform this extra authorization step, the receiving backend follows the process described in Discovery and compares the discovered infra domain for the backend domain indicated in the Wire-Domain header with the one the sending backend authenticated as. If there is a mismatch, the receiving backend replies with a discovery error.

Finally, the receiving backend checks if the domain of the sending backend is in the Domain Allow List and replies with an authorization error if it is not.

2.2.3.1. Domain Allow List

Federation can happen between any backends on a network (e.g. the open internet); or it can be restricted via server configuration to happen between a specified set of domains on an ‘allow list’. If an allow list is configured, then:

  • outgoing requests will only happen if the requested domain is contained in the allow list.

  • incoming requests: if the domain of the sending backend is not in the allow list, any request originating from that domain is replied to with an authorization error

2.2.3.2. Per-request authorization

In addition to the general authorization step that is performed by the federator when a new, mutually authenticated TLS connection is established, the component processing the request performs an additional, per-request authorization step.

How this step is performed depends on the API endpoint, the contents of the request and the context in which it is made.

See the documentation of the individual API endpoints for details.

2.2.4. Example

The following is an example for the message and information flow between a backend with backend domain a.com and infra domain infra.a.com and another backend with backend domain b.com and infra domain infra.b.com.

The content and format of the message is meant to be representative. For the definitions of the actual payloads, please see the federation API section.

The scenario is that the brig at infra.a.com has received a user search request from one of its clients.

../../_images/federation-flow.png