TTLabs / EvaporateJS

Javascript library for browser to S3 multipart resumable uploads
1.82k stars 206 forks source link

Load Balanced Signing #468

Open rcollette opened 3 years ago

rcollette commented 3 years ago

In a load balanced Fargate service, each task has it's own set of temporary credentials but share the same IAM role.

EvaporateJS only allows the configuration of one set of access key and session key. If the instance signing a URL has a different set of keys from the one supplied in the client setup, then I get 403 errors.

The only way I can think of making this work without something like sticky sessions in the LB (yuck), is to store the temporary credentials in a some shared store (ex. Redis) such that all load balanced instances are signing with the same credentials. But then there is session expiry management throwing a wrench in there.

Is there a typical pattern for handling signing or configuring the client when using an IAM Role in a load balanced scenario?

rcollette commented 3 years ago

Hacking on this a little bit. The this context of customAuthMethod provides access to aws_key. I could change it there and it shows up in the request that is made, although I wonder if it is "multiple request safe". However, the canonical request is already formed at this point and I haven't been able to alter it so I can set the session key header.

What I need is a way to set the access key and session key on an a per request basis.

jakubzitny commented 3 years ago

Is this still relevant @rcollette? This functionality is not supported here afaik, but PRs are welcome.

rcollette commented 3 years ago

@jakubzitny This is still an issue.

Thinking about it more, the ideal situation for this would be that the API returning the signed url, could return an object rather than a string. The object would contain both the signed url and the session token that was used to sign the url, and when a session value is present, it would set the appropriate request headers.

{
  signedUrl: ""
  iamSessionToken: ""
}

The headers we currently set are shown here:

  public async uploadToS3(
    targetFile: File,
    guid: string,
    progressTracker: (p: number, stats: TransferStats) => void
  ): Promise<any> {
    // Get the signing configuration each time because the session token may get renewed.
    const configuration = await this._getSigningConfiguration();
    return this._evaporateInstance.add({
      name: `software/${guid}/${targetFile.name}`,
      file: targetFile,
      complete: (_result) => {},
      progress: progressTracker,
      error: (error) => {
        this._logger.error(error);
      },
      // IAM Roles use a temporary credential that has a session token that must be passed
      xAmzHeadersCommon: configuration.sessionToken
        ? {
            'x-amz-security-token': configuration.sessionToken,
          }
        : undefined,
      xAmzHeadersAtInitiate: configuration.sessionToken
        ? {
            'x-amz-security-token': configuration.sessionToken,
          }
        : undefined,
    });
  }
jakubzitny commented 3 years ago

If the signedUrl is not working for you, you can work around it using customAuthMethod and signResponseHandler, read more about it [here](https://github.com/TTLabs/EvaporateJS/wiki/Evaporate.create()).

rcollette commented 3 years ago

Neither of those methods provides the ability to set the session key header. That value is set in the evaporate instance. I need a way to set x-amz-security-token on a per request basis since the each instance in the load balanced pool (Fargate, Beanstalk, etc) that is performing signing has a different sessionToken.

I might be willing to contribute but "V3" has been hanging out there for so long with no momentum, I'm hesitant to allocate time that will hit a roadblock anyway.

jakubzitny commented 3 years ago

I see, sorry I didn't realize this. Okay, let's sync with others to see if we can push v3. (#450)