From 6ba0fbc176099cb84f5fb066f158c5d4275479ad Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Fri, 31 Jul 2026 11:42:35 +0700 Subject: [PATCH] rollback --- CLAUDE.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 2 +- replace-key.py | 43 +++++++++++++++++++++++++++---------------- 3 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4303322 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,50 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Назначение + +Патчит official-бинарник MinIO (AIStor), заменяя встроенный публичный ключ лицензии на собственный, и генерирует под него валидную JWT-лицензию. Итог — Docker-образ, где `minio` запускается с self-signed лицензией через `MINIO_LICENSE`. Только для образовательных/тестовых целей. + +## Архитектура пайплайна + +Весь процесс — многоэтапная сборка в `Dockerfile`, где три компонента выполняются строго по цепочке: + +1. **`generate-keys.sh`** — генерирует пару ECDSA `secp384r1` (P-384): `new-private.pem` / `new-public.pem`. Кривая жёстко привязана к алгоритму подписи лицензии (ES384) — менять нельзя. + +2. **`replace-key.py `** — бинарный патч. Скачивает оригинальный публичный ключ MinIO с `https://subnet.min.io/downloads/license-pubkey.pem` (сетевой доступ обязателен на этапе сборки) и через простой `bytes.replace()` подменяет его в бинарнике на свой. **Критичный инвариант**: длина нового PEM-ключа должна побайтово совпадать с оригиналом (`len(new) == len(original)`), иначе смещения в бинарнике поедут — скрипт падает с ошибкой. Именно поэтому нельзя произвольно менять формат/кривую ключа. + +3. **`generate-license.py `** — подписывает JWT (`jwcrypto`, alg `ES384`) приватным ключом из шага 1. Claims (`org`, `cap`, `plan`, `exp`, `aid`, …) захардкожены в самом скрипте — параметры лицензии правятся там, в словаре `jwt_data`. `exp` в формате Unix-timestamp. + +Связь между шагами: ключ из (1) → патчится публичная часть в бинарник (2) и подписывается лицензия приватной частью (3). Обе части должны быть из одной пары, иначе MinIO отвергнет лицензию. + +## Версия MinIO + +Задаётся через `ARG RELEASE` в `Dockerfile` (образ `quay.io/minio/aistor/minio`). При обновлении версии пересборка обязательна — публичный ключ и формат бинарника могут отличаться. + +## Команды + +Сборка и запуск (основной путь): +```bash +docker-compose up -d --build # API :9000, Web UI :9001 +``` + +Учётные данные и настройки MinIO берутся из `.env` (`MINIO_ROOT_USER`, `MINIO_ROOT_PASSWORD`, `MINIO_VOLUMES`, `MINIO_SITE_REGION`). + +Локальный прогон отдельных шагов (нужен оригинальный бинарник `minio` в текущей папке): +```bash +pip install -r requirements.txt +./generate-keys.sh +python3 replace-key.py minio minio-patched new-public.pem +python3 generate-license.py new-private.pem minio.license +MINIO_LICENSE=./minio.license ./minio-patched server ./storage +``` + +## CI + +`.gitea/workflows/docker-build.yml` — Gitea Actions, триггер на push git-тега (`tags: "*"`). Собирает и пушит образ в `git.realmanual.ru` с тегами `` и `latest`. Требует secret `PUSH_TOKEN`. + +## Замечания + +- `storage/` — рантайм-данные MinIO, в git не трекается (`.gitignore`), но присутствует локально. +- `.env` содержит реальные креды и закоммичен в репозиторий — при работе с ним не выводить значения. diff --git a/Dockerfile b/Dockerfile index 6540aee..d077306 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG RELEASE=RELEASE.2025-12-20T04-58-37Z +ARG RELEASE=RELEASE.2025-10-17T06-17-41Z.hotfix.5aa526582 FROM quay.io/minio/aistor/minio:${RELEASE} AS minio FROM python:3.13-alpine AS patcher diff --git a/replace-key.py b/replace-key.py index 5d1d38b..e21eb28 100644 --- a/replace-key.py +++ b/replace-key.py @@ -1,14 +1,5 @@ +import re import sys -import urllib.request - -def get_minio_public_key(): - f = urllib.request.urlopen("https://subnet.min.io/downloads/license-pubkey.pem") - public_key = f.read() - if public_key[0:27] != b'-----BEGIN PUBLIC KEY-----\n': - raise ValueError("wrong public key prefix") - if public_key[-26:] != b'\n-----END PUBLIC KEY-----\n': - raise ValueError("wrong public key suffix") - return public_key[0:-1] if len(sys.argv) < 4: print(f"Usage: ./{sys.argv[0]} ") @@ -21,17 +12,37 @@ new_public_key_path = sys.argv[3] with open(original_minio_binary_path, "rb") as f: minio_binary = f.read() -original_public_key = get_minio_public_key() - with open(new_public_key_path, "r") as f: new_public_key = f.read().strip().encode("ascii") -if len(new_public_key) != len(original_public_key): - raise ValueError(f"wrong new public key length: {len(new_public_key)} != {len(original_public_key)}") +# AIStor встраивает НЕСКОЛЬКО публичных ключей лицензии (secp384r1, SPKI/PEM): +# один — legacy-ключ subnet.min.io, второй используется для валидации лицензии. +# Заменять надо ВСЕ ключи совпадающей длины, иначе проверка подписи идёт по +# незаменённому ключу и падает с "could not verify message using any of the keys". +pem_re = re.compile(rb"-----BEGIN PUBLIC KEY-----\n.*?\n-----END PUBLIC KEY-----", re.DOTALL) -new_minio_binary = minio_binary.replace(original_public_key, new_public_key) +embedded_keys = pem_re.findall(minio_binary) +if not embedded_keys: + raise ValueError("no embedded PUBLIC KEY blocks found in binary") + +new_minio_binary = minio_binary +replaced = 0 +for key in set(embedded_keys): + # длина нового ключа обязана побайтово совпадать со встроенным, иначе поедут + # смещения в бинарнике; ключи другой длины/типа пропускаем. + if len(key) != len(new_public_key): + continue + new_minio_binary = new_minio_binary.replace(key, new_public_key) + replaced += 1 + +if replaced == 0: + raise ValueError( + f"no public key of matching length ({len(new_public_key)}) found to replace" + ) if new_minio_binary == minio_binary: - raise ValueError(f"could not replace old public key") + raise ValueError("could not replace old public key(s)") + +print(f"replaced {replaced} embedded public key(s)") with open(new_minio_binary_path, "wb") as f: f.write(new_minio_binary)