HAProxy Technologies is proud to announce the availability of an integrated Let’s Encrypt ACMEv2 Lua client for HAProxy and HAProxy Enterprise (HAPEE). HAProxy Enterprise comes bundled with Lua support in a precompiled binary conveniently distributed using your Linux distribution’s package manager.
While we are aware that there are several other existing Let’s Encrypt clients that have some level of support for HAProxy. We found that they are either too complex or require extra steps to create certificate bundles.
We wanted to create a new Let’s Encrypt client that would harness HAProxy’s ability to use dual RSA/ECDSA keys, which provides the ability automatically detect key types supported by clients while parsing TLS ClientHello messages.
During implementation, we had the pleasure of using Pebble and Boulder, which are ACME server implementations by Let’s Encrypt that assisted us in creating the new client. Pebble has some extra features which helped us to create a robust ACME implementation.
HAProxy-Lua-ACME
“HAProxy-Lua-ACME” is our Let’s Encrypt client in Lua which provides support for ACMEv2.
To get SSL certificates for your site, you will need the following:
OpenSSL to create account and domain RSA keys
An HTTP client such as curl to issue certificate orders and fetch certificate bundles
Requirements
HAProxy with Lua support; v1.8 is required, older versions could work with some modifications (b64dec)
“HAProxy-Lua-ACME”, HAProxy Lua ACME Client
“HAProxy-Lua-HTTP”, native Lua HTTP client for HAProxy
“json.lua”, JSON library for Lua (we are using https://github.com/rxi/json.lua)
“lua-ossl”, OpenSSL bindings for Lua (https://github.com/wahern/luaossl)
Except for lua-ossl which might not be packaged for your distribution (and thus requires compilation), all other required packages are pure Lua scripts, so you can just drop them into the correct LUA_PATH prefix.
Configuration
Minimal, working HAProxy configuration:
global | |
log /dev/log local0 debug | |
nbproc 1 | |
daemon | |
lua-load config.lua | |
lua-load acme.lua | |
defaults | |
log global | |
mode http | |
option httplog | |
timeout connect 5s | |
timeout client 10s | |
timeout server 10s | |
listen http | |
bind *:80 | |
http-request use-service lua.acme if { path_beg /.well-known/acme-challenge/ } | |
userlist acme_users | |
user acme password $5$Tmx0ttbvZB1TsL$QDbECr8B.rPvB9LWmSypDuVYwJJtReWrh.HWpmZNMaA | |
listen acme | |
bind 127.0.0.1:9011 | |
acl acme_auth http_auth(acme_users) | |
http-request auth realm "HAProxy ACME auth" if !acme_auth | |
http-request use-service lua.acme | |
listen acme-ca | |
bind 127.0.0.1:9012 | |
server ca acme-v02.api.letsencrypt.org:443 ssl verify required ca-file letsencrypt-x3-ca-chain.pem | |
http-request set-header Host acme-v02.api.letsencrypt.org |
Please note the following about the configuration:
haproxy-lua-acme needs to answer ACME server challenges, hence the line “use-service lua.acme if { path_beg }”
Certificate orders should be available only from your local network (listen acme block), you can also use HTTP Auth and/or SSL client certificates.
In HAProxy, Lua can’t resolve domain names during runtime, and in addition to that, peer/server certificates are not validated by the SSL layer. We are going to proxy the requests through a local proxy which will provide DNS resolution for us and allow us to validate SSL certificate for acme-v02.api.letsencrypt.org
Some additional configuration options are kept in a separate Lua file, “config.lua”. You must explicitly set the option “termsOfServiceAgreed” to “true” in order to be able to acquire certificates. Before doing that, please read latest Let’s Encrypt Terms of Service and Subscriber Agreement available at letsencrypt.org/repository/.
Contents of “config.lua”:
config = { | |
registration = { | |
-- You can read TOS here: https://letsencrypt.org/repository/ | |
termsOfServiceAgreed = false, | |
contact = {"mailto:postmaster@example.net"} | |
}, | |
-- ACME certificate authority configuration | |
ca = { | |
-- HAProxy backend/server which proxies requests to ACME server | |
proxy_uri = "http://127.0.0.1:9012", | |
-- ACME server URI (also returned by ACME directory listings) | |
-- Use this server name in HAProxy config | |
uri = "https://acme-v02.api.letsencrypt.org", | |
} | |
} |
Key creation
Although Lua is able to create account or domain keys automatically, for performance reasons you will need to create your keys separately and provide them to HAProxy.
At the moment, we only support RSA keys. ECDSA will be coming in the near future. The account key should be 4096-bit and the domain key 2048-bit (minimal key sizes are also enforced by ACME servers).
You can use the following commands to create keys. Please note that you need a modern OpenSSL version. We don’t use “openssl genrsa”. Instead, we use “openssl genpkey”, as we are going to use the genpkey command to create ECDSA keys in the future.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out account.key | |
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out example.net.key |
Usage
After you have provisioned your keys, you can run a certificate order via HTTP. For example, by using curl to POST data in multipart/form-data format:
curl -XPOST -u acme:acme http://127.0.0.1:9011/acme/order \ | |
-F 'account_key=@account.key' \ | |
-F 'domain=example.net' \ | |
-F 'domain_key=@example.net.key' \ | |
-F 'aliases=www.example.net,example.com,www.example.com' \ | |
-o example.net.pem |
Aliases are optional, and we use curl’s @-syntax to POST files. The output is a full certificate chain (with key appended), suitable for direct consumption by HAProxy. Check the output of curl command and received data (cert bundle), and reload HAProxy.
To-Do
Build Support directly into the core
Support ECDSA account and domain keys in the near future (Let’s Encrypt already supports ECDSA domain keys)
Support OCSP MustStaple
Support batch mode, for requesting certificates for multiple domains at once
Investigate ways to expand HAProxy Lua API, at least in the part to be able to list info about the currently installed certificates (i.e. if we have the current certificate info, we could automatically trigger renewals)
Extract JWS/JWK/JWT parts of Lua code into a reusable library (for Lua on HAProxy)
Conclusion
We hope that this module will allow you conveniently manage SSL certificates provided by Let’s Encrypt.
At HAProxy Technologies, we are here to assist you in implementing any advanced logic with Lua that you might require, and provide you with custom-built scripts as part of our HAProxy Enterprise offering.
Do you have other ideas for Lua integration that you’d like to see in HAProxy? Let us know!
If you would like to use HAProxy Enterprise backed by professional support from HAProxy Technologies, please see our HAProxy Enterprise – Trial Version or contact us for expert advice.
Happy scripting and stay tuned!
Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.