LetsEncrypt on a Ubuntu System without Apache or nginx

In this example, I use as the instance name. Of course you will want to change that.


As strongly advised in the acme_tiny, create a separate user acme, in which to do all this.

To keep things tidy, I create three sub-directories

mkdir work secrets challenges
chmod 750 work
# protect secrets
chmod 700 secrets
chmod 750 challenges

challenges has to be readable by the web server, but this hack can only be done by root

chown acme:www-data /home/acme/challenges

I do not have git on small machines, so just grab the code with fetch. Being lazy, I just fetch the source into /home/acme.

curl >
chmod 700

LetsEncrypt Account Key

I use the same LetsEncrypt account key for all servers. so I copy it

rsync <somewhere>:/home/acme/secrets/account.key /home/acme/secrets
chmod 600 /home/acme/secrets/account.key

If you do not already have a Let's Encrypt account key, generate one per the

openssl genrsa 4096 > /home/acme/secrets/account.key
chmod 600 /home/acme/secrets/account.key

If you generate an account key, keep it very safe and use it for all LetsEncrypt transactions from now on.

Domain Private Key

The domain private key will be the server's TLS private key, and should be protected. Generate it as described in the It remains constant for the initial use and subsequent certificate refresh requests.

openssl genrsa 4096 > /home/acme/secrets/domain.key
chmod 600 /home/acme/secrets/domain.key

Generate the Certificate Request

As described in the, generate the certificate request. It remains constant for the initial use and subsequent certificate refresh requests.

openssl req -new -sha256 -key /home/acme/secrets/domain.key -subj "/" > /home/acme/work/domain.csr

If the web site virtualizes multiple domains,

cat /etc/ssl/openssl.cnf |
awk '
  END {
        print "[SAN]";
        print ",,";
  }' |
openssl req -new -sha256 -key secrets/domain.key -subj "/" \
        -reqexts SAN -config /dev/stdin -out work/domain.csr

Installl and Configure micro-http to Provide Proof of Ownership

LetsEncrypt will send a challenge nonce that we have to store where they can subsequently GET it from the web server for the domain being registered. uses what it calls a challenge directory. It must be writable by the script and readable by the web server; hence the perms and ownership used above.

LetsEncrypt fetches the challenge over port 80 from a URL of [http:your.domain/.well-known/acme-challenge/]. So we need to configure a web server to make the challenge data readable on port 80. We do not want to run full apache or ngix, so we use micro-httpd.

apt install xinetd micro-httpd

I suggest hacking /etc/services to use micro-httpd

#http           80/tcp          www             # WorldWideWeb HTTP             
#http           80/udp                          # HyperText Transfer Protocol   
micro_httpd     80/tcp                          # WorldWideWeb HTTP             

Since you will use micro-http very rarely, run it out of inetd, well xinetd

cat > /etc/xinetd.d/micro_http << EOF
service micro_httpd
     disable         = no
     protocol        = tcp
     port            = 80
     socket_type     = stream
     wait            = no
     user            = nobody
     server          = /usr/sbin/micro-httpd
     server_args     = /home/acme/challenges
service restart xinetd

It is a good check to ensure that you can browse to the challenge directory [].

Getting my browsers not to fall forward to https was very painful. So I tested as follows:

echo gobbledygook > challenges/foo
chmod 644 challenges/foo

Then, from a remote system, this should work


Getting the Certificate

I lost a number hours because I have a conservative umask. So I made a handy script to get the certificate and, while we're at it, the certificate chain from LetsEncrypt.

Before you go for real, I strongly suggest you test using the staging server so as not to encounter LetsEncrypt's rate limiting. To do this, hack the as follows:


When it all works, then you can go for real.

#!/bin/sh -x

umask 0022

python \
    --account-key \
    $SEC/account.key \
    --csr $WK/domain.csr \
    --acme-dir $CH \
    > $WK/signed.crt || exit

curl \
    > $WK/intermediate.pem

# note that you may have the certs stored elsewhere
sudo $H/CAT $SEC/domain.key      /etc/ssl/private/ssl-cert-snakeoil.key
sudo $H/CAT $WK/signed.crt       /etc/ssl/certs/ssl-cert-snakeoil.pem

Where /home/acme/CAT is

/bin/cat $1 > $2

And you will have needed to hack /etc/sudoers to have

acme ALL = (root) NOPASSWD: /home/acme/CAT
acme ALL = (root) NOPASSWD: /usr/sbin/service


You can run the same script as above for certificate renewal. Put

36  0  1   */2 *     /home/acme/LE

in acme's crontab.


Last modified 3 years ago Last modified on Aug 6, 2017, 11:46:41 PM