1.2. Безопасная среда исполнения | Tdg
1. Руководство по разработке приложений 1.2. Безопасная среда исполнения

1.2. Безопасная среда исполнения

Пользовательский код в TDG исполняется отдельно от собственного кода TDG — в безопасной изолированной среде, называемой песочницей (или sandbox). При этом используется JIT-компилятор LuaJIT.

Для доступа к данным, хранящимся в TDG, используйте функции программного интерфейса репозитория. Все остальные функции и возможности, которые можно использовать при написании пользовательского кода обработки в песочнице, описаны в данном разделе.

Все функции можно разделить на следующие категории:

Также имеется возможность подключить к безопасной среде исполнения новые произвольные функции. Как это сделать, описано в разделе Подключение новых функций.

Важно

Подключать новые функции к безопасной среде исполнения категорически запрещено для сертифицированной версии TDG. Перед подключением новых функций в обычной версии TDG рекомендуется самостоятельно проверять их на уязвимости.

1.2.1. Стандартные модули и функции

Общедоступные Lua-модули и функции:

  • assert — вызывает исключение, если первый аргумент является nil или false;

  • math — интерфейс стандартной математической библиотеки C;

  • next — возвращает следующий элемент таблицы по индексу;

  • pairs — позволяет выполнять итерации по парам ключ-значение (key-value) таблицы;

  • pcall — защищенный вызов с заданными аргументами (позволяет обработать исключения);

  • print — простой вывод в stdout;

  • select — если первый аргумент функции имеет числовое значение, возвращаются все аргументы, следующие за аргументом с этим номером. Если первый аргумент — строка ’#’, возвращается общее число полученных аргументов;

  • string — модуль для работы со строками;

  • table — модуль для работы с таблицами. Расширен двумя собственными функциями (см. подробнее);

  • tonumber — преобразование в число;

  • tostring — преобразование в строку;

  • type — возвращает тип переданного аргумента в виде строки;

  • unpack — возвращает элементы таблицы;

  • xpcall — подобна pcall, но устанавливает новый обработчик ошибок;

  • error — вызов исключения.

Модули Tarantool:

  • ipairs — подобно pairs, но использует числовые ключи;

  • fun — модуль Luafun для Tarantool;

  • uuid — модуль для работы с UUID (Universally unique identifier);

  • digest — модуль кодирования и хэширования;

  • utf8 — модуль для работы со строками в кодировке UTF-8;

  • decimal — модуль для точных вычислений с числами.

А также отдельные функции из модулей Tarantool:

  • 2 функции из модуля json>:

    • json.encode — конвертация Lua-объекта в JSON-строку;

    • json.decode — конвертация JSO-строки в Lua-объект.

  • 1 функция из модуля yaml:

    • yaml.encode — конвертация Lua-объекта в YAML-строку.

  • 1 функция из модуля fiber:

    • fiber.sleep — ожидание с передачей управления планировщику.

  • 1 функция из модуля box:

    • box.NULL — нулевой указатель (NULL pointer).

  • 3 функции из модуля metrics:

    • metrics.counter — монотонно возрастающий счетчик;

    • metrics.gauge — метрика для числовых значений;

    • metrics.histogram — метрика для оценки интенсивности потока во времени.

Также доступны нестандартные функции собственной разработки, описание которых приведено далее.

1.2.2. Функции доступа к данным

Основные функции для доступа к данным в TDG входят в программный интерфейс репозитория. Кроме них, также доступны:

  • model_accessor.find(type_name, filter, options, context) — функция, возвращающая объекты, соответствующие заданным условиям. По умолчанию возвращаются первые 10 результатов. Пагинация осуществляется аналогично операциям программного интерфейса репозитория. Не рекомендуется к использованию — используйте более высокоуровневую функцию find из программного интерфейса репозитория;

  • shared_storage.new(namespace) — создаёт новое общее хранилище;

  • connector.send(output_name, obj, output_options) — направляет объект в секцию output для отправки в смежную систему;

  • 2 функции ODBC:

    • odbc.execute(connection_name, statement, params) — выполнение запроса через ODBC;

    • odbc.prepare(connection_name, query) — подготовка запроса через ODBC. Возвращает объект подготовленного запроса.

1.2.3. Функции управления обработкой

  • commands — позволяет выполнить произвольные команды, в том числе модифицируя объект или порядок его обработки. Для этого достаточно вернуть из клиентского кода не объект, а соответствующую команду или команды. В случае, если команд несколько, необходимо вернуть таблицу, содержащую все команды. Всего возможны четыре варианта команд:

    • commands.make_delete(routing_key, filter, options) — удаляет объекты, соответствующие условиям фильтра. См. Синтаксис поля filter;

    • commands.make_update(routing_key, filter, updaters, options) — обновляет данные объектов, соответствующих условиям. См. синтаксис поля updaters в разделе про Запрос на обновление данных;

    • commands.make_insert(obj) — указанный объект возвращается в конвейер обработки данных (pipeline). Передав несколько команд, можно вернуть несколько объектов;

  • request_context.get — возвращает контекст запроса.

  • this_storage.snapshot — аналог функции box.snapshot.

  • get_function — возвращает ссылку на функцию по её имени, если она доступна в песочнице.

  • spawn(pipeline, func_name, args, options) — запускает один или несколько файберов для выполнения функции func_name. Количество запускаемых файберов определяется количеством args;

  • spawn_n(pipeline, func_name, func_num, options) — запускает func_num количество файберов для выполнения функции func_name без аргументов.

1.2.4. Функции преобразования данных

  • lom.get_by_path (lom, path) — функция возвращает объект из Lua-таблицы в нотации Lua Object Model. Выполняется разделение path на отдельные части с разделителем в виде точки. Затем в переданном lom выполняется поиск объекта с полем tag, равным первой части path. Далее операция поиска повторяется для каждой части path в результатах поиска предыдущего этапа.

    • lom — таблица в нотации Lua Object Model;

    • path — путь до объекта внутри lom с разделителем в виде точки .;

  • 3 функции для работы со значениями внутри объектов:

    • mapping_tools.get_by_path(obj, path, delimeter) — получение значения из объекта по пути;

    • mapping_tools.set_by_path(obj, path, value, delimeter) — задание значения в объекте по пути;

    • mapping_tools.just_get_and_set(source_object, source_path, target_object, target_path) — получить значение из одного объекта и записать в другой;

  • 2 функции, расширяющие модуль table:

    • table.cmpdeeply(got, expected) — глубокое сравнение двух таблиц. Возвращает результат сравнения, истину (true) или ложь (false);

    • table.append_table(where, from) — добавление одной таблицы к другой. При этом используется неглубокое (shallow) копирование.

  • 2 функции модуля soap, который позволяет преобразовывать SOAP-запрос в формате XML в объекты Lua и обратно:

    • soap.decode(doc) — получает на вход строку, содержащую XML-документ. Возвращает объекты Lua, полученные в результате парсинга XML:

      • строка, содержащая указатель на пространство имен (namespace);

      • строка с именем метода, переданного в SOAP-запросе (method);

      • Lua-таблица, которая содержит значения, переданные в тэгах SOAP-запроса (entries);

    • soap.encode(data) — получает на вход Lua-таблицу. Возвращает строку, содержащую XML-документ.

1.2.5. Функции логирования

  • Функции записи сообщений в журнал (по аналогии с модулем Tarantool log):

    • log.error('Error text');

    • log.warn('Warning text');

    • log.info('Information text');

    • log.verbose('Verbose level text');

    • log.debug('Debug level text').

  • Функция трассировки:

    • tracing.start_span — начинает span (основной блок трассировки в распределенных системах) и возвращает специальный объект. Для завершения трассировки выполнения функции используется метод finish возвращаемого объекта. Также у этого объекта имеется метод set_error, регистрирующий ошибку.

1.2.6. Функции работы с датами и временем

  • datetime — функции для работы со временем:

    • now — возвращает текущее время по Гринвичу (GMT) в наносеундах;

    • sec_to_iso_8601_date — преобразует число секунд в строку формата yyyy-MM-dd;

    • nsec_to_iso_8601_datetime — преобразует число наносекунд в строку формата yyyy-MM-ddTHH:mm:ss.SSSZ;

    • nsec_to_iso_8601_date — преобразует число наносекунд в строку формата yyyy-MM-dd;

    • iso_8601_datetime_to_nsec — преобразует строку даты и времени в число наносекунд;

      • Здесь строка даты и времени принимается в одном из следующих форматов:

        • yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ;

        • yyyy-MM-dd'T'HH:mm:ssZZZZZ;

        • yyyy-MM-dd'T'HH:mm:ss.SSS;

        • yyyy-MM-dd'T'HH:mm:ss;

        • yyyy-MM-dd'T'HH-mm-ss;

        • yyyy-MM-dd'T'HHmmss.SZZZZZ;

        • yyyy-MM-dd'T'HHmmssZZZZZ;

        • yyyy-MM-dd'T'HHmmss.SSS;

        • yyyy-MM-dd'T'HHmmss;

    • iso_8601_date_to_nsec — преобразует строку даты формата yyyy-MM-dd в число наносекунд;

    • millisec_to_formatted_datetime — преобразует милисекунды в заданную шаблоном строку даты и времени;

    • custom_datetime_str_to_nsec — преобразует заданную шаблоном строку даты или даты и времени в наносекунды;

    • to_sec — преобразует наносекунды в секунды и преобразует к типу number;

    • to_millisec — преобразует наносекунды в милисекунды и преобразует к типу number;

    • seconds_since_midnight — возвращает количество секунд с начала суток по Гринвичу (GMT);

    • Набор констант, используемых для работы со временем:

      • NSEC_IN_SEC — число наносекунд в секунде;

      • NSEC_IN_MILLISEC — число наносекунд в милисекунде;

      • NSEC_IN_DAY — число наносекунд в сутках;

  • timezone — функции для работы с часовыми поясами:

    • now(timezone_id) — возвращает текущее местное время в наносекундах для указанного часового пояса;

    • seconds_since_midnight(timezone_id) — возвращает число секунд с начала текущих суток для указанного часового пояса;

    • curr_date_nsec(timezone_id) — возвращает время (дату) начала текущих местных суток в указанном часовом поясе в наносекундах.

1.2.7. Функции работы с последовательностями

  • sequence — генератор уникальных упорядоченных целых чисел. Уникальность чисел гарантируется в пределах отдельной последовательности с заданным именем, даже при вызове из разных файберов или на разных экземплярах. Для обеспечения уникальности при вызовах из разных экземпляров используется роль sequence_generator, которая выделяет доступные диапазоны чисел. При первом обращении или при исчерпании выданного ранее диапазона происходит выделение нового незанятого ранее диапазона уникальных чисел.

    • get(sequence_name) — возвращает ссылку на объект последовательности (если последовательность с таким именем отсутствует, то создаёт новую). У объекта есть единственный метод — next;

      • next — возвращает следующий элемент последовательности.

Примечание

По умолчанию файберам или экземплярам выделяются диапазоны по 10 номеров. Пример использования: если на двух разных экземплярах вызвать 1 раз пайплайн, который заполняет поле объекта уникальным номером с помощью метода next, то номера у объектов будут 1 и 11 соответственно. Если вызвать еще по 9 раз пайплайны, то номера объектов будут 2-10 и 12-20 на соответствующих экземплярах. Дальше, если запустить пайплайн еще раз на первом экземпляре, то будет получен номер 21. Если мы продолжим вызывать пайплайн на одном из экземпляров, то номера будут идти по порядку (22, 23, …).

1.2.8. Подключение новых функций

Важно

Подключать новые функции к безопасной среде исполнения категорически запрещено для сертифицированной версии TDG. Перед подключением новых функций в обычной версии TDG рекомендуется самостоятельно проверять их на уязвимости.

Для подключения к безопасной среде исполнения (песочнице) новых функций необходимо выполнить следующие действия:

1.2.8.1. Создание модуля с подключаемыми функциями

Новые подключаемые функции необходимо разместить в специальном файле модуля. Данный файл должен иметь специальный формат, продемонстрированный в примере далее.

local exported_function = nil

local check_custom_function = os.getenv('CUSTOM_FUNCTION')
if check_custom_function ~= nil and check_custom_function ~= "" then
    local function custom_function()
        local magic_constant = 42
        return magic_constant
    end
    exported_function = custom_function
end

return {
    name = 'new_awesome_function',
    exports = exported_function
}

В примере продемонстрированы следующие возможности:

  • реализация произвольной логики проверок перед подключением новой функции;

  • задание логики функции непосредственно в файле модуля;

  • задание произвольного имени для вызова функции внутри песочницы (new_awesome_function).

Из файла должен возвращаться результат в виде имени и кода новой функции.

Имя новой функции, подключаемой к безопасной среде исполнения, указывается в виде строки name.

Код новой функции приводится в поле exports в одном из следующих форматов:

  • Ссылка на функцию, описанную в этом же файле (как в примере выше);

  • Ссылка на подключенный при помощи директивы require() модуль Tarantool.

Примечание

Код модуля с подключаемыми функциями выполняется за пределами песочницы. Поэтому в коде модуля нельзя воспользоваться функциями программного интерфейса репозитория и функциями, доступными из песочницы (описаны в данной главе выше).

1.2.8.2. Размещение файла модуля в файловой структуре

Возможны два варианта размещения файла модуля с подключаемыми функциями. Оба варианта равноценны с функциональной точки зрения.

Для того, чтобы новая функция была доступна в песочнице, необходимо разместить файл модуля подключаемой функции в специальной директории. Возможно, эту директорию потребуется создать, если она не существовала ранее. Также нужно проследить, чтобы у пользователей были необходимые права для чтения данной директории.

Файл размещается по адресу ../extensions/sandbox, где .. — это директория, в которой размещена директория с файлами TDG, обозначенная на следующей схеме как tdg.

.
├── tdg
├── extensions
    └── sandbox
        └── custom_function.lua    <-- файл модуля с подключаемыми функциями

Альтернативно, файл модуля можно поместить в архив .zip, посредством которого загружается конфигурация TDG. В архиве файл модуля необходимо разместить по такому же пути — ../extensions/sandbox.

1.2.8.3. Применение конфигурации

После создания файла модуля с новой функцией для безопасной среды исполнения и размещения данного файла в корректной директории, новая функция еще не будет доступна. Считывание файлов из данной директории выполняется на этапе применения конфигурации экземпляра.

Поэтому для добавления функции понадобится вызвать процесс применения конфигурации. Это можно сделать разными способами, например:

Примечание

При применении к кластеру TDG новой конфигурации она проходит валидацию.

Попытка применения той же самой конфигурации, что уже используется, закончится неудачей.

При этом, если конфигурация является валидной (корректной), то в ответ на запрос не будет выведено ошибок. Сообщение о том, что конфигурация совпадает с имеющейся и не будет применена, будет доступно только в системном журнале (на вкладке logger данное сообщение будет отсутствовать).

Found what you were looking for?
Feedback