minio / minio-js

MinIO Client SDK for Javascript
https://docs.min.io/docs/javascript-client-quickstart-guide.html
Apache License 2.0
928 stars 273 forks source link

Host Header is set incorrectly for ipv6 addresses with custom port #1241

Closed BenjaminBruenau closed 9 months ago

BenjaminBruenau commented 9 months ago

Specifiying an ipv6 address as an endpoint with a non-default port in the MinioClient constructor, similar to the snippet seen below, will result in an S3Error: Invalid Request (address 2606:4700::6810:1922:9000: too many colons in address) Error every time the Object Storage is accessed (e.g. when trying to upload a file).

new Minio.Client({
            endPoint: '2606:4700::6810:1922',
            port: 9000,
            useSSL: false,
            accessKey: useRuntimeConfig().minioKey,
            secretKey: useRuntimeConfig().minioSecret,
 })

It seems like the problem is caused by this line: https://github.com/minio/minio-js/blob/796d0d6e57475edbd4a1c18c8bf314c1e285311f/src/internal/client.ts#L420

Which is not taking into account that an ipv6 address should be enclosed in square brackets and sets the host-header to 2606:4700::6810:1922:9000, probably leading to the port being read as part of the ipv6 address later, resulting in a rejection of the request. Enclosing the endpoint in the Client constructor with square brackets will throw an InvalidEndpointError.

I would expect ipv6 addresses in the host header to be handled in a similar way as it is done by nodejs internally (when no host-header is specified): (https://github.com/nodejs/node/blob/59ebf6d397c9c468e5beb75b9ed8c82ab0603e3b/lib/_http_client.js#L290-L307)

    if (host && !this.getHeader('host') && setHost) {
      let hostHeader = host;

      // For the Host header, ensure that IPv6 addresses are enclosed
      // in square brackets, as defined by URI formatting
      // https://tools.ietf.org/html/rfc3986#section-3.2.2
      const posColon = StringPrototypeIndexOf(hostHeader, ':');
      if (posColon !== -1 &&
          StringPrototypeIncludes(hostHeader, ':', posColon + 1) &&
          StringPrototypeCharCodeAt(hostHeader, 0) !== 91/* '[' */) {
        hostHeader = `[${hostHeader}]`;
      }

      if (port && +port !== defaultPort) {
        hostHeader += ':' + port;
      }
      this.setHeader('Host', hostHeader);
    }

We worked around this problem by having a domain point to our ipv6-only cloud server and using that for the endpoint option.