Skip to content
This repository was archived by the owner on Jun 18, 2024. It is now read-only.

Commit 012d4ce

Browse files
author
Adis Nezirovic
committed
Initial Release
0 parents  commit 012d4ce

File tree

5 files changed

+735
-0
lines changed

5 files changed

+735
-0
lines changed

NOTES.rst

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
Notes
2+
=====
3+
4+
A few development notes and tips for HAProxy Lua ACME implementation.
5+
6+
RFCs
7+
----
8+
9+
In addition to standard HTTP, JSON, TLS formats and protocols, knowledge of
10+
other protocols/formats was necessary to develop ACME client.
11+
12+
* ACME protocol - https://tools.ietf.org/html/draft-ietf-acme-acme-09
13+
* JWS (JSON Web Signature) - https://tools.ietf.org/html/rfc7515
14+
* JWK (JSON Web Key) - https://tools.ietf.org/html/rfc7517
15+
16+
Tools
17+
-----
18+
19+
Aside from `haproxy` and `curl`, additional software was used during
20+
development, for testing and debugging.
21+
22+
Pebble
23+
++++++
24+
25+
peble_ is a test ACME server with intented purpose to help with development of
26+
ACME clients. It uses slightly different ACME endpoints than referent ACME
27+
servers (all within specification), to force clients to properly implement
28+
endpoint discovery. It also presents some other transient challenges to clients
29+
(e.g. 15% of *nonces* are invalid), with the purpose of creating more robust
30+
client implementations.
31+
32+
Server is a single binary, with configuration file in JSON format. By default
33+
it includes a test CA infrastructure, and creates server side certificates on
34+
the fly. There is also a simple acme client in the distribution,
35+
`peble-client`, which can be used as a reference/starting point other clients.
36+
37+
Usage
38+
~~~~~
39+
After installing pebble, following their instructions, you can start it with:
40+
41+
::
42+
43+
cd ~/go/src/github.com/letsencrypt/pebble
44+
~/go/bin/pebble
45+
46+
It loads the configuration and certificates from `test` subdirectory, and binds
47+
to port 14000 by default.
48+
49+
50+
Boulder
51+
+++++++
52+
53+
boulder_ is a reference ACME server, used by Let's Encrypt organization. One
54+
can run it locally, via Docker Compose, for easier testing and validation.
55+
56+
MitmProxy
57+
+++++++++
58+
59+
ACME protocol v2 mandates that clients use TLS to access the ACME server. That
60+
means we can't easily track request flow from ACME client to server (e.g. to
61+
debug our own client, or to observe `peble-client` behaviour). Unfortunately,
62+
while `wireshark` can support/decrypt static SSL traffic, it doesn't support
63+
Diffie-Hellman key exchanges, so decrypting standard TLS traffic is impossible.
64+
65+
mitmproxy_ is a TLS capable HTTP proxy for software developers and penetration
66+
testers. We can use it to observe traffic between local ACME client and local
67+
ACME server.
68+
69+
Usage
70+
~~~~~
71+
72+
When you run peble it will create server key and cert in
73+
`~/go/src/github.com/letsencrypt/pebble/test/certs/localhost`. You need to
74+
concat test key and cert into single file (`mitm.pem` in our example)
75+
76+
::
77+
78+
sudo mitmweb -R https://localhost:14000 -p 443 --no-browser --insecure --cert localhost=~/go/src/github.com/letsencrypt/pebble/test/certs/localhost/mitm.pem
79+
Proxy server listening at http://0.0.0.0:443/
80+
Web server listening at http://127.0.0.1:8081/
81+
82+
For the time being, it is necesarry to run mitmproxy with sudo, binding to port
83+
443, even if we'd like to run it on some nonprivileged port, e.g. 8443
84+
85+
::
86+
87+
sudo mitmweb -R https://localhost:14000 -p 8443 --no-browser --insecure --cert localhost=~/go/src/github.com/letsencrypt/pebble/test/certs/localhost/mitm.pem
88+
Proxy server listening at http://0.0.0.0:443/
89+
Web server listening at http://127.0.0.1:8081/
90+
91+
When we run discovery query against the ACME server (through mitmproxy), we
92+
get this:
93+
94+
::
95+
96+
curl -k https://localhost:8443/dir
97+
{
98+
"meta": {
99+
"termsOfService": "data:text/plain,Do%20what%20thou%20wilt"
100+
},
101+
"newAccount": "https://localhost/sign-me-up",
102+
"newNonce": "https://localhost/nonce-plz",
103+
"newOrder": "https://localhost/order-plz"
104+
}
105+
106+
were we'd expect to get
107+
108+
::
109+
110+
{
111+
"meta": {
112+
"termsOfService": "data:text/plain,Do%20what%20thou%20wilt"
113+
},
114+
"newAccount": "https://localhost:8443/sign-me-up",
115+
"newNonce": "https://localhost:8443/nonce-plz",
116+
"newOrder": "https://localhost:8443/order-plz"
117+
}
118+
119+
mitmproxy doesn't pass full host info to the upstream, i.e. it doesn't respect
120+
HTTP 1.1 RFC (https://tools.ietf.org/html/rfc7230#section-5.4), where `Host`
121+
header **must** include hostname and port if port is not 80 (for http) or 443
122+
(for https). Hence, we force mitmproxy to run on standard HTTPS port, where
123+
explicit port is not required.
124+
125+
.. _peble: https://github.com/letsencrypt/pebble
126+
.. _boulder: https://github.com/letsencrypt/boulder
127+
.. _mitmproxy: https://mitmproxy.org

README.rst

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
HAProxy ACME v2 client
2+
======================
3+
4+
This is a client implementation for ACME (Automatic Certificate Management
5+
Environment) protocol, currently draft IETF standard
6+
(https://tools.ietf.org/html/draft-ietf-acme-acme-12)
7+
8+
The protocol will be supported by Let's Encrypt project from March 2018.
9+
and it is expected that other *Certificate Authorities* will support this
10+
ACME version in the future.
11+
12+
Intro
13+
-----
14+
The main idea of this ACME client is to implement as much functionality inside
15+
HAProxy. In addition to supporting single instance HAProxy installations, we
16+
also aim to support multi-instance deployments (i.e. you have a cluster of load
17+
balancers on which you want to use ACME issued certs).
18+
19+
By using the internal HTTP interface (and http client such as `curl`), you will
20+
be able to execute the following:
21+
22+
- Upload your own account and domain keys (only RSA keys for now)
23+
- Automatically register your account on ACME servers (linked to your account
24+
key)
25+
- Request and receive certificates for your domains
26+
27+
The only thing you need to do on your own is to save the received certificate
28+
bundles and reload HAProxy.
29+
30+
31+
Requirements
32+
------------
33+
34+
* A modern HAProxy version (v1.8) with Lua support (check with
35+
``haproxy -vv | grep USE_LUA=1``)
36+
* `haproxy-lua-http` - Lua HTTP server/client for HAProxy Lua host
37+
* `json.lua` - Lua JSON library (https://github.com/rxi/json.lua)
38+
* `lua-ossl` - OpenSSL bindings for Lua (https://github.com/wahern/luaossl)
39+
40+
41+
Configuration
42+
-------------
43+
44+
Install the required Lua libraries to proper LUA_PATH location, and configure
45+
haproxy as follows:
46+
47+
::
48+
49+
global
50+
log /dev/log local0 debug
51+
nbproc 1
52+
daemon
53+
lua-load config.lua
54+
lua-load acme.lua
55+
56+
defaults
57+
log global
58+
mode http
59+
option httplog
60+
timeout connect 5s
61+
timeout client 10s
62+
timeout server 10s
63+
64+
listen http
65+
bind *:80
66+
http-request use-service lua.acme if { path_beg /.well-known/acme-challenge/ }
67+
68+
listen acme
69+
bind 127.0.0.1:9011
70+
http-request use-service lua.acme
71+
72+
listen acme-ca
73+
bind 127.0.0.1:9012
74+
server ca acme-v02.api.letsencrypt.org:443 ssl verify required ca-file letsencrypt-x3-ca-chain.pem
75+
76+
Configuration is kept in a separate Lua file, where you must explicitly set
77+
``termsOfServiceAgreed`` option to ``true`` in order to be able to acquire
78+
certs. Before doing that, please read latest Let's Encrypt terms of service and
79+
subscriber agreement available at https://letsencrypt.org/repository/
80+
81+
::
82+
83+
config = {
84+
registration = {
85+
-- You can read TOS here: https://letsencrypt.org/repository/
86+
termsOfServiceAgreed = false,
87+
contact = {"mailto:postmaster@example.net"}
88+
},
89+
90+
-- ACME certificate authority configuration
91+
ca = {
92+
-- HAProxy backend/server which proxies requests to ACME server
93+
proxy_uri = "http://127.0.0.1:9012",
94+
-- ACME server URI (also returned by ACME directory listings)
95+
-- Use this server name in HAProxy config
96+
uri = "https://acme-v02.api.letsencrypt.org",
97+
}
98+
}
99+
100+
Key creation
101+
------------
102+
103+
Although Lua module is able to create account key or domain automatically, for
104+
performance and security reasons we require that you create your keys
105+
separately.
106+
107+
Currently, we only support RSA keys. For account key, key size should be
108+
4096bits, and for domain key 2048bits (minimal key sizes are also enforced by
109+
Let's Encrypt).
110+
111+
You can use the following commands to create keys. Note that you need a modern
112+
openssl version, we don't use ``openssl genrsa`` but ``openssl genpkey``, as
113+
we're going to use the same command to create ECDSA keys in the future.
114+
115+
::
116+
117+
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out account.key
118+
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out example.net.key
119+
120+
121+
Usage
122+
-----
123+
124+
After you have provisioned your keys, you can run certificate order via HTTP.
125+
For example by using curl to POST data in *multipart/form-data* format:
126+
127+
::
128+
129+
curl -XPOST http://127.0.0.1:9011/acme/order -F 'account_key=@account.key' \
130+
-F 'domain=example.net' -F 'domain_key=@example.net.key' \
131+
-F 'aliases=www.example.net,example.com,www.example.com' \
132+
-o example.net.pem
133+
134+
Aliases are optional, and we use curl ``@`` syntax to post files.
135+
The output is full certificate chain (with key appended), suitable for direct
136+
consumption by HAProxy.

0 commit comments

Comments
 (0)