Что такое ALD Pro и зачем его едят можно почитать в других статья на Хабре:
А я расскажу о своем пути в установке данного комплекса, а также какие проблемы были у меня, чтобы их не было у Вас:)
Ну и конечно же, как и любая моя статья, эта также будет связана со скриптами, автоматизацией и удобствами для конечного пользователя.
Глава 1: Зачем нужна автоматизация для установки ALD Pro?

ALD Pro — это мощное решение для управления доменами и аутентификации в инфраструктуре Astra Linux. Однако его установка и настройка — это многоэтапный процесс, требующий выполнения множества команд в терминале, редактирования конфигурационных файлов и перезагрузки системы. Вручную это не только отнимает много времени, но и чревато ошибками, особенно для администраторов, которые не часто сталкиваются с подобными задачами.
Типичные проблемы ручной установки:
Сложность: Необходимость точного ввода команд, настройки сети, DNS, репозиториев и других параметров.
Время: Процесс может занять несколько часов, особенно если требуется обновление системы или повторение шагов из-за ошибок.
Человеческий фактор: Опечатки, пропущенные шаги или неверные параметры могут привести к неработоспособности системы.
Документация: Официальные инструкции часто объемны и требуют адаптации под конкретную инфраструктуру.
Почему автоматизация?
Автоматизация установки ALD Pro решает эти проблемы, предоставляя следующие преимущества:
Скорость: Скрипт выполняет все шаги за считанные минуты, минимизируя время простоя.
Точность: Исключаются ошибки, связанные с ручным вводом. Каждый шаг проверяется, а параметры валидируются.
Удобство: Администратору не нужно запоминать последовательность действий — достаточно запустить скрипт и следовать подсказкам.
Масштабируемость: Один и тот же скрипт можно использовать для развертывания ALD Pro на множестве серверов, обеспечивая идентичную конфигурацию.
Поддержка разных сценариев: Возможность выбора версии ALD Pro, настройки глобального каталога или модуля синхронизации через интерактивный интерфейс.
Эволюция инструментов
Изначально я использовал bash-скрипты с zenity для создания простого графического интерфейса. Это работало, но имело ограничения:
Сложность поддержки: добавление новых функций усложняло код.
Ограниченный UI: zenity предоставляет базовые диалоговые окна, но не подходит для сложных сценариев.
Нет возможности удаленного управления.
Переход на Python и Flask позволил:
Создать полноценный веб-интерфейс с интуитивной навигацией.
Реализовать прогресс-бар, логирование и обработку ошибок.
Обеспечить доступ к установке с любого устройства в сети.
Легко расширять функционал (например, добавление поддержки новых версий ALD Pro).
Автоматизация установки ALD Pro — это не просто удобство, а необходимость для современных ИТ-инфраструктур. Она экономит время, снижает риски ошибок и позволяет сосредоточиться на более важных задачах. Дальше я расскажу, как прошел путь от простых bash-скриптов до веб-интерфейса на Flask, и какие подводные камни встретил на этом пути.
Глава 2: Bash + Zenity — Первая версия автоматизации

1. Почему начали с Bash?
Простота: Bash — это стандартный инструмент администрирования Linux, не требующий дополнительных зависимостей.
Скорость разработки: для простых задач не нужны сложные языки.
Доступность: все команды установки ALD Pro уже выполнялись вручную, оставалось лишь объединить их в скрипт.
2. Структура bash-скрипта
Разберём ключевые части скрипта:
2.1. Проверки перед установкой
internet_error="У вас проблемы с доступом к сайту dl.astralinux.ru. Проверьте настройку интернет соединения и правильность dns."
if ping -c 1 dl.astralinux.ru &> /dev/null; then
echo "Доступ к репозиторию есть"
else
zenity --info --text="$internet_error"
exit 1
fi
Проверка Интернет-соединения перед началом установки.
Использование
zenity
для вывода ошибок в GUI.
2.2. Работа с администратором системы и правами sudo
if id -nG | grep -qw "astra-admin"; then
echo ok
else
zenity --info --text="Пользователь не принадлежит группе astra-admin. Необходимо зайди под пользователем с правами администратора."
exit 1
fi
passwd=$(zenity --forms --title="Пароль для администратора" \
--text="Введите пароль администратора" \
--add-password="Пароль")
echo "$passwd" | sudo -Sv >/dev/null 2>&1
if [ $? -ne 0 ]; then
#остальной код
else
zenity --info --text="Неправильный пароль от sudo. Перезапустите скрипт."
exit 1
fi
Интерактивный ввод пароля через
zenity
.Проверка корректности перед выполнением привилегированных команд.
2.3. Динамические параметры установки
form_data=$(zenity --forms --title="Введите данные" --text="Введите данные:" \
--add-entry="Введите имя контроллера домена имя типа: dc" \
--add-entry="Введите имя домена типа: domain.test" \
--add-entry="Введите имя полное доменное имя типа: dc.domain.test" \
--add-entry="Введите статический ip-address вашего будущего домена типа: 10.10.10.10" \
--add-entry="Введите маску подсети вашего будущего домена типа: 255.255.255.0" \
--add-entry="Введите gateway сети вашего будущего домена типа: 10.10.10.1" \
--add-entry="Введите ваш dns для доступа в интернет: 10.10.10.9" \
--add-password="Придумайте пароль для администратора домена:" \
--add-combo="Версия ALDPro" \
--combo-values="2.2.1|2.3.0|2.4.0" )
# Разбиение строки с данными на отдельные переменные
small_fqdn=$(echo "$form_data" | awk -F '|' '{print $1}')
big_fqdn=$(echo "$form_data" | awk -F '|' '{print $2}')
fqdn=$(echo "$form_data" | awk -F '|' '{print $3}')
ipaddres=$(echo "$form_data" | awk -F '|' '{print $4}')
mask=$(echo "$form_data" | awk -F '|' '{print $5}')
gateway=$(echo "$form_data" | awk -F '|' '{print $6}')
dns=$(echo "$form_data" | awk -F '|' '{print $7}')
passwd_dom=$(echo "$form_data" | awk -F '|' '{print $8}')
version_aldpro=$(echo "$form_data" | awk -F '|' '{print $9}')
Графический ввод параметров (FQDN, IP, пароль и т.д.).
Демонстрация примеров заполнения, для удобства пользователя.
2.4. Настройка сети и репозиториев
if [ "$version_aldpro" == 2.3.0 ]; then
#репы 2.3.0 и 1.7.5
echo $passwd | sudo -S bash -c "echo -e 'deb https://download.astralinux.ru/aldpro/frozen/01/2.3.0 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"
echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.5/uu/1/repository-base/ 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"
echo $passwd | sudo -S bash -c "echo -e 'deb http://download.astralinux.ru/astra/frozen/1.7_x86-64/1.7.5/repository-extended 1.7_x86-64 main contrib non-free' >> /etc/apt/sources.list"
elif [ "$version_aldpro" == 2.4.0 ]; then
#репы 2.4.0 и 1.7.6
echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.0/ 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"
echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.6/repository-base 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"
echo $passwd | sudo -S bash -c "echo -e 'deb https://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.6/repository-update 1.7_x86-64 main contrib non-free' >> /etc/apt/sources.list"
else
#репы 2.2.1 и 1.7.4
echo $passwd | sudo -S bash -c "echo -e 'deb https://download.astralinux.ru/aldpro/frozen/01/2.2.1 1.7_x86-64 main base' > /etc/apt/sources.list.d/aldpro.list"
echo $passwd | sudo -S bash -c "echo -e 'deb http://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.4/repository-extended 1.7_x86-64 main contrib non-free' > /etc/apt/sources.list"
echo $passwd | sudo -S bash -c "echo -e 'deb http://dl.astralinux.ru/astra/frozen/1.7_x86-64/1.7.4/repository-base 1.7_x86-64 main non-free contrib' >> /etc/apt/sources.list"
fi
echo $passwd | sudo -S bash -c "echo -e 'search $big_fqdn' > /etc/resolv.conf"
echo $passwd | sudo -S bash -c "echo -e 'nameserver 127.0.0.1' >> /etc/resolv.conf"
Замена DNS для работы ALD Pro.
Добавление репозитория в зависимости от выбранной версии.
2.5. Запуск установки ALD Pro
echo $passwd | sudo -S aldpro-server-install -d "$big_fqdn" -n "$small_fqdn" -p "$passwd_dom" --ip "$ipaddres" --no-reboot --setup_syncer --setup_gc
Автоматический вызов официального инсталлятора с нужными параметрами.
3. Плюсы и минусы решения
✅ Плюсы:
Минимальные зависимости (
bash
,zenity
,sudo
).Быстрая разработка для базового сценария.
Подходит для разовых установок.
❌ Минусы:
Сложность поддержки: при добавлении новых функций код становится запутанным.
Ограниченный UI: Zenity не поддерживает прогресс-бар в реальном времени или сложные формы.
Нет обработки ошибок: если что-то пошло не так, скрипт может «зависнуть» без понятного лога.
Глава 3: Переход на Python + Flask — Современный веб-интерфейс

1. Почему Bash перестал устраивать?
Масштабируемость: добавление новых функций (например, выбор версии ALD Pro) усложняло код.
Удобство: администраторы хотели удалённо запускать установку, а не подключаться к серверу через SSH.
Интерактивность: нужен был прогресс-бар, логирование и возможность перезапуска прерванной установки.
2. Архитектура Flask-приложения
Рассмотрим ключевые компоненты:
2.1. REST API для управления установкой
@app.route('/installation_progress')
def get_progress():
if installation_progress.get('is_complete') and installation_progress.get('access_info'):
return jsonify({
'status': 'complete',
'access_info': installation_progress['access_info'],
'progress': installation_progress
})
return jsonify(installation_progress)
def start_installation(config_data):
try:
# New Step 0: Configure ALDPro repository
...
Запуск установки в отдельном потоке, чтобы не блокировать веб-интерфейс.
2.2. Прогресс в реальном времени
@app.route('/submit', methods=['POST'])
# Reset progress and SAVE CONFIG DATA
global installation_progress
installation_progress = {
'current_step': 0,
'total_steps': 9,
'steps': [
{'name': 'Настройка репозитория ALD Pro и имени хоста', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Настройка сети и файла hosts', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Обновление системы', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Установка компонентов ALD Pro', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Установка ALD Pro', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Отключение DNSSEC', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Перезапуск служб', 'status': 'pending', 'start_time': None, 'end_time': None},
{'name': 'Очистка', 'status': 'pending', 'start_time': None, 'end_time': None},
],
'start_time': None,
'end_time': None,
'is_complete': False,
'error': None,
'config_data': config_data # Сохраняем конфигурацию для возможного повтора
}
threading.Thread(target=start_installation, args=(config_data,)).start()
return render_template('progress.html', reboot_info=REBOOT_INFO)
Frontend (
progress.html
) опрашивает этот endpoint и обновляет прогресс.
2.3. Основной процесс установки
@app.route('/installation_progress')
def get_progress():
if installation_progress.get('is_complete') and installation_progress.get('access_info'):
return jsonify({
'status': 'complete',
'access_info': installation_progress['access_info'],
'progress': installation_progress
})
return jsonify(installation_progress)
def start_installation(config_data):
try:
# New Step 0: Configure ALDPro repository
update_progress(0, 'in_progress')
time.sleep(1)
version = config_data['version_aldpro']
repo_content = ""
if version == "2.5.0":
repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.5.0/ 1.7_x86-64 main base"
elif version == "2.4.1":
repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.1/ 1.7_x86-64 main base"
elif version == "2.4.0":
repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.4.0/ 1.7_x86-64 main base"
elif version == "2.3.0":
repo_content = "deb https://dl.astralinux.ru/aldpro/frozen/01/2.3.0 1.7_x86-64 main base"
elif version == "local":
repo_content = config_data['local_repo_path']
else:
raise Exception(f"Неизвестная версия ALDPro: {version}")
# Write repository file
with open("/etc/apt/sources.list.d/aldpro.list", "w") as f:
f.write(repo_content + "\n")
# Меняем имя хоста
fqdn = config_data.get('fqdn', '')
if fqdn:
cmd = f"hostnamectl set-hostname {fqdn}"
success, output = run_command(cmd)
if not success:
raise Exception(f"Ошибка смены имени хоста: {output}")
update_progress(0, 'completed')
time.sleep(1)
# Step 1: Network config
update_progress(1, 'in_progress')
time.sleep(1)
# Определяем активный сетевой интерфейс
get_active_iface_cmd = "ip route | grep default | awk '{print $5}' | head -n 1"
success, output = run_command(get_active_iface_cmd)
if not success or not output.strip():
raise Exception("Не удалось определить активный сетевой интерфейс")
active_interface = output.strip()
# Проверяем, активен ли NetworkManager
nm_active = False
check_nm_cmd = "systemctl is-active NetworkManager"
success, output = run_command(check_nm_cmd)
if success and output.strip() == "active":
nm_active = True
commands = []
# Если NetworkManager активен, добавляем команды для его отключения
if nm_active:
commands.extend([
"systemctl stop NetworkManager",
"systemctl disable NetworkManager",
"systemctl mask NetworkManager",
# Ждем немного, чтобы служба точно остановилась
"sleep 2"
])
# Настраиваем статический IP в /etc/network/interfaces
interfaces_content = f"""# This file describes the network interfaces available on your system
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
auto {active_interface}
iface {active_interface} inet static
address {config_data['ipaddres']}
netmask {config_data['mask']}
gateway {config_data['gateway']}
source /etc/network/interfaces.d/*.cfg"""
# Добавляем остальные команды настройки сети
commands.extend([
f"echo '{interfaces_content}' > /etc/network/interfaces",
f"sed -i 's/dns-nameservers {config_data.get('dns', '')}/dns-nameservers 127.0.0.1/g' /etc/network/interfaces",
f"echo -e 'search {config_data.get('big_fqdn', '')}' > /etc/resolv.conf",
f"echo -e 'nameserver {config_data.get('dns', '')}' >> /etc/resolv.conf",
# Очистка старых записей localhost
"sed -i '/^127\\.0\\.1\\.1/d' /etc/hosts",
# Добавление новых записей
f"echo '{config_data['ipaddres']} {config_data['fqdn']} {config_data['small_fqdn']}' >> /etc/hosts",
# Перезапускаем сеть для применения изменений
"systemctl restart networking.service",
# Ждем применения настроек сети
"sleep 5"
])
for cmd in commands:
success, output = run_command(cmd)
if not success:
# Для systemctl mask ошибка может быть ожидаемой, если служба уже замаскирована
if not (nm_active and "mask" in cmd and "is already masked" in output):
raise Exception(f"Ошибка настройки сети: {output}")
time.sleep(0.5)
update_progress(1, 'completed')
time.sleep(1)
# и остальные шаги
Каждый этап фиксируется в объекте
installation_progress
.При ошибке можно перезапустить конкретный шаг.
2.4. Валидация данных
<div class="form-group">
<label for="passwd_dom">Пароль администратора домена:
<span class="help-icon">?
<span class="tooltip">Пароль должен содержать: минимум 8 символов, заглавные и строчные буквы, цифры</span>
</span>
</label>
<input type="password" id="passwd_dom" name="passwd_dom" value="{{ config.passwd_dom }}"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
title="Пароль должен содержать минимум 8 символов, включая заглавные и строчные буквы, и цифры"
required
oninput="validatePassword()">
<div class="password-requirements">
<div class="requirement">
<span id="length-icon" class="requirement-icon">❌</span>
<span>Минимум 8 символов</span>
</div>
<div class="requirement">
<span id="uppercase-icon" class="requirement-icon">❌</span>
<span>Хотя бы одна заглавная буква</span>
</div>
<div class="requirement">
<span id="lowercase-icon" class="requirement-icon">❌</span>
<span>Хотя бы одна строчная буква</span>
</div>
<div class="requirement">
<span id="number-icon" class="requirement-icon">❌</span>
<span>Хотя бы одна цифра</span>
</div>
</div>
</div>
Проверка паролей перед запуском.
3. Преимущества перед Bash
✅ Гибкость:
Можно добавлять новые функции без переписывания всей логики.
Поддержка разных версий ALD Pro через конфигурацию.
✅ Удобство:
Веб-интерфейс доступен с любого устройства в сети.
Прогресс-бар, логи и возможность повтора неудачного шага.
✅ Надёжность:
Обработка ошибок на каждом этапе.
Возможность перезапуска прерванной установки.
4. Итог
Переход на Python + Flask превратил простой скрипт в полноценный инструмент администрирования, который:
экономит время,
уменьшает количество ошибок,
даёт больше контроля над процессом.
5. Новый интерфейс




Источники
Если вы дочитали до конца, то исходники можно посмотреть:
GitFlick - исходники кода, и собранные deb-пакеты;
YouTube - видео по работе web-версии установщика;
RuTube - видео на отечественной площадке по работе web-версии установщика;