🏠: it

Dell Inspiron 14-3452 - укрощение строптивого

Принесли ноутбук 2015-го года выпуска (Celeron N3050/4GB RAM/500GB HDD), который нужно было привести в порядок. Судя по виду, он использовался мало, но, тем не менее, имел одну ярко выраженную проблему — при включении он оглушительно пищал и выдавал сообщение, что не может распознать, что за блок питания в него воткнули, поэтому он снижает производительность и не будет заряжать аккумулятор.

Оказалось, что фирма Делл делает блоки питания с микросхемкой, которая по центральному контакту в разъёме передаёт информацию о блоке ноутбуку. Блок питания был оригинальным, и я отнёс его на работу, где прекрасный коллега Геннадий, разобрав блок, прозвонил провод тестером и установил, что центральный провод оборван в районе разъёма. Видимо, за провод либо сильно дёрнули, либо изогнули.

Был заказан новый «кабель для блока питания Dell 4.5x3.0мм с центральным контактом» и впаян на место старого, а корпус блока питания аккуратно склеен изолентой. Ноутбук начал распознавать блок питания как родной, но громкий писк при запуске никуда не делся, к тому же оказалось, что аккумулятор сдох окончательно — он не заряжался, даже простояв воткнутым в розетку всю ночь.

Из-за неисправности аккумулятора нельзя прошить новый BIOS — для этого нужно как минимум 10% заряда. Я заказал новую батарею и SSD Samsung 870 EVO 500GB на замену медленной хрустящей штатной Тошибы. Но вернёмся к оглушительно громким сигналам POST.

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

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

Жёсткий диск приехал, и я поставил в качестве системы Windows 10 Home 22H2 x64, которую собрал с помощью сайта uupdump.net. Что приятно, система активировалась сама без каких-либо действий с моей стороны. Всё вроде бы нормально, но операционка даже после всех обновлений почему-то ни в какую не видела микрофона. В BIOS никаких настроек по этому поводу нет, Xubuntu Linux, загруженный с флешки, тоже ничего не увидел, что косвенно указывает на то, что проблема может быть и аппаратной, хотя выглядит это очень странно. Я решил отложить вопрос с микрофоном до прошивки нового BIOS.

Наконец, я получил аккумулятор. Я брал не самый дешёвый, а тот, где было написано «оригинал» или что-то подобное, на Озоне есть такой фильтр. Установил его в ноутбук — и при загрузке получаю предупреждение, что батарея не рекомендована для этой системы и заряжаться она не будет.

Изображение без описания

Тем не менее, аккумулятор нормально работает и заряжается. Единственная проблема — для загрузки системы теперь нужно нажимать на кнопку Continue на экране этого предупреждения про батарею. Но фокус стоит уже на ней, поэтому надо просто нажать клавишу «ввод».

Информация о новом аккумуляторе из HWInfo:

Battery #0

[Общие свойства]
    Имя устройства: DELL 991XP55
    Имя производителя:  Samsung SD
    Серийный номер: 708
    Уникальный идентификатор:   708Samsung SDDELL 991XP55
    Химия:  
    Расчетная мощность: 32560 mWh
    Полная заряженная емкость:  32560 mWh
    Уровень износа: 0.0 %

[Текущее состояние питания]
    Состояние питания:  Разрядка
    Текущая вместимость:    29230 mWh (89.8 %)
    Текущее напряжение: 15.895 V
    Скорость разряда:   -5535 mW 

Ну, батарейка есть, теперь можно и BIOS прошивать. Скачал последнюю версию 4.4.0, доступную для этого ноутбука, запускаю. Что?

Изображение без описания

Система несовместима сама с собой! Опять полез в поиск и нашёл обходной путь. Оказывается, есть недокументированный ключ запуска /writehdrfile, который извлекает из .exe файл .hdr, подходящий для прошивки альтернативным способом.

rem Перейти в рабочий каталог (здесь: C:\temp)
cd C:\temp
rem Извлечение BIOS из exe-файла
Inspiron_3452_3552_4.4.0.EXE /writehdrfile

Потом нужно залезть на сайт American Megatrends, которая пишет биосы, зайти в раздел Developers → Tools & Utilities → BIOS / UEFI и скачать там Aptio V AMI Firmware Update Utility. Так как система у нас 64-битная, надо распаковать из скачанного архива afu\afuwin\64\AfuWin64.zip\AfuWin64\AFUWINx64.exe в каталог, где лежит наш .hdr. В оригинальном описании обходного пути прошивка запускается после загрузки компьютера под DOS, но в этом нет никакой необходимости, всё прекрасно делается и из-под Windows.

Наконец-то добрались и до самой прошивки:

rem Сохранить старый BIOS в файл
AFUWINx64.exe oldBios.rom /O

rem Сравнение GUID старого и нового биосов
AFUWINx64.exe /S
AFUWINx64.exe Inspiron_3452_3552_4.4.0.hdr /U
rem Проверить процесс без реальной прошивки
AFUWINx64.exe Inspiron_3452_3552_4.4.0.hdr /D

rem Прошить новый BIOS
AFUWINx64.exe Inspiron_3452_3552_4.4.0.hdr

Успешный результат прошивки

После перезагрузки новый BIOS требует ввести номер модели, но вводить его некуда — это какой-то мелкий глюк, впоследствии не проявляющийся. Но главное — Windows, загрузившись с новой прошивкой, узрел микрофон и ещё несколько устройств, которые он спокойно установил самостоятельно без посторонней помощи.

Признаться, я не припомню в своей практике настолько капризной машинки. Тем не менее, остались только мелкие недостатки, а именно:

  • Предупреждение о неоригинальном аккумуляторе, требующее нажатия Enter для дальнейшей загрузки.
  • Не работают клавиши регулировки яркости (F11-F12). Советы в интернете включать-выключать устройства видеоадаптера и монитора ни к чему не приводят.

Вот он, этот тип гражданской наружности после всех манипуляций, лет 5-7 ещё может проработать:

Стоимость деталей:

  1. Аккумулятор — 2857 руб.
  2. Жёсткий диск — 3539 руб.
  3. Провод БП — 335 руб.
  4. Батарейка CR2032 — 159 руб.

Итого: 6890 руб.

P. S. Процессор N3050 по сегодняшним меркам очень медленный — Youtube, например, сильно тормозит. Дело в том, что стандартно вещание там идёт в формате VP9, для которого у этого процессора семейства Braswell (8 поколение) нет аппаратного декодера. Чтобы заставить браузер выбирать видео в формате H.264, для которого у N3050 декодер есть, я установил расширение h264ify. Не сказать, чтобы проблема вообще исчезла, но нагрузка на процессор упала на 15-20 процентных пунктов и работать стало получше.

Нагрузка при воспроизведении VP9
Нагрузка при воспроизведении AVC

Автоматизация установки SSL-сертификата на Cisco ASA

Пересадить всю контору на сертификаты Let’s Encrypt оказалось не очень сложно — certbot на реверс-прокси работает, сертификаты после перевыпуска лепятся вместе со своими ключами и кладутся в нужный каталог скриптом, лежащим в /etc/letsencrypt/renewal-hooks/deploy, а в определённое время в cron срабатывает команда, которая при наличии новых сертификатов перечитывает конфигурацию сервера.

На некоторых сервисах, которые выставлены в интернет напрямую, например, сервера видеоконференций, работает свой бот и он обновляет сертификаты локально, но иногда есть нюансы — например, на сервере TrueConf 80-й порт, который требуется для обновления сертификата, занят, и приходится подкручивать /lib/systemd/system/certbot.timer на еженедельный запуск где-то в глухой ночи, а в юнит-файле /lib/systemd/system/certbot.service рисовать следующее, чтобы стопорить службу, занимающую порт, и после процедуры стартовать её заново:

ExecStart=/usr/bin/certbot -q renew \
--pre-hook 'systemctl stop trueconf-web' \
--deploy-hook 'cp /etc/letsencrypt/live/tconf.example.com/cert.pem /opt/trueconf/server/etc/webmanager/ssl/custom.crt && \
cp /etc/letsencrypt/live/tconf.example.com/privkey.pem /opt/trueconf/server/etc/webmanager/ssl/custom.key && \
chown trueconf:trueconf /opt/trueconf/server/etc/webmanager/ssl/custom.\*' \
--post-hook 'systemctl start trueconf-web'

Окно ввода логина и пароля VPN на веб-странице Cisco ASA

Последним бастионом оставался довольно почтенного возраста аппаратный шлюз Cisco ASA, который со всеми этими новомодными удостоверяющими центрами работать не умеет. Так как раньше покупался wildcard-сертификат на год и вставлялся туда руками, проблем его менять не было, кроме оскорбления здравого смысла при виде очередного рассовывания этого несчастного сертификата по всем серверам и раздумий, куда его ещё забыли скопировать. Но так как Let’s Encrypt выпускает сертификаты на 90 дней, ручная установка выглядит совсем уж неуместной, и автоматизация совершенно необходима.

Let’s Encrypt даёт возможность выпускать и wildcard, но для него нужно автоматизировать ещё и создание TXT-записей в DNS через API, что усложняло задачу. Для нашего nic.ru существует программа, но эта схема мне показалось чересчур усложнённой, к тому же, у компании не так много доменов, в случае компрометации сертификата не будет затронуто сразу всё, тем более, что в один сертификат можно поместить сразу несколько альтернативных имён (SAN), что позволяет сократить общее количество выпускаемых сертификатов.

После небольшого гуглежа я обнаружил в некотором роде бриллиант — инструмент для автоматизации работы с интерактивным вводом expect. То есть, пишешь, что должно появиться в командной строке, а потом то, что нужно ввести в ответ. Так как это не bash, а другой интерпретатор, основанный на языке TCL (Tool Command Language), у него свой шебанг — #!/usr/bin/expect -f.

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

На сервере, куда будет приходить 80-й порт и где будет происходить всё последующее, понадобятся 3 файла: скрипт сборки сертификата в нужный формат (pkcs12, закодированный в base64), запускаемый сертботом после его перевыпуска, который, в свою очередь, запускает скрипт expect. Третий файл — это пароль экспорта/импорта сертификата: можно пароль и так захардкодить в первые два файла, но это неудобно. Предполагается, что все действия делаются от учётки root.

# скрипт, выполняющийся при обновлении сертификата
touch /etc/letsencrypt/renewal-hooks/deploy/vpncert-asa.sh
chmod 700 /etc/letsencrypt/renewal-hooks/deploy/vpncert-asa.sh
# скрипт expect
touch /scripts/vpncert-install.exp
chmod 700 /scripts/vpncert-install.exp
# пароль экспорта сертификата для openssl
touch /scripts/vpncert-asa.txt
chmod 600 /scripts/vpncert-asa.txt
# Записать пароль в файл пароля
nano /scripts/vpncert-asa.txt

Содержимое vpncert-asa.sh:

#!/bin/bash

openssl pkcs12 -export \
-password file:/scripts/vpncert-asa.txt \
-in $RENEWED_LINEAGE/fullchain.pem \
-inkey $RENEWED_LINEAGE/privkey.pem \
-out /root/gate.pfx && \

openssl base64 -in /root/gate.pfx -out /root/gate.base64 && \

/scripts/vpncert-install.exp

vpncert-install.exp:

#!/usr/bin/expect -f

set timeout 5
set send_slow {10 .001}
set sshUser "sshuser"
set sshIP "192.168.1.254"
set sshPass "sshPass12345"
set exportPass [exec cat /scripts/vpncert-asa.txt]

spawn ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa -o StrictHostKeyChecking=no $sshUser@$sshIP
expect "password:"
send -- "$sshPass\r"
expect ">"
send -- "enable\r"
expect "Password:"
send -- "$sshPass\r"
expect "#"
send -- "configure terminal\r"
expect "(config)#"
send -- "no crypto ca trustpoint ca_le\r"
expect {
  "]:" {send -- "yes\r"; exp_continue}
  "(config)#"
}
send -- "crypto ca trustpoint ca_le\r"
expect "trustpoint)#"
send -- "enrollment terminal\r"

expect "#"
send -- "exit\r"
expect "(config)#"

send -- "crypto ca import ca_le pkcs12 $exportPass\r"
expect "itself:"
send -- [exec cat /root/gate.base64]\n
send -s "quit\r"

# % The CA cert is not self-signed.
# % Do you also want to create trustpoints for CAs higher in the hierarchy? [yes/no]:
# OR
# % You already have RSA or ECDSA keys named ca_le.
# % If you replace them, all device certs issued using these keys
# % will be removed.
# % Do you really want to replace them? [yes/no]:
expect {
  "]:" {send -- "yes\r"; exp_continue}
  "(config)#"
}

send -- "ssl trust-point ca_le outside\r"

expect "(config)#"
send -- "exit\r"
expect "#"
send -- "exit\r"
expect eof

После запуска всей конструкции скрипт изготавливает сертификат требуемого формата, лезет по SSH на железку, вводит все логины-пароли, вставляет полученный сертификат и делает всё прочее как если бы это делалось руками, но только быстро, точно и без забывчивости.

Вывод во время выполнения работы:

spawn ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa -o StrictHostKeyChecking=no sshuser@192.168.1.254
sshuser@192.168.1.254's password:
User sshuser logged in to gate
Logins over the last 91 days: 54. Last login: 11:39:24 MSK Jan 9 2023 from 192.168.1.100
Failed logins since the last login: 0.
Type help or '?' for a list of available commands.
gate> enable
Password: **********
gate# configure terminal
gate(config)# no crypto ca trustpoint ca_le
WARNING: Removing an enrolled trustpoint will destroy all
certificates received from the related Certificate Authority.

Are you sure you want to do this? [yes/no]: yes
INFO: Be sure to ask the CA administrator to revoke your certificates.
gate(config)# crypto ca trustpoint ca_le
gate(config-ca-trustpoint)# enrollment terminal
gate(config-ca-trustpoint)# exit
gate(config)# crypto ca import ca_le pkcs12 verySecretPassword12345

Enter the base 64 encoded pkcs12.
End with the word "quit" on a line by itself:
MIIWzwIBAzCCFoUGCSqGSIb3DQEHAaCCFnYEghZyMIIWbjCCEOIGCSqGSIb3DQEH
BqCCENMwghDPAgEAMIIQyAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG
...
dt/zTsIeIqQq05PLFpOTIzBBMDEwDQYJYIZIAWUDBAIBBQAEIMyVqqTQhaaqlHOH
D3XnctPJR1TYytiVRCaVWuZHz+G0BAiEKmqw9Y6C4AICCAA=
quit
% You already have RSA or ECDSA keys named ca_le.
% If you replace them, all device certs issued using these keys
% will be removed.
% Do you really want to replace them? [yes/no]: yes

Trustpoint 'ca_le' is a subordinate CA and holds a non self-signed certificate.

Trustpoint CA certificate accepted.
WARNING: CA certificates can be used to validate VPN connections,
by default.  Please adjust the validation-usage of this
trustpoint to limit the validation scope, if necessary.
INFO: Import PKCS12 operation completed successfully.
gate(config)# ssl trust-point ca_le outside
gate(config)# exit
gate# exit

Logoff

Connection to 192.168.1.254 closed by remote host.
Connection to 192.168.1.254 closed.

Результат — компьютеры занимаются рутиной, а человек освободил время для чего-то более интересного.

Обновил OpenMediaVault на сетевом хранилище

Сетевое хранилище, история которого началась в марте 2018 года, продолжает свою работу. После пересадки в новый корпус в начале 2020-го никаких событий, связанных с ним, не происходило, за исключением замены вышедшей из строя флешки, на которой стояла система, на 120 ГБ SSD Gigabyte GP-GSTF в августе того же года.

На днях я обнаружил, что вышел OpenMediaVault версии 6 — надо обновиться, чтобы не отставать от прогресса и заодно разнообразить своё существование.

Обновление прошло безо всяких проблем, интерфейс стал более крупным, что, видимо, связано с увеличением средней диагонали и разрешения мониторов. Вообще, симпатично.

omv6-login-page.png
omv6-dashboard.png

Вечер дня сисадмина

После ужина обновил систему на своём сервере-неттопе, а потом решил ещё разик поковырять давно висевшее предупреждение в Nextcloud:

The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If not, this is a security issue and can allow an attacker to spoof their IP address as visible to the Nextcloud. Further information can be found in the documentation.

Сначала я попробовал добавить в параметр trusted_proxies конфигурации Nextcloud не имя контейнера реверс-прокси, а приватный диапазон IP, используемый Докером — 172.16.0.0/12; так много где советуют сделать, но это ничего не дало. В конце концов, проблема решилась заданием того же имени контейнера, но не единым параметром, а первым элементом массива, а также установкой ожидаемого заголовка пересылки.

docker exec -uwww-data nc-php php /var/www/html/cloud/occ config:system:set forwarded_for_headers 0 --value="X-Forwarded-For"
docker exec -uwww-data nc-php php /var/www/html/cloud/occ config:system:set trusted_proxies 0 --value="reverse-proxy"

Наконец-то:

Ободрённый успехом, я решил обновить контейнер с PHP 7.4 на PHP 8.0. Он у меня самосборный — это Alpine c последующей установкой необходимых пакетов и настройкой с помощью командной строки. Я поменял пакеты в Dockerfile на соответствующую версию и поправил пути, пересобрал контейнер, и вроде как заработало, но перестали выполняться команды, вызывающие php, подобные приведённым выше, и остановился cron, выполняющий фоновые задания. Ошибка была такая:

OCI runtime exec failed: exec failed: unable to start container process: exec: ″php″: executable file not found in $PATH: unknown

Полез внутрь контейнера. Команда php8 выполняется, а php — нет. Оказалось, что в дистрибутиве Alpine для php8 не готовы пакеты, поэтому символическая ссылка автоматически при установке не прописывается. Речь об этом шла год назад, не знаю, та же причина сейчас или нет, но результат всё равно один. Добавил ещё одну строчку в Dockerfile, после чего всё заработало:

ln -sf /usr/bin/php8 /usr/bin/php

Cron снова в порядке

Заодно напишу про упомянутый в прошлом тексте выпуск сертификатов Let’s Encrypt для HAProxy. Всё получилось и отлично работает. У Certbot есть каталог /etc/letsencrypt/renewal-hooks/deploy, и если туда положить скрипт, то он будет выполняться после каждого успешного перевыпуска сертификата. В данном случае нужно слепить полную цепочку сертификатов и закрытый ключ в одно целое, положив результат в каталог сертификатов HAProxy:

#!/bin/bash
cat $RENEWED_LINEAGE/{fullchain.pem,privkey.pem} > /etc/ssl/certs/haproxy/$(basename $RENEWED_LINEAGE).pem

# --deploy-hook DEPLOY_HOOK
#    Command to be run in a shell once for each
#    successfully issued certificate. For this command, the
#    shell variable $RENEWED_LINEAGE will point to the
#    config live subdirectory (for example,
#    "/etc/letsencrypt/live/example.com") containing the
#    new certificates and keys; the shell variable
#    $RENEWED_DOMAINS will contain a space-delimited list
#    of renewed certificate domains (for example,
#    "example.com www.example.com") (default: None)

Чтобы сертификат подхватился, HAProxy должен перечитать конфигурацию. Можно, конечно, добавить строку и в скрипт, приведённый выше, но если сертификатов выпускается сразу штук десять, то столько раз дёргать реверс-прокси за несколько секунд вряд ли хорошая идея. Поэтому я сделал отдельный скрипт, который ищет сертификаты с датой изменения меньше суток, и если они есть, перечитывает конфигурацию:

#!/bin/bash
if [[ $(find /etc/ssl/certs/haproxy/* -mtime -1) ]]
then
systemctl reload haproxy.service
fi

В cron нужно добавить запуск этого скрипта раз в сутки, например, в 3 часа ночи:

echo -e "\n# Reload HAProxy if there are new certs\n0 3\t* * *\troot\t/scripts/haproxy-reload-if-new-certs.sh" >> /etc/crontab

Ну, с праздничком всех причастных и сочувствующих, спокойной ночи.

Проброс USB по сети

Берём «железный» linux-сервер, например, Ubuntu 22.04 LTS, на котором есть порты USB, вставляем туда ключ HASP, широко использующийся для аппаратной защиты программ типа 1С или цифровой подписи. Проверяем, определился ли он в системе:

Определился, прекрасно. Теперь ставим usbip.

# Установка (usbip входит в стандартный набор утилит)
apt install linux-tools-common linux-tools-generic
# Вкл модуль ядра для сервера:
modprobe usbip-host
echo "usbip-host" >> /etc/modules
# Вкл службу
usbipd -D
# Проверить версию
usbip version # usbip (usbip-utils 2.0)

Выводим список локальных устройств usbip и предоставляем нужное устройство в общий доступ.

Теперь нужно настраивать клиента, в данном случае на Windows 11, работающей в виртуальной среде Hyper-V, где, как известно, USB с хоста пробросить в виртуалку нельзя (и это правильно). Предварительно на клиентской машине необходимо установить драйвер usbip, а для этого импортировать сертификат, идущий в комплекте, и отключать цифровую подпись драйверов. Это можно сделать из меню восстановления при загрузке, так как команда bcdedit /set TESTSIGNING ON в современных системах Windows уже не действует.

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

Работает! Наш ключ определился как три устройства Sentinel (после установки драйверов HASP). Ниже виден ранее установленный usbip.

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

Всё функционирует корректно. Флешка и внешний привод DVD-RW тоже успешно подключаются, а вот веб-камеру мне пробросить на Windows не удалось.

Конечно, применимость этой технологии в промышленной среде под вопросом, прежде всего из-за возни с неподписанными драйверами и отключения механизма проверки подписи для Windows-клиента (Upd: автор подписал драйвер, теперь всё гораздо проще). Как будут себя вести клиенты, если сервер перезагрузится? А если убрать устройство на сервере без отключения на клиенте - как восстанавливать работу, не повиснет ли система? Вопросами автоматической привязки и подключения устройств на сервере и клиентах я также пока не занимался, хотя и знаю, что это возможно. Ещё одной особенностью, которую необходимо учитывать, является невозможность ограничения доступа к USB-устройству, опубликованному на сервере; по всей видимости, нужно будет это делать с помощью межсетевого экрана.

Тем не менее, технология интересная и потенциально полезная, нужно только как-то грамотно подобрать железо для сервера ключей - а им может быть что угодно, хоть Raspberry Pi - и получше оттестировать её.