L’agente MySQL è un proxy di rete indipendente: i client si collegano al proxy al posto del server, e il proxy verifica le istruzioni supportate rispetto alle policy firmate del servizio di dati mysql prima che raggiungano MySQL. Non richiede un’installazione all’interno di MySQL. La distribuzione include il JAR eseguibile agents/mysql/grantforge-agent-mysql-<version>.jar, che richiede Java 17 o successivo.
I test di integrazione Testcontainers del repository usano mysql:8.0 e mysql:8.4 con client MariaDB Connector/J e MySQL Connector/J. Questo non certifica altre versioni del server né tutte le funzionalità del protocollo MySQL. Consulta MySQL per i requisiti di versione e connessione del plug-in di servizio.
Funzionamento
- L’autenticazione viene inoltrata. Il messaggio iniziale del server raggiunge il client e le risposte del client raggiungono il server, quindi è MySQL a verificare le password (
caching_sha2_passwordemysql_native_password); il proxy apprende l’utente accettato da MySQL. Le policy indicano questo nome utente, senza la parte host. - Le risorse SQL vengono verificate. Il proxy analizza COM_QUERY e le istruzioni preparate lato server e controlla i tipi di accesso richiesti su database, tabelle e colonne. Un’istruzione rifiutata non raggiunge MySQL; il proxy genera un errore con un codice compatibile con MySQL, come 1142 o 1143.
- Le letture vengono mascherate e filtrate. Quando un’istruzione legge una tabella con colonne mascherate o un filtro delle righe per l’utente, il proxy la sostituisce con una tabella derivata dello stesso nome che mostra i valori mascherati e solo le righe filtrate. Join, condizioni e funzioni vedono così ciò che l’utente può vedere.
- Gli eventi di autorizzazione delle risorse vengono inviati a GrantForge con utente, indirizzo del client, risorsa, tipo di accesso, decisione e istruzione. L’ambito dell’audit è descritto di seguito.
- I risultati dell’esecuzione MySQL vengono inoltrati al client; il proxy non modifica i valori originali memorizzati nel database.
Distribuzione
-
Aggiungi un servizio di dati
mysqle configura le sue policy. Apri Permessi sui dati → Agenti, seleziona il servizio ed emetti un token di agente. Viene mostrato una sola volta; conservalo in un file leggibile solo dall’account del proxy. -
Crea un utente di metadati per il proxy. Legge le colonne delle tabelle e le funzioni memorizzate da
information_schemae non ha bisogno dei dati:REFERENCESrende visibili tutte le colonne,SHOW_ROUTINEtutte le funzioni memorizzate. Concedi questi privilegi direttamente all’utente, non tramite un ruolo; senzaSHOW_ROUTINEil proxy non si avvia.CREATE USER 'grantforge_meta'@'proxy-host' IDENTIFIED BY '…'; GRANT REFERENCES, SHOW_ROUTINE ON *.* TO 'grantforge_meta'@'proxy-host'; -
Scrivi il file di configurazione, sostituendo indirizzi, percorsi e nomi di esempio:
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.passwordQuesto esempio attiva TLS dai client al proxy e dal proxy a MySQL.
mysql-proxy.crtdeve corrispondere al nome host del proxy usato dai client; il certificato MySQL deve corrispondere amysql.internal. I percorsi sono sulla macchina del proxy e la directory della cache deve essere scrivibile. I file delle password vengono letti in UTF-8 e gli spazi iniziali e finali vengono rimossi; le password che richiedono questi spazi non possono essere memorizzate in questo modo. -
Avvia il proxy, per esempio come servizio systemd:
java -jar agents/mysql/grantforge-agent-mysql-<version>.jar /etc/grantforge/mysql-proxy.propertiesSostituisci
<version>con la versione della distribuzione. Le impostazioni passate comejava -Dgrantforge.…=… -jar …prevalgono sul file. Il proxy termina correttamente su SIGTERM e invia o conserva su disco gli eventi in attesa. -
Indirizza i client all’indirizzo e alla porta del proxy. MySQL ora vede l’indirizzo del proxy, quindi la parte host degli account deve consentirlo.
-
Chiudi il percorso che aggira il proxy: i client che raggiungono direttamente MySQL non vengono verificati. Consenti solo al proxy e agli amministratori di raggiungere la porta del server.
| Impostazione | Scopo |
|---|---|
grantforge.server.url, grantforge.token.file, grantforge.instance, grantforge.cache.dir |
Obbligatori: server GrantForge, file del token di agente, nome del proxy e una directory scrivibile per lo snapshot firmato e gli eventi conservati su disco |
grantforge.signing.key.file |
File facoltativo che fissa la chiave di firma degli snapshot; copia dalla pagina Agenti la chiave pubblica Ed25519 X.509 codificata in Base64. Se omesso, la chiave viene recuperata dal server al primo contatto |
grantforge.proxy.host, grantforge.proxy.port |
Indirizzo di ascolto; tutti gli indirizzi e porta 3307 per impostazione predefinita. Intervallo 0..65535; 0 assegna automaticamente una porta |
grantforge.proxy.tls.cert, grantforge.proxy.tls.key |
Certificato e chiave privata PEM, entrambi necessari insieme per offrire TLS ai client |
grantforge.proxy.tls.required |
Valore predefinito false; true rifiuta i client senza TLS (errore 3159) e richiede certificato e chiave privata |
grantforge.mysql.host, grantforge.mysql.port |
Host MySQL obbligatorio; porta predefinita 3306, intervallo 1..65535 |
grantforge.mysql.tls |
TLS verso MySQL: disabled (predefinito), required (cifrato, non verificato), verify-ca o verify-identity |
grantforge.mysql.tls.ca |
CA PEM che verifica MySQL; vuoto usa le autorità considerate attendibili da Java |
grantforge.mysql.metadata.user, grantforge.mysql.metadata.password.file |
Utente di metadati obbligatorio; il file della password è facoltativo e, se omesso, al driver non viene passata una password |
grantforge.mysql.metadata.cache.seconds |
Durata della cache di colonne e funzioni memorizzate: valore predefinito 30, intervallo 0..86400 secondi; 0 ne disattiva il riutilizzo |
grantforge.mysql.connect.timeout.ms |
Timeout di connessione MySQL: valore predefinito 10000, intervallo 1..600000 millisecondi; le letture dei metadati hanno un timeout fisso di 30000 millisecondi |
grantforge.proxy.check.threads |
Thread di verifica delle istruzioni: valore predefinito 8, intervallo 1..256 |
grantforge.connect.timeout.ms, grantforge.read.timeout.ms |
Timeout di connessione e lettura GrantForge: valori predefiniti 5000 / 8000 millisecondi, intervallo 1..2147483647 |
grantforge.refresh.interval.ms |
Intervallo iniziale o di ripiego quando il server è irraggiungibile: valore predefinito 30000 millisecondi, minimo 1000; gli heartbeat normali usano l’intervallo restituito dal server |
grantforge.audit.batch.size, grantforge.audit.queue.capacity |
Batch predefinito 500, coda 10000 eventi; intervallo del batch 1..1000, coda almeno pari al batch e al massimo 1000000 |
grantforge.audit.flush.interval.ms |
Intervallo di invio dell’audit: valore predefinito 5000 millisecondi, intervallo 1..2147483647 |
grantforge.audit.spool.limit.bytes |
Limite di archiviazione su disco degli eventi di audit: valore predefinito 67108864 (64 MiB), minimo 0 |
TLS sui due lati è indipendente. Quando solo un lato lo usa e MySQL richiede la password completa (caching_sha2_password dopo un mancato riscontro nella cache), il proxy la trasferisce: risponde a un client senza TLS che richiede la chiave del server con una propria chiave RSA e cifra la password di un client TLS con la chiave MySQL per un server senza TLS. I client non devono essere configurati con il file della chiave pubblica MySQL quando passano attraverso il proxy.
Accessi richiesti dalle istruzioni
| Istruzione | Accesso |
|---|---|
SELECT, sottoquery, WITH, EXPLAIN, SET @x = (SELECT …) |
select su ogni colonna usata; una lettura senza colonne, come COUNT(*) o EXISTS, richiede select sull’intera tabella |
INSERT, LOAD DATA |
insert sulle colonne scritte (tutte se nessuna è elencata); ON DUPLICATE KEY UPDATE aggiunge update |
REPLACE |
insert sulle colonne e delete sulla tabella |
UPDATE |
update sulle colonne impostate e select sulle colonne lette |
DELETE |
delete sulla tabella e select sulle colonne lette |
CREATE TABLE, CREATE VIEW / DROP TABLE, DROP VIEW, TRUNCATE / ALTER TABLE, ALTER VIEW, OPTIMIZE |
create / drop / alter sulla tabella o vista |
RENAME TABLE |
alter e drop sul vecchio nome, create e insert sul nuovo |
CREATE INDEX, DROP INDEX |
index sulla tabella |
CREATE DATABASE, DROP DATABASE |
create, drop sul database |
LOCK TABLES, ANALYZE, CHECK, CHECKSUM TABLE |
select sulla tabella |
Le istruzioni che non usano dati passano ai privilegi nativi di MySQL: transazioni, SET, USE, SHOW, DESCRIBE, KILL e istruzioni sugli account come GRANT e CREATE USER. information_schema e performance_schema sono database come gli altri e richiedono policy.
Il proxy rifiuta le istruzioni che non può verificare completamente: commenti eseguibili (/*! … */), funzioni memorizzate e CALL, CREATE PROCEDURE, FUNCTION, TRIGGER e EVENT, PREPARE/EXECUTE a livello SQL, HANDLER, comandi di replica e clonazione, USE o SET NAMES insieme ad altre istruzioni nello stesso batch e istruzioni che non può analizzare.
Maschere, filtri delle righe e relativi limiti
- Maschere e filtri si applicano ovunque un’istruzione legga la tabella, incluse sottoquery, join,
INSERT … SELECTeCREATE TABLE … SELECT(la cui copia contiene i valori mascherati). - Un
UPDATEoDELETEsu una singola tabella modifica solo le righe mostrate dal filtro. Sono rifiutatiUPDATEoDELETEmultitabella su una tabella filtrata, così come una scrittura che usa una colonna mascherata della tabella modificata, perché potrebbe copiare o sondare i valori nascosti. - Non è possibile creare né modificare una vista su una tabella mascherata o filtrata: MySQL esegue la vista in seguito, senza il proxy.
- Le istruzioni preparate riutilizzano verifica e riscrittura finché la versione delle policy non cambia. Una nuova versione provoca un’altra verifica: l’esecuzione continua se l’accesso è ancora consentito e il testo riscritto non cambia; altrimenti l’errore 1615 richiede al client di preparare nuovamente l’istruzione.
- Una colonna indicata senza la tabella viene considerata per ogni tabella visibile che abbia una colonna con quel nome; qualifica le colonne per evitare rifiuti dovuti a una colonna limitata con lo stesso nome.
- Le colonne lette tramite
*provengono dalla vista diinformation_schemadell’utente di metadati e restano in cache pergrantforge.mysql.metadata.cache.seconds. Un DDL riuscito inviato attraverso il proxy come comando SQL ordinario invalida la cache: la ricerca successiva rilegge i metadati. Non è garantita l’invalidazione durante l’esecuzione di DDL esterno o preparato; attendi la scadenza della cache o riavvia il proxy.
Ambito di applicazione delle policy
Il proxy verifica le risorse che può analizzare nel SQL del client. Non espande ricorsivamente viste esistenti, trigger o altre operazioni eseguite internamente dal server. Per proteggere le tabelle sottostanti, limita anche i privilegi nativi MySQL e l’uso di viste e trigger esistenti. Non concedere privilegi amministrativi agli utenti di business aspettandoti che il proxy intercetti le istruzioni amministrative.
Un filtro delle righe non è un vincolo generale sulle scritture: INSERT, REPLACE e LOAD DATA verificano i permessi di scrittura, ma non richiedono automaticamente che le nuove righe soddisfino l’espressione del filtro. Neppure al ramo di INSERT … ON DUPLICATE KEY UPDATE che aggiorna righe esistenti viene aggiunta una condizione di filtro delle righe. Compressione del protocollo, ZSTD, attributi delle query e metadati facoltativi dei risultati vengono disattivati durante la negoziazione.
Policy e guasti
- Senza uno snapshot utilizzabile o con un token legato a un servizio che non è
mysql, tutte le istruzioni che usano dati vengono rifiutate. - Se GrantForge è temporaneamente irraggiungibile o la firma di un nuovo snapshot non è valida, il proxy conserva lo snapshot già applicato. Un primo avvio senza alcuno snapshot utilizzabile rifiuta l’accesso alle risorse.
- Disattivare il servizio salta l’autorizzazione delle risorse, il mascheramento e il filtro delle righe di GrantForge; l’analisi SQL e le restrizioni del protocollo continuano ad applicarsi. Disattivare il servizio non blocca l’accesso ai dati.
- I privilegi nativi MySQL continuano ad applicarsi dopo le verifiche del proxy; il proxy può solo restringerli.
COM_CHANGE_USERviene seguito e rifiutato quando il lato client e il lato server differiscono nell’uso di TLS; collegati nuovamente con l’altro utente.
Esempio di verifica
In un database di test separato, chiedi a un amministratore del database di creare i dati e l’account seguenti. Sostituisci proxy-host con l’indirizzo sorgente del proxy effettivamente visto da 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';Crea tre policy GrantForge per alice: consenti select su database=grantforge_demo, table=orders, column=*; maschera card con mask_show_last_4; filtra la tabella con region = 'EU'. Mantieni attivi il servizio e tutte e tre le policy. Attendi che Permessi sui dati → Agenti mostri che è stata applicata l’ultima versione delle policy.
Collegati al proxy con il client MySQL da riga di comando. -p richiede la password; il file della CA deve considerare attendibile il certificato del proxy:
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;
-- One row only: 1 | EU | xxxxxxxxxxxx1111
UPDATE orders SET region = 'US' WHERE id = 1;
-- 1143 / 42000: GrantForge does not grant updateIn questo esempio i privilegi nativi MySQL consentono già UPDATE, quindi il suo rifiuto verifica che il proxy applica la policy. Cambia poi il filtro delle righe in region = 'US', attendi che il proxy applichi la nuova versione e ripeti la query. Deve apparire solo la riga 2, con il valore della carta xxxxxxxxxxxx4444.
Risoluzione dei problemi e audit
| Codice di errore / SQLSTATE | Significato e verifiche |
|---|---|
1044 / 42000, 1142 / 42000, 1143 / 42000 |
Accesso al database, alla tabella o alla colonna negato. Per i messaggi che contengono by GrantForge, verifica utente della policy, risorse, tipo di accesso e versione dello snapshot; per altri rifiuti verifica anche i privilegi nativi MySQL |
1046 / 3D000 |
Nessun database selezionato; usa USE, un database predefinito nella connessione o un nome di tabella qualificato |
1227 / 42000 |
Un’operazione è vietata o non può essere verificata in sicurezza; usa il messaggio per controllare restrizioni delle istruzioni e privilegi sui metadati |
1064 / 42000 |
Il SQL non può essere analizzato |
1615 / HY000 |
Prepara nuovamente l’istruzione dopo una modifica delle policy |
3159 / HY000 |
Il proxy richiede TLS, ma il client si è collegato senza TLS |
1043 / 08S01 |
Errore del protocollo o della negoziazione TLS con il server di backend; verifica modalità TLS, certificati, nome host e funzionalità del server |
2003 / HY000 |
Il proxy non può collegarsi a MySQL; verifica indirizzo, rete e stato del backend |
In Permessi sui dati → Audit degli accessi, cerca gli eventi di accesso consentito e negato per alice e le risorse corrispondenti. Una singola istruzione SQL può produrre più eventi di autorizzazione delle risorse. Vengono registrati prima dell’esecuzione nel backend, quindi ALLOWED non significa che MySQL abbia eseguito correttamente. Errori di analisi, istruzioni senza accesso alle risorse e servizi disattivati potrebbero non produrre eventi di risorsa; le esecuzioni ripetute di un’istruzione preparata con la stessa versione delle policy non ripetono gli eventi di autorizzazione. Questo è un audit dell’autorizzazione delle risorse, non un registro completo dell’esecuzione SQL.
