Skip to content
Snippets Groups Projects
letsencrypt.mdwn 5.24 KiB
[[!toc levels=3]]

# How to get an X.509 certificate for your new name

## The letsencrypt-domains git repository

If not already done, clone git repos letsencrypt-domains and
backup-keys.

    git clone ssh://git@git-rw.torproject.org/admin/letsencrypt-domains
	cd letsencrypt-domains
	git clone pauli.torproject.org:/srv/puppet.torproject.org/git/tor-backup-keys.git backup-keys

## Add your new name

Add your domain name and optional alternative names (`SAN`) to the
`domains` file:
    
    $EDIT domains

## Public key pinning

If you do not want to use HPKP, skip this section.

Generate backup [HPKP][]:

[HPKP]: https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning

	./bin/manage-backup-keys create

See `tor-passwords/000-backup-keys` for the passphrase when prompted.

The private key is a backup RSA certificate that can be used to rotate
HTTPS certificates in case of a compromise, while respecting the pins
sent as `Public-Key-Pins` headers.

Push the new key to the backup-keys repo:

    cd backup-keys
	git status
	git add $yourfiles
	git commit
	git push
	cd ..

## Push the updated domain list to the letsencrypt-domains repo

	git diff domains
	git add domains
	git commit
	git push

The last command will produce output from the `dehydrated` command
which talks with the DNS primary (currently `nevii`) to fetch new keys
and update old ones.

The new keys and certs are being copied to the LDAP host
(currently `pauli`) under
`/srv/puppet.torproject.org/from-letsencrypt/`. Then [[Puppet]] pick
those up in the `ssl` module. Use the `ssl::service` resource to
deploy them.

See the "Design" section below for more information on how that works.

See also [[static-component]] for an example of how to deploy an
encrypted virtual host and onion service.

# Disabling HPKP

To disable key pinning ([HPKP][]) on a given domain, just remove the
backup key from the repository:

    cd backup-keys
	git rm example.torproject.org*
	git commit
	git push

Then run Puppet on all affected hosts, for example the static mirrors:

    cumin 'C:roles::static_mirror_web' 'puppet agent -t'

# Renewing a certificate before its expiry date

If a certificate has been revoked, it should be renewed before its
expiry date. To do so, you can drop a special file in the
`per-domain-config` directory to change the expiry date range and run
the script by hand.

Create a file matching the primary domain name of the certificate on
the DNS master:

    cat <<EOF > /srv/letsencrypt.torproject.org/repositories/letsencrypt-domains/per-domain-config/example.torproject.org
    RENEW_DAYS="85"
    EOF

Here we tell the ACME client (dehydrated) to renew the cert if it is
85 days or older (instead of the 30 days period).

Then run the script by hand (or wait for cron to do its thing):

    letsencrypt@nevii:~$ dehydrated-wrap --cron
    [...]
    Processing example.torproject.org with alternative names: example.torproject.org
     + Using certificate specific config file!
       + RENEW_DAYS = 85
     + Checking domain name(s) of existing cert... unchanged.
     + Checking expire date of existing cert...
     + Valid till May 18 20:40:45 2020 GMT Certificate will expire
    (Less than 85 days). Renewing!
     + Signing domains...
    [..]

Then remove the file.

# Design

How is this built anyways?

When you push to the git repository on the `git-rw.torproject.org`
server (currently `cupani`):

 1. a per-repository hook gets called in
    `/srv/git.torproject.org/git-helpers/post-receive-per-repo.d/admin\%letsencrypt-domains/trigger-letsencrypt-server`

 2. this hooks hits the DNS master over SSH (`letsencrypt@nevii`) and
    there the `authorized_keys` file hardcodes the command to
    `/srv/letsencrypt.torproject.org/bin/from-githost`

 3. ... which in turns just calls `bin/update` in the same directory
    (`/srv/letsencrypt.torproject.org`)

 4. ... which in turns pulls the `letsencrypt-domains` repository and
    runs `dehydrated-wrap --cron` with a special `BASE` variable that
    points dehydrated at our configuration, in
    `etc/dehydrated-config`, again in the same directory

 5. Through that special configuration, the dehydrated command is
    configured to call a custom hook (`bin/le-hook`) which implements
    logic around the DNS-01 authentication challenge, notably adding
    challenges, bumping serial numbers in the primary nameserver, and
    waiting for secondaries to sync. Note that there's a configuration
    file for that hook in `/etc/dsa/le-hook.conf`.

 6. The `le-hook` also pushes the changes around. The hook calls the
    `bin/deploy` file which installs the certificates files in
    `var/result`. 

 7. It also generates a Public Key Pin (PKP) hash with the
    `bin/get-pin` command and appends Diffie-Hellman paramets
    (`dh-$size.pem`) to the certificate chain.

 8. It finally calls the `bin/push` command which runs `rsync` to the
    Puppet server, which in turns hardcodes the place where those
    files are dumped (in
    `pauli:/srv/puppet.torproject.org/from-letsencrypt`) through its
    `authorized_keys` file.

 9. Finally, those certificates are collected by Puppet through the
    `ssl` module. Pay close attention to how the
    `tor-puppet/modules/apache2/templates/ssl-key-pins.erb` template
    works: it will not deploy key pinning if the backup `.pin` file is
    missing.