feat(s3tables): support for AWS Lake Formation filters for S3 Tables#1108
feat(s3tables): support for AWS Lake Formation filters for S3 Tables#1108The-Alchemist wants to merge 6 commits into
Conversation
Enforce LF row filters at scan time with temporary Glue credentials, cloud test suite, and CI setup scripts. Preserve DuckDB Labs defaults for S3 Tables/Glue regions and bucket names so existing cloud tests pass. Document Glue/LF API boundaries where policy discovery, credential vending, and row-filter enforcement interact.
Remove duplicate jobs and duckdb_iceberg_ref passthrough so scheduled and manual cloud tests validate and run against the triggering branch.
…ckDB API compile errors. Reject access_delegation_mode lf_filtered in favor of the boolean flag, rename the internal mode to LAKE_FORMATION, and update Lake Formation code for Identifier-based secret/bind APIs.
Reintroduce REGION, TABLE_BUCKET, and DATABASE_NAME as canonical defaults for S3 Tables config and drop unused DATABASE variables.
|
Hi @The-Alchemist, can you provide a PR description with some more documentation as to what is being added here? Will make reviewing a lot easier |
Not sure how i missed it. Thanks! |
Resolve CloudTestingReusable.yml conflict by keeping duckdb_iceberg_ref checkout and Lake Formation test steps. Signed-off-by: Karl Pietrzak <karl@medplum.com>
Restore format-check compliance after CI reported formatting drift in LF integration code. Signed-off-by: Karl Pietrzak <karl@medplum.com>
| return schema_component.encoded; | ||
| } | ||
|
|
||
| static string ExtractAccountId(const string &warehouse) { |
There was a problem hiding this comment.
Looks a lot like logic we have in IcebergCatalog::GetConfig
| throw ConversionException("Could not get interval information from %s", interval_option.ToString()); | ||
| } | ||
| attach_options.max_table_staleness_micros = interval_in_micros; | ||
| } else if (lower_name == "lake_formation_data_filters") { |
There was a problem hiding this comment.
SIGV4Authorization::FromAttachOptions ?
|
I went over the PR, and as far as I can tell, the core of what is added is:
As it stands, this PR is much too invasive, and much too large in scope to review |
Thanks for the feedback, @Tishj . I totally agree the PR is too big. We can certainly break this up into separate PRs:
WDYT? |
|
@The-Alchemist a bigger issue here is that this PR starts to implement features that deviate significantly from what is in the Iceberg spec and in the IRC. We would like to keep this as close to pure Iceberg features as possible. In addition, we would like to get rid of the aws dependency in this repo since we only have some legacy code that relies on it, which we will deprecate. So also for that reason, adding LakeFormation filters is not idea. We are discussing how to go about adding this kind of functionality in the DuckDB-Iceberg ecosystem. When we have a better idea, we can update here. |
|
@Tmonster : understood. does a Community Extension make more sense for this, then? |
|
For now yes, a community extension is the way to go 👍 |
|
@Tmonster : thanks for the quick replies! WRT to removing the aws dependency: does this mean that S3 Tables support is planned to be removed? Unfortunately, AWS S3 tables auth depends on the aws library. Anyway, wrote a separate PR to allow external auth in this repo to allow community extensions to "plug in" their own auth mechanism. That would allow this PR to basically become an external repo and community extension. |
|
S3Tables will still be supported, since we have hand rolled our own Sigv4 signing in this repo. In it's current state the AWS extension is mostly used for finding aws credentials on a machine. It is on our roadmap to expand it to include more aws api functionality. |
Summary
Adds Lake Formation (LF from now on ) data filter support for AWS Glue Iceberg catalogs. When enabled, DuckDB participates in Lake Formation’s application integration / external data filtering flow: it discovers the caller’s effective row filter via Glue, vends scoped S3 credentials via Lake Formation, and enforces the filter at scan time so user predicates cannot widen the grant.
Use Case
AWS admins want to filter S3 Tables by, say,
account_id, for simple row-level security. This is support OOTB in AWS, but requires a separate credential path and APIs thatduckdb-icebergdoes not use.Out of Scope
Column filters (will be a separate PR). This is just for row filters.
Motivation
Glue Iceberg REST catalogs expose table metadata, but row-level access control for Lake Formation–registered tables is enforced through a separate integration path:
GetUnfilteredTableMetadata, andGetUnfilteredPartitionsMetadatafor partitioned tables).GetTemporaryGlueTableCredentials/GetTemporaryGluePartitionCredentials).Without this, a client attached to Glue with normal credentials could read more data than Lake Formation grants allow, resulting in runtime errors.
How it works
sequenceDiagram participant User participant DuckDB participant GlueIRC as Glue Iceberg REST participant GlueAPI as Glue Data Catalog API participant LF as Lake Formation participant S3 User->>DuckDB: ATTACH ... LAKE_FORMATION_DATA_FILTERS true User->>DuckDB: SELECT ... FROM catalog.schema.table DuckDB->>GlueIRC: GetTable (metadata, no vended-credentials header) DuckDB->>GlueAPI: GetUnfilteredTableMetadata (CELL_FILTER_PERMISSION) GlueAPI-->>DuckDB: row_filter SQL, queryAuthorizationId, ... DuckDB->>LF: GetTemporaryGlueTableCredentials (scoped S3 keys) LF-->>DuckDB: temporary credentials DuckDB->>DuckDB: Apply mandatory row filter (bind + optimizer) DuckDB->>S3: Read Parquet with LF-scoped credentialsCredential path: IRC
access_delegation_modestaysnone(noX-Iceberg-Access-Delegation: vended-credentials). S3 access uses LF temporary credentials instead of catalog-vended credentials.Policy discovery: Per-table LF policy is fetched from Glue and cached on
IcebergTableInformation. For partitioned tables, partition-scoped credentials are refreshed lazily during manifest enumeration.Enforcement: LF row-filter SQL is parsed and bound as mandatory scan filters at bind time, with a
LakeFormationRowFilterOptimizersafety net so predicates likeWHERE id = 999cannot bypass the grant.Auth: Requires
SIGV4catalog authentication. If the catalog secret usesassume_role_arn, the extension assumes that role with an STS session tagLakeFormationAuthorizedCaller=<LF_SESSION_TAG>(see application integration settings).Usage
CREATE SECRET glue_lf_secret ( TYPE S3, REGION 'us-east-1', PROVIDER credential_chain, CHAIN 'sts', ASSUME_ROLE_ARN 'arn:aws:iam::<account>:role/<lf-role>' ); ATTACH '<catalog-id>' AS lf_datalake ( TYPE ICEBERG, ENDPOINT_TYPE 'GLUE', SECRET glue_lf_secret, LAKE_FORMATION_DATA_FILTERS true, LF_SESSION_TAG 'duckdb' ); SELECT * FROM lf_datalake.test_inserts.basic_insert_test;Attach options
LAKE_FORMATION_DATA_FILTERSLF_SESSION_TAGENDPOINT_TYPE 'GLUE'access_delegation_modenone(default) or omitted; notvended_credentialsENDPOINT_TYPE 'GLUE'ENDPOINT_TYPE 'S3TABLES'LAKE_FORMATION_DATA_FILTERSaccess_delegation_mode 'lf_filtered'is rejected; useLAKE_FORMATION_DATA_FILTERS trueinstead.v1 limitations
Fails closed on grant shapes we do not enforce yet:
EMSCRIPTEN)Testing
test/sql/cloud/lakeformation/*— row filters (partitioned/unpartitioned), bypass attempt, no grant, unsupported column filterscripts/setup_lf_data_filters.pyprovisions data cell filters and grants; enables external data filtering on the test accountscripts/create_s3_insert_table.pyextended for LF test tables on GlueAWS documentation
Glue vs S3 Tables
This integration uses the Glue Iceberg REST catalog (
ENDPOINT_TYPE 'GLUE'), which is the entry point Lake Formation’s application integration expects. It does not support attaching withENDPOINT_TYPE 'S3TABLES'.Cloud tests use this pattern: Iceberg metadata comes from Glue REST; physical table data lives in S3 Tables; LF row filters and temporary credentials are applied on top.