This example demonstrates how to configure client certificate authentication on HAProxy Ingress controller.
This document has the following prerequisites:
- Deploy HAProxy Ingress controller, you should
end up with controller, a sample web app and an ingress resource named
app
to thefoo.bar
domain - Configure TLS termination
- Create a CA, certificate and private key,
following these steps you should have a secret named
caingress
, a certificate fileclient.crt
and it's private keyclient.key
- Use these same steps and create another CA and generate another certificate and private
key
fake.crt
andfake.key
just for testing
Secret, certificates and keys can be created using these shortcuts:
CA and it's secret:
$ 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:
$ 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:
$ 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
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.
$ 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:
$ 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:
$ 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:
$ 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
curl
on macOS since 10.9 Mavericks has some issues regarding certificate on command line
parameters:
-
sni 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 from curl mailing list. In short, in order to test client auth use a Linux VM or the options below on macOS.
Add foo.bar
to /etc/hosts
and change 31692
below to the
port of HAProxy Ingress controller:
$ 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:
$ 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:
Change 31692
below to the port of HAProxy Ingress controller:
$ 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:
$ 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:
...