Об S3 (Simple Storage Service)
S3 (Simple Storage Service) — это сервис облачного хранения данных от компании Amazon. Он позволяет хранить и получать данные в любом объеме и в любое время.
Принцип работы S3 заключается в том, что данные хранятся на распределенной сети серверов, расположенных в разных дата-центрах. Это обеспечивает высокую надежность и доступность данных. Кроме того, S3 поддерживает георепликацию данных, то есть автоматическое копирование данных в несколько регионов для обеспечения их доступности в случае аварий или сбоев.
Для работы с S3 используются специальные API, которые позволяют загружать, скачивать и удалять файлы, а также управлять доступом к ним. Также S3 интегрирован с многими другими сервисами Amazon, что позволяет использовать его в комплексе с другими облачными сервисами.
Как работает S3
Сервис включает доступ к защищенному облачному хранилищу, API-интерфейс с прямым доступом к хранимым объектам через интернет и дополнительные опции, зависящие от поставщика сервиса.
Объект в S3 — это двоичные данные, которые хранятся в бакете (backet). Каждый объект имеет свой уникальный ключ, который используется для доступа к нему. Ключи объектов могут быть любой длины и содержать любые символы, кроме ‘/’, '' и ‘+’.
Кроме того объект содержит метаданные - прочие значимые атрибуты объекта (размер, тип и другие пользовательские данные для отбора и сортировки). Они нужны, чтобы находить однотипные объекты и работать с ними.
Инструменты для работы
Для работы с хранилищем S3 существует несколько инструментов: консольные клиенты (AWS CLI, S3cmd), файловые браузеры (Cyberduck, WinSCP), а так же SDK для популярных языков программирования - Java, JavaScript, Python, .NET, C++, PHP, Go.
Для 1С готовых решений нет. Однако, поскольку 1С:Предприятие 8.3 поддерживает выполнение HTTP запросов из коробки, можно реализовать основные функции с использованием механизма подписанных (pre-signed) ссылок.
С помощью подписанных ссылок произвольный пользователь Сети может выполнять с хранилищем S3 различные операции, например, скачать, загрузить или удалить объект.
Подписанная ссылка — это URL, содержащий данные для авторизации запроса в своих параметрах. Составить подписанный URL может пользователь, владеющий статическими ключами доступа. Описание создание подписанной ссылки для Amazon Simple Storage Service можно изучить по ссылке.
В качестве примера для практической реализации механизма мы будем использовать ресурсы Object Storage Яндекс.Облако.
Общий вид подписанной ссылки в Яндекс.Облако
https://storage.yandexcloud.net/<имя_бакета>/<ключ_объекта>?
X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Expires=<интервал>
&X-Amz-SignedHeaders=<список_заголовков>
&X-Amz-Signature=<подпись>
&X-Amz-Date=<время>
&X-Amz-Credential=<идентификатор_ключа_доступа>%2F<дата>%2Fru-central1%2Fs3%2Faws4_request
Создание бакета и получение ключей доступа.
Для работы вы должны предварительно авторизоваться в консоли управления и создать платежный аккаунт. При первой регистрации выдается стартовый грант, который действует в течении 60 дней и позволит использовать ресурсы без оплаты. Подробнее о начале работы в облаке можно прочитать по ссылке.
Далее выберите каталог, в котором хотите создать бакет и нажмите кнопку Создать ресурс и выберите Бакет. Укажите имя бакета, которое должно быть уникальным для всего Object Storage. Это имя используется как часть URL для доступа к данным и его будут видеть пользователи. При необходимости можно ограничить размер. Обратите внимание, что на данный момент первый гигабайт хранения не тарифицируется. В качестве класса хранилище укажите Стандартное. Подробнее о классах хранилищ можно прочитать тут.
Для для доступа к бакету и формирования подписанных ссылок следует получить статические ключи доступа. Для этого в консоли управления в верхней части экрана перейдите на вкладку Сервисные аккаунты. Выбираем аккаунт с нужными правами и жмем кнопку Создать новый ключ на верхней панели и выбираем Создать статический ключ доступа. Копируем ключи из диалогового окна в надежное место, так как после закрытия диалога значение ключей уже будет недоступно. С помощью них мы будем подписывать ссылки для доступа к хранилищу.
Реализация.
Подписанная ссылка для работы с Object Storage Яндекса должна иметь следующие параметры:
- X-Amz-Algorithm - идентифицирует версию подписи и алгоритм ее вычисления. Это константа, значение AWS4-HMAC-SHA256.
- X-Amz-Date - время в формате ISO8601, например, 20180719T094539Z. Указанная дата должна по значению (не по формату) совпадать с датой в параметре X-Amz-Credential.
- X-Amz-Expires - время в секундах, в течение которого ссылка действительна. Начало отсчета — момент, указанный в параметре X-Amz-Date. Максимальное значение — 2592000 секунд (30 дней).
- X-Amz-SignedHeaders - заголовки запроса, которые вы хотите подписать. Обязательно следует подписать заголовок
Host
и все заголовки X-Amz-*, которые используются в запросе, в том числе заголовки метаданных X-Amz-Meta-*. Другие заголовки подписывать не обязательно, однако чем больше вы подпишете заголовков, тем безопаснее будет запрос. Заголовки запроса отделяются символами ;.
- X-Amz-Credential - идентификатор для подписи. Строка формата <идентификатор_ключа_доступа>/<дата>/ru-central1/s3/aws4_request, где <дата> (в формате YYYYMMDD — год, месяц, день) должна совпадать с датой, установленной в заголовке X-Amz-Date.
- X-Amz-Signature - подпись запроса, формирование которой будет описано далее.
В первую очередь следует отметить то, что механизм подписания ссылок использует HMAC с хэширующей функцией SHA256. Данный механизм был реализован средствами 1С:Предприятие 8.3.
#Область HMAC
Функция HMACSHA256(Знач Ключ, Знач Данные) Экспорт
//Возврат нрег(ПолучитьHexСтрокуИзДвоичныхДанных(HMAC(Ключ, Данные, ХешФункция.SHA256, 64)));
Возврат HMAC(Ключ, Данные, ХешФункция.SHA256, 64);
КонецФункции
Функция Хеш(ДвоичныеДанные, Тип)
Хеширование = Новый ХешированиеДанных(Тип);
Хеширование.Добавить(ДвоичныеДанные);
Возврат Хеширование.ХешСумма;
КонецФункции
Функция HMAC(Знач Ключ, Знач Данные, Тип, РазмерБлока)
Если Ключ.Размер() > РазмерБлока Тогда
Ключ = Хеш(Ключ, Тип);
КонецЕсли;
Если Ключ.Размер() < РазмерБлока Тогда
Ключ = ПолучитьHexСтрокуИзДвоичныхДанных(Ключ);
Ключ = Лев(Ключ + ПовторитьСтроку("00", РазмерБлока), РазмерБлока * 2);
КонецЕсли;
Ключ = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки(Ключ));
ipad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("36", РазмерБлока));
opad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("5c", РазмерБлока));
ipad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
ikeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ipad);
opad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
okeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(opad);
Возврат Хеш(СклеитьДвоичныеДанные(okeypad, Хеш(СклеитьДвоичныеДанные(ikeypad, Данные), Тип)), Тип);
КонецФункции
Функция СклеитьДвоичныеДанные(ДвоичныеДанные1, ДвоичныеДанные2)
МассивДвоичныхДанных = Новый Массив;
МассивДвоичныхДанных.Добавить(ДвоичныеДанные1);
МассивДвоичныхДанных.Добавить(ДвоичныеДанные2);
Возврат СоединитьДвоичныеДанные(МассивДвоичныхДанных);
КонецФункции
Функция ПовторитьСтроку(Строка, Количество)
Части = Новый Массив(Количество);
Для к = 1 По Количество Цикл
Части.Добавить(Строка);
КонецЦикла;
Возврат СтрСоединить(Части, "");
КонецФункции
#КонецОбласти
Опишем вспомогательные функции для формирования подписанной ссылки:
Функция КодировкаURI(Строка)
Возврат КодироватьСтроку(Строка, СпособКодированияСтроки.КодировкаURL , "UTF-8");
КонецФункции
Функция ПолучитьХешСтроки(Значение)
ДД = Хеш(ПолучитьДвоичныеДанныеИзСтроки(Значение), ХешФункция.SHA256);
Возврат нрег(ПолучитьHexСтрокуИзДвоичныхДанных(ДД));
КонецФункции
Функция Подписать(Ключ, Значение)
Возврат HMACSHA256(Ключ, ПолучитьДвоичныеДанныеИзСтроки(Значение) )
КонецФункции
Функция ПолучитьСигнатуру(Ключ, ДатаСтр, Регион, Сервис)
КлючДата = Подписать(ПолучитьДвоичныеДанныеИзСтроки("AWS4" + Ключ), ДатаСтр);
КлючРегион = Подписать(КлючДата, Регион);
КлючСервис = Подписать(КлючРегион, Сервис);
Сигнатура = Подписать(КлючСервис, "aws4_request");
Возврат Сигнатура
КонецФункции
//Протокол требует сортировку заголовков по алфавиту
Функция СортироватьМассив(Массив)
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Массив);
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
Возврат Список.ВыгрузитьЗначения();
КонецФункции
И собственно сама функция.
СоздатьСсылку(КлючДоступа, СекретныйКлюч, Бакет, Путь, Метод="GET", ВремяЖизни=300, Заголовки=Неопределено)
Функция СоздатьСсылку(КлючДоступа, СекретныйКлюч, Бакет, Путь, Метод="GET", ВремяЖизни = 300, Заголовки=Неопределено) Экспорт
ПутьКРесурсу = "/"+Бакет+"/"+Путь;
Сервис = "s3";
Регион = "ru-central1";
Хост = "storage.yandexcloud.net";
Время = ТекущаяУниверсальнаяДата();
СтрДата = Формат(Время,"ДФ=yyyyMMddTHHmmssZ");
ОтметкаДаты = Формат(Время,"ДФ=yyyyMMdd");
МассивКаноническихЗаголовков = Новый Массив;
МассивКаноническихЗаголовков.Добавить("host:"+Хост);
МассивПодписанныхЗаголовков = Новый Массив;
МассивПодписанныхЗаголовков.Добавить("host");
Если Заголовки<>Неопределено Тогда
Для каждого КлючИЗначение из Заголовки Цикл
КлючЗаголовка = нрег(КлючИЗначение.Ключ);
МассивКаноническихЗаголовков.Добавить(КлючЗаголовка+":"+КодировкаURI(КлючИЗначение.Значение));
МассивПодписанныхЗаголовков.Добавить(КлючЗаголовка);
КонецЦикла
КонецЕсли;
КаноническиеЗаголовки = СтрСоединить(СортироватьМассив(МассивКаноническихЗаголовков), Символы.ПС)+Символы.ПС;
ПодписанныеЗаголовки = СтрСоединить(СортироватьМассив(МассивПодписанныхЗаголовков), ";");
Алгоритм = "AWS4-HMAC-SHA256";
КонтекстУдостоверения = ОтметкаДаты + "/" + Регион + "/" + Сервис + "/" + "aws4_request";
Поля = Новый Массив;
Поля.Добавить("X-Amz-Algorithm=" + Алгоритм);
Поля.Добавить("X-Amz-Credential=" + КодировкаURI(КлючДоступа+"/"+КонтекстУдостоверения));
Поля.Добавить("X-Amz-Date=" + СтрДата);
Поля.Добавить("X-Amz-Expires="+ВремяЖизни);
Поля.Добавить("X-Amz-SignedHeaders=" + КодировкаURI(ПодписанныеЗаголовки));
ПараметрыЗапроса = СтрСоединить(Поля, "&");
КаноническийЗапрос = Метод + Символы.ПС +
ПутьКРесурсу + Символы.ПС +
ПараметрыЗапроса + Символы.ПС +
КаноническиеЗаголовки + Символы.ПС +
ПодписанныеЗаголовки + Символы.ПС +
"UNSIGNED-PAYLOAD";
СтрокаДляПодписи = Алгоритм + Символы.ПС +
СтрДата + Символы.ПС +
КонтекстУдостоверения + Символы.ПС +
ПолучитьХешСтроки(КаноническийЗапрос);
КлючДляПодписи = ПолучитьСигнатуру(СекретныйКлюч, ОтметкаДаты, Регион, Сервис);
Сигнатура = HMACSHA256(КлючДляПодписи, ПолучитьДвоичныеДанныеИзСтроки(СтрокаДляПодписи));
Поля.Добавить("X-Amz-Signature=" + ПолучитьHexСтрокуИзДвоичныхДанных(Сигнатура));
Адрес = "https://"+Хост + ПутьКРесурсу + "?" + СтрСоединить(Поля, "&");
возврат Адрес
КонецФункции
В качестве параметров принимает следующие аргументы:
- КлючДоступа, СекретныйКлюч - ключи, полученные в консоли управления Яндекс.Облако
- Бакет - имя бакета, который Вы создали в консоли.
- Метод:
- GET - получение данных из хранилища
- PUT - отправка данных в хранилище
- ВремяЖизни - время жизни ссылки, максимальное значение — 2592000 секунд (30 дней).
- Заголовки - заголовки метаданных. Ключи всегда должны начинаться на X-Amz-Meta-. Например X-Amz-Meta-Author, X-Amz-Meta-KratkoeOpisanie.
По описанию на Amazon, стандарт позволяет производить загрузку данных частями. Т.е. можно обрабатывать большие файлы частями - последовательно или параллельно. Однако у Яндекса я не нашел описания в документации, поэтому не пробовал это реализовать. А с иностранными облаками сейчас проблемы.
Примеры использования
Функция ЗагрузитьДанные(Бакет, Путь)
Заголовки = Новый Соответствие;
Заголовки.Вставить("X-Amz-Meta-Header-1", "this_is_header_1");
Заголовки.Вставить("X-Amz-Meta-Header-2", "this_is_header_1");
адрес = РеквизитФормыВЗначение("Объект").СоздатьСсылку(access_key, secret_key, Бакет, Путь, "PUT", 60, Заголовки);
СтруктураURI=ОбщегоНазначенияКлиентСервер.СтруктураURI(адрес);
//Прокси = ПолучениеФайловИзИнтернета.ПолучитьПрокси("https");
Прокси = Неопределено;
ЗащищенноеСоединение = ?(НРег(СтруктураURI.Схема) = "http", Неопределено, ОбщегоНазначенияКлиентСервер.НовоеЗащищенноеСоединение());
Соединение = Новый HTTPСоединение(СтруктураURI.Хост, , , , Прокси, 60, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере, Заголовки);
Запрос.УстановитьТелоИзСтроки("Hello, world!");
Ответ = Соединение.Записать(Запрос);
Сообщить(Ответ.КодСостояния);
КонецФункции
Функция Скачать(Бакет, Путь)
адрес = СоздатьСсылку(access_key, secret_key, Бакет, Путь);
СтруктураURI=ОбщегоНазначенияКлиентСервер.СтруктураURI(адрес);
//Прокси = ПолучениеФайловИзИнтернета.ПолучитьПрокси("https");
Прокси = Неопределено;
ЗащищенноеСоединение = ?(НРег(СтруктураURI.Схема) = "http", Неопределено, ОбщегоНазначенияКлиентСервер.НовоеЗащищенноеСоединение());
Соединение = Новый HTTPСоединение(СтруктураURI.Хост, , , , Прокси, 60, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере);
Ответ = Соединение.Получить(Запрос);
Сообщить(Ответ.КодСостояния);
Сообщить(Ответ.ПолучитьТелоКакСтроку());
КонецФункции
Выводы
Объектное хранилище S3 отличный инструмент для надежного хранения данных. Сферы его использования варьируются от озер данных до структурированных файловых хранилищ.
Описанный вариант интеграции с 1С можно использовать, например, для отправки данных в облачные СУБД (YDB, ClickHouse) для последующего доступа к ним из BI-систем (Datalens).
Или просто для хранения резервных копий информационных баз.