Импортозамещение на практике. Часть 6. Пользовательские рабочие места

от автора

Эта статья довольно долго не могла увидеть свет, по ряду причин, от проблем со здоровьем после 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.

Я довольно долго думал о том, нужно ли приводить листинг плейбуков. И, посоветовавшись с командой, мы решили, что не все стоит выкладывать. По ряду причин.

Спойлер

Да, мы знаем, что в нижеследующем можно сделать все иначе. Использовать роли, переменные и т.д. Хранить все в гите, отслеживать там изменения и прочее, прочее, прочее. Мы знаем, спасибо. Но читать это в рамках статьи будет невозможно. Поэтому плейбуки. Поэтому разбито так, как разбито.

 Итак, требования следующие:

  1. Доставить внутренние сертификаты, импортировать их

  2. Добавить локальные репозитории

  3. Установить в систему весь требуемый софт, закинуть конфиги для установленного софта, настроить skel, чтобы при первом входе в систему, пользователь получил нужные линки и каталоги, предусмотреть подключение для пользователей сетевых шар, живущих на Windows-сервере, предусмотреть подключение пользователям списка баз 1С, как это описано здесь (ну это просто удобно)

  4. Сформировать конфиги для подключения ПК к домену, предусмотреть запуск логон-скриптов, обойти проблему авторизации на proxy-сервере (в нашем случае это скрипт, который стучится в proxy-сервер с Kerberos-тикетом, посредством curl’а, и получает на проксе тикет на 8 часов). Да, WSA – она такая..

  5. Настроить ssh согласно требованиям от ИБ, в том числе ограничить доступ для определенных групп AD

  6. Настроить доступ к sudo для определенной группы AD

  7. Соблюсти прочие требования ИБ (мне запретили это показывать. ИБ – те еще параноики)

  8. Ввести ПК в домен

  9. Предусмотреть механизм «брендирования» — обои рабочего стола, загрузчика и прочее (это мне тоже запретили вам показывать)

  10. Предусмотреть возможность массового обновления

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

«Кто в теме и так поймет, кто не в теме – тот все равно листинг читать не будет».

Оговорюсь лишь о том, что для доставки файлов, открытых ключей и прочего, мы опубликовали их рядом с репозиториями. Там все равно 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 ничего не подсказал. И перестроить мышление довольно трудно, потому что «ну там же так работает! И это удобно! Так зачем уходить от удобного и привычного к сложному и непонятному?». Но это уже политика, это на другом ресурсе обсуждается.

«Мыши плакали и кололись..» А, стоп, где-то я это уже писал..

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
А вы уже импортозамещаете?
100% Импортозамещаем 1
0% Нет, мы в домике. 0
Проголосовал 1 пользователь. Воздержавшихся нет.

ссылка на оригинал статьи https://habr.com/ru/post/695284/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *