Pull to refresh

CommuniGate Pro, закрытые ключи и Web-API

Reading time 6 min
Views 8.3K
В силу разных процессов сейчас уже практически все слышали о таком явлении, как импортозамещение. В частности, сейчас активно замещается импортный продукт MS Exchange исконно русским без единого гвоздя* Communigate Pro. Если мои коллеги найдут время, думаю, они смогут многое рассказать про кластеры, нагруженность и миграцию, я же хочу рассказать одну леденящую кровь, но сильно менее масштабную историю про национальные особенности обновления сертификатов в этом замечательном продукте.

Собственно, краткая предыстория. У меня есть небольшой ноутбук в чулане, на котором до недавнего времени жужжал почтовый сервер в связке Windows + hMailServer. Естественно, в силу импортозамещения мне захотелось поближе познакомиться с Communigate Pro, благо, он весьма скромен в требованиях и бесплатен в некоторых масштабах:
Мы предоставляем полную версию CommuniGate Pro бесплатно для пяти пользователей в целях тестирования и для использования в небольших проектах (компаниях).
Знакомство можно начать в разделе «О нас». Там очень хорошо видно, что, в 1997 году была достигнута веха «First reliase», слово «Release» маркетологи Stalker, Inc научились писать только к 2004 году, а сделать для русскоязычного сайта русскоязычные маркетинговые материалы не смогли до сих пор.



Установка продукта (я его водрузил на CentOS 7) затруднений не вызвала, пользуясь случаем, я туда же поставил CertBot, прикрутил сертификаты от Let’s Encrypt и, в общем-то, всё завелось.
Спустя 3 месяца сертификаты планово истекли и пришло время их менять.

Тут я открыл для себя, что в Windows завезли весьма неожиданную замену клиенту Telnet:



Перегенерация ключа средствами CertBot оказалась скучной, порадовал, разве что, сервер dns1.yandex.ru, который в течение часа выдавал через раз то устаревшую, то обновлённую txt-запись _acme-challenge, из-за чего я смог сгенерировать сертификаты только с третьей попытки.
А дальше началась магия.

У сервера Communigate нет возможности через интерфейс заменить ключевую пару на новую, нужно сначала удалить старую ключевую пару:



Я, как сознательный админ локалхоста, включил аутентификацию только по ssl и благополучно забыл про это, так что после удаления ключевой пары сервер общаться со мной отказался:



Паранойю серверу я пригасил, добавив в файл /var/CommuniGate/Accounts/postmaster.macnt/account.settings вот такую строчку:
RequireAPOP = NO;
но, осадочек, конечно, остался. Естественно, я захотел сделать себе кнопку скрипт замены ключевой пары одним запуском этого самого скрипта, а не хождением кругами по настройкам пользователей.

Средства автоматизации в Communigate присутствуют в количестве ажно четырёх интерфейсов: общение текстом через TCP-соединение (PWD), перловый модуль CLI.pm (CG/PL), простые веб-запросы и XIMSS.

В силу разных причин (в основном, лени, конечно), я выбрал веб-запросы.

С самого начала что-то пошло не так:

[root@mx ~]# curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/getdomainsettings'
[root@mx ~]#

Как оказалось, я невнимательно прочитал документацию, нужно было делать вот так:

[root@mx ~]# curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/?command=getdomainsettings'
{CAChain=[тут-была-цепочка-сертификатов];CertificateType=YES;ClientCertCA="Let's Encrypt Authority X3";ClientIPs="";DKIMenabled=YES;DKIMkey=[тут-был-ключ-DKIM];DKIMselector=dkim;DomainComment="";IPMode="All Available";PrivateSecureKey=[тут-был-закрытый-ключ];SecureCertificate=[тут-был-сертификат];TrustedCertificates=();}

Потом что-то опять пошло не так:

[root@mx ~]# curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/?command=getdomainsettings&domainName=test'
{CAChain=[тут-была-цепочка-сертификатов];CertificateType=YES;ClientCertCA="Let's Encrypt Authority X3";ClientIPs="";DKIMenabled=YES;DKIMkey=[тут-был-ключ-DKIM];DKIMselector=dkim;DomainComment="";IPMode="All Available";PrivateSecureKey=[тут-был-закрытый-ключ];SecureCertificate=[тут-был-сертификат];TrustedCertificates=();}

Я озадачился таким поворотом событий и обратился в поддержку вендора.
На правах рекламы.

Да, у них есть чатик. И в нём даже отвечают специалисты. Даже безо всякой корпоративной подписки.
Как оказалось, http-запрос не понимает именованные параметры. Только позиционные, только хардкор:

[root@mx ~]# curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/?command=getdomainsettings%20test'
{}
[root@mx ~]#

Домен test, в полном соответствии со своим названием, тестовый, так что настроек в нём нету.
Тут я представил, как я в URL буду вставлять сертификат, и решил, что надо с этим что-то делать. Например, использовать для передачи данных вместо GET-запроса курильщика POST-запрос здорового человека:

[root@mx ~]# curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/' --data-urlencode 'command=getdomainsettings test'
{}
[root@mx ~]#

Ну, пока всё идёт по плану.

Let's Encrypt держит свои сокровища в директории /etc/letsencrypt/live/domain.my/ оттуда и будем их доставать.

Чуть выше в запросе getdomainsettings я видел, что закрытый ключ лежит в поле PrivateSecureKey, при чём, лежит он там с выкушенными хедером и футером, а всё остальное добро слито в одну строку. Давайте, попробуем его импортировать.

[root@mx ~]#  curl -u postmaster:password -k 'https://127.0.0.1:9100/cli/' --data-urlencode "command=updatedomainsettings test {PrivateSecureKey=[`grep -v '\-\-' /etc/letsencrypt/live/domain.my/privkey.pem | tr -d '\n'`];}"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru" dir="ltr">
<head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

        <title> domain.my</title>
        <link rel="stylesheet" href="/SkinFiles/domain.my/Pronto/style.css" type="text/css" />
  <meta http-equiv="x-dns-prefetch-control" content="off" />
  <meta name="referrer" content="no-referrer" />
</head>
<body background="/SkinFiles/domain.my/Pronto/bodybgcolor.gif">
<form  method="post" enctype="multipart/form-data">
<input type="hidden" name="FormCharset" value="utf-8" />
<table width="100%" border="0" cellspacing="0" cellpadding="0">


<tr><td height="25"> </td></tr>
<tr valign="middle"><td align="center" bgcolor="#ffcccc" class="externalError">private key data is corrupted</td></tr>

</table>
</form>
</body>
</html>

[root@mx ~]#

Э-э-э… Ну… Я не этого ожидал.

Я подумал было, что это certbot мне вместо закрытого ключа подсунул что-то странное, вытащил содержимое файла:

[root@mx ~]# cat /etc/letsencrypt/live/domain.my/privkey.pem
-----BEGIN PRIVATE KEY-----
многа
букаф
закрытого
ключа
-----END PRIVATE KEY-----
[root@mx ~]#

и вставил через Web-интерфейс:



Внезапно, всё прошло нормально:



Я удалил ключ и снова попытался импортировать его HTTP-запросом. Чуда не произошло, private key data оказалась всё ещё is corrupted.

Озадачившись, я снова пошёл в гости к кролику техподдержке. Техподдержка сказала, что нужно из файла закрытого ключа выкусить хедер с футером, а результат объединить в одну строку. Когда я спросил, всё ли я правильно сделал вот этой командой:

grep -v '\-\-' /etc/letsencrypt/live/domain.my/privkey.pem | tr -d '\n'

сотрудник поддержки ответил, что он не специалист в grep и не знает.

В процессе диалога я обнаружил, что, если вытащить старый ключ запросом getdomainsettings, то HTTP-запрос импортирует его нормально, решил, что мой grep | tr выкусывает что-то лишнее, и попрощался с чатиком Сталкера.

Однако всё оказалось не так просто. Почистив закрытый ключ вручную, я обнаружил, что так он всё равно не импортируется.

Здесь я забрёл в окончательный тупик.

Промучившись с этим феноменом, я было решил плюнуть и делать всё вручную, импортировал закрытый ключ через веб-интерфейс… И напоследок сделал запрос getdomainsettings. Внезапно оказалось, что Communigate возвращает мне совсем не то, что я в него вкормил. Более того, закрытый ключ Let's Encrypt после чистки был в длину 1624 символа, а то, что мне показал Communigate, в длину было всего 1592.

К такому повороту я не был готов и полез в openssl. Первый же выстрел попал в цель:

[root@mx ~]# openssl rsa -in /etc/letsencrypt/live/domain.my/privkey.pem
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
вот ровно
то, что надо,
только разбитое
на строчки
-----END RSA PRIVATE KEY-----
[root@mx ~]#

Ура, миссия выполнена.

С сертификатами пляски не понадобились, там просто выкусываются хедер с футером и остатки объединяются в одну строку.

Итого, конечный итог у меня выглядит вот так:

curl --user postmaster:password -k 'https://127.0.0.1:9100/cli/' --data-urlencode "command=updatedomainsettings domain.my {SecureCertificate=[`grep -v '\-\-' /etc/letsencrypt/live/domain.my/cert.pem | tr -d '\n'`];PrivateSecureKey=[`openssl rsa -in /etc/letsencrypt/live/domain.my/privkey.pem  2> /dev/null | grep -v '\-\-' | tr -d '\n'`];CAChain=[`grep -v '\-\-' /etc/letsencrypt/live/domain.my/chain.pem | tr -d '\n'`];}"

Поскольку unix-shell — не моя родная среда, оптимизации приму с благодарностью.

Ну, и мало ли, вдруг понадобится кому, я вот этого вот нагуглить не смог.

* гвоздей в Communigate Pro действительно найдено не было
Tags:
Hubs:
+1
Comments 14
Comments Comments 14

Articles