181 lines
6.1 KiB
Markdown
181 lines
6.1 KiB
Markdown
# HAProxy Ingress Client Certificate Authentication
|
||
|
||
This example demonstrates how to configure client certificate
|
||
authentication on HAProxy Ingress controller.
|
||
|
||
## Prerequisites
|
||
|
||
This document has the following prerequisites:
|
||
|
||
* Deploy [HAProxy Ingress](/examples/deployment/haproxy) controller, you should
|
||
end up with controller, a sample web app and an ingress resource named `app` to
|
||
the `foo.bar` domain
|
||
* Configure [TLS termination](/examples/tls-termination/haproxy)
|
||
* Create a [CA, certificate and private key](/examples/PREREQUISITES.md#ca-authentication),
|
||
following these steps you should have a secret named `caingress`, a certificate file
|
||
`client.crt` and it's private key `client.key`
|
||
* Use these same steps and create another CA and generate another certificate and private
|
||
key `fake.crt` and `fake.key` just for testing
|
||
|
||
As mentioned in the deployment instructions, you MUST turn down any existing
|
||
ingress controllers before running HAProxy Ingress.
|
||
|
||
Secret, certificates and keys can be created using these shortcuts:
|
||
|
||
CA and it's secret:
|
||
|
||
```console
|
||
$ openssl req -x509 -newkey rsa:2048 -nodes -subj '/CN=example-ca' -keyout ca.key -out ca.crt
|
||
$ kubectl create secret generic caingress --from-file=ca.crt
|
||
```
|
||
|
||
Valid certificate and private key:
|
||
|
||
```console
|
||
$ openssl req -new -newkey rsa:2048 -nodes -subj '/CN=client' -keyout client.key | \
|
||
openssl x509 -req -CA ca.crt -CAkey ca.key -set_serial 1 -out client.crt
|
||
```
|
||
|
||
Another CA, certificate and private key that should be refused by ingress:
|
||
|
||
```console
|
||
$ openssl req -x509 -newkey rsa:2048 -nodes -subj '/CN=example-ca' -keyout ca-fake.key -out ca-fake.crt
|
||
$ openssl req -new -newkey rsa:2048 -nodes -subj '/CN=client' -keyout fake.key | \
|
||
openssl x509 -req -CA ca-fake.crt -CAkey ca-fake.key -set_serial 1 -out fake.crt
|
||
```
|
||
|
||
## Using Client Certificate Authentication
|
||
|
||
HAProxy Ingress read one or a bundle of certificate authorities from a secret.
|
||
Only client certificates signed by one of these certificate authorities should be
|
||
allowed to make requests.
|
||
|
||
Annotate the ingress resource to use our valid certificate authority. The ingress resource and the
|
||
secret `caingress` were created on the prerequisites.
|
||
|
||
```console
|
||
$ kubectl annotate ingress/app ingress.kubernetes.io/auth-tls-secret=default/caingress
|
||
```
|
||
|
||
Make some SSL requests against domain `foo.bar`. Change `31692:172.17.4.99` below to the IP and
|
||
port of HAProxy Ingress controller.
|
||
|
||
Note: `curl`'s `--cert` and `-k` options on macOS (since 10.9 Mavericks) doesn't work as
|
||
expected, see troubleshooting below if using macOS.
|
||
|
||
Connect without a certificate:
|
||
|
||
```console
|
||
$ curl -ik https://foo.bar:31692 --resolve 'foo.bar:31692:172.17.4.99'
|
||
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
|
||
```
|
||
|
||
Connect using the correct certificate and private key:
|
||
|
||
```console
|
||
$ curl -ik https://foo.bar:31692 --resolve 'foo.bar:31692:172.17.4.99' --cert client.crt --key client.key
|
||
HTTP/1.1 200 OK
|
||
Server: nginx/1.9.11
|
||
Date: Fri, 26 Mar 2017 13:41:26 GMT
|
||
Content-Type: text/plain
|
||
Transfer-Encoding: chunked
|
||
Strict-Transport-Security: max-age=15768000
|
||
|
||
CLIENT VALUES:
|
||
...
|
||
```
|
||
|
||
Now connect using a private key and certificate signed by another CA:
|
||
|
||
```console
|
||
$ curl -ik https://foo.bar:31692 --resolve 'foo.bar:31692:172.17.4.99' --cert fake.crt --key fake.key
|
||
curl: (35) error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
`curl` on macOS since 10.9 Mavericks has some issues regarding certificate on command line
|
||
parameters:
|
||
|
||
* [sni](https://en.wikipedia.org/wiki/Server_Name_Indication) TLS extension isn't used
|
||
if `-k` (unsecure connection) is provided. The sni extension is used by HAProxy to identify
|
||
the host of the request. Without sni, the default backend will be used. The TLS
|
||
certificate should be added to Keychain instead and `-k` should be avoided.
|
||
|
||
* `--cert` option is broken.
|
||
|
||
These issues and it's workarounds are described on
|
||
[this message](https://curl.haxx.se/mail/archive-2013-10/0036.html) from curl mailing list.
|
||
In short, in order to test client auth use a Linux VM or the options below on macOS.
|
||
|
||
### Using wget
|
||
|
||
Add `foo.bar` to `/etc/hosts` and change `31692` below to the
|
||
port of HAProxy Ingress controller:
|
||
|
||
```console
|
||
$ wget https://foo.bar:31692 -S -nv -O- --no-check-certificate
|
||
OpenSSL: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
|
||
Unable to establish SSL connection.
|
||
```
|
||
|
||
Now with certificate and private key:
|
||
|
||
```console
|
||
$ wget https://foo.bar:31692 -S -nv -O- --no-check-certificate --certificate client.crt --private-key client.key
|
||
WARNING: cannot verify foo.bar's certificate, issued by ‘/CN=foo.bar’:
|
||
Self-signed certificate encountered.
|
||
HTTP/1.1 200 OK
|
||
Server: nginx/1.9.11
|
||
Date: Sun, 26 Mar 2017 13:57:53 GMT
|
||
Content-Type: text/plain
|
||
Transfer-Encoding: chunked
|
||
Strict-Transport-Security: max-age=15768000
|
||
CLIENT VALUES:
|
||
```
|
||
|
||
### Using openssl
|
||
|
||
Change `31692` below to the port of HAProxy Ingress controller:
|
||
|
||
```console
|
||
$ openssl s_client -connect 172.17.4.99:31692 -servername foo.bar
|
||
CONNECTED(00000003)
|
||
depth=0 /CN=foo.bar
|
||
verify error:num=18:self signed certificate
|
||
verify return:1
|
||
depth=0 /CN=foo.bar
|
||
verify return:1
|
||
91929:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/ssl/s3_pkt.c:1145:SSL alert number 40
|
||
91929:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/ssl/s23_lib.c:185:
|
||
```
|
||
|
||
Now with certificate and private key - copy these two lines to the clipboard
|
||
(HAProxy will timeout after 5 seconds waiting a http request):
|
||
|
||
```
|
||
GET / HTTP/1.0
|
||
Host: foo.bar
|
||
```
|
||
|
||
Type the command below, paste the http request (two lines above) and send a blank line
|
||
pressing enter twice:
|
||
|
||
```console
|
||
$ openssl s_client -connect 172.17.4.99:31692 -servername foo.bar -cert client.crt -key client.key
|
||
...
|
||
---
|
||
GET / HTTP/1.0
|
||
Host: foo.bar
|
||
|
||
HTTP/1.1 200 OK
|
||
Server: nginx/1.9.11
|
||
Date: Sun, 26 Mar 2017 14:06:30 GMT
|
||
Content-Type: text/plain
|
||
Content-Length: 268
|
||
Connection: close
|
||
Strict-Transport-Security: max-age=15768000
|
||
|
||
CLIENT VALUES:
|
||
...
|
||
```
|