Агент MySQL — это независимый сетевой прокси: клиенты подключаются к нему вместо сервера, а он проверяет поддерживаемые SQL-операторы по подписанным политикам сервиса данных mysql до их передачи MySQL. Установка внутрь сервера MySQL не требуется. Дистрибутив содержит исполняемый JAR agents/mysql/grantforge-agent-mysql-<version>.jar, для которого нужна Java 17 или новее.
Интеграционные тесты Testcontainers в репозитории рассчитаны на mysql:8.0 и mysql:8.4 с клиентами MariaDB Connector/J и MySQL Connector/J. Это не подтверждает совместимость с другими версиями серверов или всеми возможностями протокола MySQL. Требования к версии и подключению сервисного плагина приведены в разделе MySQL.
Принцип работы
- Вход передаётся серверу. Приветствие сервера поступает клиенту, а ответы клиента — серверу, поэтому пароль проверяет сам MySQL (
caching_sha2_passwordиmysql_native_password); прокси узнаёт имя принятого пользователя. В политиках указывается это имя без части с хостом. - Проверяются ресурсы SQL. Прокси разбирает COM_QUERY и подготовленные операторы на стороне сервера и проверяет типы доступа, необходимые для их баз данных, таблиц и столбцов. Отклонённый оператор не попадает в MySQL: прокси формирует ошибку с совместимым с MySQL кодом, например 1142 или 1143.
- Чтение маскируется и фильтруется. Если для пользователя задано маскирование столбцов или фильтр строк читаемой таблицы, прокси заменяет её производной таблицей с тем же именем. Она показывает замаскированные значения и только строки, прошедшие фильтр, поэтому соединения, условия и функции работают с доступным пользователю представлением данных.
- События авторизации ресурсов отправляются в GrantForge вместе с пользователем, адресом клиента, ресурсом, типом доступа, решением и SQL-оператором. Область аудита описана ниже.
- Результаты выполнения MySQL передаются клиенту; прокси не меняет исходные значения, сохранённые в базе данных.
Развёртывание
-
Добавьте сервис данных
mysqlи настройте его политики. Откройте Права доступа к данным → Агенты, выберите сервис и выпустите токен агента. Он показывается только один раз; сохраните его в файл, доступный для чтения только учётной записи прокси. -
Создайте пользователя метаданных для прокси. Он читает столбцы таблиц и хранимые функции из
information_schema, не читая сами данные:REFERENCESделает видимыми все столбцы, аSHOW_ROUTINE— все хранимые функции. Выдайте права непосредственно пользователю, не через роль; безSHOW_ROUTINEпрокси не запустится.CREATE USER 'grantforge_meta'@'proxy-host' IDENTIFIED BY '…'; GRANT REFERENCES, SHOW_ROUTINE ON *.* TO 'grantforge_meta'@'proxy-host'; -
Создайте файл настроек, заменив адреса, пути и имена в примере:
grantforge.server.url=https://grantforge.example.com grantforge.token.file=/etc/grantforge/mysql-proxy.token grantforge.instance=mysql-proxy-1 grantforge.cache.dir=/var/lib/grantforge/mysql-proxy grantforge.proxy.host=0.0.0.0 grantforge.proxy.port=3307 grantforge.proxy.tls.cert=/etc/grantforge/mysql-proxy.crt grantforge.proxy.tls.key=/etc/grantforge/mysql-proxy.key grantforge.proxy.tls.required=true grantforge.mysql.host=mysql.internal grantforge.mysql.port=3306 grantforge.mysql.tls=verify-identity grantforge.mysql.tls.ca=/etc/grantforge/mysql-ca.pem grantforge.mysql.metadata.user=grantforge_meta grantforge.mysql.metadata.password.file=/etc/grantforge/mysql-meta.passwordЭтот пример включает TLS между клиентами и прокси, а также между прокси и MySQL.
mysql-proxy.crtдолжен соответствовать имени хоста прокси, используемому клиентами; сертификат MySQL должен соответствоватьmysql.internal. Пути относятся к машине прокси, а каталог кеша должен быть доступен для записи. Файлы паролей читаются как UTF-8 с удалением пробелов в начале и конце, поэтому пароли, которым нужны такие пробелы, нельзя хранить этим способом. -
Запустите прокси, например как службу systemd:
java -jar agents/mysql/grantforge-agent-mysql-<version>.jar /etc/grantforge/mysql-proxy.propertiesЗамените
<version>версией дистрибутива. Параметрыjava -Dgrantforge.…=… -jar …имеют приоритет над файлом. При SIGTERM прокси корректно останавливается и отправляет или сохраняет ожидающие события на диск. -
Направьте клиентов на адрес и порт прокси. MySQL теперь видит адрес прокси, поэтому часть хоста в учётных записях должна разрешать его.
-
Закройте обходной путь: клиенты, которые могут подключаться напрямую к MySQL, не проверяются. Доступ к порту сервера должен быть только у прокси и администраторов.
| Параметр | Назначение |
|---|---|
grantforge.server.url, grantforge.token.file, grantforge.instance, grantforge.cache.dir |
Обязательные: сервер GrantForge, файл токена агента, имя этого прокси и доступный для записи каталог подписанного снимка и отложенных событий |
grantforge.signing.key.file |
Необязательный файл закреплённого ключа подписи снимков; скопируйте открытый ключ Ed25519 в формате X.509 с кодированием Base64 со страницы агентов. Без этой настройки ключ загружается с сервера при первом обращении |
grantforge.proxy.host, grantforge.proxy.port |
Адрес прослушивания; по умолчанию все адреса и порт 3307. Диапазон портов 0..65535; 0 назначает порт автоматически |
grantforge.proxy.tls.cert, grantforge.proxy.tls.key |
Сертификат и закрытый ключ PEM; для TLS клиентам необходимо задать оба |
grantforge.proxy.tls.required |
По умолчанию false; true отклоняет клиентов без TLS (ошибка 3159) и требует сертификат с закрытым ключом |
grantforge.mysql.host, grantforge.mysql.port |
Хост MySQL обязателен; порт по умолчанию 3306, диапазон 1..65535 |
grantforge.mysql.tls |
TLS к MySQL: disabled (по умолчанию), required (шифрование без проверки), verify-ca или verify-identity |
grantforge.mysql.tls.ca |
CA в формате PEM для проверки MySQL; если не задан, используются сертификаты, которым доверяет Java |
grantforge.mysql.metadata.user, grantforge.mysql.metadata.password.file |
Пользователь метаданных обязателен; файл пароля необязателен, без него пароль драйверу не передаётся |
grantforge.mysql.metadata.cache.seconds |
Срок хранения кеша столбцов и хранимых функций: по умолчанию 30 секунд, диапазон 0..86400; 0 отключает повторное использование кеша |
grantforge.mysql.connect.timeout.ms |
Тайм-аут соединения с MySQL: по умолчанию 10000 мс, диапазон 1..600000; тайм-аут чтения метаданных фиксирован и равен 30000 мс |
grantforge.proxy.check.threads |
Потоки проверки операторов: по умолчанию 8, диапазон 1..256 |
grantforge.connect.timeout.ms, grantforge.read.timeout.ms |
Тайм-ауты соединения и чтения GrantForge: по умолчанию 5000 / 8000 мс, диапазон 1..2147483647 |
grantforge.refresh.interval.ms |
Начальный или резервный интервал обновления при недоступности сервера: по умолчанию 30000 мс, минимум 1000; обычные heartbeat используют интервал, возвращённый сервером |
grantforge.audit.batch.size, grantforge.audit.queue.capacity |
По умолчанию пакет 500, очередь 10000 событий; диапазон пакета 1..1000, очередь не меньше пакета и не больше 1000000 |
grantforge.audit.flush.interval.ms |
Интервал отправки аудита: по умолчанию 5000 мс, диапазон 1..2147483647 |
grantforge.audit.spool.limit.bytes |
Предел дискового буфера аудита: по умолчанию 67108864 (64 MiB), минимум 0 |
TLS с двух сторон независим. Если TLS используется только с одной стороны, а MySQL запрашивает полный пароль (caching_sha2_password при отсутствии записи в кеше), прокси передаёт его между сторонами: на запрос открытого ключа сервера от незашифрованного клиента он отвечает собственным RSA-ключом, а пароль TLS-клиента шифрует ключом MySQL для незашифрованного сервера. При подключении через прокси не настраивайте у клиента файл открытого ключа MySQL.
Права, необходимые операторам
| Оператор | Доступ |
|---|---|
SELECT, подзапросы, WITH, EXPLAIN, SET @x = (SELECT …) |
select на каждый используемый столбец; чтение таблицы без чтения столбцов, например COUNT(*) или EXISTS, требует select на всю таблицу |
INSERT, LOAD DATA |
insert на записываемые столбцы (на все, если список не указан); ON DUPLICATE KEY UPDATE дополнительно требует update |
REPLACE |
insert на столбцы и delete на таблицу |
UPDATE |
update на изменяемые столбцы и select на читаемые |
DELETE |
delete на таблицу и select на читаемые столбцы |
CREATE TABLE, CREATE VIEW / DROP TABLE, DROP VIEW, TRUNCATE / ALTER TABLE, ALTER VIEW, OPTIMIZE |
create / drop / alter на таблицу или представление |
RENAME TABLE |
alter и drop на старое имя, create и insert на новое |
CREATE INDEX, DROP INDEX |
index на таблицу |
CREATE DATABASE, DROP DATABASE |
create, drop на базу данных |
LOCK TABLES, ANALYZE, CHECK, CHECKSUM TABLE |
select на таблицу |
Операторы без обращения к данным передаются штатной системе прав MySQL: транзакции, SET, USE, SHOW, DESCRIBE, KILL, а также управление учётными записями, например GRANT и CREATE USER. information_schema и performance_schema требуют политик так же, как другие базы данных.
Прокси отклоняет то, что не может проверить полностью: исполняемые комментарии (/*! … */), хранимые функции и CALL, CREATE PROCEDURE, FUNCTION, TRIGGER и EVENT, SQL-операторы PREPARE/EXECUTE, HANDLER, команды репликации и клонирования, USE или SET NAMES вместе с другими операторами в одном пакете, а также операторы, которые не удаётся разобрать.
Маскирование, фильтры строк и ограничения
- Маски и фильтры применяются при чтении таблицы, в том числе в подзапросах, соединениях,
INSERT … SELECTиCREATE TABLE … SELECT(копия содержит уже замаскированные значения). UPDATEилиDELETEдля одной таблицы меняет только строки, видимые через фильтр. МноготабличныеUPDATEилиDELETEдля таблицы с фильтром отклоняются, как и запись, использующая маскируемый столбец изменяемой таблицы, поскольку это могло бы скопировать или раскрыть скрытые значения.- Нельзя создать или изменить представление над таблицей с маскированием или фильтром строк: впоследствии MySQL выполняет представление без участия прокси.
- Подготовленные операторы повторно используют проверку и переписывание, пока версия политик не меняется. При смене версии выполняется повторная проверка: если доступ по-прежнему разрешён и переписанный текст не изменился, выполнение продолжается; иначе ошибка 1615 требует подготовить оператор заново.
- Столбец без имени таблицы учитывается для каждой таблицы в его области видимости, где есть одноимённый столбец. Указывайте таблицу, чтобы ограниченный столбец с тем же именем не вызвал отказ.
- Столбцы, читаемые через
*, берутся из видимого пользователю метаданныхinformation_schemaи кешируются наgrantforge.mysql.metadata.cache.seconds. Успешный DDL, отправленный через прокси обычной SQL-командой, делает кеш недействительным, поэтому следующий поиск читает данные заново. Внешний или подготовленный DDL не гарантирует сброс кеша при выполнении; дождитесь истечения срока кеша или перезапустите прокси.
Область применения
Прокси проверяет ресурсы, которые может разобрать в SQL клиента. Он не разворачивает рекурсивно существующие представления, триггеры и другие операции, выполняемые внутри сервера. Для защиты базовых таблиц также ограничьте штатные права MySQL и использование существующих представлений и триггеров. Не выдавайте бизнес-пользователям права администратора в расчёте на перехват административных операторов прокси.
Фильтр строк не является общим ограничением записи: INSERT, REPLACE и LOAD DATA проверяют права записи, но не требуют автоматически, чтобы новые строки удовлетворяли выражению фильтра. В ветку обновления существующей строки INSERT … ON DUPLICATE KEY UPDATE также не добавляется условие фильтра. Сжатие протокола, ZSTD, атрибуты запросов и необязательные метаданные результата отключаются при согласовании соединения.
Политики и сбои
- Без пригодного снимка или с токеном, привязанным к сервису не типа
mysql, все операторы, обращающиеся к данным, отклоняются. - При временной недоступности GrantForge или неверной подписи нового снимка прокси сохраняет уже применённый снимок. Первый запуск без пригодного снимка отклоняет доступ к ресурсам.
- Отключение сервиса пропускает авторизацию ресурсов GrantForge, маскирование и фильтрацию строк; разбор SQL и ограничения протокола сохраняются. Отключение сервиса не блокирует доступ к данным.
- После проверок прокси по-прежнему действуют права MySQL; прокси может только сузить их.
- Прокси отслеживает
COM_CHANGE_USER, но отклоняет его, если TLS на стороне клиента и сервера различается; вместо этого подключитесь заново под другим пользователем.
Пример проверки
Попросите администратора базы данных создать следующие данные и учётную запись в отдельной тестовой базе. Замените proxy-host адресом источника прокси, который фактически видит MySQL:
CREATE DATABASE grantforge_demo;
CREATE TABLE grantforge_demo.orders (id INT PRIMARY KEY, region VARCHAR(8), card VARCHAR(32));
INSERT INTO grantforge_demo.orders VALUES (1, 'EU', '4111111111111111'), (2, 'US', '5555555555554444');
CREATE USER 'alice'@'proxy-host' IDENTIFIED BY 'replace-with-a-password';
GRANT SELECT, UPDATE ON grantforge_demo.* TO 'alice'@'proxy-host';Создайте три политики GrantForge для alice: разрешите select для database=grantforge_demo, table=orders, column=*; маскируйте card типом mask_show_last_4; задайте фильтр таблицы region = 'EU'. Сервис и все три политики должны быть включены. Дождитесь, пока раздел Права доступа к данным → Агенты покажет применение последней версии политик.
Подключитесь к прокси командным клиентом MySQL. -p запрашивает пароль; файл CA должен обеспечивать доверие сертификату прокси:
mysql --host=mysql-proxy.example.com --port=3307 --user=alice -p \
--ssl-mode=VERIFY_IDENTITY --ssl-ca=/path/to/proxy-ca.pem grantforge_demoSELECT id, region, card FROM orders ORDER BY id;
-- Только одна строка: 1 | EU | xxxxxxxxxxxx1111
UPDATE orders SET region = 'US' WHERE id = 1;
-- 1143 / 42000: GrantForge не разрешает updateВ этом примере штатные права MySQL уже разрешают UPDATE, поэтому отказ подтверждает применение политики прокси. Затем измените фильтр на region = 'US', дождитесь применения новой версии и повторите запрос. Должна появиться только строка 2 с картой xxxxxxxxxxxx4444.
Устранение неполадок и аудит
| Код ошибки / SQLSTATE | Значение и проверки |
|---|---|
1044 / 42000, 1142 / 42000, 1143 / 42000 |
Доступ к базе данных, таблице или столбцу запрещён. Если сообщение содержит by GrantForge, проверьте пользователя политики, ресурсы, тип доступа и версию снимка; при других отказах также проверьте штатные права MySQL |
1046 / 3D000 |
База данных не выбрана; используйте USE, базу по умолчанию в соединении или полное имя таблицы |
1227 / 42000 |
Операция запрещена или не может быть безопасно проверена; по сообщению проверьте ограничения операторов и права на метаданные |
1064 / 42000 |
Не удаётся разобрать SQL |
1615 / HY000 |
После изменения политик подготовьте оператор заново |
3159 / HY000 |
Прокси требует TLS, но клиент подключился без него |
1043 / 08S01 |
Ошибка согласования протокола или TLS с сервером; проверьте режим TLS, сертификаты, имя хоста и возможности сервера |
2003 / HY000 |
Прокси не может подключиться к MySQL; проверьте адрес, сеть и состояние сервера |
В разделе Права доступа к данным → Аудит доступа найдите разрешения и отказы для alice и соответствующих ресурсов. Один SQL-оператор может создавать несколько событий авторизации ресурсов. Они записываются до выполнения сервером, поэтому ALLOWED не означает успешное выполнение в MySQL. Ошибки разбора, операторы без доступа к ресурсам и отключённые сервисы могут не создавать таких событий; повторные выполнения подготовленного оператора при той же версии политик не повторяют события авторизации. Это аудит авторизации ресурсов, а не полный журнал выполнения SQL.
