Request Routing¶
Each incoming HTTP request is processed by a configurable routing engine. The engine matches every request against a list of virtual hosts. Subsequently the request is matched against a set of path based rules. The matching rule determines the actual action that is applied to the request. A request not matching any virtual host or rule is denied with 404 Not Found
.
The routing engine is configured via routing.yml
.
Note
The built-in routing engine can be bypassed based on custom inbound filters.
Virtual Hosts¶
Virtual hosts are referred to as vhosts in routing.yml
. The file requires a vhosts
key as top-level directive, under which a list of vhosts are declared.
A vhost specification consists of the following attributes:
- hostAddress
Mandatory: Target IP address to match against incoming request.
Can be specified explicitly or as a wildcard (*). The hostAddress can be either an IPv4 or IPv6 address. In case of IPv6 address the compressed and uncompressed notation is allowed.
- port
Optional: Target port to match against incoming request.
Can be specified explicitly or as a wildcard (*).
- hostNames
Optional: One more more host names, matched against the requested host.
Can be specified explicitly or as a wildcard (*), can not be specified with IPv6 notation.
- brand
Optional: A brand identifier that can be applied as header to forwarded requests in multi-tenant environments.
- rules
Mandatory: A list of path matching rules. See Path Matching.
- hostRewrite
Optional: A list of optional hostname rewrites. The requests original
Host
will be matched against the value in thefrom
parameter. Wildcards are allowed as prefix. TheHost
andX-Forwarded-Host
headers will be replaced with the value into
, ignoring all present values.
Note
To enable the host rewrite feature, the property has to be set and containing at least one from & to
key/value pair. Only the hostname is considered when trying to match the requests original host against the from
value, not the port or protocol type. Also, wildcards are allowed for subdomains in the from
value. For example: *.example.com
and sub.*.example.com
.
Example:
vhosts:
- hostAddress: 203.0.113.1
port: 80
hostNames:
- example.com
- example.org
hostRewrite:
- from: "*.example.com"
to: "rewrite.com"
brand: example
rules:
- path: /
...
- hostAddress: 1234:1234:0000:0000:0000:0000:3456:3434
port: 80
hostNames:
- example.com
- example.org
hostRewrite:
...
- hostAddress: 1235:1235::3457:3476
port: 80
hostNames:
- example.com
- example.org
hostRewrite:
...
Host matching¶
With the exception of the host address, all matching related attributes are optional. The default value is always *
.
The mechanism of finding the best matching vhost is based on the combination of matching attributes between request and vhost. These are prioritised as follows:
Priority |
Host address |
Port |
Hostname |
---|---|---|---|
1 |
match |
match |
- |
2 |
match |
* |
match |
3 |
* |
* |
* |
4 |
* |
match |
match |
5 |
* |
match |
* |
6 |
* |
* |
match |
7 |
* |
* |
* |
In general hostAddress > port > hostNames > *.
Hostnames are matched against requests Host
or X-Forwarded-Host
headers. If an X-Forwarded-Host
header was preserved according to configuration, its value is taken for matching and Host
is ignored.
Example configuration:
vhosts:
- hostAddress: 172.20.30.50
port: 9001
hostNames:
- www.example.org
brand: default
...
- hostAddress: 127.0.0.1
port: "*"
hostNames:
- localhost
brand: default
...
- hostAddress: "*"
port: 80
hostNames:
- webmail.example.com
- dav.example.com
brand: default
...
- hostAddress: "*"
port: 80
hostNames:
- "*"
brand: default
...
- hostAddress: "*"
port: "*"
hostNames:
- www.example2.org
brand: default
...
- hostAddress: "*"
port: "*"
hostNames:
- "*"
brand: default
...
- hostAddress: "*"
brand: default
...
Path-based rules¶
The rules
attribute of a vhost
contains a list of path matching rules and associated proxy actions. The best match for a given request decides what action is applied to this request.
A rule specification consists of these attributes:
- path
Mandatory: A path match expression
- action
Mandatory: An action specification
- restrictions
Optional: List of restrictions that are applied to matching requests before the action is taken.
Example:
- path: /
action:
type: redirect
location: /other
Path Matching¶
In general it is possible to define the path to match in three different ways:
absolute path matching
extended path matching by using wildcards
regex based path matching
Each path is associated to a given rule. The mechanism to identify which rule to chose will be outlined at the end of this page (see Best match).
Absolute matching¶
The easiest path to set is an absolute path. The matching algorithm will match all path below the configured one.
Example:
Configuration:
/appsuite/api
will match:
/appsuite/api/
/appsuite/api/mail
Extended absolute matching¶
In addition to the absolute path matching it is possible to add wildcards to the configured path. *
defines any number of any character while ?
defines one of any character.
Example:
Configuration:
/*/api/v?
will match:
/appsuite/api/v1/
/ajax/api/v2/
Regex matching¶
For anything that cannot be handled with the matching algorithms above you can use the regex format which provides endless configuration options for your path. Regex path definitions have to start with a tilde (~) followed by the regex path.
Example 1:
Configuration:
~ /appsuite/api/([^/]+/)?auth
will match:
/appsuite/api/auth/
/appsuite/api/example.com/auth/sub/
Example 2:
Configuration:
~ /(extra|special)/data
will match
/extra/data
/special/data/2
Best match¶
The mechanism to find the best matching rule based on the path evaluates the requested path against all configured paths regardless of they are defined absolute, by using wildcards or regex. The most specific path (which means it matches most path elements) will be used. If two or more paths match the same number of path elements the first defined will be used.
Example 1:
Configuration:
Path 1: /
Path 2: ~ ^/api/.*$
Path 3: /api/v?/books
Request:
/api/v1/books/by-isbn/12345
=> Path 2 matches complete path and will be taken
Example 2:
Configuration:
Path 1: /
Path 2: /api/v1
Path 3: /api/v?/books
Request:
/api/v1/books/by-isbn/12345
=> Path 3 matches 3 path elements and will be taken
Routing Actions¶
Routing actions take an incoming request and apply all the configured settings and actions. Whether an action is applied or not, depends on the configured condition. It can either be a path match or another conditional match. The conditional match is tested by a dedicated action, the conditional action.
Forwarding¶
The forward
action actually forwards an incoming HTTP request to a an upstream/origin server.
Configuration¶
- path: /appsuite/api
action:
type: forward
rewritePath: /ajax
shardDetector: httpApi
shardBootstrapHandler: httpApi
backendPool: appsuite-mw
- path: /appsuite
action:
type: forward
shardDetector: httpApi
shardBootstrapHandler: httpApi
backendPool: appsuite-ui
- path: /Microsoft-Server-ActiveSync
action:
type: forward
shardDetector: httpApi
shardBootstrapHandler: eas
backendPool: appsuite-mw
clientConfig:
readTimeout: 1900000
- backendPool
Mandatory: Define backend pool to forward the request to. Values are taken from
name
values found under the global or shards section in backends.yml- rewritePath
Optional: Rewrite request path to specified value before forwarding the request to an origin server.
- clientConfig
Optional: Additional configuration parameters that can be applied to the client that handles the forwarding. For additional information see client configuration section.
- shardDetector
Optional: The shard detector which should be used to determine the route for the forward request. Values are taken from
name
values found under the detectors section in sharding.yml. For more information please look at the sharding section.- shardBootstrapHandler
Optional: The shard bootstrap handler which should be used to determine the route for the forward request. Values are taken from
name
values found under the bootstrapHandlers section in sharding.yml. This setting becomes mandatory if ashardDetector
is set. For more information please look at the sharding section.
Path rewrite¶
Rewriting the request URI path is supported for all types of path definitions. In all cases the part that matches the requested path will be rewritten by the given configuration.
Example 1:
Configuration:
match: /ajax/
rewrite: /servlets/ajax/
Request:
/ajax/chronos
Rewrite result:
/servlets/ajax/chronos/
Example 2:
Configuration:
match: ~ /(appsuite/api|ajax)/
rewrite: /servlets/
Request:
/appsuite/api/chronos/
Rewrite result:
/servlets/chronos/
Example 3:
Configuration:
match: ~ /(appsuite/api|ajax)/
rewrite: /servlets/$1/
Request:
/appsuite/api/chronos/accounts
Rewrite result:
/servlet/appsuite/api/chronos/accounts
It is possible to reference capture groups of regex matches in the rewrite configuration as well. This has to be done by using $
followed by a 1-based index ($1
, $2
, $3
, …).
Redirect¶
The redirect
action redirects any incoming request to the configured location and provides a corresponding status code. If the location is missing, a 500 server error will be returned.
It’s also possible to specify placeholders in the location like $scheme, $host, $port, $path and $query.
Configuration¶
rules:
- path: /
action:
type: redirect
status: 302
location: https://$host$path$query
- status
Optional: The status code of the response (default value 301 Moved Permanently)
- location
Mandatory: The location where the request should be redirected to.
Conditional¶
A conditional action matches a list of conditions against the request and applies the first match. If no match could be found, a default action can be configured.
Configuration¶
- path: /
action:
type: conditional
defaultAction:
type: redirect
location: /appsuite/
conditions:
- type: user-agent
match: anyOf
values:
- Calendar
- Reminders
- DataAccess
- DAVKit
- Lightning
action:
type: redirect
location: https://dav.example.com/
- conditions
Mandatory: The conditions that should be checked with the actions that should be applied on a match
- defaultAction
Mandatory: The default action that should be used if no other action can be applied
Conditions¶
Conditions have a distinct set of attributes that help to identify the condition and the matching algorithm. The properties are:
- type
Mandatory: The type of the condition handler that should be applied. Possible values:
user-agent
.- match
Mandatory: The matching algorithm that should be applied. Regular expression to match against. Expressions starting with an exclamation mark (!) are negated. Possible match value keys are:
value:
Literal value (regex) to match against,anyOf:
List of regular expressions, evaluated in the specified order. Any matching value fulfils the condition.,allOf:
List of regular expressions, evaluated in the specified order. All values must match to fulfil the condition.- values
Mandatory: A list of values for the matcher
- action
Mandatory: An action that should be applied. Can be any of the known actions, another conditional action included.
User agent condition¶
Checks the value of the requests user-agent header and applies the configured action.
WebSocket Tunnel¶
The ws-tunnel
action actually forwards an incoming HTTP request to an upstream/origin server but unlike the forward action this action enables proper handshake handling and establish a tunneled connection between client and server after successful handshake roundtrip.
Configuration¶
- path: /socket.io
action:
type: ws-tunnel
backendPool: oxcluster
clientConfig:
readTimeout: 60000
- path: /appsuite/rt2
action:
type: ws-tunnel
rewritePath: /rt2
backendPool: oxcluster
clientConfig:
readTimeout: 60000
- backendPool
Mandatory: Define backend pool to forward the request to. Values are taken from
name
values found under the global or shards section in backends.yml- rewritePath
Optional: Rewrite request path to specified value before forwarding the request to an origin server.
- clientConfig
Optional: Additional configuration parameters that can be applied to the client that handles the forwarding. For additional information see client configuration section. After successful establish a WebSocket tunnel the settings did not have any affect any more expect the readTimeout. After exceeding this value without any read operation proxy will terminate the connection. If read timeout is set to 0 proxy hold the connection open until client or origin terminate the connection.
Restrictions¶
It is possible to limit the access to paths of a vhost for requests that match certain restrictions. A restriction can either allow or deny to handle a request if matching a certain criteria.
IP-based access restrictions¶
Restrict access by client IP.
- path: '/webservices'
action:
...
restrictions:
# Possible restriction types:
# client-ip: Client IP address (from client socket, PROXY protocol or X-Forwarded-For header)
- type: 'client-ip'
# (ALLOW, DENY|DENY, ALLOW)
order: 'ALLOW, DENY'
# List of IP addresses and subnets in CIDR notation. '*' matches any IP.
allowFrom:
- '192.168.0.0/24'
- '127.0.0.1'
- '::1'
- 'fd35:8e34:80d5:5fc6::/64'
denyFrom:
- '*'
The order determines how the specified IP addresses are to be checked. In this example, the allowed IP addresses are checked first and afterwards the denied addresses. An asterisk represents all addresses, which in this example means that all addresses that are not explicitly allowed are rejected. In a path without restrictions, all IP addresses are allowed and none are rejected. The check is terminated as soon as a match is found.
By specifying the subnet mask (CIDR), it is also possible to filter by individual subnets. If this is omitted, only the specified IP address will be filtered.
For a detailed explanation review the developers guide on IP-based access restrictions
Response Headers¶
It is possible to alter upstream response headers before sending them back to the client. Therefor headers can be added to configuration on vHost and / or rule level. In case of any ambiguous naming the rule level configuration takes precedence over vHost level configuration.
Whether changes to response headers take place or not depends on the HTTP response status and the override flag always
. If this flag is set to true
changes will always be applied, otherwise (set to false or even empty) changes will only be applied if response status is one of 200, 201, 204, 206, 301, 302, 303, 304, 307 or 308
.
The concrete change which would be applied to origin response headers can be defined by a type attribute:
add
-> would simply add the configured header no matter which ones already exists on original response
set
-> would first remove all headers with the given name from origin and then add a new single header with the configured value
remove
-> would remove all headers with the given name. In case a value is configured only headers with the exact name and value will be removed from origin response
If no value is configured the default add
will be used.
Example with further comments¶
vhosts:
- hostNames:
- 'webmail.example.com'
responseHeaders:
- name: 'Access-Control-Allow-Origin'
# value is mandatory for types 'add' and 'set' and optional for 'remove'
value: 'wss://webmail-ws.example.com' # mandatory for types 'add' and 'set'
# type values: add [default], set, remove
type: 'add'
# default: false
always: true
rules:
- path: /
responseHeaders:
- name: 'Content-Security-Policy'
value: 'connect-src \self\ wss://webmail-ws.example.com'
type: 'set'
always: true
Full Example¶
# List of virtual hosts to initially match requests against. Order does not matter.
vhosts:
# Accept any to webmail.example.com on port 80
- hostAddress: "*"
port: 80
hostNames:
- webmail.example.com
rules:
# Redirect any request immediately to the respective HTTPS location
- path: /
# Respond with '301 Moved Permanently' and an according Location header
action:
type: redirect
# Value of the Location response header
location: https://webmail.example.com
# Route any HTTPS request to webmail.example.com
- hostAddress: "*"
port: 443
hostNames:
- webmail.example.com
# Response headers on vHost level
responseHeaders:
- name: 'Access-Control-Allow-Origin'
# value is mandatory for types 'add' and 'set' and optional for 'remove'
value: 'wss://webmail-ws.example.com' # mandatory for types 'add' and 'set'
# type values: add [default], set, remove
type: 'add'
# default: false
always: true
# The brand ID
brand: example.com.oxcloud.net
# Set of proxy rules to match requests for this host against. Order does not matter.
rules:
- path: /
# Response headers on rule level
responseHeaders:
- name: 'Content-Security-Policy'
value: 'connect-src \self\ wss://webmail-ws.example.com'
type: 'set'
always: true
# Conditionally matches a list of conditions against the request.
# The first matching condition determines the actual action.
action:
type: conditional
# Mandatory default action for conditional rules. Will be applied, if no condition matches.
defaultAction:
type: redirect
location: /appsuite/
# The list of conditions to match against. Matching occurs in the specified order.
conditions:
# match: <request-property>
# Possible request properties are:
# - user-agent: User-Agent request header
- type: user-agent
# Regular expression to match against. Expressions starting with an exclamation mark (!) are negated.
# Possible match value keys are:
# value: Literal value (regex) to match against
# anyOf: List of regular expressions, evaluated in the specified order.
# Any matching value fulfils the condition.
# allOf: List of regular expressions, evaluated in the specified order.
# All values must match to fulfil the condition.
match: anyOf
values:
- Calendar
- Reminders
- DataAccess
- DAVKit
- Lightning
- Adresboek
- dataaccessd
- Preferences
- Adressbuch
- AddressBook
- Address\ Book
- CalendarStore
- CalendarAgent
- accountsd
- eM Client
- OX Sync
- CalDav
- CoreDAV
action:
type: redirect
location: https://dav.example.com$path$query
- path: /appsuite/api
action:
type: forward
# Rewrite request path to specified value before forwarding the request to an origin server.
rewritePath: /ajax
# The shard detectors to use to determine the route for the forwarded request. Values are keys
# of the sharding.detectors section.
shardDetector: httpApi
shardBootstrapHandler: httpApi
# Define backend pool to forward the request to
backendPool: appsuite-mw
- path: /appsuite
action:
type: forward
shardDetector: httpApi
shardBootstrapHandler: httpApi
backendPool: appsuite-ui
- path: /Microsoft-Server-ActiveSync
action:
type: forward
shardDetector: httpApi
shardBootstrapHandler: eas
backendPool: appsuite-mw
clientConfig:
# Override read timeout in milliseconds
readTimeout: 1900000
- path: /webservices
action:
type: forward
shardDetector: provisioning
shardBootstrapHandler: provisioning
backendPool: appsuite-mw
# Restrict endpoint access. The list of restrictions is evaluated in the specified order.
# The first match determines the result and ends processing.
restrictions:
# Possible restriction types:
# client-ip: Client IP address (from client socket or X-Forwarded-For header)
- type: client-ip
order: ALLOW, DENY
# List of IP addresses and subnets in CIDR notation. '*' matches any IP.
allowFrom:
- 192.168.0.0/24
- 127.0.0.1
- ::1
- fd35:8e34:80d5:5fc6::/64
denyFrom:
- "*"
- path: /servlet/dav
action:
type: forward
shardDetector: webdav
shardBootstrapHandler: webdav
backendPool: appsuite-mw
- hostAddress: "*"
hostNames:
- dav.example.com
brand: default
rules:
- path: /
action:
type: forward
rewritePath: /servlet/dav
shardDetector: webdav
shardBootstrapHandler: webdav
backendPool: appsuite-mw
- path: /servlet/dav
action:
type: forward
rewritePath: /servlet/dav
shardDetector: webdav
shardBootstrapHandler: webdav
backendPool: appsuite-mw
- path: /.well-known/caldav
action:
type: redirect
location: /
- path: /.well-known/carddav
action:
type: redirect
location: /