Add CryptoPro license update and service restart scripts

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
a.gritsenko
2026-09-20 11:25:21 +03:00
co-authored by Claude Opus 5
commit 8703b66154
5 changed files with 1698 additions and 0 deletions
+744
View File
@@ -0,0 +1,744 @@
# Скрипты управления лицензиями и службами CryptoPro
## 1. Назначение
Набор Bash-скриптов предназначен для централизованного выполнения операций на удалённых Linux/Astra Linux-серверах:
* установки лицензий CryptoPro CSP;
* изменения параметров лицензии в JSON-конфигурациях приложений;
* перезапуска служб после изменения лицензии;
* проверки успешности выполненных операций;
* создания резервных копий изменяемых конфигурационных файлов;
* ведения локального журнала выполнения.
Работа выполняется по SSH под указанной учётной записью.
Для выполнения административных операций используется `sudo`.
Пароли не хранятся в конфигурационных файлах, не передаются в параметрах командной строки и не записываются в журнал.
---
# 2. Формат конфигурационного файла
Основной конфигурационный файл использует формат:
```text
host;file;company;serial_number
```
Пример:
```text
# host;file;company;serial_number
10.11.1.11;csp;;50500-03010-03MCZ-921DC-HGLK4
10.11.1.12;/etc/cprocsp/app/appsettings.json;ООО Ромашка;50500-03010-03MCZ-921DC-HGLK5
```
Поля:
| Поле | Описание |
| --------------- | ------------------------------------------------- |
| `host` | IP-адрес или DNS-имя удалённого сервера |
| `file` | Специальное значение `csp` либо путь к JSON-файлу |
| `company` | Наименование организации для JSON-конфигурации |
| `serial_number` | Серийный номер лицензии |
Разделителем является символ `;`.
Для строк с JSON-файлом поле `company` обязательно; для `csp` оно игнорируется.
Файл может быть с окончаниями строк CRLF (Windows) и BOM.
Компания может содержать пробелы:
```text
10.11.1.12;/etc/app/appsettings.json;ООО Тестовая Организация;50500-03010-03MCZ-921DC-HGLK5
```
Символ `;` внутри значения компании использовать нельзя, поскольку он является разделителем полей.
---
# 3. Комментарии и пустые строки
В конфигурационном файле разрешены пустые строки.
Также разрешены комментарии, начинающиеся с `#`:
```text
# Серверы УЦ
10.11.1.11;csp;;50500-03010-03MCZ-921DC-HGLK4
### Сервер приложений
10.11.1.12;/etc/app/appsettings.json;ООО Ромашка;50500-03010-03MCZ-HGLK5
```
Комментарии и пустые строки не обрабатываются.
---
# 4. Скрипт `license_update.sh`
## 4.1. Назначение
`license_update.sh` выполняет установку или изменение лицензии на удалённых серверах.
Запуск:
```bash
./license_update.sh -u administrator -c ./licenses_local_test.csv
```
где:
* `-u` — SSH-пользователь;
* `-c` — путь к конфигурационному файлу.
Например:
```bash
./license_update.sh \
-u administrator \
-c ./licenses_local_test.csv
```
---
# 5. Подключение к серверу
Для каждого сервера скрипт устанавливает SSH-соединение:
```text
administrator@10.11.1.11's password:
```
Пароль SSH вводится интерактивно.
Пароль не передаётся в командной строке:
```text
НЕ используется:
sshpass
-p password
```
и не записывается в лог.
---
# 6. Авторизация sudo
После успешного подключения сформированный скрипт операций передаётся на сервер
(в командной строке ssh, в кодировке base64, без использования stdin) и запускается:
```bash
sudo bash /tmp/tmp.XXXXXX
```
Пароль `sudo` запрашивается непосредственно на удалённом сервере, один раз на хост:
```text
[sudo] password for administrator:
```
Для этого SSH-соединение создаётся с TTY (`ssh -tt`), а локальный stdin остаётся
терминалом и используется только для ввода паролей SSH и `sudo`.
После успешной авторизации выполняются все административные операции текущего сеанса.
При первом подключении к серверу ssh стандартно спросит подтверждение ключа хоста
(`Are you sure you want to continue connecting (yes/no)?`).
Пароль `sudo`:
* не передаётся через stdin;
* не хранится в переменной Bash;
* не записывается в лог;
* не находится в конфигурационном файле.
---
# 7. Обработка `file=csp`
Если в конфигурации указано:
```text
10.11.1.11;csp;;50500-03010-03MCZ-921DC-HGLK4
```
скрипт не изменяет JSON-файлы.
Выполняется:
```bash
/opt/cprocsp/sbin/amd64/cpconfig \
-license \
-set "50500-03010-03MCZ-921DC-HGLK4"
```
Команда выполняется с правами `sudo`.
При успешной установке лицензии выполняется перезапуск службы:
```bash
systemctl restart cprocsp
```
После перезапуска проверяется состояние службы:
```bash
systemctl is-active --quiet cprocsp
```
Ожидаемый результат:
```text
cprocsp is active
```
Если служба не перешла в состояние `active`, в журнал дополнительно выводится:
```bash
systemctl status cprocsp --no-pager --full
```
Операция считается ошибочной.
---
# 8. Обработка JSON-файлов
Если вместо `csp` указан путь к JSON:
```text
10.11.1.12;/etc/app/appsettings.json;ООО Ромашка;50500-03010-03MCZ-HGLK5
```
скрипт выполняет следующие операции.
## 8.1. Проверка файла
Проверяется существование файла:
```bash
test -f "$FILE"
```
Если файл отсутствует, операция завершается ошибкой.
---
## 8.2. Сохранение атрибутов
До изменения файла сохраняются:
* UID владельца;
* GID владельца;
* права доступа.
Например:
```text
Owner: 1000:1000
Mode : 640
```
---
## 8.3. Создание резервной копии
Перед изменением создаётся backup:
```text
/etc/app/appsettings.json.license_backup_20260918_123456_12345
```
Резервная копия создаётся с сохранением исходных атрибутов:
```bash
cp -p
```
Путь к backup записывается в журнал.
---
# 9. Изменение JSON
Скрипт изменяет только два параметра внутри объекта:
```json
"License": {
"SerialNumber": "...",
"Company": "..."
}
```
Изменяются:
```text
License.SerialNumber
License.Company
```
Например, было:
```json
"License": {
"SerialNumber": "OLD-LICENSE",
"Company": "Старая организация"
}
```
становится:
```json
"License": {
"SerialNumber": "50500-03010-03MCZ-921DC-HGLK5",
"Company": "ООО Ромашка"
}
```
Остальные параметры JSON не должны изменяться скриптом.
---
# 10. Временный файл
Изменение выполняется через временный файл:
```text
appsettings.json.tmp.<PID>
```
Сначала формируется новая версия файла, после чего она заменяет исходный.
Это позволяет не изменять исходный файл непосредственно во время обработки.
---
# 11. Восстановление атрибутов
После замены файла восстанавливаются исходные:
```text
owner
permissions
```
Используются:
```bash
chown
chmod
```
Таким образом, изменение содержимого файла не должно приводить к изменению его владельца или прав доступа.
---
# 12. Проверка результата
После изменения выполняется повторная проверка параметров:
```text
License.SerialNumber
License.Company
```
Проверяется наличие установленных значений.
Если проверка не пройдена, операция считается ошибочной.
При ошибке изменения JSON скрипт пытается восстановить исходный файл из backup.
Итог по каждой операции выводится строкой:
```text
[REMOTE] OP_RESULT OK /opt/app/appsettings.json
[REMOTE] OP_RESULT FAIL csp
```
По этим строкам формируется итоговая статистика (успешно / ошибок).
---
# 13. Обработка нескольких записей одного сервера
Если один сервер встречается в конфигурации несколько раз:
```text
10.11.1.11;csp;;LICENSE-001
10.11.1.11;/etc/app/appsettings.json;ООО Ромашка;LICENSE-002
```
операции группируются по хосту.
Для одного хоста используется один SSH-сеанс.
После подключения выполняется авторизация `sudo`, затем последовательно обрабатываются все операции этого сервера.
Это позволяет не выполнять повторное подключение для каждой строки конфигурации.
---
# 14. Журнал работы
Основной журнал:
```text
license_update.log
```
Пример:
```text
[2026-09-18 09:23:08] [INFO] Запуск license_update.sh
[2026-09-18 09:23:08] [INFO] SSH user: administrator
[2026-09-18 09:23:08] [INFO] Config: ./licenses_local_test.csv
[2026-09-18 09:23:08] [INFO] Host: 10.11.1.11
```
В журнале фиксируются:
* начало выполнения;
* используемый SSH-пользователь;
* конфигурационный файл;
* обрабатываемые серверы;
* результаты операций;
* ошибки;
* путь к backup;
* итоговая статистика.
Пароли в журнал не записываются.
---
# 15. Итоговая статистика
В конце выполнения выводится:
```text
==============================================
Результат выполнения
Всего операций : 10
Успешно : 9
Ошибок : 1
Log : ./license_update.log
==============================================
```
Если хотя бы одна операция завершилась ошибкой, скрипт возвращает код:
```text
1
```
При полном успешном выполнении:
```text
0
```
Это позволяет использовать скрипт в других Bash-скриптах или системах автоматизации.
---
# 16. Скрипт `restart_services.sh`
`restart_services.sh` предназначен для централизованного перезапуска служб на серверах.
Формат конфигурации:
```text
host;services
```
Пример:
```text
# host;services
ip_ca_tech;cp.ca-tech
ip_ca_tls;cp.ca-tls
ip_ca_digrub;cp.ca-digrub
ip_ra_tech;cp.ra-tech,cp.ra-web-tech
ip_ra_tls;cp.ra-tls,cp.ra-web-tls,cp.cr-tls,cp.cr-web.tls
ip_ra_digrub;cp.ra-digrub,cp.ra-web-digrub,cp.cr-digrub,cp.cr-web.digrub
```
---
# 17. Запуск `restart_services.sh`
Пример:
```bash
./restart_services.sh \
-u administrator \
-c ./services.csv
```
Подключение и авторизация `sudo` выполняются так же, как в `license_update.sh`
(см. разделы 5 и 6): пароль SSH спрашивает ssh, пароль `sudo` запрашивается
на удалённом сервере через TTY, один раз на хост. Пароль в переменных скрипта не хранится.
Затем службы перезапускаются последовательно.
---
# 18. Перезапуск службы
Для каждой службы выполняется:
```bash
systemctl restart SERVICE
```
Например:
```bash
systemctl restart cp.ca-tech
```
После перезапуска выполняется небольшая пауза и проверяется состояние:
```bash
systemctl is-active --quiet cp.ca-tech
```
Если служба успешно запущена:
```text
cp.ca-tech is active
```
Если запуск не удался, выводится подробная информация:
```bash
systemctl status cp.ca-tech --no-pager --full
```
---
# 19. Несколько служб на одном сервере
Службы разделяются запятыми:
```text
ip_ra_tech;cp.ra-tech,cp.ra-web-tech
```
Скрипт преобразует эту запись в две операции:
```bash
systemctl restart cp.ra-tech
systemctl restart cp.ra-web-tech
```
Службы перезапускаются последовательно.
---
# 20. Журнал `restart_services.sh`
Для отдельного скрипта используется собственный журнал:
```text
restart_services.log
```
В журнале фиксируются:
* сервер;
* служба;
* результат `systemctl restart`;
* состояние службы после перезапуска;
* диагностический вывод при ошибке;
* итоговая статистика.
---
# 21. Общая схема работы
Общая последовательность для удалённого сервера:
```text
┌──────────────────────┐
│ Конфигурационный │
│ файл │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Группировка по host │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ SSH connection │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ sudo -v │
│ интерактивный пароль │
└──────────┬───────────┘
│
┌─────────┴─────────┐
│ │
▼ ▼
file = csp JSON-файл
│ │
▼ ▼
cpconfig -license backup
│ │
▼ ▼
restart cprocsp изменение
│ │
▼ ▼
проверка active проверка
│
▼
восстановление
owner/mode
│
┌───────────────────┘
▼
Результат
│
▼
Локальный log
```
---
# 22. Требования
Для работы скриптов необходимы:
### На управляющей машине
* Bash;
* OpenSSH client;
* стандартные Unix-утилиты (coreutils):
* `base64`;
* `grep`;
* `sed`;
* `mktemp`;
* `date`;
* `tee`.
### На удалённых серверах
* SSH-сервер;
* `sudo`;
* `systemd`;
* `bash`, `awk` (gawk или mawk), `base64`, `mktemp`;
* для установки CSP-лицензии:
`/opt/cprocsp/sbin/amd64/cpconfig`;
* для JSON-конфигураций:
доступ к указанному файлу.
Пользователь SSH должен иметь возможность выполнять соответствующие команды через `sudo`.
---
# 23. Безопасность
Скрипты не должны содержать пароли в конфигурации.
Не рекомендуется использовать:
```bash
./license_update.sh -u administrator -p password
```
Пароль SSH вводится стандартным механизмом OpenSSH.
Пароль `sudo` вводится стандартным механизмом `sudo`.
Пароли не должны попадать:
* в командную строку;
* в конфигурационные файлы;
* в переменные скрипта;
* в `license_update.log`;
* в `restart_services.log`.
---
# 24. Рекомендации по эксплуатации
Перед массовым запуском рекомендуется:
1. Проверить подключение по SSH:
```bash
ssh administrator@10.11.1.11
```
2. Проверить возможность выполнения `sudo`:
```bash
ssh -tt administrator@10.11.1.11 'sudo -v'
```
3. Проверить наличие CryptoPro:
```bash
ssh -tt administrator@10.11.1.11 \
'sudo /opt/cprocsp/sbin/amd64/cpconfig -license -view'
```
4. Проверить конфигурационный файл.
5. Сначала выполнить скрипт на одном тестовом сервере.
6. Проверить backup и журнал.
7. После успешной проверки выполнить обработку остальных серверов.
---
# 25. Коды завершения
| Код | Значение |
| --: | ----------------------------------------------- |
| `0` | Все операции выполнены успешно |
| `1` | Одна или несколько операций завершились ошибкой |
Пример использования в другом скрипте:
```bash
./license_update.sh -u administrator -c licenses.csv
if [[ $? -eq 0 ]]; then
echo "Лицензии успешно обновлены"
else
echo "При обновлении лицензий произошли ошибки"
fi
```
---
# 26. Результат
В результате работы:
* лицензии CryptoPro CSP устанавливаются централизованно;
* JSON-конфигурации изменяются только в части параметров `License.SerialNumber` и `License.Company`;
* перед изменением JSON создаётся резервная копия;
* исходные права и владелец файла сохраняются;
* службы после необходимых изменений проверяются;
* операции выполняются с административными правами через `sudo`;
* пароли не сохраняются и не попадают в журналы;
* все результаты доступны в локальных log-файлах.