Skip to content

Instantly share code, notes, and snippets.

@tomdz
Last active January 22, 2016 12:03
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save tomdz/5339163 to your computer and use it in GitHub Desktop.
Save tomdz/5339163 to your computer and use it in GitHub Desktop.
Vagrant test setup for haproxy with ssl client certificates

Setup vagrant vm

vagrant box add precise64 http://files.vagrantup.com/precise64.box
vagrant init precise64
sed -i 's/# config.vm.network :hostonly/config.vm.network :hostonly/' Vagrantfile
vagrant up
vagrant ssh

Install base software and apache (to have something to proxy)

sudo apt-get update
sudo apt-get install -y curl build-essential libpcre3-dev libssl-dev apache2

Install haproxy

export HAPROXY_VERSION=haproxy-1.5-dev18
curl -O http://haproxy.1wt.eu/download/1.5/src/devel/$HAPROXY_VERSION.tar.gz
tar zxf $HAPROXY_VERSION.tar.gz
cd $HAPROXY_VERSION
make PREFIX=/usr/local \
     IGNOREGIT=true \
     MANDIR=/usr/local/share/man \
     DOCDIR=/usr/local/share/doc/haproxy \
     TARGET=linux2628 \
     CPU=native \
     USE_PCRE=1 \
     USE_STATIC_PCRE=1 \
     USE_OPENSSL=1 \
     USE_ZLIB=1
sudo make install

Setup Ubuntu's official haproxy init.d script for 12.04

curl -O http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/precise/haproxy/precise/download/head:/haproxy.init-20090727115323-3c2mw7iu0du8tdiw-208/haproxy.init
sed -i 's/PATH=/PATH=\/usr\/local\/sbin:/' haproxy.init
sed -i 's/HAPROXY=\/usr\/sbin\/haproxy/HAPROXY=\/usr\/local\/sbin\/haproxy/' haproxy.init
sudo mv haproxy.init /etc/init.d/haproxy
sudo chmod +x /etc/init.d/haproxy

Setup chroot and haproxy configuration

sudo addgroup --system haproxy
sudo adduser --system --no-create-home --ingroup haproxy haproxy
sudo mkdir /opt/haproxy
sudo chown haproxy:haproxy /opt/haproxy/
sudo chmod a-w /opt/haproxy/
sudo mkdir /etc/haproxy
echo "ENABLED=1" | sudo tee /etc/default/haproxy
echo 'global
    daemon
    maxconn 256
    user haproxy
    group haproxy
    chroot /opt/haproxy

defaults
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option  http-server-close

frontend http-in
    mode http
    bind *:443 ssl crt /etc/haproxy/server.pem ca-file /etc/haproxy/ca.crt verify required
    default_backend servers
    reqadd X-Forwarded-Proto:\ https if { ssl_fc }
    option forwardfor

backend servers
    mode http
    server server1 localhost:80 maxconn 32' | sudo tee /etc/haproxy/haproxy.cfg

CA, server and client certificate setup. This is a slightly tweaked form of https://github.com/exceliance/haproxy/blob/master/blog/ssl_client_certificate_management_at_application_level/ca.sh for only the valid client certificate, which also makes use of the -subj commandline option to avoid re-typing those values.

export SUBJECT='/C=US/ST=California/L=San Francisco/CN=test@example.com'

# certificate authority creation
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "$SUBJECT"

# server certificate creation
openssl genrsa -out server.key 1024
openssl req -new -key server.key -out server.csr -subj "$SUBJECT"
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

# client certificate creation
openssl genrsa -out client.key 1024
openssl req -new -key client.key -out client.csr -subj "$SUBJECT"
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -out client.crt

cat server.crt server.key > server.pem
sudo cp server.pem /etc/haproxy/server.pem
sudo cp ca.crt /etc/haproxy/ca.crt

Start haproxy and test it:

sudo /etc/init.d/haproxy start
openssl s_client -connect localhost:443 -cert client.crt -key client.key

This gives me:

CONNECTED(00000003)
depth=0 C = US, ST = California, L = San Francisco, CN = test@example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = US, ST = California, L = San Francisco, CN = test@example.com
verify return:1
139701807867552:error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca:s3_pkt.c:1256:SSL alert number 48
139701807867552:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/CN=test@example.com
   i:/C=US/ST=California/L=San Francisco/CN=test@example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDmjCCAYICAQEwDQYJKoZIhvcNAQEFBQAwVTELMAkGA1UEBhMCVVMxEzARBgNV
BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGTAXBgNVBAMM
EHRlc3RAZXhhbXBsZS5jb20wHhcNMTMwNDA4MTgyMTQ1WhcNMTQwNDA4MTgyMTQ1
WjBVMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
U2FuIEZyYW5jaXNjbzEZMBcGA1UEAwwQdGVzdEBleGFtcGxlLmNvbTCBnzANBgkq
hkiG9w0BAQEFAAOBjQAwgYkCgYEA672DeWWtseWeQnRa32Zhh9untI6F/gxRTvkv
vNZZgZqP1fgi/2+t6lmmZV5yM31A2wq54Qf00z+5uWwbOoxCeoyLiXQ7PDtWYdhP
zW+jyLWRzY4ndpI+dlA2HfLD9lbcoTMrcPJTab0TvhWSEaXSMCQWNIvhYPBsZ/HT
yy5WQNUCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEA8EWe7WgcBthvFI+9d15jkCX+
SuVn0rSTzKNcU3IAyktm8vVaamhfWqdeV/21ocwUMdQcGSPYJ2DEN8X5YaiCLGup
IT/3CHg1fmDLYrB732vZ0u1Vqz4O0rS3JpPRTQOryjvgDAHyR9V0oe0dP65oDtSA
TqAuaJC79YTB36XEWWgxZIfQCvKqpLYgP9cnlhdy8UOWW7e8a2mic9tw8JYro57/
d3q4l/WoOZ5UkYo4o3y7GviGUldW834nue0Tsdrb8csl0bqR+vqpCU2f6joh1VlF
pMOyrmWm6M4LeqFPTAY6U/S7LjUnYcC/AYvOUyIntNpf8kLRFsmbQapqG5q7jDjW
oWIqdhAzQIG1wS8TevQVHtg8MXKJcNPhcR4lT/xEqtcdiY6F3IFg95STnb41aQXm
7Trnw9j1gTa6cfuQcuTNh4Y9GPQdYhMeJiUUhpZOxOZx86VE5cfSxbY7awYZ+EO2
4NYUajdvyAvwV5WGfTbyJ3+qU5eoOfMKvntHhSYHeHU4jiQ/6q3p1u27b1LoQnr9
k8+CF04AK+V/qhGkeMjg7zcwA6zLdChGpP0sizV3kwcTk4SjNrfepLHgXoOvMQsK
uX00ZsEgZ7EMJqI1NX+Ma4WHUcNMA7eHsb+TuRYudsG2YGLLCsrwNK4Mni1swqKU
vneURb2jEpRn8t6kTv4=
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=San Francisco/CN=test@example.com
issuer=/C=US/ST=California/L=San Francisco/CN=test@example.com
---
Acceptable client certificate CA names
/C=US/ST=California/L=San Francisco/CN=test@example.com
---
SSL handshake has read 1335 bytes and written 1230 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.1
    Cipher    : ECDHE-RSA-AES256-SHA
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 14DAE59074B21C9AEB0ADB6B77D0833D8BF5FD9B03FF5E5F92EB697483313BFAAB0E028A56925ECBE4FF3AFE001201FA
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1365446671
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---

Note the two error lines:

139701807867552:error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca:s3_pkt.c:1256:SSL alert number 48
139701807867552:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:

This

curl -vk --key client.key --cert client.crt https://localhost

gives me a similar error:

* About to connect() to localhost port 443 (#0)
*   Trying 127.0.0.1... connected
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS alert, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS handshake, CERT verify (15):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS alert, Server hello (2):
* error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca
* Closing connection #0
curl: (35) error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca
@tomdz
Copy link
Author

tomdz commented Apr 14, 2013

Turns out, you're not supposed to use the same CN for CA, server & client. Changing it to:

export CA_SUBJECT='/C=US/ST=California/L=San Francisco/CN=ca@example.com'
export SERVER_SUBJECT='/C=US/ST=California/L=San Francisco/CN=webmaster@example.com'
export CLIENT_SUBJECT='/C=US/ST=California/L=San Francisco/CN=user@example.com'

# certificate authority creation
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "$CA_SUBJECT"

# server certificate creation
openssl genrsa -out server.key 1024
openssl req -new -key server.key -out server.csr -subj "$SERVER_SUBJECT"
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

# client certificate creation
openssl genrsa -out client.key 1024
openssl req -new -key client.key -out client.csr -subj "$CLIENT_SUBJECT"
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -out client.crt

cat server.crt server.key > server.pem
sudo cp server.pem /etc/haproxy/server.pem
sudo cp ca.crt /etc/haproxy/ca.crt

makes both s_client and curl happy.

@tomdz
Copy link
Author

tomdz commented Apr 14, 2013

Browsers generally want a .p12 file, so we should generate one as well:

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

@jagadeeshtakkalaki
Copy link

I am getting error in haproxy as keyword ssl is not registered, while ssl validation in haproxy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment