Skip to content

Commit 14c944a

Browse files
ckolli5Yannic
andauthored
Wire up credential helper to command-line flag(s) (bazelbuild#15976)
RELNOTES: Add support for fetching RPC credentials from credential helper. Progress on bazelbuild#15856 Closes bazelbuild#15947. PiperOrigin-RevId: 463083408 Change-Id: I286aa82fbb3c2376e4dba82cbb01e541c40e405e Co-authored-by: Yannic Bonenberger <[email protected]>
1 parent 508f185 commit 14c944a

File tree

10 files changed

+258
-30
lines changed

10 files changed

+258
-30
lines changed

src/main/java/com/google/devtools/build/lib/authandtls/AuthAndTLSOptions.java

+38
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,44 @@ public class AuthAndTLSOptions extends OptionsBase {
139139
+ "pings are disabled, then this setting is ignored.")
140140
public Duration grpcKeepaliveTimeout;
141141

142+
@Option(
143+
name = "experimental_credential_helper",
144+
defaultValue = "null",
145+
allowMultiple = true,
146+
converter = UnresolvedScopedCredentialHelperConverter.class,
147+
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
148+
effectTags = {OptionEffectTag.UNKNOWN},
149+
help =
150+
"Configures Credential Helpers to use for retrieving credentials for the provided scope"
151+
+ " (domain).\n\n"
152+
+ "Credentials from Credential Helpers take precedence over credentials from"
153+
+ " <code>--google_default_credentials</code>, `--google_credentials`, or"
154+
+ " <code>.netrc</code>.\n\n"
155+
+ "See https://github.com/bazelbuild/proposals/blob/main/designs/2022-06-07-bazel-credential-helpers.md"
156+
+ " for details.")
157+
public List<UnresolvedScopedCredentialHelper> credentialHelpers;
158+
159+
@Option(
160+
name = "experimental_credential_helper_timeout",
161+
defaultValue = "5s",
162+
converter = DurationConverter.class,
163+
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
164+
effectTags = {OptionEffectTag.UNKNOWN},
165+
help =
166+
"Configures the timeout for the Credential Helper.\n\n"
167+
+ "Credential Helpers failing to respond within this timeout will fail the"
168+
+ " invocation.")
169+
public Duration credentialHelperTimeout;
170+
171+
@Option(
172+
name = "experimental_credential_helper_cache_duration",
173+
defaultValue = "30m",
174+
converter = DurationConverter.class,
175+
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
176+
effectTags = {OptionEffectTag.UNKNOWN},
177+
help = "Configures the duration for which credentials from Credential Helpers are cached.")
178+
public Duration credentialHelperCacheTimeout;
179+
142180
/** One of the values of the `--credential_helper` flag. */
143181
@AutoValue
144182
public abstract static class UnresolvedScopedCredentialHelper {

src/main/java/com/google/devtools/build/lib/authandtls/GoogleAuthUtils.java

+24-11
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
import com.google.common.annotations.VisibleForTesting;
2020
import com.google.common.base.Preconditions;
2121
import com.google.common.base.Strings;
22+
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperCredentials;
2223
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment;
2324
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider;
2425
import com.google.devtools.build.lib.events.Event;
25-
import com.google.devtools.build.lib.events.Reporter;
2626
import com.google.devtools.build.lib.runtime.CommandLinePathFactory;
2727
import com.google.devtools.build.lib.vfs.FileSystem;
2828
import com.google.devtools.build.lib.vfs.Path;
@@ -222,36 +222,49 @@ public static CallCredentialsProvider newCallCredentialsProvider(@Nullable Crede
222222
}
223223

224224
/**
225-
* Create a new {@link Credentials} with following order:
225+
* Create a new {@link Credentials} retrieving call credentials in the following order:
226226
*
227227
* <ol>
228-
* <li>If authentication enabled by flags, use it to create credentials
229-
* <li>Use .netrc to provide credentials if exists
230-
* <li>Otherwise, return {@code null}
228+
* <li>If a Credential Helper is configured for the scope, use the credentials provided by the
229+
* helper.
230+
* <li>If (Google) authentication is enabled by flags, use it to create credentials.
231+
* <li>Use {@code .netrc} to provide credentials if exists.
231232
* </ol>
232233
*
233234
* @throws IOException in case the credentials can't be constructed.
234235
*/
235-
@Nullable
236236
public static Credentials newCredentials(
237-
Reporter reporter,
238-
Map<String, String> clientEnv,
237+
CredentialHelperEnvironment credentialHelperEnvironment,
238+
CommandLinePathFactory commandLinePathFactory,
239239
FileSystem fileSystem,
240240
AuthAndTLSOptions authAndTlsOptions)
241241
throws IOException {
242+
Preconditions.checkNotNull(credentialHelperEnvironment);
243+
Preconditions.checkNotNull(commandLinePathFactory);
244+
Preconditions.checkNotNull(fileSystem);
245+
Preconditions.checkNotNull(authAndTlsOptions);
246+
242247
Optional<Credentials> credentials = newGoogleCredentials(authAndTlsOptions);
243248

244249
if (credentials.isEmpty()) {
245250
// Fallback to .netrc if it exists.
246251
try {
247-
credentials = newCredentialsFromNetrc(clientEnv, fileSystem);
252+
credentials =
253+
newCredentialsFromNetrc(credentialHelperEnvironment.getClientEnvironment(), fileSystem);
248254
} catch (IOException e) {
249255
// TODO(yannic): Make this fail the build.
250-
reporter.handle(Event.warn(e.getMessage()));
256+
credentialHelperEnvironment.getEventReporter().handle(Event.warn(e.getMessage()));
251257
}
252258
}
253259

254-
return credentials.orElse(null);
260+
return new CredentialHelperCredentials(
261+
newCredentialHelperProvider(
262+
credentialHelperEnvironment,
263+
commandLinePathFactory,
264+
authAndTlsOptions.credentialHelpers),
265+
credentialHelperEnvironment,
266+
credentials,
267+
authAndTlsOptions.credentialHelperCacheTimeout);
255268
}
256269

257270
/**

src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper/BUILD

+3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ java_library(
1717
"//src/main/java/com/google/devtools/build/lib/events",
1818
"//src/main/java/com/google/devtools/build/lib/shell",
1919
"//src/main/java/com/google/devtools/build/lib/vfs",
20+
"//third_party:auth",
2021
"//third_party:auto_value",
22+
"//third_party:caffeine",
2123
"//third_party:error_prone_annotations",
2224
"//third_party:gson",
2325
"//third_party:guava",
26+
"//third_party:jsr305",
2427
],
2528
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2022 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.devtools.build.lib.authandtls.credentialhelper;
16+
17+
import com.github.benmanes.caffeine.cache.CacheLoader;
18+
import com.github.benmanes.caffeine.cache.Caffeine;
19+
import com.github.benmanes.caffeine.cache.LoadingCache;
20+
import com.google.auth.Credentials;
21+
import com.google.common.base.Preconditions;
22+
import com.google.common.collect.ImmutableMap;
23+
import java.io.IOException;
24+
import java.net.URI;
25+
import java.time.Duration;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Optional;
29+
import javax.annotation.Nullable;
30+
31+
/**
32+
* Implementation of {@link Credentials} which fetches credentials by invoking a {@code credential
33+
* helper} as subprocess, falling back to another {@link Credentials} if no suitable helper exists.
34+
*/
35+
public class CredentialHelperCredentials extends Credentials {
36+
private final Optional<Credentials> fallbackCredentials;
37+
38+
private final LoadingCache<URI, GetCredentialsResponse> credentialCache;
39+
40+
public CredentialHelperCredentials(
41+
CredentialHelperProvider credentialHelperProvider,
42+
CredentialHelperEnvironment credentialHelperEnvironment,
43+
Optional<Credentials> fallbackCredentials,
44+
Duration cacheTimeout) {
45+
Preconditions.checkNotNull(credentialHelperProvider);
46+
Preconditions.checkNotNull(credentialHelperEnvironment);
47+
this.fallbackCredentials = Preconditions.checkNotNull(fallbackCredentials);
48+
Preconditions.checkNotNull(cacheTimeout);
49+
Preconditions.checkArgument(
50+
!cacheTimeout.isNegative() && !cacheTimeout.isZero(),
51+
"Cache timeout must be greater than 0");
52+
53+
credentialCache =
54+
Caffeine.newBuilder()
55+
.expireAfterWrite(cacheTimeout)
56+
.build(
57+
new CredentialHelperCacheLoader(
58+
credentialHelperProvider, credentialHelperEnvironment));
59+
}
60+
61+
@Override
62+
public String getAuthenticationType() {
63+
if (fallbackCredentials.isPresent()) {
64+
return "credential-helper-with-fallback-" + fallbackCredentials.get().getAuthenticationType();
65+
}
66+
67+
return "credential-helper";
68+
}
69+
70+
@Override
71+
public Map<String, List<String>> getRequestMetadata(URI uri) throws IOException {
72+
Preconditions.checkNotNull(uri);
73+
74+
Optional<Map<String, List<String>>> credentials = getRequestMetadataFromCredentialHelper(uri);
75+
if (credentials.isPresent()) {
76+
return credentials.get();
77+
}
78+
79+
if (fallbackCredentials.isPresent()) {
80+
return fallbackCredentials.get().getRequestMetadata(uri);
81+
}
82+
83+
return ImmutableMap.of();
84+
}
85+
86+
@SuppressWarnings("unchecked") // Map<String, ImmutableList<String>> to Map<String<List<String>>
87+
private Optional<Map<String, List<String>>> getRequestMetadataFromCredentialHelper(URI uri) {
88+
Preconditions.checkNotNull(uri);
89+
90+
GetCredentialsResponse response = credentialCache.get(uri);
91+
92+
return Optional.ofNullable(response).map(value -> (Map) value.getHeaders());
93+
}
94+
95+
@Override
96+
public boolean hasRequestMetadata() {
97+
return true;
98+
}
99+
100+
@Override
101+
public boolean hasRequestMetadataOnly() {
102+
return false;
103+
}
104+
105+
@Override
106+
public void refresh() throws IOException {
107+
if (fallbackCredentials.isPresent()) {
108+
fallbackCredentials.get().refresh();
109+
}
110+
111+
credentialCache.invalidateAll();
112+
}
113+
114+
private static final class CredentialHelperCacheLoader
115+
implements CacheLoader<URI, GetCredentialsResponse> {
116+
private final CredentialHelperProvider credentialHelperProvider;
117+
private final CredentialHelperEnvironment credentialHelperEnvironment;
118+
119+
public CredentialHelperCacheLoader(
120+
CredentialHelperProvider credentialHelperProvider,
121+
CredentialHelperEnvironment credentialHelperEnvironment) {
122+
this.credentialHelperProvider = Preconditions.checkNotNull(credentialHelperProvider);
123+
this.credentialHelperEnvironment = Preconditions.checkNotNull(credentialHelperEnvironment);
124+
}
125+
126+
@Nullable
127+
@Override
128+
public GetCredentialsResponse load(URI uri) throws IOException, InterruptedException {
129+
Preconditions.checkNotNull(uri);
130+
131+
Optional<CredentialHelper> maybeCredentialHelper =
132+
credentialHelperProvider.findCredentialHelper(uri);
133+
if (maybeCredentialHelper.isEmpty()) {
134+
return null;
135+
}
136+
CredentialHelper credentialHelper = maybeCredentialHelper.get();
137+
138+
return credentialHelper.getCredentials(credentialHelperEnvironment, uri);
139+
}
140+
}
141+
}

src/main/java/com/google/devtools/build/lib/buildeventservice/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ java_library(
3939
"//src/main/java/com/google/devtools/build/lib:runtime",
4040
"//src/main/java/com/google/devtools/build/lib/analysis:test/test_configuration",
4141
"//src/main/java/com/google/devtools/build/lib/authandtls",
42+
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper",
4243
"//src/main/java/com/google/devtools/build/lib/bugreport",
4344
"//src/main/java/com/google/devtools/build/lib/buildeventservice/client",
4445
"//src/main/java/com/google/devtools/build/lib/buildeventstream",

src/main/java/com/google/devtools/build/lib/buildeventservice/BazelBuildEventServiceModule.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.common.collect.ImmutableSet;
2525
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
2626
import com.google.devtools.build.lib.authandtls.GoogleAuthUtils;
27+
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment;
2728
import com.google.devtools.build.lib.buildeventservice.client.BuildEventServiceClient;
2829
import com.google.devtools.build.lib.buildeventservice.client.BuildEventServiceGrpcClient;
2930
import com.google.devtools.build.lib.runtime.CommandEnvironment;
@@ -86,8 +87,13 @@ protected BuildEventServiceClient getBesClient(
8687

8788
Credentials credentials =
8889
GoogleAuthUtils.newCredentials(
89-
env.getReporter(),
90-
env.getClientEnv(),
90+
CredentialHelperEnvironment.newBuilder()
91+
.setEventReporter(env.getReporter())
92+
.setWorkspacePath(env.getWorkspace())
93+
.setClientEnvironment(env.getClientEnv())
94+
.setHelperExecutionTimeout(authAndTLSOptions.credentialHelperTimeout)
95+
.build(),
96+
env.getCommandLinePathFactory(),
9197
env.getRuntime().getFileSystem(),
9298
newConfig.authAndTLSOptions());
9399

src/main/java/com/google/devtools/build/lib/remote/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ java_library(
4545
":Retrier",
4646
"//src/main/java/com/google/devtools/build/lib:build-request-options",
4747
"//src/main/java/com/google/devtools/build/lib:runtime",
48+
"//src/main/java/com/google/devtools/build/lib:runtime/command_line_path_factory",
4849
"//src/main/java/com/google/devtools/build/lib/actions",
4950
"//src/main/java/com/google/devtools/build/lib/actions:action_input_helper",
5051
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",

0 commit comments

Comments
 (0)