-
Notifications
You must be signed in to change notification settings - Fork 689
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Evan Cordell <[email protected]>
- Loading branch information
Showing
1 changed file
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# IP Filtering | ||
|
||
Status: Approved | ||
|
||
## Abstract | ||
|
||
Allow or deny traffic to services behind contour with an IP allowlist or an IP denylist. | ||
|
||
## Background | ||
|
||
There have been [long standing requests](https://github.com/projectcontour/contour/issues/62) to support some form of IP filtering. | ||
IP filtering is a useful tool for network segregation that is supported by other ingress controllers like [ingress-nginx](https://github.com/kubernetes/ingress-nginx/blob/87d1b8bbf28265386b339e65d8943d8c3f8582ed/docs/user-guide/nginx-configuration/annotations.md#whitelist-source-range) and [ambassador](https://www.getambassador.io/docs/edge-stack/latest/topics/running/ambassador#ip-allow-and-deny), and is a [common](https://github.com/projectcontour/contour/issues/62#issuecomment-698141472) [request](https://github.com/projectcontour/contour/issues/62#issuecomment-843193956) among the community, often listed as a [blocker](https://github.com/projectcontour/contour/issues/3693#issuecomment-1317049708) for adopting contour. | ||
|
||
## Goals | ||
|
||
- Support ipv4 and ipv6 allowlists and denylists via Contour APIs. | ||
- Support filtering on request IP or on forwarded IP (i.e. via `X-Forwarded-For`) | ||
|
||
## Non Goals | ||
|
||
- Define Gateway API support for ip filtering. This is an obvious next step, but requires broader agreement. | ||
- Define a global IP filter that works across multiple Routes or HTTPProxy objects. | ||
- Supporting envoy's network rbac filter (this design is for the http filter only). | ||
|
||
## High-Level Design | ||
|
||
The `virtualhost` and `route` sections of an `HTTPProxy` will each have two new fields added: `ipAllowPolicy` and `ipDenyPolicy`. | ||
|
||
Example `HTTPProxy`: | ||
|
||
```yaml | ||
apiVersion: projectcontour.io/v1 | ||
kind: HTTPProxy | ||
metadata: | ||
name: basic | ||
spec: | ||
virtualhost: | ||
fqdn: foo-basic.bar.com | ||
ipAllowPolicy: | ||
# traffic is allowed if it came from localhost (i.e. co-located reverse proxy) | ||
- cidr: 127.0.0.1/32 | ||
source: Peer | ||
routes: | ||
- conditions: | ||
- prefix: / | ||
services: | ||
- name: s1 | ||
port: 80 | ||
# route-level ip filters overrides the virtualhost-level filters | ||
ipAllowPolicy: | ||
# traffic is allowed if it came from localhost (i.e. co-located reverse proxy) | ||
- cidr: 127.0.0.1/32 | ||
source: Peer | ||
# and the request originated from an IP in this range | ||
- cidr: 99.99.0.0/16 | ||
source: Remote | ||
``` | ||
## Detailed Design | ||
### API | ||
`ipAllowPolicy`/`ipDenyPolicy` has the following structure: | ||
|
||
```yaml | ||
ipAllowPolicy: | ||
- cidr: 127.0.0.1/32 | ||
source: Peer | ||
- cidr: 99.99.0.0/16 | ||
source: Remote | ||
``` | ||
|
||
```yaml | ||
ipDenyPolicy: | ||
- cidr: 127.0.0.1/32 | ||
source: Peer | ||
- cidr: 99.99.0.0/16 | ||
source: Remote | ||
``` | ||
|
||
Note that: | ||
- Allow rules deny all requests that don't match a rule. | ||
- Deny rules allow all requests that don't match a rule. | ||
- Rules defined on the virtualhost can be overridden by rules defined on a route. | ||
- Allow rules cannot be mixed with Deny rules: it won't be evident if a deny rule should be an exception to an allow rule, or if an allow rule should be an exception to a deny rule. | ||
- Multiple allow/deny CIDR ranges can be specified by repeating a key. | ||
- `Peer` refers to the request IP | ||
- `Remote` refers to the originating IP, i.e. `X-Forwarded-For`. `numTrustedHops` may also need to be set to filter on the desired IP. | ||
|
||
### Processing | ||
|
||
`HTTPProxyProcessor` and `dag.Route` will store the ip filtering rules in a new field. | ||
|
||
These settings will be converted convert to a per-route or per-virtualhost envoy filter, i.e.: | ||
|
||
```yaml | ||
routes: | ||
- match: | ||
prefix: / | ||
route: | ||
cluster: example-cluster | ||
typed_per_filter_config: | ||
envoy.filters.http.rbac: | ||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute | ||
rbac: | ||
rules: | ||
# action is ALLOW if ipAllowPolicy is used, otherwise DENY is used | ||
action: ALLOW | ||
policies: | ||
ip-rules: | ||
permissions: | ||
- any: true | ||
principals: | ||
# direct_remote_ip is used if `source: Peer` is specified | ||
- direct_remote_ip: | ||
address_prefix: 127.0.0.1 | ||
prefix_len: 32 | ||
# remote_ip is used if `source: Remote` is specified | ||
- remote_ip: | ||
address_prefix: 99.99.0.0 | ||
prefix_len: 16 | ||
``` | ||
Please see [the Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/rbac/v3/rbac.proto) for more detailed explanations of the filter behavior. | ||
## Alternatives | ||
The ip filtering could be a top-level field on the `HTTPProxy`. | ||
|
||
This would allow building "trees" of ip filters via `includes`: | ||
```yaml | ||
apiVersion: projectcontour.io/v1 | ||
kind: HTTPProxy | ||
metadata: | ||
name: basic | ||
spec: | ||
virtualhost: | ||
fqdn: foo-basic.bar.com | ||
ipAllowPolicy: | ||
# traffic is allowed if it came from localhost (i.e. co-located reverse proxy) | ||
- peer: 127.0.0.1 | ||
# and the request originated from an IP in this range | ||
- remote: 99.99.0.0/16 | ||
includes: | ||
- name: service2 | ||
conditions: | ||
- prefix: /service2 | ||
routes: | ||
- conditions: | ||
- prefix: / | ||
services: | ||
- name: s1 | ||
port: 80 | ||
--- | ||
apiVersion: projectcontour.io/v1 | ||
kind: HTTPProxy | ||
metadata: | ||
name: service2 | ||
spec: | ||
ipDenyPolicy: | ||
# if the request made it through the first filter, it could still be denied here | ||
- remote: 99.99.0.1 | ||
routes: | ||
- conditions: | ||
- prefix: /service2 | ||
services: | ||
- name: s2 | ||
port: 80 | ||
``` | ||
|
||
But no other request filtering options work this way today, so this approach seems like a mismatch with current Contour. |