Эта статья довольно долго не могла увидеть свет, по ряду причин, от проблем со здоровьем после COVID (я отупел), до отсутствия времени. Время шло, импортозамещение набирало обороты, пилоты становились продакш-средами, и все катилось под откос шло к светлому будущему.
Какое-то время назад мы с командой тоже приступили к пилотированию миграции на «отечественное» ПО. И вот, что из этого вышло.
Началось все с разговоров. А куда без них?
С чего начинается миграция? С выбора ПО? Нет, с планирования и анализа.
— Чего мы хотим достичь?
— Заместить импортные программные продукты отечественными аналогами.
— Полностью?
— Да.
— А если аналогов нет?
— Тогда в максимально возможном объеме.
И тут у нас встала дилемма. Потому что сотрудники Компании в работе помимо прочего используют CAD и PLM софт, который просто не умеет работать на Linux. Вернее, умеет, но через костыли wine’а, что вызывает недовольство пользователей в плане быстродействия. Так же мы используем VDI Horizon, который умеет работать только с AD. А аналогов у Horizon нет. И не говорите про всякие там надстройки для ProxMox и вот это вот все. Ни одна из других существующих VDI-систем не умеет работать с Instant Clones. Максимум, что предлагает OpenSource (и импортозамещение, которое построено на OpenSource) — это Linked Clones. А у нас на IC построены как Windows пулы, так и Linux. И администрировать VDI-рабочие столы как полноценные машины — это смерти подобно, и стоимость владения вырастает за счет необходимости держать огромный штат техподдержки. Поэтому мы не сможем полностью уйти от Windows, как бы нам этого ни хотелось. И из этого вытекает следующее:
-
Мы не сможем уйти от домена на базе Windows
-
Для обеспечения работоспособности инфраструктуры нужны также сервисы DNS, DHCP, WSUS, CA and etc, которые нет смысла (или невозможно) переносить на Linux.
А раз мы не сможем полностью импортозаместиться, то и проект не имеет особого смысла. На этом можно было бы и закончить, но мы решили, что раз уж инфраструктуру импортозаместить не выйдет — займемся хотя бы пользовательской средой. А со стороны инфры сделаем все, что сможем. Zabbix перевезем на отечественную ОСь.. еще чего-нибудь.. Потому что проблема, например, почтового сервера заключается в том, что Exchange у нас есть, и он работает.. А чтобы его заменить на условный Communigate Pro нам нужно потратить кучу денег. А кто же любит тратить деньги, если и так все работает?.. И так с большинством развернутых в проде систем.
На том и порешали.
0. Ansible
Как поднять Ansible я писать не буду, информации об этом в интернетах более, чем достаточно, не вижу смысла загромождать статью лишней информацией. Главное, что там нужно сделать — это сформировать ssh-ключ, с которым Ansible будет ходить к клиентам, и продумать доставку этого ключа на клиентов.
И еще один нюанс – динамическое inventory. Клиентские ПК – дело непостоянное: кто-то уволился, кто-то пришел, какие-то ПК выключены, какие-то больше не существуют. И как это поддерживать в актуальном состоянии – целая задача. Особенно, когда речь идет не о десятках, и даже не о сотнях, а о тысячах машин. Мы пошли по более-менее простому пути – использование плагина nmap в связке с плагином constructed для Ansible. При запуске он опрашивает указанные подсети и формирует список хостов для применения плейбуков или ролей, а потом делает свои грязные делишки на отобранные по правилам хосты.
0.1. Муки выбора дистрибутива
Из представленных на рынке «отечественных» ОС по факту выбор-то и не велик, благо, 99% наших рабочих мест не нужно закрывать сертифицированными ОСями. DEB или RPM, Gentoo-подобные поделки для централизованного администрирования даже не рассматривались, спасибо. Астра\Альт\РедОС\ROSA. Проблема в том, что все они платные. Но. У Альта есть форк — Simply Linux, который бесплатен для коммерческого использования. Думаю, дальше не стоит объяснять, почему мы выбрали Simply Linux.
Ниже есть глава про преобразование ISO-образа.
1. Локальный репозиторий
В нашем случае все даже проще, чем обычно. БазАльт сделал веб-интерфейс, который в пару кликов позволяет настроить репликацию их репозитория (выбранных веток).
Выглядит вот так:
Чтобы попасть на эту страничку, нужно доустановить пакеты ahttpd, alterator-fbi и alterator-mirror, и запустить-активировать ahttpd:
systemctl start ahttpd systemctl enable ahttpd
В целом в этой админке довольно много функций помимо зеркалирования репозитория, доступ к которым открывается с доустановкой нужных пакетов альтератора.
Дальше мы столкнулись с тем, что нельзя выбрать, куда будут зеркалироваться пакеты. В Вике Альта пишут про то, что надо лезть в fstab
, и там монтировать желаемое расположение к /srv/public/mirror
*. Но нам это показалось странным. В итоге нашли файлик /usr/lib/alterator/backend3/mirror
, в котором указаны пути для alterato-mirror. Поменяли dest_dir=/repos/alt_main-repo
и все поехало.
*(Статью в Вике Альта правил я по результатам наших изысканий)
Дальше мы подумали о том, что нам нужен будет кастомный репозиторий, в который мы будем сваливать пакеты того же Каспера и 1С.. да и прочие, которых нет в репозах Альта (Simply). Список доп.софта у нас получился примерно такой:
-
1С толстый и тонкий клиент
-
Google Chrome
-
Касперский агент и клиент
-
OnlyOffice (ну не Libre же офисным работникам отдавать..)
-
VNC-Viewer
-
VMWare Horizon Client (про него отдельная история, как Windows-админ (я) пытался RPM собрать)
Чтобы создать кастомный репозиторий, нужно прочитать это и это, создать нужную структуру каталогов, наполнить нужное место RPM-пакетами, создать gpg-ключ, создать и подписать индексы, подключить репозиторий клиентам, и можно радоваться.
В целом – ничего сложного, если не накосячить с деревом каталогов, как я по началу.
После заполнения каталога с RPM-пакетами, в моем случае это /repos/alt_custom-repo/x86_64/RPMS.alt_custom_repo/
, нужно создать и подписать индексы:
genbasedir --create --progress --sign --default-key=[gpg-key без скобок] --topdir=/repos/alt_custom-repo x86_64 alt_custom_repo
*genbasedir
является командой из пакета apt-utils
.
Основные сложности у меня вызвали 2 пакета – OnlyOffice и VMWare Horizon Client. Первый не может удовлетворить зависимости, второй просто не существует.
1.3. Пакеты
Тут я вынужден сделать лирическое отступление — я и моя команда — чистый SRE, парни, которые про эти ваши CI\CD и DevOps практики знают только из интернетов и разговоров с коллегами. Это я к тому, что пересобрать RPM-пакет для сисадмина — это тёмный лес. Но мы не отступаем перед сложностями. Самое сложное было разобраться в скриплетах и найти инфу о том, в какой последовательности они применяются при обновлении пакета. И тут мне помогла вот эта дока.
1.3.1. OnlyOffice
Тут все просто – пересобрать RPM без отсутствующих зависимостей. Но стоит сразу уточнить, что Альт (Simply) по дефолту не даст руту собрать RPM-пакет, для этого предлагается использовать непривилегированного пользователя. Делаем:
cd /tmp wget https://download.onlyoffice.com/install/desktop/editors/linux/onlyoffice-desktopeditors.x86_64.rpm?_ga=2.238710784.1633530263.1666265796-724860256.1666265796 sudo apt-get install wget wget https://download.onlyoffice.com/install/desktop/editors/linux/onlyoffice-desktopeditors.x86_64.rpm?_ga=2.238710784.1633530263.1666265796-724860256.1666265796 rpmrebuild -enp onlyoffice-desktopeditors.x86_64.rpm\?_ga\=2.238710784.1633530263.1666265796-724860256.1666265796 sudo apt-get install rpmrebuild rpmrebuild -enp onlyoffice-desktopeditors.x86_64.rpm\?_ga\=2.238710784.1633530263.1666265796-724860256.1666265796
=Ъ
И в открывшейся спеке комментируем пакеты, которых нет в репозиториях Р10. Приводим к виду:
Requires: /bin/sh Requires: /bin/sh Requires: /bin/sh Requires: /bin/sh Requires: /bin/sh Requires: atk Requires: boost-filesystem Requires: curl #Requires: dejavu-sans-fonts #Requires: dejavu-sans-mono-fonts #Requires: dejavu-serif-fonts Requires: gtk3 Requires: libX11 Requires: libXScrnSaver Requires: liberation-mono-fonts Requires: liberation-narrow-fonts Requires: liberation-sans-fonts Requires: liberation-serif-fonts Requires: libstdc++ >= 4.8.0 Requires: libxcb #Requires: rpmlib(CompressedFileNames) <= 3.0.4-1 #Requires: rpmlib(FileDigests) <= 4.6.0-1 #Requires: rpmlib(PayloadFilesHavePrefix) <= 4.0-1 #Requires: rpmlib(PayloadIsXz) <= 5.2-1 #Requires: xcb-util-image #Requires: xcb-util-keysyms #Requires: xcb-util-renderutil #Requires: xcb-util-wm #Requires: xdg-utils
И сохраняем. На выходе получается RPM-пакет без неудовлетворенных зависимостей. Да, так делать неправильно. Но, на Вике Альта так и написано – игнорируй зависимости, и «спина болеть не будет».
Более правильным решением, конечно же, было бы собрать пакет из исходников. Но мы же SRE.. И поэтому это мы записали в планы. На потом.
1.3.2. VMWare Horizon Client
Собрать из исходников проприетарное ПО не получится, даже если вы — не я. Horizon можно скачать в виде tarball-архива с бинарниками, и писать спеку под них. Но я пошел по более простому пути. Я скачал bundle, написал spec-файл для его установки, и запаковал это все в RPM-пакет.
А потом я решил автоматизировать это дело. И написал скриптик, который, получая на вход ссылку на скачивание bundle’а Horizon Client’а, создает структуру каталогов для сборки, меняет в спеке «хххххх» на «версию», и запускает сборку.
Spec-файл Horizon Agent for Linux выглядит так:
Name: vmware-horizon-client Version: xxxxxx Release: 1 Summary: custom rpm package vmware-horizon-client License: GPL Source0: %{name}-%{version}.tar.gz Requires: bash Requires: python3-modules-sqlite3 Requires: python3-modules-curses Requires: python3-module-pexpect Requires: python-modules-json Requires: python-module-pygtk Requires: python-module-jinja2 Requires: python-module-yaml Requires: python-module-distutils-extra %description VMware Horizon Client for LAB.RU team custom repo %prep %setup -q %install install -d -m 0755 $RPM_BUILD_ROOT/opt/vdi/ install -m 0755 %{name}-%{version} $RPM_BUILD_ROOT/opt/vdi/%{name}-%{version} %pretrans if !([ -z "$(pgrep vmware-view)" ]); then pkill -f vmware-view fi %preun if !([ -z "$(pgrep vmware-view)" ]); then pkill -f vmware-view fi file=(`ls /opt/vdi/`) if !([ -z $file ]); then env VMWARE_KEEP_CONFIG=no /opt/vdi/$file -u vmware-horizon-client --console rm -f /opt/vdi/$file fi if test -f "/etc/skel/Рабочий стол/vmware-view.desktop"; then rm -f "/etc/skel/Рабочий стол/vmware-view.desktop" fi %posttrans env TERM=dumb VMWARE_EULAS_AGREED=yes /opt/vdi/%{name}-%{version} --console --set-setting vmware-horizon-integrated-printing vmipEnable yes --set-setting vmware-horizon-html5mmr html5mmrEnable yes --set-setting vmware-horizon-usb usbEnable yes --set-setting vmware-horizon-smartcard smartcardEnable yes --set-setting vmware-horizon-rtav rtavEnable yes --set-setting vmware-horizon-tsdr tsdrEnable yes --set-setting vmware-horizon-scannerclient scannerEnable yes --set-setting vmware-horizon-serialportclient serialportEnable yes --set-setting vmware-horizon-mmr mmrEnable yes --set-setting vmware-horizon-media-provider mediaproviderEnable yes --set-setting vmware-horizon-teams-optimization teamsOptimizationEnable no echo "view.defaultBroker = 'uag.lab.ru'" > /etc/vmware/view-mandatory-config echo "view.defaultDomain = 'lab'" >> /etc/vmware/view-mandatory-config echo "view.defaultProtocol = 'BLAST'" >> /etc/vmware/view-mandatory-config echo "view.autoHideToolbar = 'TRUE'" >> /etc/vmware/view-mandatory-config echo "view.autoConnectBroker = 'uag.lab.ru'" >> /etc/vmware/view-mandatory-config echo "view.enableMMR = 'TRUE'" >> /etc/vmware/view-mandatory-config chmod +x /usr/share/applications/vmware-view.desktop if !([[ -f "/etc/skel/Рабочий\ стол/vmware-view.desktop" ]]); then ln -sf /usr/share/applications/vmware-view.desktop /etc/skel/Рабочий\ стол/vmware-view.desktop chmod 755 /etc/skel/Рабочий\ стол/vmware-view.desktop fi if [[ -d /home/LAB.RU/ ]]; then for dir in $(ls /home/LAB.RU/ | grep -v _); do if !([[ -f "/home/LAB.RU/$dir/Рабочий\ стол/vmware-view.desktop" ]]); then ln -sf /usr/share/applications/vmware-view.desktop /home/LAB.RU/$dir/Рабочий\ стол/vmware-view.desktop chmod 755 /home/LAB.RU/$dir/Рабочий\ стол/vmware-view.desktop fi done fi %files /opt/vdi/%{name}-%{version} %changelog * August 2022 ANSysoev -
Скрипт для выглядит так:
#!/bin/bash if [ -z "$@" ]; then echo "run script with link to download Horizon Client as argument at run" else cd /home/user/rpmbuild/ rm -rf /home/user/rpmbuild/* mkdir BUILD BUILDROOT SOURCES SPECS SRPMS cd /home/user/rpmbuild/SOURCES wget "$@" version=$(ls | sed -e "s/VMware-Horizon-Client-//" -e "s/.bundle//" -e "s/-/./g") name="vmware-horizon-client" fullname=$(echo $name-$version) mkdir $fullname mv $(find /home/user/rpmbuild/SOURCES/ -maxdepth 1 -type f) /home/user/rpmbuild/SOURCES/$fullname/$fullname tar -czvf $fullname.tar.gz $fullname cp /home/user/vmware-horizon-client.spec /home/user/rpmbuild/SPECS/vmware-horizon-client.spec sed -i "s/xxxxxx/$version/" /home/user/rpmbuild/SPECS/vmware-horizon-client.spec cd /home/user/rpmbuild/ rpmbuild -bb SPECS/vmware-horizon-client.spec fi
Для запуска надо сделать вот такую магию:
horizon-client-RPM-1.sh https://download3.vmware.com/software/CART23FQ3_LIN64_2209/VMware-Horizon-Client-2209-8.7.0-20616018.x64.bundle
И На выходе получится собранный пакетик:
tree rpmbuild ├── BUILD │ └── vmware-horizon-client-2209.8.7.0.20616018.x64 │ └── vmware-horizon-client-2209.8.7.0.20616018.x64 ├── BUILDROOT ├── RPMS │ └── x86_64 │ └── vmware-horizon-client-2209.8.7.0.20616018.x64-1.x86_64.rpm ├── SOURCES │ ├── vmware-horizon-client-2209.8.7.0.20616018.x64 │ │ └── vmware-horizon-client-2209.8.7.0.20616018.x64 │ └── vmware-horizon-client-2209.8.7.0.20616018.x64.tar.gz ├── SPECS │ └── vmware-horizon-client.spec └── SRPMS
1.3.3. Google Chrome and etc.
Тут вообще проблем нет, просто скачивается RPM-пакет с официального сайта, копируется в каталог, и все. Все пакеты с удовлетворенными зависимостями ставятся так. И это хорошо.
2. Преобразование ISO-образа
Мы выбрали Simply, потому что он бесплатный.
Но. Отдавать офисным работникам Linux с xfce на борту — это парализовать работу офиса на неопределенный срок. Если более-менее привычные к ПК ребята освоятся достаточно быстро, но тоже не сразу, то тётушки-бухгалтеры — скорее никогда, чем быстро. Поэтому что? Правильно, надо поставить KDE! Это тоже не решит все проблемы, но порог вхождения существенно снизится.
Но. Если выпилить xfce и поставить сверху KDE — получится не кошерно. Останутся лишние пакеты от DM и DE, поэтому надо лезть в ISO и понять, как же происходит установка Linux, чтобы вмешаться в этот процесс и вместо одной DE поставить другую.
На самом деле ничего сложного в этом нет (если разобраться), но все по порядку.
Прочитав Вику Альта Модификация установочного ISO образа, Создание образов устройств, Autoinstall, и ничего в ней не поняв, я решил отвязаться от дистрибутива, и полез в теорию. Хотя последняя ссылка мне помогла во многом.
Что есть дистрибутив Linux? Дистрибутив – это, сильно упрощая, ядро Linux + набор пакетов. Всё. Набор пакетов ПО – суть «встроенный в ISO репозиторий». У Simply пакеты лежат в \ISOroot\ALTLinux\RPMS.main\
. Чтобы собрать дистрибутив, нужно набрать выбранные пакеты, удовлетворить их зависимости, и положить в ISO, описать «группы ПО», пересчитать и подписать индексы. . В директории Metadata\pkg-groups.tar\lists\
лежит файл .base
, содержащий в себе список пакетов, которые накатываются в любом случае, а так же директория slinux
, содержащая в себе описание этих самых групп ПО (имя и состав пакетов каждой группы).
Для того, чтобы избавиться от xfce и добавить вместо него KDE есть 2 пути: сложный и очень сложный.
Очень сложный предполагает скачивание из репозитория нужных пакетов (KDE и его зависимости). Но мне было лень. Наверное, есть какой-то специальный софт, который умеет считывать зависимости из метадаты указанного пакета, и выкачивать эти зависимости.. Но я такого не нашел, поэтому пошел по сложному пути, а именно — скачал Альт Workstation 10 K (у которого та же пакетная база, что и у Simply, только с KDE искаропке), и подменил пакеты в ISO-репозитории и файл .base
, собрал, установил… … … … ..ииииии.. получил свежеустановленный Альт 10 K. Удивился. Много думал. В основном о том, что моих теоретических познаний явно недостаточно для реализации поставленной мной же задачи. (Как же сложно и неприятно быть тупым! Проклятый COVID!)
После пролитых слез, покрасневших глаз, вырванных волос и обгрызенных ногтей поиска причин, я понял, что в списке пакетов есть RPM’ки со словом branding в имени, и понял, что именно их установка в целевую систему отвечает за то, какой дистрибутив получится на выходе. Например, один из пакетов правит файлик/etc/system-release
, другой накидывает нескучные обои и т.д.
Словом, после нехитрых, но скучных и утомительных правок файлика .base
и закидывания в репозиторий недостающих пакетов (у меня на это ушло примерно 10 попыток установки системы), я получил на выходе Simply Linux 10 K. Потом я решил, что этого недостаточно, и отсобрал еще один ISO уже без DE.. Simply Linux Server. А то на чем мне Ansible и локальный репозиторий разворачивать?
Список команд чуть ниже.
Далее — доставка на свежеустановленную систему ключей Ansible и установка hostname. Если в ОСи будут установлены ключи от Ansible, то дальше можно все сделать через сам Ansible. Тут нам поможет пакет alterator-postinstall и простейший скрипт, который доставит в /root/.ssh/authorized_keys
нужные ключи. У меня 2 Ansible, 1 в головном офисе, и 1 в филиале, канал с которым, мягко говоря, оставляет желать лучшего. Поэтому 2 ключа. Так же нужно позаботиться об инженерах техподдержки, и закинуть в свежую систему скрипт, который на основе существующих в AD записей для компьютеров, будет подбирать подходящий hostname для нового ПК перед вводом в домен. Эти скрипты тоже нужно будет доставить в целевую систему. Скрипт назовем 87-set-ansbls-keys.sh, и напишем в нем следующее (ну почти так, ключи я вам не покажу =Ъ):
#!/bin/sh . install2-init-functions echo "ssh-rsa бла-бла-бла-набор-символов root@ansible-filial-hostname" > $destdir/root/.ssh/authorized_keys echo "ssh-rsa бла-бла-бла-набор-символов root@ansible-hostname" >> $destdir/root/.ssh/authorized_keys cp /var/ChangeHostName.py $destdir/var/
Тут одна тонкость — таргет указывается с преффиксом $destdir
, иначе установщик, выполняя директиву postinstall, запишет ключи в свой /root, а не в устанавливаемую ОС.
Скрипт поиска подходящего hostname прост, как 5 копеек (которых никто не видел уже черт знает сколько лет). Я создал в AD бесправную учетку, чтобы Python мог сходить в AD и считать уже существующие в определенной OU учетки компьютеров, и выбрать следующий по списку.
#!/usr/bin/python3 # -*- coding: utf-8 -*- import os import sys from getpass import getpass from ldap3 import Server, Connection, SUBTREE, LEVEL import time username="lab.ru\linux_to_domain" password="Passw0rd!" server = Server("dc-1.lab.ru", port=389, use_ssl=False, get_info='ALL') connection = Connection(server, user=username, password=password, fast_decoder=True, auto_bind=True, auto_referrals=True, check_names=False, read_only=True, lazy=False, raise_exceptions=False) hostnamedigit=1 hostname = "ARM-"+'{:0>4}'.format(hostnamedigit) def get_all_ad_hosts(connection): results = list() elements = connection.extend.standard.paged_search( search_base='OU=LINUX,OU=Computers,dc=lab,dc=ru', search_filter='(&(objectCategory=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))', search_scope=SUBTREE, attributes=['name'], paged_size=100) for element in elements: host = dict() if 'dn' in element: host['dn'] = element['dn'] host['name'] = element['attributes'][u'name'][0] results.append(host) return(results) connection.unbind() def search_for_duplicatename(hostname,list_of_computers): for computer in list_of_computers: if computer['name'].casefold() == hostname.casefold(): print(hostname+" already exists") return 1 return 0 computers = get_all_ad_hosts(connection) while search_for_duplicatename(hostname, computers) != 0: hostnamedigit += 1 hostname = "ARM-"+'{:0>4}'.format(hostnamedigit) print(hostname) os.system("hostnamectl set-hostname "+hostname) print("Your system is gonna reboot in 10 seconds....") time.sleep(10) os.system("reboot now")
Теперь о том, куда же эти скрипты поместить. Целевая директория — архив altinst, находящийся в корне ISO. В архиве скрипт нужно расположить в директории /usr/share/install2/postinstall.d/
и не забыть сделать его исполняемым, иначе чуда не произойдет. Скрипт подбора hostname я положил в /var, хотя это не играет особой роли.
Но. Прежде, чем все это сделать, нужно смонтировать скачанный ISO, скопировать куда-то его содержимое, распаковать архив с помощью squashfs-tools (пакет нужно поставить), скопировать скрипты, поменять файлик .base
.. Словом, сделать все свои грязные делишки, и потом запаковать архив и собрать ISO. Итого (перед выполнением genbasedir
нужно скопировать необходимые пакеты, удалить лишние, и привести в порядок файлы, отвечающие за установку групп ПО, как было описано выше):
apt-get install apt-utils squashfs-tools mkdir /home/user/slinux_iso /home/user/altinst cd /home/user/altinst sudo mount /home/user/slinux.iso /mnt cp -r /mnt/* /home/user/slinux_iso/ unsquashfs /home/user/slinux_iso/altinst cp /path_to_script_ansible_keys.sh /home/user/altinst/squashfs-root/usr/share/install2/postinstall.d/87-set-ansbls-keys.sh chmod +x /home/user/altinst/squashfs-root/usr/share/install2/postinstall.d/87-set-ansbls-keys.sh cp /path_to_script_hostname /home/user/altinst/squashfs-root/var/ChangeHostName.py chmod +x /home/user/altinst/squashfs-root/var/ChangeHostName.py mksquashfs /home/user/altinst/squashfs-root/ /home/user/slinux_iso/altinst genbasedir --create --topdir=/home/user/slinux_iso/ ALTLinux main sudo xorriso \ -as mkisofs \ -joliet \ -partition_cyl_align "off" \ -partition_offset 16 \ -iso-level 3 \ -full-iso9660-filenames \ -sysid "LINUX" \ -volid "Simply Linux 10.1 x86_64" \ -volset "ALT" \ -publisher "BASEALT LTD" \ -appid "SIMPLY LINUX 10.1 X86_64 2022-06-28" \ -copyright "LICENSE_ALL_HTML" \ -eltorito-boot syslinux/isolinux.cfg \ -eltorito-catalog syslinux/boot.cat \ -no-emul-boot \ -boot-load-size 4 \ -boot-info-table \ -isohybrid-mbr syslinux/isohdpfx.bin \ -eltorito-alt-boot \ -e EFI/.efiboot.img \ -no-emul-boot \ -isohybrid-gpt-basdat \ -output ../SimplyLinux10_KDE.iso
И ждем.
В целом, можно было заскриптовать и еще какие-то штуки, которые делаются при первом запуске системы, как то – установка доменных сертификатов, прописывание локального репозитория и т.д. Но мы предпочли делать это через Ansible. Так это проще контролировать, и проще что-то изменить, если это потребуется.
3. Ansible playbooks
Дальше мы приступили к написанию плейбуков для доставки на клиенты нужных настроек:
-
ssh
-
sssd
-
samba
-
kerberos
-
pam
-
cups
-
ввод в домен
-
… etc.
Я довольно долго думал о том, нужно ли приводить листинг плейбуков. И, посоветовавшись с командой, мы решили, что не все стоит выкладывать. По ряду причин.
Спойлер
Да, мы знаем, что в нижеследующем можно сделать все иначе. Использовать роли, переменные и т.д. Хранить все в гите, отслеживать там изменения и прочее, прочее, прочее. Мы знаем, спасибо. Но читать это в рамках статьи будет невозможно. Поэтому плейбуки. Поэтому разбито так, как разбито.
Итак, требования следующие:
-
Доставить внутренние сертификаты, импортировать их
-
Добавить локальные репозитории
-
Установить в систему весь требуемый софт, закинуть конфиги для установленного софта, настроить skel, чтобы при первом входе в систему, пользователь получил нужные линки и каталоги, предусмотреть подключение для пользователей сетевых шар, живущих на Windows-сервере, предусмотреть подключение пользователям списка баз 1С, как это описано здесь (ну это просто удобно)
-
Сформировать конфиги для подключения ПК к домену, предусмотреть запуск логон-скриптов, обойти проблему авторизации на proxy-сервере (в нашем случае это скрипт, который стучится в proxy-сервер с Kerberos-тикетом, посредством curl’а, и получает на проксе тикет на 8 часов). Да, WSA – она такая..
-
Настроить ssh согласно требованиям от ИБ, в том числе ограничить доступ для определенных групп AD
-
Настроить доступ к sudo для определенной группы AD
-
Соблюсти прочие требования ИБ (мне запретили это показывать. ИБ – те еще параноики)
-
Ввести ПК в домен
-
Предусмотреть механизм «брендирования» — обои рабочего стола, загрузчика и прочее (это мне тоже запретили вам показывать)
-
Предусмотреть возможность массового обновления
В плейбуках все понятно по названиям тасков. Поэтому в проде мы вообще не делали комментариев. Для статьи я добавил немного комментов там, где посчитал необходимым.
«Кто в теме и так поймет, кто не в теме – тот все равно листинг читать не будет».
Оговорюсь лишь о том, что для доставки файлов, открытых ключей и прочего, мы опубликовали их рядом с репозиториями. Там все равно web-сервер поднят, почему бы его не использовать?
3.1. Доставить внутренние сертификаты, импортировать их
- name: Install local CA certs gather_facts: false hosts: simply tasks: - name: Execute script shell: | mkdir /tmp/certs cd /tmp/certs wget --no-check-certificate https://local-repo-srv.lab.ru/alt_custom-repo/certs/root.crt wget --no-check-certificate https://local-repo-srv.lab.ru/alt_custom-repo/certs/subca.crt cp ./rootca.crt /etc/pki/ca-trust/source/anchors/ cp ./subca.crt /etc/pki/ca-trust/source/anchors/ chmod a-x /etc/pki/ca-trust/source/anchors/* update-ca-trust extract
3.2. Добавить локальные репозитории
- name: add repositories gather_facts: false hosts: simply tasks: - name: delete all /etc/apt/sources.list.d/ shell: rm -f /etc/apt/sources.list.d/* - name: create lab.list copy: dest: /etc/apt/sources.list.d/lab.list content: | rpm [p10] http:// local-repo-srv.lab.ru /alt_main-repo p10/branch/x86_64 classic rpm [p10] http:// local-repo-srv.lab.ru /alt_main-repo p10/branch/noarch classic rpm [alt_custom_repo] http:// local-repo-srv.lab.ru /alt_custom-repo x86_64 alt_custom_repo - name: add custom gpg key shell: curl http:// local-repo-srv.lab.ru /alt_custom-repo/x86_64/base/custom_repo.pgp >> /etc/apt/custom_repo.pgp && gpg --no-default-keyring --keyring /usr/lib/alt-gpgkeys/pubring.gpg --import /etc/apt/custom_repo.pgp - name: add /etc/apt/vendors.list.d/lab.list copy: dest: /etc/apt/vendors.list.d/lab.list content: | simple-key "alt_custom_repo" { Fingerprint "бла-бла-бла-буквы-и-цЫфры"; Name "Vasily <Vasya@lab.ru>"; } - name: apt-get update shell: | apt-get update apt-get dist-upgrade -y
3.3. Установить в систему весь требуемый софт, …
- name: soft installation gather_facts: false hosts: simply tasks: - name: update shell: apt-get update -y - name: install packages apt_rpm: name: - sudo - apt-scripts - openssh - task-auth-ad-sssd - sssd-ad - samba-client - 1c-preinstall-full - vmware-view-preinstall - onlyoffice-desktopeditors - nano - firefox - libinput - libinput-devel - xorg-drv-libinput - xorg-drv-libinput-devel - x11vnc - x11vnc-service - 1c-enterprise-8.3.18.1483-thin-client - vlc - google-chrome-stable - autofs - vmware-horizon-client - system-config-printer - kde5-spectacle - evolution - evolution-ews - conky - remmina - remmina-plugins - cups state: present - name: remove Libre, stop cups shell: | apt-get remove libreoffice5 -y && apt-get clean -y && apt-get autoremove -y systemctl stop cups #для VNC есть еще таска для установки пароля, но я вам ее не покажу. #Там тривиально - name: x11vnc config copy: dest: /usr/sbin/x11vnc-start-daemon content: | #!/bin/bash AUTH=`ps aux | grep "\-auth " | head -n 1` AUTH=${AUTH/*\-auth /} AUTH=${AUTH/ */} /usr/bin/x11vnc -auth $AUTH -dontdisconnect -usepw -shared -forever -rfbport 5900 -rfbauth /etc/vncpasswd -display :0 -repeat - name: catalogs and files file: path: "{{ item.path }}" state: "{{ item.state }}" with_items: - { path: /etc/skel/Рабочий стол/, state: directory } #каталог для создания ярлыков - { path: /mnt/share/, state: directory } #каталог для монтирования «сетевых дисков» - { path: /var/ChangeHostName.py, state: absent } #удаление скрипта подбора hostname - { path: /opt/1cv8/x86_64/8.3.18.1483/libstdc++.so.6, state: absent } #для работы 1С этот файл надо удалить. Не спрашивайте, это не баг, это фича. - { path: /etc/skel/.1C/1cestart/, state: directory } #каталог для монтирования шары со списком баз для 1С - name: create links file: src: "{{ item.src }}" dest: "{{ item.dest }}" state: "{{ item.state }}" mode: "{{ item.mode }}" force: yes with_items: - { src: /mnt/share/, dest: /etc/skel/Рабочий стол/Сетевые_Папки, state: link, mode: '755' } - { src: /usr/share/applications/firefox.desktop, dest: /etc/skel/Рабочий стол/firefox.desktop, state: link, mode: '755' } - { src: /usr/share/applications/google-chrome.desktop, dest: /etc/skel/Рабочий стол/google-chrome.desktop, state: link, mode: '755' } - { src: /usr/share/applications/1cestart-8.3.18-1483.desktop, dest: /etc/skel/Рабочий стол/1C.desktop, state: link, mode: '755' } - { src: /usr/share/kf5/applications/kf5/org.kde.dolphin.desktop, dest: /etc/skel/Рабочий стол/Dolphin.desktop, state: link, mode: '755' } - { src: /usr/share/applications/onlyoffice-desktopeditors.desktop, dest: /etc/skel/Рабочий стол/onlyoffice-desktopeditors.desktop, state: link, mode: '755' } - { src: /usr/share/applications/vmware-view.desktop, dest: /etc/skel/Рабочий стол/vmware-view.desktop, state: link, mode: '755' } - { src: /mnt/.services/1CBases/1cestart_alt.cfg, dest: /etc/skel/.1C/1cestart/1cestart.cfg, state: link, mode: '755' } - name: copy files copy: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: "{{ item.owner }}" group: "{{ item.group }}" mode: "{{ item.mode }}" with_items: #блок копирования настроек cups. Они для всех одинаковы, подключается очередь #печати на принтер MyQ - { src: /etc/ansible/playbooks/files/cups/cupsd.conf, dest: /etc/cups/cupsd.conf, owner: root, group: lp, mode: '640' } - { src: /etc/ansible/playbooks/files/cups/cups-files.conf, dest: /etc/cups/cups-files.conf, owner: root, group: root, mode: '644' } - { src: /etc/ansible/playbooks/files/cups/printers.conf, dest: /etc/cups/printers.conf, owner: root, group: lp, mode: '600' } - name: enable services service: name: "{{ item }}" enabled: yes state: restarted with_items: - x11vnc - cups - name: firefox set krb enable copy: dest: /usr/lib64/firefox/browser/defaults/preferences/myprefs.js content: | pref("network.negotiate-auth.trusted-uris",".lab.ru"); pref("network.automatic-ntlm-auth.trusted-uris",".lab.ru"); pref("network.automatic-ntlm-auth.allow-non-fqdn","true"); pref("network.negotiate-auth.allow-non-fqdn","true"); pref("network.negotiate-auth.delegation-uris",".lab.ru"); - name: chrome set krb enable copy: dest: /etc/opt/chrome/policies/managed/krb.json content: | { "AuthServerAllowlist": "*.lab.ru", "AuthNegotiateDelegateAllowlist": "*.lab.ru" } - name: apt dedup, enable cups shell: | apt-get dedup -y systemctl start cups
3.4. Сформировать конфиги для подключения ПК к домену, ..
- name: pre-domain config gather_facts: false hosts: simply tasks: - name: krb config copy: dest: /etc/krb5.conf content: | [logging] # default = FILE:/var/log/krb5libs.log [libdefaults] default_realm = LAB.RU dns_lookup_realm = true dns_lookup_kdc = true ticket_lifetime = 24h renew_lifetime = 7d rdns = false forwardable = yes default_ccache_name = FILE:/tmp/krb5cc_%{uid} - name: samba config copy: dest: /etc/samba/smb.conf content: | [global] security = ads realm = LAB.RU workgroup = LAB netbios name = {{inventory_hostname}} template shell = /bin/bash kerberos method = system keytab wins support = no idmap config * : range = 10000-20000000 idmap config * : backend = tdb - name: sssd config copy: dest: /etc/sssd/sssd.conf content: | [sssd] config_file_version = 2 user = root domains = LAB.RU services = pam,nss,autofs [nss] [pam] [domain/LAB.RU] id_provider = ad auth_provider = ad chpass_provider = ad default_shell = /bin/bash fallback_homedir = /home/%d/%u ad_server = dc-1.lab.ru,dc-2.lab.ru ad_backup_server = _srv_ cache_credentials = true debug_level = 2 #монтирование сетевых дисков. Через pam mount ничего не вышло. Он либо багованый, #либо фича у него такая, но мы перешли на смб, который монтирует шары при #обращении к ним пользователя - name: autofs config copy: dest: /etc/auto.master content: | /mnt/share /etc/auto.samba --ghost /mnt/.services /etc/auto2.samba --ghost --timeout 60 - name: autofs config 1 copy: dest: /etc/auto.samba content: | disk_1 -fstype=cifs,multiuser,cruid=$UID,sec=krb5,domain=LAB.RU ://dfs-server.lab.ru/Share disk_2 -fstype=cifs,multiuser,cruid=$UID,sec=krb5,domain=LAB.RU ://file-server.lab.ru/Share2 disk_3 -fstype=cifs,multiuser,cruid=$UID,sec=krb5,domain=LAB.RU ://file-server.lab.ru/Share3 - name: autofs config 2 copy: dest: /etc/auto2.samba content: | 1CBases -fstype=cifs,multiuser,cruid=$UID,sec=krb5,domain=LAB.RU ://file-server.lab.ru /1CBases background -fstype=cifs,multiuser,cruid=$UID,sec=krb5,domain=LAB.RU ://file-server.lab.ru/background - name: enable autofs service: name: autofs enabled: yes state: restarted - name: configure nsswitch and cronyd lineinfile: path: "{{ item.path }}" regexp: "{{ item.regexp }}" line: "{{ item.line }}" loop: - { path: /etc/nsswitch.conf, regexp: '^passwd', line: 'passwd: files sss' } - { path: /etc/nsswitch.conf, regexp: '^shadow', line: 'shadow: tcb files sss' } - { path: /etc/nsswitch.conf, regexp: '^group', line: 'group: files sss' } - { path: /etc/chrony.conf, regexp: '^pool', line: 'pool dc-1.lab.ru iburst' } - name: set control policy and system-auth shell: | control sudo public control system-auth sss #Cisco WSA – довольно «интересный» proxy-сервер.. #И так как далеко не все Linux’овые приложения умеют использовать krb-тикеты #для авторизации на прокси, приходится использовать костыль. Нет, можно было #заставить пользователя сначала запустить браузер, авторизоваться на проксе, #и только после этого получить доступ в интернет, скажем, с мессенджера.. #но мы посчитали это издевательством. - name: proxy auth script copy: dest: /var/proxy_auth.sh content: | #!/bin/bash ip=$(echo `ifconfig eth0 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://'`) echo "curl -isL --negotiate -u : https://proxy-server.lab.ru/same_text/$ip/http://lab.ru/ > /dev/null" > /tmp/proxy_auth.sh /bin/bash /tmp/proxy_auth.sh rm -f /tmp/proxy_auth.sh mode: "755" - name: create logon script fpr proxy auth copy: dest: /etc/profile.d/proxy_auth.sh content: | #!/bin/bash /var/proxy_auth.sh mode: "755"
3.5. Настроить ssh согласно требованиям от ИБ, в том числе ограничить доступ для определенных групп AD
- name: ssh gather_facts: false hosts: simply tasks: - name: edit sshd config lineinfile: path: /etc/openssh/sshd_config regex: "^(#)?{{item.key}}" line: "{{item.key}} {{item.value}}" state: present loop: - { key: "LogLevel", value: "VERBOSE" } - { key: "PermitRootLogin", value: "prohibit-password" } - { key: "MaxAuthTries", value: "3" } - { key: "MaxSessions", value: "2" } - { key: "PermitEmptyPasswords", value: "no" } - { key: "UsePAM", value: "yes" } - { key: "AllowGroups", value: "domain?users root wheel linux-sudoers" } #да, да, именно в таком формате тут нужно указывать доменные группы с пробелами #в названиях notify: - restart sshd - enable sshd handlers: - name: restart sshd service: name: sshd state: restarted - name: enable sshd service: name: sshd enabled: yes
3.6. Настроить доступ к sudo для определенной группы AD
- name: sudoers gather_facts: false hosts: simply tasks: - name: edit sudoers file blockinfile: path: /etc/sudoers backup: yes block: | %Linux-Sudoers ALL=(ALL) ALL %Linux-Users ALL=/usr/bin/apt-cache %Linux-Users ALL=/usr/sbin/poweroff %Linux-Users ALL=/usr/sbin/NetworkManager validate: /usr/sbin/visudo -cf %s - name: replace line lineinfile: path: /etc/sudoers regexp: '^@includedir /etc/sudoers.d' line: '#@includedir /etc/sudoers.d' validate: /usr/sbin/visudo -cf %s
3.8. Ввести ПК в домен
#При запуске спрашивает логин и пароль (в «приватном» виде). #После чего получает керберос-тикет и подключает ОС к домену - name: domain join gather_facts: false hosts: simply vars_prompt: - name: "adlogin" prompt: "Enter AD Login" private: no - name: "password" prompt: "Enter password" private: yes tasks: - name: domain check shell: timeout 6s net ads testjoin register: domain_state failed_when: domain_state.rc == 0 - name: Clear the sssd cache shell: rm -f /var/lib/sss/db/* /var/lib/sss/mc/* - name: get krb ticket shell: echo '{{ password }}'| kinit "{{ adlogin }}" - name: join domain command: net ads join -U "{{ adlogin }}"%"{{ password }}" createcomputer="/Computers/Linux" - name: sssd enable service: name: sssd enabled: yes state: restarted - name: reboot reboot: reboot_timeout: 120
3.10. Предусмотреть возможность массового обновления
Тут пришлось сделать отдельный playbook для обновления, и отдельный playbook для брендирования, так как мы пошли по простому пути – не стали пилить тему для кедов, а просто поменяли интересующие нас картинки. И поэтому при обновлении пакетов картинки затираются. Поэтому сразу после обновления происходит брендирование.
Playbook апдейта:
- name: update and upgrade hosts: simply gather_facts: false tasks: - name: update & upgrade shell: | apt-get update -y && apt-get dist-upgrade -y apt-get dedup -y
Брендинг у нас уже был (но я вам его не покажу, мне запретили). И поэтому playbook обновления выглядят так:
- import_playbook: update.yml - import_playbook: branding.yml
4. Server-side
Следующий этап импортозамещения – перенос инфраструктурных сервисов на «отечественные» программные продукты. Об этом мы долго думали, рассматривали варианты, поднимали стенды (но стенд и боевые тесты – это совсем не одно и то же). Словом, перекинуть на Альт или Астру, или какую-то еще ОС, например, Zabbix, dhcp, dns, proxy-сервер – не проблема. Это все есть и работает. Но заменить VMWare Hypervisor просто нечем. OpenNebula? Ну да, ну да. KVM? ProxMox? VDI – отсутствует. Проблема «отечественного» софта осталась той же – это пересобранное OpenSource ПО, которое стоит денег. И как весь OpenSource, его стоимость заключается не в «коробка + 2 инженера», а в «десяток инженеров с зарплатами сильно выше, чем у «коробочного» (в зависимости от размера инфраструктуры).
По итогу мы перекинули на Simply Linux продуктовый Zabbix, и подняли Ansible, jitsi и локальный репозиторий. Дальше по плану перенос Nextcloud, Kaspersky Security Center, Certification Authority. Но все это.. такое.. Фикция и профанация.
5. Выводы
В целом, со слов тестовой группы, работать на Linux «можно». Да, недостает привычных виндовых удобств, типа SSO для Outlook, предпросмотра документов в 1С (над этим работает наш центр компетенций 1С), «адок с банк-клиентами и криптопрой» (с) техподдержка.
«Работать можно». Базовый функционал есть. Да, не искаропке, его приходится допиливать, ибо парадигма Linux такова, что каждый каждый делает «это» так, как хочет. Если вы понимаете, о чем я. А допиливать функционал приходится в основном силами админов и ТП.
В итоге: используя данный инструментарий, сотрудник сможет выполнять свои обязанности.
Но. Шаг влево\шаг вправо вызывает жуткий butthurt как у пользователя, так у техпода, привыкшего к Windows. У нас же как заведено? Логи начинают читать только тогда, когда google ничего не подсказал. И перестроить мышление довольно трудно, потому что «ну там же так работает! И это удобно! Так зачем уходить от удобного и привычного к сложному и непонятному?». Но это уже политика, это на другом ресурсе обсуждается.
«Мыши плакали и кололись..» А, стоп, где-то я это уже писал..
ссылка на оригинал статьи https://habr.com/ru/post/695284/
Добавить комментарий