fix(ops): выгрузка бэкапа в S3 не проходила TLS — у aws-cli свой CA-бандл (#2203) #3009

Merged
lekss361 merged 1 commit from fix/backup-s3-ca-bundle into main 2026-08-20 20:14:16 +00:00
Owner

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

Что было

Блок выгрузки в S3 в обоих скриптах запускает docker run --rm ... amazon/aws-cli:latest. Этот образ не проходит TLS к эндпоинту Selectel:

aws: [ERROR]: SSL validation failed for https://s3.ru-1.storage.selcloud.ru/...
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
self-signed certificate in certificate chain

Почему

AWS CLI v2 использует собственный набор корневых сертификатов, вшитый в botocore, а не системное хранилище. Корня, которым подписан сертификат Selectel, в нём нет.

При этом сам сертификат совершенно нормальный, и системное хранилище контейнера его принимает. Проверено тремя независимыми способами:

  • с хоста: openssl s_client → цепочка *.s3.ru-1.storage.selcloud.ru → GlobalSign AlphaSSL CA → GlobalSign Root CA R6, Verify return code: 0 (ok);
  • Python внутри того же контейнера к тому же адресу → TLS проходит, отвечает HTTP 403 (ожидаемо для анонимного доступа);
  • aws в том же контейнере → CERTIFICATE_VERIFY_FAILED.

Разница ровно в том, чьим хранилищем пользуется клиент.

Что сделано

В оба блока docker run добавлена одна переменная:

-e AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \

--no-verify-ssl намеренно не использован — это отключение проверки TLS, а не решение.

Как проверено на проде

Обе ветки прогнаны живьём против боевого бакета gendsgn-backups:

результат
без переменной SSL validation failed, объект не появляется, бакет пуст
с переменной upload: ... to s3://gendsgn-backups/_selftest/multipart-probe.bin, код возврата 0

Загрузка подтверждена вторым ключом (сервисный пользователь-читатель): 11.0 MiB _selftest/multipart-probe.bin.

Заодно проверен путь multipart, которым пойдёт реальный дамп на 1,4 ГБ: порог принудительно снижен до 5 МБ, файл 11 МБ, три сегмента — проходит. Политика бакета покрывает multipart корректно (PutObject + три multipart-действия).

Побочное

В бакете остался тестовый объект _selftest/multipart-probe.bin (11 МБ). Удалить его текущими ключами нельзя — DeleteObject намеренно запрещён и писателю, и читателю. Убирается через панель управления.

Refs #2203

Блокер, найденный при боевой проверке выгрузки в только что заведённый бакет Selectel. Без этой правки ночной бэкап падал бы на выгрузке **каждый раз**, а причина выглядела бы как проблема с ключами или политикой доступа — и её искали бы не там. ## Что было Блок выгрузки в S3 в обоих скриптах запускает `docker run --rm ... amazon/aws-cli:latest`. Этот образ не проходит TLS к эндпоинту Selectel: ``` aws: [ERROR]: SSL validation failed for https://s3.ru-1.storage.selcloud.ru/... [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain ``` ## Почему AWS CLI v2 использует **собственный** набор корневых сертификатов, вшитый в botocore, а не системное хранилище. Корня, которым подписан сертификат Selectel, в нём нет. При этом сам сертификат совершенно нормальный, и системное хранилище контейнера его принимает. Проверено тремя независимыми способами: - с хоста: `openssl s_client` → цепочка `*.s3.ru-1.storage.selcloud.ru` → GlobalSign AlphaSSL CA → GlobalSign Root CA R6, `Verify return code: 0 (ok)`; - Python **внутри того же контейнера** к тому же адресу → TLS проходит, отвечает HTTP 403 (ожидаемо для анонимного доступа); - `aws` в том же контейнере → `CERTIFICATE_VERIFY_FAILED`. Разница ровно в том, чьим хранилищем пользуется клиент. ## Что сделано В оба блока `docker run` добавлена одна переменная: ``` -e AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \ ``` `--no-verify-ssl` намеренно **не** использован — это отключение проверки TLS, а не решение. ## Как проверено на проде Обе ветки прогнаны живьём против боевого бакета `gendsgn-backups`: | | результат | |---|---| | без переменной | `SSL validation failed`, объект не появляется, бакет пуст | | с переменной | `upload: ... to s3://gendsgn-backups/_selftest/multipart-probe.bin`, код возврата 0 | Загрузка подтверждена **вторым ключом** (сервисный пользователь-читатель): `11.0 MiB _selftest/multipart-probe.bin`. Заодно проверен путь multipart, которым пойдёт реальный дамп на 1,4 ГБ: порог принудительно снижен до 5 МБ, файл 11 МБ, три сегмента — проходит. Политика бакета покрывает multipart корректно (`PutObject` + три multipart-действия). ## Побочное В бакете остался тестовый объект `_selftest/multipart-probe.bin` (11 МБ). Удалить его текущими ключами нельзя — `DeleteObject` намеренно запрещён и писателю, и читателю. Убирается через панель управления. Refs #2203
lekss361 added 1 commit 2026-08-20 20:12:12 +00:00
fix(ops): выгрузка бэкапа в S3 не проходила TLS — у aws-cli свой CA-бандл (#2203)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
74d503fe36
aws-cli v2 (образ amazon/aws-cli:latest) использует собственный набор
корневых сертификатов из botocore, а не системное хранилище контейнера.
В нём нет корня, которым подписан сертификат Selectel S3
(s3.ru-1.storage.selcloud.ru), поэтому docker run падал на
SSL validation failed / CERTIFICATE_VERIFY_FAILED: self-signed
certificate in certificate chain — и ночной S3-upload не проходил
каждый раз, хотя ключи и bucket были верны.

Проверено на проде: без AWS_CA_BUNDLE — SSL validation failed; с
AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt (системное хранилище
контейнера, содержит нужный GlobalSign-корень) — upload 11 МБ проходит,
код возврата 0, объект подтверждён чтением вторым ключом.

Правка — одна переменная окружения в docker run в обоих скриптах:
- ops/backup.sh (main DB backup)
- tradein-mvp/deploy/backup-tradein-db.sh (tradein DB backup, #3004)
lekss361 merged commit 1663f22795 into main 2026-08-20 20:14:16 +00:00
lekss361 deleted branch fix/backup-s3-ca-bundle 2026-08-20 20:14:19 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3009
No description provided.