Skip to content

Commit

Permalink
GrpcRemoteDownloader: optionally propagate credentials to remote server
Browse files Browse the repository at this point in the history
In a multi-tenancy server deployment setup, the clients might want to
treat the remote downloader server as a pull-through proxy and use it to
download from private storage systems.

Currently, we do support it via --remote_downloader_headers. However
this scheme does not apply to the specific URL, while credentials and
authentication could sometimes be host/domain specific.

Add a flag to let users opt-in to credentials propagation to the remote
server. This is off by default as not all remote servers can be
trusted. When the flag is enabled, URL-specific credentials from Netrc
or a custom credentials helper can be propagated to the remote server.

The server implementation needs to support the new
http_header_url:<url-index>:<header-key> qualifier where the
url-index is a 0-based position of the URL inside the
FetchBlobRequest's uris field. This new qualifier is modeled after the
existing http_header qualifier.
  • Loading branch information
sluongng committed Sep 10, 2024
1 parent 9187a7e commit 555fd05
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.grpc.StatusRuntimeException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -83,6 +84,9 @@ public class GrpcRemoteDownloader implements AutoCloseable, Downloader {
// delimit the qualifier prefix which denotes an HTTP header qualifer from the
// header name itself.
private static final String QUALIFIER_HTTP_HEADER_PREFIX = "http_header:";
// Same as HTTP_HEADER_PREFIX, but only apply for a specific URL.
// The index starts from 0 and corresponds to the URL index in the request.
private static final String QUALIFIER_HTTP_HEADER_URL_PREFIX = "http_header_url:";

public GrpcRemoteDownloader(
String buildRequestId,
Expand Down Expand Up @@ -135,7 +139,13 @@ public void download(

final FetchBlobRequest request =
newFetchBlobRequest(
options.remoteInstanceName, urls, checksum, canonicalId, digestFunction, headers);
options.remoteInstanceName,
urls,
checksum,
canonicalId,
digestFunction,
headers,
credentials);
try {
FetchBlobResponse response =
retrier.execute(
Expand Down Expand Up @@ -184,13 +194,35 @@ static FetchBlobRequest newFetchBlobRequest(
Optional<Checksum> checksum,
String canonicalId,
DigestFunction.Value digestFunction,
Map<String, List<String>> headers) {
Map<String, List<String>> headers,
Credentials credentials)
throws IOException {
FetchBlobRequest.Builder requestBuilder =
FetchBlobRequest.newBuilder()
.setInstanceName(instanceName)
.setDigestFunction(digestFunction);
for (URL url : urls) {
for (int i = 0; i < urls.size(); i++) {
var url = urls.get(i);
requestBuilder.addUris(url.toString());

if (!options.remoteDownloaderPropagateCredentials) {
continue;
}

try {
var metadata = credentials.getRequestMetadata(url.toURI());
for (var entry : metadata.entrySet()) {
for (var value : entry.getValue()) {
requestBuilder.addQualifiers(
Qualifier.newBuilder()
.setName(QUALIFIER_HTTP_HEADER_URL_PREFIX + i + ":" + entry.getKey())
.setValue(value)
.build());
}
}
} catch (URISyntaxException e) {
throw new IOException(e);
}
}

if (checksum.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ public final class RemoteOptions extends CommonRemoteOptions {
help = "Whether to fall back to the local downloader if remote downloader fails.")
public boolean remoteDownloaderLocalFallback;

@Option(
name = "experimental_remote_downloader_propagate_credentials",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.REMOTE,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Whether to propagate credentials from netrc and credential helper to the remote downloader server. "
+ "This requires the server implementation to support Bazel's `http_header_url` Qualifier.")
public boolean remoteDownloaderPropagateCredentials;

@Option(
name = "remote_header",
converter = Converters.AssignmentConverter.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ public void testFetchBlobRequest() throws Exception {
DIGEST_UTIL.getDigestFunction(),
ImmutableMap.of(
"Authorization", ImmutableList.of("Basic Zm9vOmJhcg=="),
"X-Custom-Token", ImmutableList.of("foo", "bar")));
"X-Custom-Token", ImmutableList.of("foo", "bar")),
StaticCredentials.EMPTY);

assertThat(request)
.isEqualTo(
Expand Down

0 comments on commit 555fd05

Please sign in to comment.