Нюансы обновления до VCD 10.4.1

После обновления до VCD 10.4.1 одна из Advisores отсылает нас к KB78885 (но да кто их читает, да?), которая опубликована в далеком уже 2021 году и которая напоминает нам о том, что после обновления VCD у нас может пропасть доверненное подключение к различным элементам нашей инфраструктуры. Ужесточение требований к сертификатам в инфраструктуре началось в VCD еще с версии 10.1 (о чём так же отдельно пишется во врезке, на странице документации посвященной обновлению VCD).

В 10.4.1 ужесточили еще один аспект инфраструктуры, а именно подключение к ESXi хостам. В результате, после обновления до 10.4.1 (и автоматическом переходе на новую консоль, старую выпилили таки, и слава богу), в VCD перестанут работать операции требующие прямого доступа к хосту, а именно доступ к консоли ВМ, ипорту OVF/ISO, Guest Customization.

В логах это может отобразиться в виде следующих сообщений:

[ 88fc5c17-f0c7-4021-922b-5dfe1b84b7cd ] PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 - unable to find valid certification path to requested target
 2023-02-24 02:59:01,050 | ERROR    | pool-jetty-81             | ServerWebSocket                | Connecting to ESX esx01.lab.ihumster.ru [server: [L=/192.168.168.25:443 R=/192.168.168.30:60004]] [client: [id: 0x13240ef6, L:/192.168.168.25:52056 ! R:esx01.lab.ihumster.ru/192.168.168.101:443]] |
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Решение

Самый простой вариант — это запустить повторно CMT trust-infra-certs:

./cell-management-tool trust-infra-certs --vsphere --unattended
Downloading certificates for 2 host(s), including 1 vCenter(s):
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS] (VMCA)
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS] (VMCA)
        vc-mng.lab.ihumster.ru                                      [Download: SUCCESS]
        nsx.lab.ihumster.ru                                         [Download: SUCCESS]
Downloaded certificates for 3/2 host(s).
Trusting certificates for 2 host(s):
        vc-mng.lab.ihumster.ru                                       [Already Trusted: SUCCESS]
        nsx.lab.ihumster.ru                                          [Already Trusted: SUCCESS]
Trusting VMCA certificates for 1 vCenter(s):
        vc-mng.lab.ihumster.ru                                       [Add Trusted: SUCCESS] (VMCA) - alias=vc-mng.lab.ihumster.ru_vmca_2023-02-24-11-05-13
Trusted 3/3 downloaded certificates.

Или самостоятельно скачать с vCenters их сертификаты VMCA (<vcenter>/certs/download.zip) и вручную добавить VMCA сертификаты в Trusted Certificates вашего VCD.

Для VCD с большим количеством vCenters (или организаций с высокими требованиями к безопасности инфраструктурных компонентов) самым правильным путём, по моему мнению, будет вариант с заменой сертификата VMCA на vCenters на подписанный с внутреннего или внешнего CA и последующей регенерацией сертификатов ESXi.

VMware Cloud Director Availability Initial Setup API

Встала задача по автоматизации развертывания VCDA для инсталляций VCD где VCDA еще не развернут. Начав изучение вопроса и прочтя внимательно документацию по VCDA с удивлением понял что для развертывания самих аплайнсов есть способ по автоматизации процесса, т.к. аплайнсы поставляются в формате OVF с обычными, для аплайнсов VMware, возможностями деплоя их как с помощью UI, так и с помощью OVFTool ну и прочими средствами (например, community.vmware.vmware_deploy_ovf из коллекции ansible). Но для дальнейшей первоначальной настройки нет иного официального пути, кроме как пойти в UI и потыкать кнопки, заполняя поля в Initial Setup Wizard.

Вооружившись DevTools я за несколько итераций записал все необходимые API вызовы, которые происходят при выполнении этапов Initial Setup Wizard и после, проверив возможность их воспроизведения сторонними средствами (воспроизводил в Postman) решил что хочу записать их в виде документации OpenAPI 3.0 что бы любой мог импортировать ее в Postman и ознакомиться с деталями запросов.

По этому спешу представить Вам первую версию документации к VCDA Initial Setup API. В репозиторий добавлена также готовая коллекция для Postman (на случай если вы не умеете генерировать коллекцию из OpenAPI спецификации).

В коллекции все запросы пронумерованы в порядке выполнения их в Wizard. Отдельно не пронумерован один Get запрос — он используется для проверки доступности той или иной системы (VCD, vCenter server, ReplicatorAppliance, Tunnel Appliance) из VCDA Manager, а также возвращает SSL-сертификат используемый системой (сам сертефикат в PEM-encoded формате, его thumbprint — используются в остальных запросах).

Конфигурация VCDA которую я изучал — это рекомендуемая в продуктивной среде конфигурация из отдельных «ролевых» аплайнсов — manager, replicator, tunnel. Для combined и vc_combined версии, последовательность и структура запросов скорее всего иная (в vc_combined например не используется VCD).

Также, если вы используете несколько replicator аплайнсов то соответствующие запросы необходимо будет проделать для каждого аплайнса последовательно.

Для работы с этим API вам необходимы минимум 3 задеплоенных удобным вам способом аплайнса.

Комфортная работа в Postman с VMware Cloud Director (2)

В процессе работы с OpenAPI разных версий VMware Cloud Director понял что меня раздражает постоянно следить за версией API, которая указывается в Header ‘Accept’. Чтобы автоматизировать актуализацию версии API в моих запросах я модифицировал свой pre-request Script из предыдущей статьи.

Добавьте следующую строку между запросами версии и получением access_token, либо после них:

pm.request.headers.get('Accept').replace('application\/json;version=(?<version>\d{2}\.\d{1})','application/json;version={{apiVersion}}')

Regexp для парсинга URL по RFC

Не претендуя на истину в последней инстанции, однако, хотелось закрыть для себя этот вопрос на всегда. Частенько в скриптах posh\python и т.п. приходится на входе принимать в качестве параметра строку с URL и быть уверенным что это:

  1. точно URL
  2. иметь возможность легко получить его составляющие (схему\fqdn\query\etc)

Подсматривая в интернет, получилось написать вот такую «красивую» регулярку, которая входящую строку бъет на следующие группы:

  1. url (собственно если строка соответствует паттерну url — в этой группе будет отображаться полный url
  2. scheme — схема URL http:// | ftp:// | anycustomscheme://
  3. hostname — FQDN
  4. port
  5. path
  6. query

Как работает этот regexp вы можете увидеть ниже

(?<url>(?:(?<scheme>[a-zA-Z]+:\/\/)?(?<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?<port>[[:digit:]]+))?(?<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))

На Python именованные группы нужно обозначать через символ P, поэтому для Python regex будет выглядеть чуть иначе:

(?P<url>(?:(?P<scheme>[a-zA-Z]+:\/\/)?(?P<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?P<port>[[:digit:]]+))?(?P<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?P<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))

Ну и пример кода на Powershell:

$URL = "https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile"
$URL -match "(?<url>(?:(?<scheme>[a-zA-Z]+:\/\/)?(?<hostname>(?:[-a-zA-Z0-9@%_\+~#=]{1,256}\.){1,256}(?:[-a-zA-Z0-9@%_\+~#=]{1,256})))(?::(?<port>[[:digit:]]+))?(?<path>(?:\/[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~%]*)*)(?<query>(?:(?:\#|\?)[-a-zA-Z0-9!$&'()*+,\\\/:;=@\[\]._~]*)*))"
$Matches

True

Name                           Value
----                           -----
path                           /api/query
hostname                       vcloud.lab.ihumster.ru
url                            https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile
scheme                         https://
query                          ?type=adminOrgVdcStorageProfile
0                              https://vcloud.lab.ihumster.ru/api/query?type=adminOrgVdcStorageProfile

Операнд -math возвращает boolean результат соответствия строки в $URL нашему регулярному выражению и если он True, то автопеременная $Matches будет содержать key-value словарь в котором ключами будут имена групп с соответствующими values.

Спасибо что дочитали до конца, надеюсь это будет вам полезно.

Комфортная работа в Postman с VMware Cloud Director

В последний раз что-то полезное на эту тему писал Tomas Fojta в, далеком уже, 2018 году. С тех пор в VCD появились новые интересные методы API и в частности в 10.3.1 появились Cloud Director API Token.

По этому я решил слегка модернизировать подход к работе с VCD API в Postman с использованием токенов.

Если вам интересно — добро пожаловать под cut.

Читать далее

Ansible Cheat Sheet или как не стрелять себе в ногу при написании ролей и плейбуков

Так как я в настоящее время плотно погружаюсь в «чудесный» мир Ansible, то много читаю (и официальную документацию, да) статей по теме.

Недавно наткнулся на хороший цикл из трех статей от @amarao на Habr:

Они безусловно стоят того чтобы их прочесть внимательно (а также прочесть комментарии — там тоже изложены ценные мысли). Я выписал себе отдельно из них несколько тезисов которые планирую держать перед глазами при написании своих плейбуков и ролей.

Плейбуки и роли

  • Не используй tasks и roles одновременно в play. Если play с ролями — для тасков лучше использовать pre_tasks или post_tasks.
  • Роль — не функция! В нее нельзя передать параметры (не путать с переменными) и получить на выходе результат.
  • Если у переменных роли должны быть дефолтные значениея — определяй их в defaults роли
  • Избегай when — ansible task должен быть декларативным (и следовательно не опираться на предыдущие register)
  • Если можно что-то красиво (без выкрутас) написать без хэндлеров лучше делать без них. Если красиво не получается — лучше с ними.
  • Чем меньше if‘ов (явных или декларативных — в форме when или форме include_vars по набору переменных), тем лучше роль.
  • Хотите строгой логики (программирования) — выносите ее в плагины, модули, библиотеки

Инвентори

  • При работе с SSH, всегда (кроме спецслучаев) использовать ansible_host внутри которого IP-адрес. (Самый спорный тезис, имхо)
  • Перменные в инвентори. Основной вопрос, который надо себе задавать: «почему эта переменная должна быть в инвентори»? Другими словами, инвентори — это специальное место для перменных, и вам нужны специальные причины записывать их туда.

Переменные

  • Переменные разименовываются в момент интерполяции их в Jinja
  • Не интерполируются ключи словарей на любом уровне вложенности yaml. Нельзя использовать шаблоны в именах модулей, ключах словарей в переменных, именах переменных (которые по сути те же ключи).
  • Никогда не создавай перекрытие по именам между групповыми переменными (инвентори и плейбук), никогда не создавай перекрытия между hostvars , между инвентори и плейбукой.

Ну и, на последок, из того же источкника

Разумная таблица приоритетов переменных

  • role/defaults — всегда проигрывает. Идеальное место для размещения переменных, которые надо переопределять. Дефолты ролей видны в соответствующих pre/post tasks.
  • group_vars/all — переменные группы all наименее приоритетные
  • group_vars/other_groups
  • host_vars
  • результат выполнения gather_facts: true (host_facts)
  • Переменные уровня play
  • Переменные уровня block
  • Переменные уровня task
  • set_fact/register. set_fact ещё и живёт дольше, чем плей (т.е. может передавать данные между разными play).
  • -e побеждает всех и всегда.

Ну и хорошее напутствие — «Пишите просто, пишите красиво. Сложно и некрасиво не пишите» 🙂

[Расследование] VCD Error: No row with the given identifier exists

Вернувшись на рабочее место после выходных, я получил от одного из крупных клиентов заявку о том, что у него перестали включаться некоторые ВМ, таска падает с ошибкой вида:

[ 2c728624-3b93-463e-8f99-cf5386f089bf ] Internal Server Error - No row with the given identifier exists: [com.vmware.vcloud.common.model.net.LogicalNetworkModel#40bbadee-76dc-4760-bac8-0293a79a595f]

Никаких внятных сообщений в логах VCD (vcloud-container-debug.log) я не обнаружил. Там конечно присутвовали простыни Java логов об ошибке но понять причину было нельзя, отсылка была лишь к NetworkServiceImpl, а значит искать надо было что-то связанное с сетями.

Читать далее

[VCD V2T] Проверка перед миграцией

Решил что стоит поделиться своими проверками Org vDC перед миграцией V2T. Эти проверки позволят избежать части падений процесса миграции, о которых этап —preCheck не уведомляет.

[ x ] Проверить использование Non Latin символов в дескрипшенах Org, Org vDC
[ x ] Проверить сети на Shared — отключить если сеть не шарится между vDC (позволит не создавать Data Сenter Group там где это не нужно)
[ x ] Проверить правила SNAT в EGS — если в source содержится адрес, а не сеть, валидация пройдет успешно, а этап переноса правила упадет
[ x ] Проверить наличие пустого IP Set в NSX-v EGS (раздел Grouping Objects) — его наличие сломает этап переноса правил Gateway
[ x ] Проверить настройки DHCP — с ними много странного, быть готовым что с DHCP что-то пойдет не так. Если можно его отключить — отключите
[ x ] Проверить Storage Policy у каждой ВМ — если есть не VM default Storage Policy на дисках — перенос vApp в котором есть такая ВМ не пройдет (vcdNSXMigrator 1.3.1 не умеет детектить кастомные SP и как следствие не добавляет их в api запрос на moveVApp)

[VCD V2T] Ошибка миграции «Application Port Profile CUSTOM-TCP-XXX does not exist»

Если вы из тех, кто как и я приступили к миграции с NSX-v на NSX-T в вашем VMware Cloud Director окружении, то скорее всего вы столкнулись с ошибкой «Application Port Profile CUSTOM-TCP-XXX does not exist». Возникает она с повторяющимися кастомными Application Port Profile. Грубо говоря если миграция №1 создала кастомный tcp/21 порт, то если во время миграции №2 потребуется создать аналогичный кастомный tcp/21 порт, vcdNSXMigrator не создаст такой профиль во время миграции №2 и в процессе использования этого правила (например во время настройки правил NAT или FW) упадет с этой ошибкой.

Пример ошибки

Workaround для правил NAT

Сразу оговорюсь, данный обходной путь этой ошибки был найден мною самостоятельно и не валидирован командой разработки vcdNSXMigrator Tool.

  1. Создайте данный Application Port Profile вручную (из текста ошибки понятно какого типа профиль надо создать и с каким номером порта — в моем случае это tcp/21)
  2. Определите из текста ошибки на каком этапе она произошла (в моем случае это configureTargetNAT — создание правила NAT)
  3. Найдите правло(а) NAT на NSX-v EGS и зафиксируйте их ID и прочие параметры
  4. Создайте правило(а) NAT на NSX-T Edge используя в качестве имен правил ID этих правил в NSX-v EGS
  5. Откройте ваш source vDC в провайдерской части VCD и перейдите в раздел Metadata
  6. Найдите metadata NATstatus-v2t и отредактируйте текст этого поля добавив после последнего правила, которое было перенесено vcdNSXMigrator, правило(а) которые вы создали сами (пример на скриншоте)

7. Запустите процесс миграции заново (процесс продолжится с таска на котором процесс упал).

Импорт CA-signed сертификатов в VMware Cloud Director 10.3

В официальной документации есть статья описывающая процесс импорта сертификатов (а в 10.3 его сильно упростили, избавившись от JCEKS контейнеров), однако в ней пропущен один нюанс. Чтобы cell-management-tool принял ваш сертификат и его ключ (на пункте 4) ваш ключ должен быть зашифрован с помощью пароля, который вы подставите, в качестве аргумента —key-password.

В моей практике при создании закрытого ключа и CSR для заказа у CA сертификата мы на выходе получаем не зашифрованый паролем ключ:

-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----

или

-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----

Такой ключ не подходит для использования при импорте сертификата с помощью cell-management-tool. Для этого мы должны его зашифровать паролем и в дальнейшем использовать именно в таком виде.

В разделе документации Create and Import CA-Signed SSL Certificates to the VMware Cloud Director Appliance VMware рекомендует использовать в качестве пароля ключа пароль пользователя root от вашего VCD appliance.

Ну и собственно что бы зашифровать ваш файл ключа паролем необходимо выполнить следующую команду:

openssl pkcs8 -inform PEM -in your.key -out youencrypted.key -topk8

После ввода данной команды openssl запросит у вас пароль с подтверждением и на выходе вы получите файл youencrypted.key зашифрованный паролем:

-----BEGIN ENCRYPTED PRIVATE KEY-----
-----END ENCRYPTED PRIVATE KEY-----

Дальнейшие действия по импорту осуществляются строго по инструкции.