summaryrefslogtreecommitdiff
path: root/README.md
blob: c101c435936df259d54b387b1f94fc506c5db890 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# Let's Encrypt ACME protocol

This is a simple Haskell script to obtain a certificate from [Let's
Encrypt](https://letsencrypt.org/) using their ACME protocol.


- The main source of information to write this was
  https://github.com/diafygi/letsencrypt-nosudo

- The ACME spec: https://letsencrypt.github.io/acme-spec/

Most values are still hard-coded for my initial attempt (i.e. my email address
and a domain of mine).


## Discover the URL for letsencrypt ACME endpoints

API endpoints are listed at https://acme-v01.api.letsencrypt.org/directory and
are currently hard-coded in the script.

```
> curl -s https://acme-v01.api.letsencrypt.org/directory | json_pp 
{
   "new-cert" : "https://acme-v01.api.letsencrypt.org/acme/new-cert",
   "new-authz" : "https://acme-v01.api.letsencrypt.org/acme/new-authz",
   "revoke-cert" : "https://acme-v01.api.letsencrypt.org/acme/revoke-cert",
   "new-reg" : "https://acme-v01.api.letsencrypt.org/acme/new-reg"
}
```


## Generate user account keys

You need an account with Let's Encrypt to ask and receive certificates for your
domains. The account is controlled by a public/private key pair:

```
openssl genrsa 4096 > user.key
openssl rsa -in user.key -pubout > user.pub
```


## Generate nonces


Each request to the API have a nonce to prevent replays. The nonce is currently
hard-coded in the script. New nonces can be obtained from letsencrypt with

```
> generate-nonce.sh
```


## Create user account

Generate `registration.body` by using the `acme.hs` script then POST it to
letsencrypt (note it assumes you agree to their subscriber agreement):

```
> curl -s -X POST --data-binary "@registration.body" \
  https://acme-v01.api.letsencrypt.org/acme/new-reg | json_pp
{
   "agreement" : "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf",
   "contact" : [
      "mailto:noteed@gmail.com"
   ],
   "key" : {
      "e" : "...",
      "kty" : "RSA",
      "n" : "..."
   },
   "id" : 36009,
   "createdAt" : "2015-12-04T14:22:08.321951547Z",
   "initialIp" : "80.236.245.73"
}
```


## Request a challenge


Let's Encrypt needs a proof that you control the claimed domain. You can
request a challenge with `challenge-request.body`.

```
> curl -s -X POST --data-binary "@challenge-request.body" \
  https://acme-v01.api.letsencrypt.org/acme/new-authz | json_pp
{
   "expires" : "2015-12-21T18:44:52.331487674Z",
   "challenges" : [
      {
         "status" : "pending",
         "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844047",
         "type" : "tls-sni-01",
         "token" : "oielAbB7MdyCl29mqjzlqGdrCQSB8SyJaxHbAgQBA7Q"
      },
      {
         "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048",
         "status" : "pending",
         "type" : "http-01",
         "token" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg"
      }
   ],
   "identifier" : {
      "type" : "dns",
      "value" : "aaa.reesd.com"
   },
   "combinations" : [
      [
         0
      ],
      [
         1
      ]
   ],
   "status" : "pending"
}
```

The script assumes you'll answer the challenge by hosting a file at a location
chosen by Let's Encrypt. Extract the token for the `http-01` challenge and run
the script again. Now you have to host the file at the location reported by the
script.

Once this is done, you can ask Let's Encrypt to check the file.

```
> curl -s -X POST --data-binary "@challenge-response.body" \
  https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048 | json_pp
{
   "token" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg",
   "keyAuthorization" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg.EJe0KReqzCUq6leNOerMC9naZSHxP9TJzGxCcsGkNrw",
   "type" : "http-01",
   "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048",
   "status" : "pending"
}
```

The same URL can then be polled until the status becomes valid.


## Send CSR / Receive certificate

The CSR is created with:

```
> openssl genrsa 4096 > domain.key
> openssl req -new -sha256 -key domain.key -subj "/CN=aaa.reesd.com" > aaa.reesd.com.csr
> openssl req -in aaa.reesd.com.csr -outform DER > aaa.reesd.com.csr.der
```

And the signed certificate can be obtained from Let's Encrypt:

```
> curl -s -X POST --data-binary "@csr-request.body" \
  https://acme-v01.api.letsencrypt.org/acme/new-cert > aaa.reesd.com.cert.der
```


## Create a certificate for HAProxy

Including explicit DH key exchange parameters to prevent Logjam attack
(https://weakdh.org/).

```
> openssl x509 -inform der -in aaa.reesd.com.cert.der \
    -out aaa.reesd.com.cert.pem
> openssl dhparam -out aaa.reesd.com-dhparams.pem 2048
> cat aaa.reesd.com.cert.pem \
    lets-encrypt-x1-cross-signed.pem \
    aaa.reesd.com.key \
    aaa.reesd.com-dhparams.pem > aaa.reesd.com-combined.pem
```