Skip to content

Latest commit

 

History

History
 
 

client-certs

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

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 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
  • Create a CA, certificate and private key, 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

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

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.

$ 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

Troubleshooting

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.

Using wget

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:

Using openssl

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:
...