Дорогие читатели!

Добро пожаловать в увлекательное путешествие по миру автоматизации в Linux! Хочу предупредить вас, что формат этой статьи будет необычен — я решил представить материал в виде захватывающего рассказа.

Немного об авторе и его прошлом

В землях Linux, где каждый день приносит новые задачи и вызовы, я — ваш проводник в мире автоматизации. Меня зовут Александр Габидуллин, и я рад приветствовать вас в своём повествовании о приключениях трёх мушкетёров: Ansible, Bash и меня.

Про ansible и bash поговорим чуть дальше, а сейчас обо мне!

Я занимаю должность инженера в отделе архитектуры и интеграции в одном из больших вендоров отечественного ПО. До этого я начинал как обычный рыцарь в мире linux, и помогал windows администраторам сделать первые шаги на встречу к linux системам и автоматизировать их рутинный труд.

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

Присоединяйтесь ко мне в этом путешествии, и давайте вместе отправимся навстречу новым открытиям и достижениям!

«На пути к переменам: проблема, которая стала отправной точкой»

Когда-то в далеком 2022 году, работая в государственном учреждении на должности инженер информационной безопасности, я получил письмо счастья, а точнее копию письма с указом президента РФ №250.

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

P.S. да-да, я начинал свои первые шаги с большого государственного университета.

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

Необходимо было в кратчайшие сроки перевести более 1000 рабочих мест сотрудников и студентов на отечественное ПО и без ручного труда.

В поисках пути к автоматизации: первые шаги и вызовы

Изначально я не любил программирование, но любил скрипты на bash. И учитывая инфраструктуру с которой приходилось работать, когда АРМ может включаться и выключаться несколько раз за день, а переустановку с *Окон* на Linux производят в ручную администраторы, то было принято решение подготовить небольшой набор скриптов, которые запускались после накатывания ОС.


И казалось бы все встало на рельсы и должно работать?

Но увы, как всегда бывает в интерпрайзе, появились различные проблемы: отваливалась сеть, появились различные отделы с дополнительными требованиями, ну и не стоит забывать про железо и драйвера необходимы для его работы, а самое важно нужна была идемпотентность и разграничение прав.

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

От хаоса к порядку: первый проект «Помогатор»

Я понимая, что не могу постоянно бегать и помогать в запуске скриптов, начинаю создавать проект под название Помогатор. Он получает базовый графический интерфейс на zenity, я собираю его в удобный deb пакет и кладу его на внутренний сервер. Таким образов администратор в любой момент может установить мой пакет двойным кликом и введя свой пароль. А далее его встретит графический интерфейс и подскажет ему возможности и требования.

Под капотом пока остаются bash скрипты, но все переменные необходимые для выполнения уже запрашиваются в интерактивном режиме, и бывшие администраторы windows понимают, что он них хотят и в какой поле необходимо ввести.

Функционал начинает набирать обороты, и начинают добавляться различные кастомные сборки пакетов и их установка.

Скрытый текст

Исходники лежат в общем доступе, кто хочет изучить или возможно взять за основу милости прошу https://gitflic.ru/project/gabidullin-aleks/pomogator

Что я хотел решить данным проектом?

  1. Автоматизация рутинных задач на месте;

    Теперь я не хожу к каждому АРМ, а любой админ нажав пару кнопок в приложение способен настроить его под нужды конечного сотрудника.

  2. Удобства установки кастомных сборок deb пакетов без флешки и подключения сетевых папок;

    Мною был поднят сервер и туда выложены все deb пакеты, которые могли понадобиться сотруднику, и которых не было в репозитории или они были собраны в ручную. А моя утилита просто переходила по ссылке, скачивала нужный пакет и устанавливала автоматически и если требовался какой-либо pre/post инстальный сценарий, то он уже был прописан в скрипте под капотом.

  3. Я могу постоянно дописывать функционал и обновлять пакет Помогатора, но тут скрывалась и проблема: а как мне обновить пакет, на уже установленных АРМ?

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

Делаю отдельную вкладку на nginx и по данному пути обновляю версию сборки, а локально установленный пакет перед каждым запуском обращаются по url и сверяет версию свою и на сервере. Если версия отличается, то появляется кнопка с требованием обновить пакет и запросом пароля администратора. И вроде все, проблемы решены, но остается одно НО и очень существенное: администратору все ровно необходимо идти и вводить пароль для установки обновленного пакета. И тут без систем автоматизации уже не обойтись.

«На пути к совершенству: развитие проекта и внедрение новых решений».

Начался поиск систем автоматизации, которые работают из коробки и не требуют большого погружения в стилистику написания. Получился всем известный список: Puppet, Chef, Salt и Ansible. Не хотелось использовать агентские службы, так как пришлось бы все ровно настраивать агента на всех АРМ.

Как видно из названия статьи, выбор пал на ansible.

Почему я выбрал именно его:

  1. Понятный для меня формат yaml;

  2. Нет агентских служб;

  3. Можно запускать с использованием пароля от локальной УЗ с необходимыми права;

  4. Идемпотентен ( при прямых руках и как оказалось не всегда они прямые);

  5. Нет необходимости настройки отдельный мастер-нод.

Теперь имея выбранную систему необходимо было улучшить проект, но понимая, что я буду использовать другой подход и технологии, я решаю приступить к новому проекту, который будет уже open-source и будет направлен на помощь всем администраторам, занимающимся импортозамещением. Проект получает свое название AstraWizard и как можно понять из название связан он с ОС Astra Linux, но сейчас не об этом.

Скрытый текст

Код проекта можно посмотреть https://gitflic.ru/project/gabidullin-aleks/astrawizard

Логотип проекта
Логотип проекта

Я понимая, что проект будет помогать в различных задачах и на разных уровнях, начинаю использовать все best-practice, которые получил на первом проекте.

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

Так я формирую базу для приложения:

#-------------------------------------Основные функции------------------------------------#

run_event() {    
    local command_to_run="$1"
    eval "$command_to_run"
}

rend_menu() {
    local choices=("$@")  
    selected_item_menu=$(zenity --list --title="Меню выбора" --column="Выберите" "${choices[@]}"  --height=400 --width=500)
}

run_menu(){
     local menu_items=("$@") 
    
     while true; do
        rend_menu "${menu_items[@]}"
        if [ $? -eq 0 ]; then
            if [ -n "${event_menu["$selected_item_menu"]}" ]; then 
            run_event "${event_menu["$selected_item_menu"]}"
            elif  [ "$selected_item_menu" == "$exit_menu" ]; then
                selected_item_menu=""
                return        
            fi
        else
            exit 1
        fi
     done
}

run_app() {
    pre-install
    disclamer
    wizard_info
    wizard_version
    make_inventory 
    run_menu "${items_menu_all[@]}"
}

#--------------------Запуск программы---------------------------
run_app

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

Теперь необходимо решить, а как я буду запускать написанные мною playbook'и по нажатию кнопки? Не самое лучшее, но точно удобное для меня решение получись таким:

#выполнить playbook ansible с доп. параметрами
playbook(){
    (
    ansible-playbook "$dir_playbook/preparation.yml" --user=$user_name --private-key=$dir_wizard/id_rsa --extra-vars "ansible_become_pass=$password_hosts" -i $choose_inventory_type --limit="$group_hosts"
    #выполнить плейбук
    ansible-playbook "$dir_playbook/$selected_item_menu.yml" --user=$user_name --private-key=$dir_wizard/id_rsa --extra-vars "ansible_become_pass=$password_hosts restart_system='$restart_system' path_shell_script='$path_shell_script' path_file_src='$path_file_src' path_file_dst='$path_file_dst' path_from='$path_from' path_to='$path_to' remove_packet='$remove_packet' install_packet='$install_packet' deb_packet_url='$deb_packet_url' deb_packet_localhost='$deb_packet_localhost' text='$text' check_packet='$check_packet' old='$old' new='$new' file_to_path='$file_to_path' command_shell='$command_shell' stop_system='$stop_system' start_system='$start_system' enable_system='$enable_system' disable_system='$disable_system' local_repo='$local_repo' list_packets_freeipa='$list_packets_freeipa'  status_system='$status_system' choose_system='$choose_system' path_to_iso='$path_to_iso' install_freeipa_replica_cmd='$install_freeipa_replica_cmd' list_packets_freeipa_replica='$list_packets_freeipa_replica' name_dir_repo='$name_dir_repo' install_freeipa_server_cmd='$install_freeipa_server_cmd' install_freeipa_client_cmd='$install_freeipa_client_cmd' domain_msad='$domain_msad' ip_msad='$ip_msad' admin_msad='$admin_msad' password_for_msad='$password_for_msad' ip_dc_ipa='$ip_dc_ipa' search_name='$search_name' wallpaper='$wallpaper' usb_cmd='$usb_cmd' install_zabbix_agent='$install_zabbix_agent'" -i $choose_inventory_type --limit="$group_hosts" > "$dir_log"
    exit_code=$?
        # Проверка кода завершения и отображение соответствующего сообщения
            if [ $exit_code -eq 0 ]; then
                zenity --info --title="Успех" --text="Групповая политика успешно применилась.Журнал хранится в $dir_log" --height=100 --width=400
            else
                zenity --error --title="Ошибка" --text="Ошибка при применении политики.Журнал хранится в $dir_log" --height=100 --width=400 
            fi
        ) | zenity --progress --pulsate --auto-close --title="Применение политики" --text="Выполняется $selected_item_menu" --height=100 --width=300 --auto-kill
}

Объясняю: моя кнопка имеет русское название, допустим - Ввести клиента в домен Freeipa.
Так как я не хочу описывать каждую кнопку, то я использую тот же принцип, что и при создании меню. То есть у меня описано, что при выборе данной кнопки, запускается функция для сбора информации о будущем клиенте, а далее через --extra-vars передает данные значения в Playbook, который называется тоже на русском как и кнопка.

#Работа с freeipa
event_menu["$item_menu_gpo_freeipa_server"]="freeipa_server; check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_freeipa_client"]="freeipa_client; check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_freeipa_replica"]="freeipa_replica; check_vars_for_playbook; playbook &"

#ввод в домен freeipa
freeipa_client(){
    #заполняем необходимые формы"    
    form_data=$(zenity --forms --title="Введите данные" --text="Введите данные:" \
        --add-entry="Введите имя домена. Пример: domain.test" \
        --add-entry="Введите ip адрес контроллера домена. Пример: 10.10.10.23" \
        --add-password="Введите пароль администратора домена" --height=300 --width=400)
    search_name=$(echo "$form_data" | awk -F '|' '{print $1}')
    ip_dc_ipa=$(echo "$form_data" | awk -F '|' '{print $2}')
    pass_ipa_admin=$(echo "$form_data" | awk -F '|' '{print $3}')
    install_freeipa_client_cmd="astra-freeipa-client -d $search_name -p $pass_ipa_admin -y --par \"--hostname \"{{ inventory_hostname }}\".$search_name --force --enable-dns-updates\" "
}

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

#установка удаленного помощника
event_menu["$item_menu_gpo_remote_desktop"]="check_vars_for_playbook; playbook &"

#Сменить репозитории#
event_menu["$item_menu_gpo_repo_stable"]="check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_repo_1_7_6frozen"]="check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_repo_1_7_7frozen"]="check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_repo_1_8_1frozen"]="check_vars_for_playbook; playbook &"
event_menu["$item_menu_gpo_repo_1_8_2frozen"]="check_vars_for_playbook; playbook &"

Важной проблемой, которую я решаю до сих пор, является безопасность подключения с использованием пароля. Я использую при подключение ssh-ключи, однако если на АРМ еще не выполнялся ни один Play, и ключа ssh нет у нас и у конечного хоста в доверенных, то необходимо ими обменяться, а тут уже нужен пароль для подключения.

Возможно не красиво и не правильно, но это мой проект и я рад любым советам.

#парсил файл с хостами и передают туда открытый ключ
ssh_key_send(){
        form_data=$(zenity --forms --title="Введите данные" --text="Введите данные:" \
        --add-entry="Введите имя администратора для управляемого хоста" \
        --add-password="Введите пароль администратора" \
        --add-combo="Выберете список для работы с компьютерами" \
        --combo-values="Статический|LDAP|Сетевой Сканер" )
        # Разбиение строки с данными на отдельные переменные
        user_name=$(echo "$form_data" | awk -F '|' '{print $1}')
        password_hosts=$(echo "$form_data" | awk -F '|' '{print $2}')
        type_inventory=$(echo "$form_data" | awk -F '|' '{print $3}')
        if [ "$type_inventory" = "LDAP" ]; then
        choose_inventory_type="$freeipa_inventory_host"
        fi
        if [ "$type_inventory" = "Сетевой Сканер" ]; then
        choose_inventory_type="$network_inventory_host"
        fi
        if [ "$type_inventory" = "Статический" ]; then
        choose_inventory_type="$inventory_host"
        fi
        ##--new version---##
        # Чтение строк из файла в массив lines
    mapfile -t lines < "$choose_inventory_type"

    # Создание пустого списка для хранения хостов
    hosts=()
    (
    # Цикл по каждой строке
    for line in "${lines[@]}"; do
        # Проверка, если строка начинается с #
        if [[ $line =~ ^\#.*$ ]]; then
            continue  # Пропускаем текущую итерацию цикла, если строка начинается с #
        fi       
        if [[ $line =~ ^\[.*$ ]]; then
            continue  # Пропускаем текущую итерацию цикла, если строка начинается с [
        fi
        # Проверка, содержит ли строка "ansible_ssh_host="
        if [[ $line == *ansible_ssh_host=* ]]; then
            # Взять IP-адрес из строки
            ip=$(echo "$line" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
        else
            # Использовать всю строку как имя хоста
            ip=$line
        fi

        # Проверка, найден ли IP-адрес
        if [[ -n $ip ]]; then
            # Добавление хоста в список
            hosts+=("$ip")
        fi
    done
    # Вывод переменной hosts для проверки
    # printf '%s\n' "${hosts[@]}"

    # Проход по каждому хосту и выполнение необходимых операций
    #ssh-keygen -R "$pc" если необходимо удалить хост
    for pc in "${hosts[@]}"; do
        if ! ssh-keygen -F "$pc" >/dev/null; then 
            sshpass -p "$password_hosts" ssh-copy-id -f -o "StrictHostKeyChecking no" -i $dir_wizard/id_rsa.pub $user_name@$pc
        fi
    done
    # Проверка кода завершения и отображение соответствующего сообщения
    ) | zenity --progress --pulsate --auto-close --title="Подготовка управляемых ПК" --text="Передача ключей для управления компьютером" --height=100 --width=400 --auto-kill
    set_hosts
}

Сейчас проект развивается только моими силами, и в свободное от моей основной работы время.

Есть много интересных задач, который я смог решить с помощью данного проекта, например:

  1. Возможность формировать inventory файл на основе: Freeipa/ALDPro, динамически на основе сканирования включенных хостов, ну и статический заполненный в ручную;

  2. Можно используя GUI выбирать группы, на которые необходимо применить ту или иную политику;

  3. Есть возможность выводить всем активным пользователям сообщение на их экран, не зависимо заблокирован он или нет;

  4. Ведение журналов по использованию каждой политики

  5. и другие.

Завершение рассказа, но не нашего пути...

Подробнее о возможностях расскажу в следующих статьях, и возможно кто-то из Вас захочет поучаствовать в проекте или предложит свои идеи для добавления.

Комментарии (11)


  1. pnmv
    28.06.2025 11:55

    В своё время, ещё на заре двухтысячных, не осилив написание чего-то там на bash, ибо громоздко выходило и было не понятно, как всё это поддерживать, взялся за серьезное изучение perl. На нём вышло более презентабельно, и, кажется, более компактно.


    1. MikeyTide Автор
      28.06.2025 11:55

      Согласен, bash был в начале проекта, далее хотелось перевести на python в силу его простоты (для меня), и использовать pyqt5 вместо графики через zenity.
      Но это займет время, а хотелось бы развивать проект в плане функционала, что я и делаю последнее время на заказ. То есть пишут пользователи с просьбой помочь с той или иной функцией, и далее она появляется в приложении.
      Благодаря Habr'у, если пойму что спрос больше чем мне казалось, то может займусь наведением красоты и правильности в коде!


      1. pnmv
        28.06.2025 11:55

        Bash - неплохая отправная точка, в плане понимания, чего же мы всё-таки хотим от нашего проекта.

        Смещение в сторону питона тоже понятно - всё-таки, не язык для однострочников, да еще и всякие юпитеры при нём, что тоже удобно. Зоопарк из библиотек, правда, может слегка дезориентировать.


        1. MikeyTide Автор
          28.06.2025 11:55

          Питон проще поддерживать, тем более если когда-то появятся помощники.
          С библиотеками все в рамках разумного думаю. Ну и красивая графика поможет пользователям использовать все это в школах, вузах и т.д.
          Где системные администраторы либо начинающие, или уже заканчивающие свой путь ( без обид, скорее всего это реальная статистика).
          И весь этот софт писался именно как помощник им, а не в прод для больших компаний, где используется уже terraform/opentofu, ansible, salt и т.д.
          В продолжение данной статьи расскажу именно о своих плейбуках и исследованиях в ОС. Возможно мои костыли, помогут решить кому-то свои собственные задачи


  1. MEGA_Nexus
    28.06.2025 11:55

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

    - Всю автоматизацию на Linux делает Python.

    - Чтобы не разбираться в Python был придуман высокоуровневый инструмент автоматизации Ansible.

    - Чтобы не запоминать с какими параметрами запускать плейбуки Ansible был придуман Web интерфейс ansible awx и ansible semaphore.

    Конкретно мы в компании используем Jenkins в качестве Web-интерфейса к Ansible. Админ, разработчик или тестировщик могут просто зайти в Jenkins и запустить нужный ранбук. Они просто жмут кнопку и далее происходит вся магия Ansible. Все креды и extra vars вшиты и о них думать не нужно. Если какие-то extra vars нужно заполнить, то jenkins предоставляет для этого красивую форму. В общем, очень удобная связка. По факту Ansible + Jenkins у нас делают то, что вы хотите сделать с помощью AstraWizard.

    Настойчиво рекомендую вам познакомиться с ansible semaphore. Он скорее всего закроет все ваши потребности. Если нет, то смотрите тогда в сторону Ansible AWX или связки Ansible + Jenkins.


    1. MikeyTide Автор
      28.06.2025 11:55

      Добрый день, да все это известно и давно работает, но как давал ответ выше. Вы говорите о проде, вы говорите о людях с базовыми компетенциями ( написание своих плейбуков хотя бы, знание модулей и т.д.).


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


      Чаще всего сотрудники там либо молодые и без опыта работы, либо работали всю жизнь с windows и боятся работать с Linux.

      И перед ними стоит выбор:
      1. развернуть продукты, изучать их связки и работу;

      2. базово использовать мой продукт, у которого есть и видео уроки, и код открыт, также как я писал выше я могу оформить любую хотелку также в плейбук и добавить его в AstraWizard.(если данный функционал мне будет интересен и большинству пользователей продукта)

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

      Я не говорю о конкуренции с гигантами, а лишь о проекте, который родился как локальный в 1 месте, а уже помогает в разных местах решать простые задачи начинающим администраторам.


  1. cordarryl_juormer
    28.06.2025 11:55

    Ansible, bash и я: три мушкетёра в мире автоматизации управления компьютерами на Linux

    а торвальдс за дартаньяна


  1. THEOILMAN
    28.06.2025 11:55

    Изобрели maas и foreman, но для более приземленных целей получается... Неплохо, без сарказма) Сам часто о таком задумывался, но слишком тупой чтобы реализовать даже с помощью llm :D


    1. MikeyTide Автор
      28.06.2025 11:55

      Да, спасибо за понимание.
      Все верно, просто помогал людям с которыми работал, а далее с добавлением ansible под капот, решил сделать публичным ПО.
      Есть еще множество разработок, о которых собираюсь рассказать в ближайших статьях.
      Возможно комьюнити поможет советами, или в комментариях напишут свои *хотелки*, а я реализую.
      Мне опыт, людям польза


  1. THEOILMAN
    28.06.2025 11:55

    По опыту работы с астрой вот что заметил - люди которые всю жизнь (20+лет) админили Винду почему-то совсем не могут в x11vnc. Сто раз приходится объяснять как подключиться к текущей сессии пользователя удаленно и прочие гадости творить. Так же без графики не в состоянии по ssh с помощью smbclient забирать файлы с ФС на клиентский пк, например. Уж больно много там всяких ключей, кавычек и экранов для них...

    Хотя вижу, что всё впринципе охвачено в этом велосипеде. Даже не знаю... Автоматизация это конечно здорово, главное чтобы со скоростью работы не увеличивалось количество задач =D


    1. MikeyTide Автор
      28.06.2025 11:55

      Приветствую, как раз про x11vnc я и рассказал в комментариях выше.
      Я в последней версии добавил его автоматическую установку на клиентские машины с помощью ansible.
      Далее у пользователя появляется на рабочем столе ярлык - удаленный помощник.


      Когда пользователь его запускает, то у него поднимается сервер vnc, а на экране появляется пароль из 6 цифр и имя пк + ip адрес, чтобы он смог их продиктовать администратору при запросе.

      Далее пользователь говорит админу данные цифры, и админ с помощью любой утилиты, которая поддерживает протокол vnc подключается используя имя пк или ip и пароль, который диктует пользователь с экрана.

      По поводу копирования файлов, в моем AstraWizard также есть возможность как положить так и стянуть с удаленного хоста файл, или несколько файлов.
      То есть вы выбираете данную функцию, и если хотите передать, то вас попросит выбрать 1 или несколько файлов. А при получение с удаленного хоста, попросит указать путь до файла и куда его положить на вашем пк.

      Все уже есть! Исходный код, а также все мои ролики можно найти на гите в проекте.

      P.S.

      Ссылки на плейлисты, где я рассказываю что умеет AstraWizard, и конечно же показываю