haproxy

haproxy (link to haproxy documentation) supports serving SCTs as part of the TLS handshake during HTTPS connections.  At time of writing (2015-05-14) this is supported only in version 1.6.

Disclaimer: the following is not intended to show best (or even good) practices for configuration of openssl or haproxy, but rather to document a reproducible set of instructions that work to demonstrate the server successfully returning SCTs during the TLS handshake.

For the purposes of this example, we started with a new virtual machine created in Google Compute Engine with the "ubuntu-1504-vivid-v20150422" image.

# Install dependencies
sudo apt-get install gcc make

# Build install static openssl 1.0.2 to temp location
wget https://www.openssl.org/source/openssl-1.0.2a.tar.gz
tar zxf openssl-1.0.2a.tar.gz
cd openssl-1.0.2a/
./config --prefix=/tmp/staticopenssl no-shared
make 
make install
cd ..

# Get 1.6 source and build / install
wget http://www.haproxy.org/download/1.6/src/snapshot/haproxy-ss-LATEST.tar.gz
tar zxf haproxy-ss-LATEST.tar.gz
cd haproxy-ss-*
make TARGET=linux2628 USE_OPENSSL=1 SSL_INC=/tmp/staticopenssl/include \
     SSL_LIB=/tmp/staticopenssl/lib ADDLIB=-ldl
sudo make install
cd ..

# Remove temp openssl build.
rm -rf /tmp/staticopenssl

# Create standard SSL configuration
sudo mkdir -p /usr/local/haproxy/conf/ssl
sudo sh -c """echo '''-----BEGIN PRIVATE KEY-----
... your private key ...
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
… your certificate ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
… append zero or more intermediate certs in order ...
-----END CERTIFICATE-----''' >/usr/local/haproxy/conf/ssl/server.pem"""

# Create a script to submit certificates to log servers
echo '''import urllib2, json, sys, base64, struct

LOGS = [
  "ct.googleapis.com/aviator",
  "ct.googleapis.com/pilot",
  "ct.googleapis.com/rocketeer",
  "ct1.digicert-ct.com/log",
  "ct.izenpe.com",
  "log.certly.io",
]

def submit(log, data):
  return json.loads(urllib2.urlopen("https://%s/ct/v1/add-chain" % log, json.dumps(data)).read())

def read_chain(f):
  in_cert = False
  chain = []
  for line in f:
    line = line.strip()
    if line == "-----BEGIN CERTIFICATE-----":
      in_cert = True
      cert = ""
    elif line == "-----END CERTIFICATE-----":
      in_cert = False
      chain.append(cert)
    else:
      if in_cert:
        cert += line
  return chain

def lpstring(b):
  return struct.pack("!H", len(b)) + b

def make_bytes(sct):
   return (struct.pack("!B", sct["sct_version"])
    + base64.b64decode(sct["id"])
    + struct.pack("!Q", sct["timestamp"])
    + lpstring(base64.b64decode(sct["extensions"]))
    + base64.b64decode(sct["signature"]))

chain = read_chain(sys.stdin)
sys.stdout.write(lpstring("".join([lpstring(make_bytes(submit(log, {"chain": chain}))) for log in LOGS])))
''' > submit_scts.py

# Submit your cert chain to logs as appropriate, and write out SCTs:
sudo sh -c "python submit_scts.py </usr/local/haproxy/conf/ssl/server.pem \
                                  >/usr/local/haproxy/conf/ssl/server.pem.sctl"

# Write a minimal haproxy config to serve:
sudo sh -c """echo '''frontend test
  mode http
  bind 0.0.0.0:443 ssl crt /usr/local/haproxy/conf/ssl/server.pem
''' > /usr/local/haproxy/conf/haproxy.conf"""

# Start server and test
sudo /usr/local/sbin/haproxy -d -f /usr/local/haproxy/conf/haproxy.conf
Comments