This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 11b19a62 fix(io/s3): allow s3.signer.uri property; only reject 
explicit s3.remote-signing-enabled=true (#744)
11b19a62 is described below

commit 11b19a627d3abfe7a36f49628eebed106fd59795
Author: Max Kuznetsov <[email protected]>
AuthorDate: Thu Feb 19 19:48:23 2026 +0000

    fix(io/s3): allow s3.signer.uri property; only reject explicit 
s3.remote-signing-enabled=true (#744)
    
    ## Summary
    
    * Remove `s3.signer.uri` from the list of unsupported properties.
    * Instead, only error when `s3.remote-signing-enabled` is explicitly set
    to `true` (which requires a remote signing implementation that doesn't
    exist yet).
    * When `s3.remote-signing-enabled` is absent or `false`, `s3.signer.uri`
    is silently ignored - matching the behavior of the [Java
    
implementation](https://github.com/apache/iceberg/blob/de3125afe64fc2b171a52b6e884c72f901e3cba1/aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIOProperties.java#L294-L296).
    
    ## Motivation
    
    The [REST Catalog
    
spec](https://github.com/apache/iceberg/blob/de3125afe64fc2b171a52b6e884c72f901e3cba1/open-api/rest-catalog-open-api.yaml#L3472-L3480)
    defines `s3.remote-signing-enabled` as the flag that controls whether
    remote signing is active (`LoadTableResult` schema). The `s3.signer.uri`
    property is a configuration detail for the signer endpoint, not a
    trigger for signing itself.
    
    Specifically, the [R2 Data
    Catalog](https://developers.cloudflare.com/r2/data-catalog/) includes
    `s3.signer.uri` in the /v1/config response but returns
    `s3.remote-signing-enabled: false` when loading a table and works fine
    with vended credentials.
    
    Since iceberg-go treats `s3.signer.uri` as unsupported and fails
    immediately, connecting to an R2 Data Catalog using just the Catalog URI
    and token is not possible.
    
    A workaround exists that involves passing the S3 endpoint and keys
    directly, but it's not ideal since it requires managing additional
    credentials.
    
    <details>
      <summary>Workaround with explicit S3 credentials</summary>
    
    ```go
    cat, err := rest.NewCatalog(
        ctx,
        "r2-catalog",
        catalogURI,
        rest.WithWarehouseLocation(warehouse),
        rest.WithOAuthToken(token),
        rest.WithAdditionalProps(iceberg.Properties{
            "s3.endpoint":          s3Endpoint,
            "s3.access-key-id":     accessKeyID,
            "s3.secret-access-key": secretAccessKey,
            "s3.region":            "auto",
        }),
    )
    ```
    </details>
    
    ---------
    
    Co-authored-by: Max Kuznetsov <[email protected]>
---
 io/s3.go      | 10 ++++++-
 io/s3_test.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/io/s3.go b/io/s3.go
index 98ad4c63..435962ab 100644
--- a/io/s3.go
+++ b/io/s3.go
@@ -19,6 +19,7 @@ package io
 
 import (
        "context"
+       "errors"
        "fmt"
        "net/http"
        "net/url"
@@ -47,12 +48,12 @@ const (
        S3ProxyURI               = "s3.proxy-uri"
        S3ConnectTimeout         = "s3.connect-timeout"
        S3SignerUri              = "s3.signer.uri"
+       S3RemoteSigningEnabled   = "s3.remote-signing-enabled"
        S3ForceVirtualAddressing = "s3.force-virtual-addressing"
 )
 
 var unsupportedS3Props = []string{
        S3ConnectTimeout,
-       S3SignerUri,
 }
 
 // ParseAWSConfig parses S3 properties and returns a configuration.
@@ -64,6 +65,13 @@ func ParseAWSConfig(ctx context.Context, props 
map[string]string) (*aws.Config,
                }
        }
 
+       // Remote S3 request signing is not implemented yet.
+       if v, ok := props[S3RemoteSigningEnabled]; ok {
+               if enabled, err := strconv.ParseBool(v); err == nil && enabled {
+                       return nil, errors.New("remote S3 request signing is 
not supported")
+               }
+       }
+
        opts := []func(*config.LoadOptions) error{}
 
        if tok, ok := props["token"]; ok {
diff --git a/io/s3_test.go b/io/s3_test.go
new file mode 100644
index 00000000..a7ac95aa
--- /dev/null
+++ b/io/s3_test.go
@@ -0,0 +1,89 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package io
+
+import (
+       "context"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestParseAWSConfigRemoteSigningEnabled(t *testing.T) {
+       t.Parallel()
+
+       t.Run("signer uri present with remote signing explicitly enabled", 
func(t *testing.T) {
+               t.Parallel()
+
+               _, err := ParseAWSConfig(context.Background(), 
map[string]string{
+                       S3SignerUri:            "https://signer.example.com";,
+                       S3RemoteSigningEnabled: "true",
+               })
+               require.ErrorContains(t, err, "remote S3 request signing is not 
supported")
+       })
+
+       t.Run("signer uri present with remote signing explicitly disabled", 
func(t *testing.T) {
+               t.Parallel()
+
+               _, err := ParseAWSConfig(context.Background(), 
map[string]string{
+                       S3SignerUri:            "https://signer.example.com";,
+                       S3RemoteSigningEnabled: "false",
+                       S3Region:               "us-east-1",
+               })
+               require.NoError(t, err)
+       })
+
+       t.Run("signer uri present without remote signing property", func(t 
*testing.T) {
+               t.Parallel()
+
+               _, err := ParseAWSConfig(context.Background(), 
map[string]string{
+                       S3SignerUri: "https://signer.example.com";,
+                       S3Region:    "us-west-2",
+               })
+               require.NoError(t, err)
+       })
+
+       t.Run("remote signing enabled without signer uri", func(t *testing.T) {
+               t.Parallel()
+
+               _, err := ParseAWSConfig(context.Background(), 
map[string]string{
+                       S3RemoteSigningEnabled: "true",
+               })
+               require.ErrorContains(t, err, "remote S3 request signing is not 
supported")
+       })
+
+       t.Run("no signer properties at all", func(t *testing.T) {
+               t.Parallel()
+
+               cfg, err := ParseAWSConfig(context.Background(), 
map[string]string{
+                       S3Region: "eu-west-1",
+               })
+               require.NoError(t, err)
+               assert.Equal(t, "eu-west-1", cfg.Region)
+       })
+}
+
+func TestParseAWSConfigUnsupportedProperty(t *testing.T) {
+       t.Parallel()
+
+       _, err := ParseAWSConfig(context.Background(), map[string]string{
+               S3ConnectTimeout: "5000",
+       })
+       require.ErrorContains(t, err, "unsupported S3 property")
+}

Reply via email to