zeroshade commented on code in PR #1999:
URL: https://github.com/apache/iceberg-go/pull/1999#discussion_r3960998750


##########
catalog/rest/rest.go:
##########
@@ -1114,6 +1115,11 @@ func (r *Catalog) createSession(ctx context.Context, 
opts *options) (*http.Clien
 
                                return nil, nil, err
                        }
+                       // Sign with the S3 credentials carried in the catalog 
properties when
+                       // present, rather than only the AWS default credential 
chain.
+                       if creds, ok := 
staticCredsFromProps(opts.additionalProps); ok {
+                               cfg.Credentials = creds

Review Comment:
   **major** — The fix itself is pinned by no test - mutation of the credential 
wiring leaves the suite green
   
   TestStaticCredsFromProps exercises only the pure helper 
staticCredsFromProps; nothing asserts that createSession actually installs the 
returned provider onto cfg.Credentials. The load-bearing line `cfg.Credentials 
= creds` can be deleted without any test noticing, so the regression this PR 
fixes can silently return. The package already has the harness to pin it: 
TestSigv4ConcurrentSigners (rest_internal_test.go:1133) stands up an httptest 
server and calls NewCatalog(WithSigV4(), WithSigV4RegionSvc(...)). A test that 
scrubs AWS_* env, passes creds only via WithAdditionalProps, and asserts the 
captured Authorization header contains the props access key would pin it in ~15 
lines. I wrote exactly that as a probe and it passes on this head, so the test 
is cheap and available.
   
   <details><summary>Evidence</summary>
   
   ```text
   Mutated rest.go in place, replacing `if creds, ok := 
staticCredsFromProps(opts.additionalProps); ok { cfg.Credentials = creds }` 
with `if _, ok := staticCredsFromProps(opts.additionalProps); ok { _ = ok }` 
(helper still called, effect removed). Result: `go test ./catalog/rest/ 
-timeout=300s` -> `ok github.com/apache/iceberg-go/catalog/rest 5.946s`. Entire 
package green with the fix neutralized. By contrast the helper test IS 
non-vacuous: swapping the provider args to 
NewStaticCredentialsProvider(secretKey, accessKey, ...) turns it red -> `Test: 
TestStaticCredsFromProps ... Diff: --- Expected -AK +++ Actual +SK / FAIL`. 
Restored via git checkout -- catalog/rest/rest.go.
   ```
   
   </details>



##########
catalog/rest/rest.go:
##########
@@ -1126,6 +1132,17 @@ func (r *Catalog) createSession(ctx context.Context, 
opts *options) (*http.Clien
        return cl, cleanup, nil
 }
 
+// staticCredsFromProps returns a static credentials provider built from the S3
+// access-key properties, or ok=false when no key pair is present.
+func staticCredsFromProps(props iceberg.Properties) (aws.CredentialsProvider, 
bool) {
+       accessKey, secretKey := props[iceio.S3AccessKeyID], 
props[iceio.S3SecretAccessKey]
+       if accessKey == "" || secretKey == "" {
+               return nil, false

Review Comment:
   **major** — A partial s3.* credential pair silently signs the request as a 
different principal
   
   staticCredsFromProps returns ok=false whenever either half of the key pair 
is empty, and the caller then leaves cfg.Credentials pointing at the AWS 
default chain. An operator who sets s3.access-key-id but mistypes or omits 
s3.secret-access-key gets no error and no warning - the catalog request is 
signed with whatever ambient identity happens to be present (env vars, shared 
credentials file, instance role). Explicitly configuring a catalog credential 
and then being authenticated as a completely different principal is a 
surprising, hard-to-debug outcome; the resulting 403 (or worse, an unintended 
success under a more privileged ambient role) gives the user no pointer to the 
real cause. The new test actively enshrines the behavior ('a lone access key 
must not produce a provider') rather than questioning it. Suggest returning an 
error when exactly one of s3.access-key-id / s3.secret-access-key is present, 
naming the missing property; the fully-empty case should keep falling through to
  the default chain. Calibrated against SECURITY-THREAT-MODEL.md this is a 
credential-selection correctness bug, not a trust-boundary violation, so major 
rather than blocking.
   
   <details><summary>Evidence</summary>
   
   ```text
   Probe TestProbe_PartialPropsCreds_FallsBackToAmbient: env 
AWS_ACCESS_KEY_ID=AKIAAMBIENTENVKEY, props {s3.access-key-id: 
AKIAPROPSONLYACCESSKEY} (secret omitted). Output: `NewCatalog err = <nil>` and 
`SIGNED WITH: AWS4-HMAC-SHA256 
Credential=AKIAAMBIENTENVKEY/20260908/us-east-1/s3tables/aws4_request, ...`. 
The explicitly configured AKIAPROPSONLYACCESSKEY was silently discarded and a 
different identity signed the request.
   ```
   
   </details>



##########
catalog/rest/rest.go:
##########
@@ -1114,6 +1115,11 @@ func (r *Catalog) createSession(ctx context.Context, 
opts *options) (*http.Clien
 
                                return nil, nil, err
                        }
+                       // Sign with the S3 credentials carried in the catalog 
properties when
+                       // present, rather than only the AWS default credential 
chain.
+                       if creds, ok := 
staticCredsFromProps(opts.additionalProps); ok {
+                               cfg.Credentials = creds

Review Comment:
   **minor** — Server /v1/config overrides can now select the SigV4 signing 
identity (NOT a vulnerability - flagged for intent confirmation)
   
   createSession is called twice: once from fetchConfig with client-only 
properties, and once from init with the merged map after fetchConfig folds in 
the server's defaults and overrides (rest.go:1012-1016, and maps.Copy(cfg, 
rsp.Overrides) at the end of fetchConfig). Because the new code reads 
opts.additionalProps, the second session's signing identity can be chosen by 
the REST server via a /v1/config override of s3.access-key-id / 
s3.secret-access-key. I checked this against SECURITY-THREAT-MODEL.md before 
filing and it is explicitly NOT a vulnerability: the REST control plane is a 
trusted input, the doc states 'a malicious REST catalog server sending 
dangerous endpoints is [not a vuln] ... credential-selection bugs are often 
correctness or specification issues', and no secret reaches a new audience (the 
server already knows the credentials it injected). Raising it only so the 
maintainer confirms the asymmetry is intended - the bootstrap /v1/config 
request signs with the client ide
 ntity while every subsequent request may sign with a server-selected one.



##########
catalog/rest/rest.go:
##########
@@ -1114,6 +1115,11 @@ func (r *Catalog) createSession(ctx context.Context, 
opts *options) (*http.Clien
 
                                return nil, nil, err
                        }
+                       // Sign with the S3 credentials carried in the catalog 
properties when
+                       // present, rather than only the AWS default credential 
chain.

Review Comment:
   **minor** — New credential precedence is user-visible but undocumented
   
   This PR establishes a three-tier precedence for SigV4 signing identity - 
WithAwsConfig > s3.* catalog properties > AWS default credential chain - and 
changes behavior for existing users: someone who today sets s3.access-key-id 
for FileIO while relying on an ambient role for catalog signing will, after 
this change, start signing catalog requests with the s3.* identity. Nothing in 
the PR documents this. website/src/configuration.md lists 
catalog.<name>.rest.sigv4-enabled / signing-name / signing-region (lines 59-61) 
but says nothing about which credentials sign; and WithSigV4 (options.go:91), 
WithSigV4RegionSvc (options.go:98) and WithAdditionalProps (options.go:156) 
have no doc comment at all. A sentence in the configuration.md SigV4 rows plus 
a godoc line on WithSigV4 would cover it.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to