Skip to content

Commit

Permalink
design: mTLS only with CA validation
Browse files Browse the repository at this point in the history
Signed-off-by: Tero Saarni <tero.saarni@est.tech>
  • Loading branch information
tsaarni committed Nov 13, 2019
1 parent 19927e4 commit da40d10
Showing 1 changed file with 65 additions and 126 deletions.
191 changes: 65 additions & 126 deletions design/tls-client-verification.md
Original file line number Diff line number Diff line change
@@ -1,174 +1,116 @@
# Client certificate validation (mTLS)
# External client certificate validation (mutual TLS authentication)

Status: Draft

This document outlines how to add support for authentication of external
clients by validating their client certificates.

## Goals

- Allow client certificate validation (mTLS) on contour (performed by Envoy)
- Allow various ways for client certificate validation; Spki, Hash and CA
- Document mTLS configuration
- Allow Contour to be used to protect the backend services from access of
unauthorized external clients.
- Allow authentication of external clients by having Envoy validate that
client certificates are signed by trusted CA (Certificate Authority)
- Allow configuration of trusted CA certificate(s) for validating the client
certificates.

## Non Goals

- Support for k8s ingress documents.
- Repeat configuration details described in Envoy documentation (make references)
- Fine grained authorization on level of individual request
(example of non-goal: only client X can access resource Y on backend
service Z)

## Background

In TLS (and https) only the server is authenticated with a
certificate, for instance you as a client can be sure that you speak
with your bank and not some malicious site. But sometimes also the client
must be authenticated. As noted in
[wikipedia](https://en.wikipedia.org/wiki/Mutual_authentication)
client certification (mTLS) is not very common for end-users but is
more widespread for business-to-business (B2B) applications (which
may use gRPC and REST APIs).

mTLS is used is to restrict access to sensiteve services to validated
clients only. Some services do not provide client validation
themselves then a reversed proxy (envoy/contour) is used, for
instance;

* Elasticsearch did not support authentication, [until very
recently](https://www.elastic.co/blog/tips-to-secure-elasticsearch-clusters-for-free-with-encryption-users-and-more). The
common pattern was to put reverse proxy in front of Elasticsearch to
handle TLS and authentication

* Prometheus does not support either
[TLS](https://prometheus.io/docs/guides/tls-encryption/) or
[authentication](https://prometheus.io/docs/guides/basic-auth/). Common
pattern is to put reverse proxy in front.
In TLS (and HTTPS) often only the client authenticates the server. TLS
supports optional authentication of client which is referred to as
*mutual TLS authentication* or *mTLS*. Mutual TLS authentication is typically
used in machine-to-machine (M2M) communication e.g. to protect sensitive REST
APIs. The application acting as TLS client authenticates itself towards the
TLS server by using x509 certificate and by providing proof of posession of
the corresponding private key.


## High-Level Design

Client certificate validation (mTLS) is supported by Envoy. It should
be possible for `contour` users to utilize this feature.

Envoy supports following options for certificate based client authentication:

## High-Level Design
1. Verify that a chain of trust can be established from the presented client
certificate to the configured trusted root CA certificate (validation of
certificate)
2. In addition to 1, verify that the subject alternative name of the client is
one of the names listed in the configuration (validation of client identity)
3. Verify that the client certificate hash or subject public key hash matches
with configured hash (hash pinning)

At a high level I propose the following:
Option 1) is proposed to be implemented in this document. It is sufficient
for the very simplest mutual TLS authentication use cases.

1. A new record "clientValidation" is added in spec.virtualhost.tls
in the IngressRoute.
At a high level following CRD change is proposed:

2. The `clientValidation` contains configuration for client
certificate validation. Many ways of client certificate validation
may be specified (all ways supported by Envoy).
* A new record `clientValidation` is added in `spec.virtualhost.tls`
in the `HTTPProxy`.

* The `clientValidation` contains parameter `caSecret` which is a reference
to a secret containing trusted CA certificate for validating client
certificates.

### Sample YAML
Sample YAML

```
apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: kahttp
name: mutual-tls-example
namespace: default
spec:
virtualhost:
fqdn: kahttp.com
fqdn: example.com
tls:
secretName: contour-secret
secretName: server-credentials
clientValidation:
secretName: clientsecret
spkis:
- 2IEpPESU/mmC30tPsnOfbGKdwKdQfN/wZw1QWpjGlmk=
subjectAltNames:
- server.example.com
caSecret: ca-cert-for-client-validation
routes:
- match: /
services:
- name: kahttp
- services:
- name: service
port: 80
```


## Detailed Design

Since this design proposal required some "learning-by-doing" most of this
is implemented by https://github.com/heptio/contour/pull/1226

Unit-tests must be added on all appropriate places.


### CAs in Secrets
### Trusted CAs in Secrets

The same approach shall be followed for configuring trusted CA certificates as
is used currently to store the CA certificates for backend (Envoy upstream)
validation:

The store of CA information is an opaque kubernetes secret.
The secret will be stored in the same namespace as the corresponding IngressRoute.
TLS certificate delegation is not in scope for this proposal.

The secret object should contain one entry named `ca.key`, the constents will be the CA public key material.
The CA certificate is stored in an opaque Kubernetes secret. The secret will
be stored in the same namespace as the corresponding `HTTPProxy` object.
The secret object shall contain entry named `ca.crt`. The constents shall
be the CA certificate in PEM format. The file may contain "PEM bundle",
that is, a list of CA certificates concatenated in single file.

Example:
```
% kubectl create secret generic my-certificate-authority --from-file=./ca.key
```

Contour already subscribes to Secrets in all namespaces so Secrets will be piped through to the `dag.KubernetsCache` automatically.


### Changes in APIs

The new configuration item `clientValidation` must be parsed and the
CRD's must be updated to validate the new item.


### Changes to the DAG

A new typed will be added to the `dag` package, `ClientValidation`
to capture the validation parameters. It will be added to `SecureVirtualHost`.

```go
package dag

type ClientValidation struct {
// The CA for client validation.
*Secret
// SPKIs used to validate the client certificate
Spkis []string
// Hashes used to validate the client certificate
Hashes []string
// Alternative subject names
SubjectAltNames []string
}
```

### Changes to internal/envoy

`DownstreamTLSContext()` is extended to take a `clientValidation` this
is a pointer to a structure and may be `nil`. A structure is preferred
before adding a whole bunch of parameters.

```go
type ClientValidation struct {
Secret *auth.Secret
Spkis []string
Hashes []string
SubjectAltNames []string
}

func DownstreamTLSContext(secretName string, clientValidation *ClientValidation, tlsMinProtoVersion
auth.TlsParameters_TlsProtocol, alpnProtos ...string) *auth.DownstreamTlsContext {
// ...
}
% kubectl create secret generic ca-cert-for-client-validation --from-file=./ca.crt
```

TLS certificate delegation is not in scope for CA certificates.

### Changes to internal/e2e

Test cases will need to be updated.
## Detailed Design

### Changes to internal/contour
The new configuration item `spec.virtualhost.tls.clientValidation` must be
parsed and the CRD's must be updated to validate the new item.

The `listener.go` will pass ClientValidation data to envoy.
Client certificate validation is enabled in Envoy by setting
`auth.DownstreamTlsContext.RequireClientCertificate` value to `true` and by
adding trusted CA certificates to `auth.CommonTlsContext.ValidationContextType`.


## Alternatives Considered

To use annotation in the `Ingress` object was considered to
clumsy. Annotation must on `Ingress` level and refere to an individual
clumsy. Annotation must on `Ingress` level and refer to an individual
virtual host.


Expand All @@ -179,8 +121,5 @@ data stored in the API server is modified, verification will be
ineffective.

This proposal also assumes that RBAC is in place and only the owners
of the Service, Secret, IngressRoute documents in a namespace can
of the Service, Secret, HTTPProxy documents in a namespace can
modify them.



0 comments on commit da40d10

Please sign in to comment.