Многие наверняка слышали об инициативе по выдаче бесплатных сертификатов Let’s Encrypt (которую скоро переименуют и она будет жить на EFF). Выдаются они всего на 3 месяца, но обновление можно автоматизировать. Я тоже решил попробовать этого зверя, установил из портов и… обломался. Получил ошибку PythonDialogBug (полный лог см. ниже). Быстро найти решение не удалось, поэтому я забросил это дело на несколько месяцев. Когда снова “дошли руки”, начал гуглить информацию по питону. На одном форуме нашел предложение попробовать маленький скрипт:
import dialog try: dialog.Dialog().msgbox("Hi") except Exception as e: print e.message
И что вы думаете? Я получил ошибку:
unexpected low-level exit status (new code?): 255
Т.е. проблема явно в диалогах.
Я выяснил, что dialog – это консольное приложение, которое на моей ОС было довольно старым:
$ dialog --version dialog version 0.3, by Savio Lam (lam836@cs.cuhk.hk). patched to version 0.4 by Stuart Herbert (S.Herbert@shef.ac.uk)
Я стал искать, что это за софтина и как ее обновить (в установленных портах ее не было). На FreeBSD 8, оказывается, как раз и используется эта версия 0.4. Вот здесь я нашел новую версию и установил:
wget https://invisible-island.net/datafiles/release/dialog.tar.gz tar zxf dialog.tar.gz cd dialog-1.3-20160209/ ./configure make install clean rehash mv /usr/bin/dialog /usr/bin/dialog.old && ln -s /usr/local/bin/dialog /usr/bin/dialog $ dialog --version Version: 1.3-20160209
После обновления, снова запустил letsencrypt и все заработало, появился диалог, где у меня попросили адрес e-mail для контакта с админом домена.
Я пробовал запускать так:
letsencrypt certonly --webroot -w /tmp/cert/ -d domain.com
Но в этом случае команду нужно запускать на сервере, для домена/ов которого генерируется сертификат, т.к. скрипт кладет в рут домена (который у меня указан вообще левый) файлик, который дергает через веб для авторизации домена. Т.е. webroot должен быть рутом домена, а не левым каталогом.
Вторым вариантом был этот:
letsencrypt certonly --standalone-supported-challenges tls-sni-01 -d domain.com
Но для такого варианта скрипт байндится на 443 порт и нужно останавливать веб-сервер (443 порт у меня вообще не использовался, так что я пробрасывал его на тестовый сервер, где и запускался скрипт). Сертификат сгенерировался.
А вот третий вариант я использовал для генерации сертификата без тушения веб-сервера на удаленном сервере:
letsencrypt certonly --manual -d domain.com
В этом случае скрипт приостанавливает своё выполнение и предлагает инструкцию по авторизации: в другой консоли на удаленном сервере нужно в руте домена создать файл с проверочным текстом, т.е. подтвердить, что вы имеете доступ к домену, а не пытаетесь сгенерировать сертификат для чужого домена. И если у вас еще нет веб-сервера, выводятся команды, которые, опять же, генерируют проверочный файл и поднимают на питоне микро веб-сервер, который слушает 80-й порт и отдает этот самый проверочный файл.
Сгенерированный сертификат кладется в /etc/letsencrypt/live/domain.com. Для автоматического обновления подойдет 1-й вариант, нужно только прописать скриптик в кроне и запускать каждые ~2.5 месяца. Единственное, что стоит добавить, это —force-renew и, возможно, —agree-dev-preview. Мануал можно почитать здесь.
P.S. Полный лог ошибки в letsencrypt.log выглядел так:
Traceback (most recent call last): File "/usr/local/bin/letsencrypt", line 11, in <module> sys.exit(main()) File "/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 1967, in main setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') File "/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 1881, in setup_logging cli_handler = cli_handler_factory(config, level, fmt) File "/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 1868, in _cli_log_handler handler = log.DialogHandler() File "/usr/local/lib/python2.7/site-packages/letsencrypt/log.py", line 29, in __init__ self.d = dialog.Dialog() if d is None else d File "/usr/local/lib/python2.7/site-packages/dialog.py", line 1038, in __init__ self.backend_version()) File "/usr/local/lib/python2.7/site-packages/dialog.py", line 1817, in backend_version use_persistent_args=False) File "/usr/local/lib/python2.7/site-packages/dialog.py", line 1541, in _perform args_file) File "/usr/local/lib/python2.7/site-packages/dialog.py", line 1502, in _handle_program_exit child_output_rfd) File "/usr/local/lib/python2.7/site-packages/dialog.py", line 1479, in _wait_for_program_termination ll_exit_code)) PythonDialogBug