markhoerth opened a new issue, #14441:
URL: https://github.com/apache/gravitino/issues/14441

   ### Version
   
   main branch, commit bb634defdc5663a8124be5fca1c8d9e52ac096ac (2.0.0-SNAPSHOT)
   
   ### Describe what's wrong
   
   A user who holds `USE_CATALOG`, `USE_SCHEMA` and `CREATE_TABLE` on their own 
namespace, and no privilege on a table in another namespace, calls the Iceberg 
REST `registerTable` endpoint on their own namespace with a new table name and 
the `metadata-location` of the other table. They expect the request to be 
refused, because registering the file would give them a table they are not 
allowed to read.
   
   Instead the register returns 200 with the full table metadata (schema, 
location, snapshots), Gravitino imports the new table and makes the caller its 
owner, and from then on the caller can load the copy. The copy points at the 
original table's metadata and data files, so the caller gets the schema and 
file locations of a table they cannot load under its real name. The caller 
needs the path of the metadata file, which can be learned from logs, an earlier 
grant, engine output or a storage listing.
   
   The problem shows up whenever authorization is enabled on the Iceberg REST 
service backed by a Gravitino catalog.
   
   Cause:
   
   - The endpoint is authorized only against the target namespace: 
`IcebergNamespaceOperations.registerTable` uses `ANY_USE_CATALOG && 
ANY_USE_SCHEMA && ANY_CREATE_TABLE` on the schema in the path 
(`iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergNamespaceOperations.java:313-317`).
 Nothing checks the caller's rights on the table that owns the metadata file.
   - `IcebergNamespaceOperationExecutor.registerTable` reads whatever file the 
request names 
(`iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/dispatcher/IcebergNamespaceOperationExecutor.java:141-142`,
 through `IcebergCatalogWrapper.loadTableMetadataFromLocation` at 
`iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java:267-270`,
 which opens any path the server's FileIO can read) and registers it (line 144).
   - `IcebergNamespaceHookDispatcher.registerTable` then imports the table and 
sets the caller as its owner 
(`iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/dispatcher/IcebergNamespaceHookDispatcher.java:201-209`),
 so every later load by the caller passes the owner check.
   
   The defect is in Gravitino's own authorization of the register endpoint. The 
Iceberg REST spec and the upstream Iceberg `CatalogHandlers.registerTable` do 
not define any authorization, so the check has to be in Gravitino. A fix would, 
before registering, resolve which table (if any) Gravitino tracks at the 
metadata file's table location and require the caller to be able to read that 
table (or to own it), or refuse metadata files outside the target namespace's 
location unless the caller holds a catalog-level privilege.
   
   ### Error message and/or stacktrace
   
   No error. The caller cannot load the original table:
   
   ```
   GET /iceberg/v1/namespaces/repro_other/tables/secret   (as repro_alice)
   
   HTTP 403
   {"error":{"message":"User 'repro_alice' is not authorized to load table 
'gvt_irc.irc_cat.repro_other.secret'","type":"ForbiddenException","code":403,...}}
   ```
   
   but registering its metadata file under their own namespace succeeds, and 
the copy loads:
   
   ```
   POST /iceberg/v1/namespaces/repro_mine/register   (as repro_alice)
   
{"name":"copy","metadata-location":"/home/ubuntu/gvt/data/irc_warehouse/repro_other/secret/metadata/00000-4be92224-960b-49fc-8a94-092c99bd6f50.metadata.json"}
   
   HTTP 200
   
{"metadata-location":"/home/ubuntu/gvt/data/irc_warehouse/repro_other/secret/metadata/00000-4be92224-...metadata.json","metadata":{"format-version":2,"table-uuid":"e2058afe-90dc-490e-92ab-bb1f17c87055","location":"/home/ubuntu/gvt/data/irc_warehouse/repro_other/secret",...}}
   
   GET /iceberg/v1/namespaces/repro_mine/tables/copy   (as repro_alice)
   
   HTTP 200   (schema fields: id, ssn; location 
/home/ubuntu/gvt/data/irc_warehouse/repro_other/secret)
   
   GET /api/metalakes/gvt_irc/owners/table/irc_cat.repro_mine.copy
   
   HTTP 200
   {"code":0,"owner":{"name":"repro_alice","type":"user"}}
   ```
   
   ### How to reproduce
   
   Gravitino server with authorization enabled and simple auth (`admin` is a 
service admin). The Iceberg REST service runs against the Gravitino catalog 
`irc_cat` in metalake `gvt_irc` (no prefix), with a local warehouse.
   
   ```bash
   ADMIN="Authorization: Basic $(printf admin:x | base64)"
   ALICE="Authorization: Basic $(printf repro_alice:x | base64)"
   I=http://127.0.0.1:9001/iceberg/v1
   G=http://127.0.0.1:8090/api
   V1='Accept: application/vnd.gravitino.v1+json'
   ```
   
   1. As admin, create two namespaces and a table in one of them:
   ```bash
   curl -s -H "$ADMIN" -H 'Content-Type: application/json' -X POST 
$I/namespaces -d '{"namespace":["repro_other"],"properties":{}}'
   curl -s -H "$ADMIN" -H 'Content-Type: application/json' -X POST 
$I/namespaces -d '{"namespace":["repro_mine"],"properties":{}}'
   curl -s -H "$ADMIN" -H 'Content-Type: application/json' -X POST 
$I/namespaces/repro_other/tables -d 
'{"name":"secret","schema":{"type":"struct","schema-id":0,"fields":[{"id":1,"name":"id","type":"long","required":false},{"id":2,"name":"ssn","type":"string","required":false}]},"properties":{"format-version":"2"}}'
   LOC=$(curl -s -H "$ADMIN" $I/namespaces/repro_other/tables/secret | python3 
-c 'import sys,json;print(json.load(sys.stdin)["metadata-location"])')
   ```
   2. Create user `repro_alice` with `CREATE_TABLE` on `repro_mine` only:
   ```bash
   curl -s -H "$ADMIN" -H "$V1" -H 'Content-Type: application/json' -X POST 
$G/metalakes/gvt_irc/users -d '{"name":"repro_alice"}'
   curl -s -H "$ADMIN" -H "$V1" -H 'Content-Type: application/json' -X POST 
$G/metalakes/gvt_irc/roles -d 
'{"name":"repro_alice_role","properties":{},"securableObjects":[{"fullName":"irc_cat","type":"CATALOG","privileges":[{"name":"USE_CATALOG","condition":"ALLOW"}]},{"fullName":"irc_cat.repro_mine","type":"SCHEMA","privileges":[{"name":"USE_SCHEMA","condition":"ALLOW"},{"name":"CREATE_TABLE","condition":"ALLOW"}]}]}'
   curl -s -H "$ADMIN" -H "$V1" -H 'Content-Type: application/json' -X PUT 
$G/metalakes/gvt_irc/permissions/users/repro_alice/grant -d 
'{"roleNames":["repro_alice_role"]}'
   ```
   3. As `repro_alice`, loading the table is refused (403):
   ```bash
   curl -s -w '%{http_code}\n' -H "$ALICE" 
$I/namespaces/repro_other/tables/secret
   ```
   4. As `repro_alice`, register its metadata file under a new name in 
`repro_mine` (200), then load the copy (200):
   ```bash
   curl -s -w '%{http_code}\n' -H "$ALICE" -H 'Content-Type: application/json' 
-X POST $I/namespaces/repro_mine/register -d 
"{\"name\":\"copy\",\"metadata-location\":\"$LOC\"}"
   curl -s -w '%{http_code}\n' -H "$ALICE" $I/namespaces/repro_mine/tables/copy
   ```
   5. `repro_alice` is now the owner of `irc_cat.repro_mine.copy`:
   ```bash
   curl -s -H "$ADMIN" -H "$V1" 
$G/metalakes/gvt_irc/owners/table/irc_cat.repro_mine.copy
   ```
   
   ### Additional context
   
   - Related: #14242 (register with `overwrite=true` is authorized only by 
`CREATE_TABLE`) and #14303 (overwrite with another table's metadata repoints 
the table). Both concern overwriting an existing name; here the caller 
registers a new name and needs no privilege on any existing table.
   - Also present in v1.3.1: the same authorization expression on 
`registerTable` (`IcebergNamespaceOperations.java:312-317` at v1.3.1), and 
`IcebergNamespaceHookDispatcher.registerTable` imports the table and sets the 
caller as owner (lines 180-195 at v1.3.1).
   - Found by the 2.0 OSS test suite check 
`test_register_cannot_adopt_unreadable_table`.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to