PDGGK opened a new pull request, #9238:
URL: https://github.com/apache/paimon/pull/9238

   ### Purpose
   
   Closes #8852.
   
   `OSSFileIO` gives no way to switch off the OSS SDK's CNAME heuristic, and on 
an OSS-compatible endpoint that is not an Aliyun domain that heuristic drops 
the bucket out of the host being signed. Every request then fails with 
`SignatureDoesNotMatch`.
   
   The path, in `aliyun-sdk-oss` 3.17.4 — the version this module pins:
   
   ```java
   // OSSUtils.buildCanonicalHost
   boolean isCname = false;
   if (clientConfig.isSupportCname()) {
       isCname = cnameExcludeFilter(host, clientConfig.getCnameExcludeList());
   }
   if (bucket != null && !isCname && !clientConfig.isSLDEnabled()) {
       cannonicalHost.append(bucket).append(".").append(host);
   } else {
       cannonicalHost.append(host);          // <- bucket never reaches the 
signed host
   }
   ```
   
   `cnameExcludeFilter` returns `false` only when the host ends with an entry 
of the exclude list, which defaults to `aliyuncs.com, aliyun-inc.com, 
aliyun.com`. Any other host — a private-cloud or Apsara Stack deployment, a 
MinIO-style gateway — therefore takes the `isCname = true` branch. 
`supportCname` defaults to `true` in `ClientConfiguration:90`, and 
`hadoop-aliyun` 3.3.4 builds a bare `new ClientConfiguration()` 
(`AliyunOSSFileSystemStore:99`) with no reference to cname anywhere in that 
artifact, so there is currently nothing a user can set to opt out.
   
   ### What changes
   
   One new key, `fs.oss.cname.enabled`, handled the same way 
`fs.oss.sld.enabled` already is:
   
   ```java
   if (!hadoopOptions.getBoolean(OSS_CNAME_ENABLED, true)) {
       disableCname(fs);
   }
   ```
   
   `disableCname` reaches the client's `ClientConfiguration` through the 
existing `getOssClient(fs)` +
   `ReflectionUtils.getPrivateFieldValue(ossClient, "serviceClient")` pair that 
`enableSecondLevelDomain` uses, and calls `setSupportCname(false)`.
   
   Setting it after `fs.initialize(...)` is enough because the flag is read per 
request, not captured at construction: `OSSRequestMessageBuilder:170` fetches 
`innerClient.getClientConfiguration()` while building each request and hands it 
to `determineFinalEndpoint` on line 177. That is the same object — 
`ServiceClient.getClientConfiguration()` returns the instance it was 
constructed with, and `OSSClient.getClientConfiguration()` (`:426`) is a 
straight delegation to it. The neighbouring `isSLDEnabled()` is consumed one 
line later, on 178, so the existing `fs.oss.sld.enabled` path already depends 
on exactly this.
   
   ### Blast radius
   
   Unset means unchanged. The default is `true`, matching the SDK's own 
default, so a catalog that does not set the key builds precisely the client it 
built before, and a public-cloud endpoint is unaffected in either position of 
the flag — its host is on the exclude list, so the heuristic never fires. The 
second test below pins that.
   
   ### Tests
   
   Two, both asserting the host the signer actually computes rather than that a 
setter was called — `signedHost` calls `OSSUtils.determineFinalEndpoint` with 
the client's own configuration, which is the call `OSSRequestMessageBuilder` 
makes for every request.
   
   | endpoint | before | after `setSupportCname(false)` |
   |---|---|---|
   | `oss-cn-x.inter.env99.example.com` | `oss-cn-x.inter.env99.example.com` | 
`my-bucket.oss-cn-x.inter.env99.example.com` |
   | `oss-cn-hangzhou.aliyuncs.com` | `my-bucket.oss-cn-hangzhou.aliyuncs.com` 
| `my-bucket.oss-cn-hangzhou.aliyuncs.com` |
   
   The first row is the bug — a bucket that never reaches the host, so the 
client signs one string and the server another. The second is the blast-radius 
check.
   
   Making `setSupportCname` a no-op fails exactly one of the 17 tests in the 
class, and it is the first row:
   
   ```
   [ERROR] OSSFileIOTest.testDisableCnameRestoresTheBucketInTheSignedHost:563
   expected: "my-bucket.oss-cn-x.inter.env99.example.com"
    but was: "oss-cn-x.inter.env99.example.com"
   [ERROR] Tests run: 17, Failures: 1, Errors: 0, Skipped: 0
   ```
   
   The public-cloud test stays green under that mutation, which is what makes 
it a blast-radius check rather than a second copy of the first.
   
   **One thing worth knowing, because it caught me out while writing the 
test.** My first version built the client with `new 
OSSClientBuilder().build(endpoint, "ak", "sk")` and the first assertion failed 
— the bucket was *already* in the host. `ClientBuilderConfiguration extends 
ClientConfiguration` and sets `supportCname = false` in its constructor, so the 
SDK's own builder opts out of this heuristic by default. It is specifically the 
bare `new ClientConfiguration()` that `hadoop-aliyun` uses which leaves it on. 
The test therefore constructs the client the way `AliyunOSSFileSystemStore` 
does; a test written against `OSSClientBuilder` would pass without the fix and 
prove nothing.
   
   I have no private-cloud OSS deployment to point at, so what is verified here 
is the host string the SDK builds for signing, not an end-to-end request 
against such an endpoint. That is the step the reporter identified as the 
failure, and it is the step this change moves.
   
   ### API and Format
   
   No change to any existing key, signature or on-disk format. One added 
configuration key, inert unless set to `false`.
   


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