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


В силу разных процессов сейчас уже практически все слышали о таком явлении, как импортозамещение. В частности, сейчас активно замещается импортный продукт 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 действительно найдено не было

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (9):

  1. immaculate
    /#19248923

    А при чем здесь импортозамещение? Вроде CGP разрабатывался нашим соотечественником, но давно уехавшим в США. Кто застал времена FIDO, тот помнит, многомесячные религиозные войны, которые он вел против Linux (общий смысла был, что Linux написан отвратительно плохо и хуже любой другой ОС по абсолютно любым параметрам).

    • gotch
      /#19249207

      Смотрите, я в эти импортозаместительные мантры тоже не верю, но лицензионное соглашение у них такое:

      Stalker Software, Inc, having its registered address at 125 Park Place Suite 210, Richmond, California 94801-3922 U.S.A;

      «StalkerSoft» AO, having its registered address at 123458 Russian Federation, Moscow, Marshala Proshliakova st. 30, office 307;

      Stalker Software GmbH, having its registered address at Schluterstrasse 37, 10629 Berlin, Germany;

      If the License is purchased from Stalker Software, Inc., this License Agreement is governed by the law of the State of California.

      If the License is purchased from AO StalkerSoft, this License Agreement is governed by the law of the Russian Federation.

      If the License is purchased from Stalker Software GmbH, this License Agreement is governed by the law of the Federal Republic of Germany.

      • Ilirium
        /#19249235

        Скорее всего это офис продаж/бухгалтерия, которые могут выставить счет в рублях и подготовить все нужные бумажки для бухгалтерии. На headhunter такой компании нет, как и просто Communigate.

    • Ilirium
      /#19249221

      На правах иронии: спонсор фразы про импортозамещение — фонд по борьбе с корупцией.

    • kloppspb
      /#19253579

      нашим соотечественником, но давно уехавшим в США.

      Кто застал времена FIDO

      С Бутенко я встречался лет дцать назад, в Москве. Тогда он уже был глубоким американцем, правда, перед этим была Германия вроде :) Собсно, ничего не имею против CGP, но называть это «исконно русским» продуктом — полнейшая глупость и незнание истории. Скорей, наоборот, вопреки :) А почему сейчас так — другой вопрос.

  2. scifinder
    /#19249005

    First reliase

    Странно, что не «Fyost».

  3. BigD
    /#19252429

    Не Российский это продукт

  4. kloppspb
    /#19253573

    исконно русским без единого гвоздя*

    Ну-ну :)