I finally figured out what was going on. I discovered that the --manual
flag steps through the authentication process interactively.
Each phase in the process will display a prompt similar to the following:
Make sure your web server displays the following content at
http://www.example.net/.well-known/acme-challenge/twJCKQm9SbPEapgHpyU5TdAR1ErRaiCyxEB5zhhw0w8 before continuing:
twJCKQm9SbPEapgHpyU5TdAR1ErRaiCyxEB5zhhw0w8.t7J7DDTbktMGCCu2KREoIHv1zwkvwGfJTAkJrnELb4U
If you don't have HTTP server configured, you can run the following
command on the target server (as root):
mkdir -p /tmp/letsencrypt/public_html/.well-known/acme-challenge
cd /tmp/letsencrypt/public_html
printf "%s" twJCKQm9SbPEapgHpyU5TdAR1ErRaiCyxEB5zhhw0w8.t7J7DDTbktMGCCu2KREoIHv1zwkvwGfJTAkJrnELb4U > .well-known/acme-challenge/twJCKQm9SbPEapgHpyU5TdAR1ErRaiCyxEB5zhhw0w8
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()"
Press ENTER to continue
As I discovered, the process, despite being run as root itself, did not have the privileges to start the challenge server itself. Surely, this might be a bug in the API.
Running the script in the prompt directly produces the following error:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
> "import BaseHTTPServer, SimpleHTTPServer; \
> s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
> s.serve_forever()"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib/python2.7/SocketServer.py", line 419, in __init__
self.server_bind()
File "/usr/lib/python2.7/BaseHTTPServer.py", line 108, in server_bind
SocketServer.TCPServer.server_bind(self)
File "/usr/lib/python2.7/SocketServer.py", line 430, in server_bind
self.socket.bind(self.server_address)
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 13] Permission denied
But running it as root (as the prompt itself stated) started the server correctly and could be monitored as the external server queried it to complete the challenge:
sudo $(command -v python2 || command -v python2.7 || command -v python2.6) -c "import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()"
66.133.109.36 - - [08/Jan/2016 21:25:10] "GET /.well-known/acme-challenge/SZ88SorxBGXBtSZCTn4FX2g7u5XjnPFOOV3f5S5DuXB HTTP/1.1" 200 -
66.133.109.36 - - [08/Jan/2016 21:25:10] "GET /.well-known/acme-challenge/twJCKQm9SbPEapgHpyU5TdAR1ErRaiCyxEB5zhhw0w8 HTTP/1.1" 200 -
This error took awhile to diagnose as many things could have prevented the challenges from failing and the servers spawned were failing silently in the background.