forked from bazelbuild/bazel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of aws-sig-v4 HTTP auth for S3 caching
- Loading branch information
1 parent
9ac5dfa
commit 1967b90
Showing
18 changed files
with
1,313 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
src/main/java/com/google/devtools/build/lib/authentication/aws/AwsAuthModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package com.google.devtools.build.lib.authentication.aws; | ||
|
||
import com.amazonaws.auth.AWSCredentialsProvider; | ||
import com.amazonaws.auth.AWSCredentialsProviderChain; | ||
import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
import com.amazonaws.auth.BasicAWSCredentials; | ||
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; | ||
import com.amazonaws.auth.profile.ProfileCredentialsProvider; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.base.Strings; | ||
import com.google.common.collect.ImmutableList; | ||
import com.google.devtools.build.lib.remote.options.RemoteOptions; | ||
import com.google.devtools.build.lib.runtime.AuthHeaderRequest; | ||
import com.google.devtools.build.lib.runtime.AuthHeadersProvider; | ||
import com.google.devtools.build.lib.runtime.BlazeModule; | ||
import com.google.devtools.build.lib.runtime.Command; | ||
import com.google.devtools.build.lib.runtime.CommandEnvironment; | ||
import com.google.devtools.build.lib.runtime.ServerBuilder; | ||
import com.google.devtools.build.lib.util.AbruptExitException; | ||
import com.google.devtools.build.lib.util.ExitCode; | ||
import com.google.devtools.common.options.OptionsBase; | ||
import com.google.devtools.common.options.OptionsParsingResult; | ||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.annotation.Nullable; | ||
|
||
public class AwsAuthModule extends BlazeModule { | ||
|
||
private static final String SERVICE = "s3"; | ||
private final AuthHeadersProviderDelegate delegate = new AuthHeadersProviderDelegate(); | ||
|
||
@Override | ||
public void serverInit(OptionsParsingResult startupOptions, ServerBuilder builder) | ||
throws AbruptExitException { | ||
super.serverInit(startupOptions, builder); | ||
builder.addAuthHeadersProvider("aws", delegate); | ||
} | ||
|
||
@Override | ||
public void beforeCommand(CommandEnvironment env) throws AbruptExitException { | ||
delegate.setDelegate(null); | ||
|
||
final AwsAuthOptions opts = env.getOptions().getOptions(AwsAuthOptions.class); | ||
final RemoteOptions remoteOpts = env.getOptions().getOptions(RemoteOptions.class); | ||
if (remoteOpts == null || opts == null) { | ||
return; | ||
} | ||
|
||
final AwsRegion region = AwsRegion.determineRegion(opts.awsRegion, remoteOpts.remoteCache); | ||
if (region == null) { | ||
return; | ||
} | ||
|
||
final AWSCredentialsProvider credsProvider = newCredsProvider(opts); | ||
if (credsProvider != null) { | ||
this.delegate.setDelegate(new AwsV4AuthHeadersProvider(region, SERVICE, credsProvider, true)); | ||
} | ||
} | ||
|
||
@Override | ||
public Iterable<Class<? extends OptionsBase>> getCommandOptions(final Command command) { | ||
return "build".equals(command.name()) | ||
? ImmutableList.of(AwsAuthOptions.class) | ||
: ImmutableList.of(); | ||
} | ||
|
||
@Nullable | ||
private static AWSCredentialsProvider newCredsProvider(final AwsAuthOptions opts) | ||
throws AbruptExitException { | ||
final ImmutableList.Builder<AWSCredentialsProvider> creds = ImmutableList.builder(); | ||
|
||
if (opts.awsAccessKeyId != null || opts.awsSecretAccessKey != null) { | ||
ensure(opts.awsAccessKeyId != null, "AWS Access key provided, but missing Secret Key"); | ||
ensure(opts.awsSecretAccessKey != null, "AWS Secret key provided, but missing Access Key"); | ||
|
||
final BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials( | ||
opts.awsAccessKeyId, opts.awsSecretAccessKey); | ||
creds.add(new AWSStaticCredentialsProvider(basicAWSCredentials)); | ||
} | ||
|
||
if (opts.awsProfile != null) { | ||
creds.add(new ProfileCredentialsProvider(opts.awsProfile)); | ||
} | ||
|
||
if (opts.useAwsDefaultCredentials) { | ||
creds.add(DefaultAWSCredentialsProviderChain.getInstance()); | ||
} | ||
|
||
final List<AWSCredentialsProvider> providers = creds.build(); | ||
return providers.isEmpty() ? null : new AWSCredentialsProviderChain(providers); | ||
} | ||
|
||
private static void ensure(final boolean condition, final String msg) throws AbruptExitException { | ||
if (!condition) { | ||
throw new AbruptExitException(msg, ExitCode.COMMAND_LINE_ERROR); | ||
} | ||
} | ||
|
||
private static class AuthHeadersProviderDelegate implements AuthHeadersProvider { | ||
|
||
private volatile AuthHeadersProvider delegate; | ||
|
||
public void setDelegate(AuthHeadersProvider delegate) { | ||
this.delegate = delegate; | ||
} | ||
|
||
@Override | ||
public String getType() { | ||
return delegate.getType(); | ||
} | ||
|
||
@Override | ||
public Map<String, List<String>> getRequestHeaders(AuthHeaderRequest request) throws IOException { | ||
Preconditions.checkState(delegate != null, "delegate has not been initialized"); | ||
return delegate.getRequestHeaders(request); | ||
} | ||
|
||
@Override | ||
public void refresh() throws IOException { | ||
Preconditions.checkState(delegate != null, "delegate has not been initialized"); | ||
delegate.refresh(); | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return delegate != null && delegate.isEnabled(); | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/main/java/com/google/devtools/build/lib/authentication/aws/AwsAuthOptions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.google.devtools.build.lib.authentication.aws; | ||
|
||
import com.google.devtools.common.options.Option; | ||
import com.google.devtools.common.options.OptionDocumentationCategory; | ||
import com.google.devtools.common.options.OptionEffectTag; | ||
import com.google.devtools.common.options.OptionsBase; | ||
|
||
public class AwsAuthOptions extends OptionsBase { | ||
|
||
@Option( | ||
name = "aws_default_credentials", | ||
defaultValue = "false", | ||
category = "remote", | ||
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, | ||
effectTags = {OptionEffectTag.UNKNOWN}, | ||
help = | ||
"Whether to use 'AWS Default Credentials' for authentication." | ||
+ "See https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html" | ||
+ " for details. Disabled by default." | ||
) | ||
public boolean useAwsDefaultCredentials; | ||
|
||
@Option( | ||
name = "aws_access_key_id", | ||
defaultValue = "null", | ||
category = "remote", | ||
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, | ||
effectTags = {OptionEffectTag.UNKNOWN}, | ||
help = "Use a specific AWS_ACCESS_KEY_ID for authentication" | ||
) | ||
public String awsAccessKeyId; | ||
|
||
@Option( | ||
name = "aws_secret_access_key", | ||
defaultValue = "null", | ||
category = "remote", | ||
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, | ||
effectTags = {OptionEffectTag.UNKNOWN}, | ||
help = "Use a specific AWS_SECRET_ACCESS_KEY for authentication" | ||
) | ||
public String awsSecretAccessKey; | ||
|
||
@Option( | ||
name = "aws_profile", | ||
defaultValue = "null", | ||
category = "remote", | ||
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, | ||
effectTags = {OptionEffectTag.UNKNOWN}, | ||
help = "Use a specific profile for credentials" | ||
) | ||
public String awsProfile; | ||
|
||
@Option( | ||
name = "aws_region", | ||
defaultValue = "null", | ||
category = "remote", | ||
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, | ||
effectTags = {OptionEffectTag.UNKNOWN}, | ||
help = "Override AWS region detection and force a specific bucket region" | ||
) | ||
public String awsRegion; | ||
|
||
|
||
} |
Oops, something went wrong.