Skip to content

bug(services/obs): internal/IP endpoints are treated as bucket-bound custom domains, causing signature mismatch #8321

Description

@zqr10159

OpenDAL version or commit

Downstream application: DBX Files plugin files-v0.1.59 (jinpy666/dbx-plugin-files, commit 3da6b99629a6bc62412b3427a2cddcaa47a0659d). Its Cargo.lock pins opendal-service-obs 0.57.0 and reqsign-huaweicloud-obs 3.0.6.

Affected service or component

services/obs, against a real OBS deployment reached through an internal service endpoint.

Describe the bug

I cannot connect to my OBS deployment through the downstream plugin: its storage check (list objects) fails with HTTP 403 / SignatureDoesNotMatch. The bucket is a separate configuration value, not the endpoint hostname. I also tried an IP endpoint and it failed. Official Go and Java OBS SDKs work with this deployment and my credentials.

Source inspection suggests that the OBS backend unconditionally interprets any endpoint outside its recognized Huawei domain suffixes as a bucket-bound custom domain (CNAME). This does not represent an ordinary internal service endpoint or an IP endpoint. There appears to be no explicit path-style/CNAME selection in this version's OBS config.

Steps to Reproduce

Actual reproduction was through DBX Files plugin files-v0.1.59, against my real internal OBS deployment:

  1. Select the Huawei OBS protocol.
  2. Configure the service endpoint, a separate existing bucket, and valid AK/SK.
  3. Run the connection test, which lists objects.
  4. Observe HTTP 403 / SignatureDoesNotMatch. Retrying with an IP endpoint also fails.

Sanitized configuration (placeholders, not a publicly reachable reproduction service):

protocol = obs
endpoint = http://obs.internal.example
bucket = example-bucket
access_key_id = <redacted>
secret_access_key = <redacted>

The exact successful Go/Java SDK addressing and signature options have not yet been included in this report. No standalone Rust reproduction has been run; the observed failure is from the downstream application, and the diagnosis below is based on the locked dependency source.

Actual Behavior

Sanitized error from the actual domain-endpoint attempt:

Storage check failed: PermissionDenied (permanent) at list
uri: http://obs.internal.example/?prefix&delimiter=/&max-keys=1
status: 403
service: obs, path: /, listed: 0
ObsError {
  code: "SignatureDoesNotMatch",
  message: "The request signature we calculated does not match the signature you provided. Check your key and signing method."
}

In opendal-service-obs 0.57.0, src/backend.rs chooses:

let (endpoint, is_obs_default) = {
    let host = uri.host().unwrap_or_default().to_string();
    if host.starts_with("obs.")
        && (host.ends_with(".myhuaweicloud.com") || host.ends_with(".huawei.com"))
    {
        (format!("{bucket}.{host}"), true)
    } else {
        (host, false)
    }
};
// ...
let request_signer = RequestSigner::new(if is_obs_default { &bucket } else { &endpoint });

For the sanitized configuration above, source tracing produces a request to the endpoint root and a canonicalized resource /obs.internal.example/, rather than addressing/signing the configured bucket. An IP similarly selects the custom-domain branch. This is appropriate for an explicitly configured bucket-bound custom domain, but not for a generic service endpoint.

Expected Behavior

Provide an explicit way to use an ordinary internal/IP service endpoint, independently of bucket-bound CNAME access. For example, with path-style addressing, the bucket should be present in the request path and included exactly once in CanonicalizedResource. Existing public endpoint and actual CNAME behavior should remain supported.

Additional Context

Reproduction confirmation

  • I reproduced the connection/list failure against the actual OBS service through the affected downstream application, not only a mock or source analysis.

Are you willing to submit a PR to fix this bug?

  • Yes, I would like to submit a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions