PythonDialogBug на FreeBSD в Letsencrypt

Автор: | 21 марта 2016

Многие наверняка слышали об инициативе по выдаче бесплатных сертификатов 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

Добавить комментарий