We have mastered how to create an ACME server
account in our acme_register
example. Now it is time to bring all pieces
together and get a signed certificate for our
domain.
The ACME protocol requires that the client
will prove the ownership of the domain
by one of available means, called challenge.
In out example we will use http-01 type
of challenge which requires the client
will obtain a token from ACME server and
made it available via well-known URL like
http://<domain>/.well-known/acme-challenges/<token>.
We consider you have a Nginx server set up and running
with config like this:
/etc/nginx/conf.d/
server {
listen 80;
server_name <domain>;
location /.well-known/acme-challenge/ {
alias /www/acme/;
}
}
So our task is:
Get a token for domain.
Place file into /www/acme/ directory.
Ask the server to perform validation.
Grab the certificate.
The ACME protocol is quite complex and really
require much more stages, but, luckily,
Gufo ACME hides all the complexity
and provides clean API.
The crucial ACME protocol concept is the Directory. The directory
is an URL which allows to fetch all necessary information about
ACME server. In our case we're using Letsencrypt staging directory.
Warning
The staging server should be used only for testing purposes.
Replace the DIRECTORY variable with the productive
endpoint to get the real certificates.
We need to provide the implementation of the challenge
fulfillment. The Gufo ACME's API provides special
methods which can be overriden in subclasses to
implement the desired behavior. So we are creating
a subclass of AcmeClient.
We're implementing http-01 challenge, so we need to override
fulfill_http_01 method. This is an asyncronous method
so we use any async function inside. It accepts two parameters:
domain - a domain name.
challenge - a AcmeChallenge structure, which has
a token field, containing challenge token.
Function returns True when fulfillment has beed processed correctly,
or False, if we wan't provide a fulfillment.
According the ACME protocol, we need to place a specially formed
data to prove our authority. The data contain challenge token and
the fingerprint of the client's key. The calculation may be tricky,
but Gufo ACME provides a AcmeClient.get_key_authorization() method,
which performs all necessary calculations. So we pass challenge
parameter and grab an authorization data as value of the variable v.
The ACME protocol definition exlicitly notes that client may
clean up prepared data after the validation. AcmeClient
allows to add own cleanup code by overriding cleanup_*
methods. In our case we're overriding clear_http_01 method.
Just like fulfill_http_01, it accepts two parameters:
domain - a domain name.
challenge - a AcmeChallenge structure, which has
a token field, containing challenge token.
We're instantiating AcmeClient directly from state by
using from_state() method. Note, we're restoring state
not into the AcmeClient, but in our SignAcmeClient subclass.
The new client instance
loads the private key, directory, and account information
directly from state.
And finally, we call an AcmeClient.sign method, which
accepts domain name and CSR. The sign method simple
hides all the protocol's complexity and simply returns
us a signed certificate in PEM format.