Обновление ключей UEFI Secure Boot в виртуальных машинах (zVirt 4.x)
Аннотация
В zVirt 4.x виртуальные машины с включенным UEFI Secure Boot проверяют загрузку ОС, используя набор сертификатов и ключей. Этот набор хранится в эмуляции NVRAM вместе с настройками прошивки и создается для каждой виртуальной машины из шаблона.
24 июня 2026 года истекает срок действия одного из сертификатов Microsoft, используемых в UEFI Secure Boot. Это не повлияет на работу существующих виртуальных машин, однако в дальнейшем могут возникнуть проблемы с запуском новых версий ОС, загрузчики которых подписаны только новыми сертификатами Microsoft.
Для предотвращения подобных ситуаций необходимо обновить пакет edk2-ovmf на хостах, который добавит в шаблон NVRAM новые сертификаты и ключи Microsoft,выпущенные в 2023 году и со сроком действия до 2038 года. После обновления новые виртуальные машины будут автоматически использовать актуальный набор ключей Secure Boot, а для существующих виртуальных машин потребуется дополнительное обновление их NVRAM.
В данной статье описан порядок установки обновления и обновления существующих виртуальных машин.
1. Установка обновления
Установите обновление на хостах через веб-интерфейс, либо с помощью команды:
dnf update edk2-ovmf
|
Команду необходимо выполнить на всех хостах. |
2. Обновление существующих виртуальных машин
- Описание процедуры
-
Для применения обновленного набора ключей Secure Boot к существующим виртуальным машинам необходимо пересоздать NVRAM. Процедура заключается во временном переключении типа прошивки виртуальной машины с Q35 c UEFI Secure Boot на Q35 c UEFI и последующем возврате к исходному значению.
Способ 1. Выполнение процедуры через REST API.
-
Выключите виртуальную машину.
-
Получите токен доступа.
curl --location 'https://{fqdn}/ovirt-engine/sso/oauth/token' \ --header 'Accept: application/json' \ --header 'Version: 4' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=password' \ --data-urlencode 'scope=ovirt-app-api' \ --data-urlencode 'username={user}' \ --data-urlencode 'password={password}'Где:
-
{fqdn} — FQDN Менеджера управления.
-
{user} — имя пользователя, например admin@zvirt@internalsso.
-
{password} — пароль пользователя.
Сохраните значение поля access_token из полученного ответа.
-
-
Получите описание виртуальной машины.
curl --location --request GET 'https://{fqdn}/ovirt-engine/api/vms/{vmId}' \ --header 'Accept: application/json' \ --header 'Version: 4' \ --header 'Authorization: Bearer {token}'Где:
-
{vmId} — идентификатор виртуальной машины.
-
{token} — токен, полученный на предыдущем шаге.
Сохраните полученный JSON.
-
-
Измените значение поля bios.type.
Исходное значение:
"bios": { "boot_menu": { "enabled": "false" }, "type": "q35_secure_boot" }Измените на:
"bios": { "boot_menu": { "enabled": "false" }, "type": "q35_sea_bios" } -
Примените изменения:
curl --location --request PUT 'https://{fqdn}/ovirt-engine/api/vms/{vmId}' \ --header 'Accept: application/json' \ --header 'Version: 4' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {token}' \ --data '{json}' -
Убедитесь, что значение поля bios.type изменено на
q35_sea_bios. -
Повторите шаги 3–5, изменив значение bios.type обратно на
q35_secure_boot. -
Убедитесь, что изменение успешно применено.
-
Запустите виртуальную машину.
| Если используется самоподписанный сертификат, добавьте к командам curl параметр -k или --insecure. |
Способ 2. Автоматическое выполнение процедуры с помощью скрипта.
Для автоматического выполнения процедуры можно использовать скрипт.
-
Сохраните скрипт в файл change_bios.sh.
#!/bin/bash set -u # Цвета RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $1"; } success() { echo -e "${GREEN}[OK]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } step() { echo -e "${MAGENTA}==>${NC} $1"; } status() { echo -e "${CYAN} $1${NC}"; } usage() { cat <<EOF Использование: $0 --vm-id <ID> --url <URL> --username <USER> [--password <PASS>] EOF exit 1 } VM_ID="" ENGINE_URL="" USERNAME="" PASSWORD="" while [[ $# -gt 0 ]]; do case "$1" in --vm-id) VM_ID="$2"; shift 2 ;; --url) ENGINE_URL="$2"; shift 2 ;; --username) USERNAME="$2"; shift 2 ;; --password) PASSWORD="$2"; shift 2 ;; *) usage ;; esac done if [[ -z "$VM_ID" || -z "$ENGINE_URL" || -z "$USERNAME" ]]; then usage fi if [[ -z "$PASSWORD" ]]; then read -s -p "Введите пароль для $USERNAME: " PASSWORD echo fi ENGINE_URL="${ENGINE_URL%/}" get_token() { curl -k -s -X POST \ -H "Accept: application/json" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&scope=ovirt-app-api&username=$USERNAME&password=$PASSWORD" \ "$ENGINE_URL/ovirt-engine/sso/oauth/token" | jq -r '.access_token' } get_vm_status() { local token=$1 curl -k -s -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID" 2>/dev/null | jq -r '.status // empty' 2>/dev/null || echo "" } wait_for_status() { local token=$1 local expected=$2 local max_attempts=90 local attempt=0 while [[ $attempt -lt $max_attempts ]]; do local status status=$(get_vm_status "$token") if [[ -z "$status" ]]; then status "Не удалось получить статус (пустой ответ). Повтор через 2 сек..." elif [[ "$status" == "$expected" ]]; then status "Статус ${GREEN}'$expected'${NC} достигнут." return 0 else status "Ожидание ${YELLOW}'$expected'${NC} (сейчас ${CYAN}'$status'${NC})..." fi sleep 2 ((attempt++)) done error "Таймаут ожидания статуса '$expected'" return 1 } stop_vm() { local token=$1 step "Останавливаем ВМ ${YELLOW}$VM_ID${NC}..." curl -k -s -X POST "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID/stop" \ -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ -H "Content-Type: application/json" \ --data '{}' > /dev/null wait_for_status "$token" "down" } set_bios() { local token=$1 local bios_type=$2 step "Меняем BIOS на ${YELLOW}$bios_type${NC}..." local vm_json vm_json=$(curl -k -s -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID") local updated_json updated_json=$(echo "$vm_json" | jq ".bios.type = \"$bios_type\"") curl -k -s -X PUT \ -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ -H "Content-Type: application/json" \ -d "$updated_json" \ "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID" > /dev/null local new_bios new_bios=$(curl -k -s -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID" | jq -r '.bios.type') if [[ "$new_bios" == "$bios_type" ]]; then success "BIOS успешно изменён на ${GREEN}$bios_type${NC}." else error "BIOS не изменился (остался ${YELLOW}$new_bios${NC})" return 1 fi } start_vm() { local token=$1 step "Запускаем ВМ ${YELLOW}$VM_ID${NC}..." curl -k -s -X POST "$ENGINE_URL/ovirt-engine/api/vms/$VM_ID/start" \ -H "Authorization: Bearer $token" \ -H "Accept: application/json" -H "Version: 4" \ -H "Content-Type: application/json" \ --data '{}' > /dev/null info "Запуск ВМ инициирован, для завершения работы ожидаем окончания запуска (статус 'up')." echo -e " ${YELLOW}Для немедленного выхода нажмите Ctrl+C.${NC}" local max_attempts=90 local attempt=0 while [[ $attempt -lt $max_attempts ]]; do local status status=$(get_vm_status "$token") if [[ "$status" == "up" ]]; then success "ВМ запущена (статус ${GREEN}up${NC})." return 0 elif [[ -n "$status" ]]; then status "Текущий статус: ${CYAN}$status${NC}" else status "Не удалось получить статус" fi sleep 2 ((attempt++)) done error "Таймаут ожидания запуска ВМ." return 1 } # === Основной блок === info "Получение токена..." TOKEN=$(get_token) if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then error "Ошибка получения токена" exit 1 fi success "Токен получен." CURRENT_STATUS=$(get_vm_status "$TOKEN") info "Текущий статус ВМ: ${CYAN}$CURRENT_STATUS${NC}" if [[ "$CURRENT_STATUS" != "down" ]]; then stop_vm "$TOKEN" else info "ВМ уже выключена." fi set_bios "$TOKEN" "q35_sea_bios" step "Ждём 2 секунды..." sleep 2 set_bios "$TOKEN" "q35_secure_boot" start_vm "$TOKEN" success "✅ Готово: Chipset переключён с UEFI SecureBoot на UEFI и обратно, ВМ запущена." -
Назначьте права на выполнение:
chmod +x change_bios.sh
./change_bios.sh \
--vm-id aae140f5-6348-4781-bccb-7748b4e4baf5 \
--url https://z1.test.local \
--username admin@zvirt@internalsso \
--password 12345678
| Параметр | Описание |
|---|---|
--vm-id |
Идентификатор виртуальной машины. |
--url |
URL Менеджера управления. |
--username |
Имя пользователя. |
--password |
Пароль. |
3. Возможные проблемы
В отдельных случаях виртуальная машина может не запускаться. В событиях отображается сообщение:
Failed to run VM …
Также может не выполняться изменение типа чипсета (после нажатия ОК в окне редактирования виртуальной машины изменения не применяются).
Причиной может быть повреждение NVRAM виртуальной машины.
Для восстановления работоспособности удалите запись NVRAM из базы данных:
DELETE FROM vm_nvram_data
WHERE vm_id = '<vm_id>';