Skip to content

Commit 96d23d3

Browse files
tjgqYannic
andauthored
Add netrc support to --bes_backend (#15970)
Progress on #15856 Fixes #15928 Closes #15930. PiperOrigin-RevId: 462379746 Change-Id: Ia7ae470bdcbd97c6cb42cc290c3891393ec9ce3a Co-authored-by: Yannic Bonenberger <[email protected]>
1 parent e4ee344 commit 96d23d3

14 files changed

+268
-200
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ java_library(
1515
srcs = glob(["*.java"]),
1616
deps = [
1717
"//src/main/java/com/google/devtools/build/lib/concurrent",
18+
"//src/main/java/com/google/devtools/build/lib/events",
19+
"//src/main/java/com/google/devtools/build/lib/vfs",
1820
"//src/main/java/com/google/devtools/common/options",
1921
"//third_party:auth",
2022
"//third_party:auto_value",

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

+94-14
Original file line numberDiff line numberDiff line change
@@ -19,6 +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.events.Event;
23+
import com.google.devtools.build.lib.events.Reporter;
24+
import com.google.devtools.build.lib.vfs.FileSystem;
25+
import com.google.devtools.build.lib.vfs.Path;
2226
import io.grpc.CallCredentials;
2327
import io.grpc.ClientInterceptor;
2428
import io.grpc.ManagedChannel;
@@ -41,6 +45,8 @@
4145
import java.io.IOException;
4246
import java.io.InputStream;
4347
import java.util.List;
48+
import java.util.Map;
49+
import java.util.Optional;
4450
import java.util.concurrent.Executor;
4551
import java.util.concurrent.TimeUnit;
4652
import javax.annotation.Nullable;
@@ -186,14 +192,17 @@ private static NettyChannelBuilder newNettyChannelBuilder(String targetUrl, Stri
186192
}
187193

188194
/**
189-
* Create a new {@link CallCredentials} object.
195+
* Create a new {@link CallCredentials} object from the authentication flags, or null if no flags
196+
* are set.
190197
*
191-
* @throws IOException in case the call credentials can't be constructed.
198+
* @throws IOException in case the credentials can't be constructed.
192199
*/
193-
public static CallCredentials newCallCredentials(AuthAndTLSOptions options) throws IOException {
194-
Credentials creds = newCredentials(options);
195-
if (creds != null) {
196-
return MoreCallCredentials.from(creds);
200+
@Nullable
201+
public static CallCredentials newGoogleCallCredentials(AuthAndTLSOptions options)
202+
throws IOException {
203+
Optional<Credentials> creds = newGoogleCredentials(options);
204+
if (creds.isPresent()) {
205+
return MoreCallCredentials.from(creds.get());
197206
}
198207
return null;
199208
}
@@ -210,18 +219,52 @@ public static CallCredentialsProvider newCallCredentialsProvider(@Nullable Crede
210219
}
211220

212221
/**
213-
* Create a new {@link Credentials} object, or {@code null} if no options are provided.
222+
* Create a new {@link Credentials} with following order:
223+
*
224+
* <ol>
225+
* <li>If authentication enabled by flags, use it to create credentials
226+
* <li>Use .netrc to provide credentials if exists
227+
* <li>Otherwise, return {@code null}
228+
* </ol>
214229
*
215230
* @throws IOException in case the credentials can't be constructed.
216231
*/
217232
@Nullable
218-
public static Credentials newCredentials(@Nullable AuthAndTLSOptions options) throws IOException {
233+
public static Credentials newCredentials(
234+
Reporter reporter,
235+
Map<String, String> clientEnv,
236+
FileSystem fileSystem,
237+
AuthAndTLSOptions authAndTlsOptions)
238+
throws IOException {
239+
Optional<Credentials> credentials = newGoogleCredentials(authAndTlsOptions);
240+
241+
if (credentials.isEmpty()) {
242+
// Fallback to .netrc if it exists.
243+
try {
244+
credentials = newCredentialsFromNetrc(clientEnv, fileSystem);
245+
} catch (IOException e) {
246+
// TODO(yannic): Make this fail the build.
247+
reporter.handle(Event.warn(e.getMessage()));
248+
}
249+
}
250+
251+
return credentials.orElse(null);
252+
}
253+
254+
/**
255+
* Create a new {@link Credentials} object from the authentication flags, or null if no flags are
256+
* set.
257+
*
258+
* @throws IOException in case the credentials can't be constructed.
259+
*/
260+
public static Optional<Credentials> newGoogleCredentials(@Nullable AuthAndTLSOptions options)
261+
throws IOException {
219262
if (options == null) {
220-
return null;
263+
return Optional.empty();
221264
} else if (options.googleCredentials != null) {
222265
// Credentials from file
223266
try (InputStream authFile = new FileInputStream(options.googleCredentials)) {
224-
return newCredentials(authFile, options.googleAuthScopes);
267+
return Optional.of(newGoogleCredentialsFromFile(authFile, options.googleAuthScopes));
225268
} catch (FileNotFoundException e) {
226269
String message =
227270
String.format(
@@ -230,10 +273,11 @@ public static Credentials newCredentials(@Nullable AuthAndTLSOptions options) th
230273
throw new IOException(message, e);
231274
}
232275
} else if (options.useGoogleDefaultCredentials) {
233-
return newCredentials(
234-
null /* Google Application Default Credentials */, options.googleAuthScopes);
276+
return Optional.of(
277+
newGoogleCredentialsFromFile(
278+
null /* Google Application Default Credentials */, options.googleAuthScopes));
235279
}
236-
return null;
280+
return Optional.empty();
237281
}
238282

239283
/**
@@ -242,7 +286,7 @@ public static Credentials newCredentials(@Nullable AuthAndTLSOptions options) th
242286
* @throws IOException in case the credentials can't be constructed.
243287
*/
244288
@VisibleForTesting
245-
public static Credentials newCredentials(
289+
public static Credentials newGoogleCredentialsFromFile(
246290
@Nullable InputStream credentialsFile, List<String> authScopes) throws IOException {
247291
try {
248292
GoogleCredentials creds =
@@ -258,4 +302,40 @@ public static Credentials newCredentials(
258302
throw new IOException(message, e);
259303
}
260304
}
305+
306+
/**
307+
* Create a new {@link Credentials} object by parsing the .netrc file with following order to
308+
* search it:
309+
*
310+
* <ol>
311+
* <li>If environment variable $NETRC exists, use it as the path to the .netrc file
312+
* <li>Fallback to $HOME/.netrc
313+
* </ol>
314+
*
315+
* @return the {@link Credentials} object or {@code null} if there is no .netrc file.
316+
* @throws IOException in case the credentials can't be constructed.
317+
*/
318+
@VisibleForTesting
319+
static Optional<Credentials> newCredentialsFromNetrc(
320+
Map<String, String> clientEnv, FileSystem fileSystem) throws IOException {
321+
Optional<String> netrcFileString =
322+
Optional.ofNullable(clientEnv.get("NETRC"))
323+
.or(() -> Optional.ofNullable(clientEnv.get("HOME")).map(home -> home + "/.netrc"));
324+
if (netrcFileString.isEmpty()) {
325+
return Optional.empty();
326+
}
327+
328+
Path netrcFile = fileSystem.getPath(netrcFileString.get());
329+
if (!netrcFile.exists()) {
330+
return Optional.empty();
331+
}
332+
333+
try {
334+
Netrc netrc = NetrcParser.parseAndClose(netrcFile.getInputStream());
335+
return Optional.of(new NetrcCredentials(netrc));
336+
} catch (IOException e) {
337+
throw new IOException(
338+
"Failed to parse " + netrcFile.getPathString() + ": " + e.getMessage(), e);
339+
}
340+
}
261341
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ java_library(
5757
"//src/main/java/com/google/devtools/build/lib/util/io:out-err",
5858
"//src/main/java/com/google/devtools/common/options",
5959
"//src/main/protobuf:failure_details_java_proto",
60+
"//third_party:auth",
6061
"//third_party:auto_value",
6162
"//third_party:flogger",
6263
"//third_party:guava",

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

+19-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
package com.google.devtools.build.lib.buildeventservice;
1616

17+
import com.google.auth.Credentials;
1718
import com.google.auto.value.AutoValue;
1819
import com.google.common.annotations.VisibleForTesting;
20+
import com.google.common.base.Preconditions;
1921
import com.google.common.base.Strings;
2022
import com.google.common.collect.ImmutableList;
2123
import com.google.common.collect.ImmutableMap;
@@ -24,9 +26,11 @@
2426
import com.google.devtools.build.lib.authandtls.GoogleAuthUtils;
2527
import com.google.devtools.build.lib.buildeventservice.client.BuildEventServiceClient;
2628
import com.google.devtools.build.lib.buildeventservice.client.BuildEventServiceGrpcClient;
29+
import com.google.devtools.build.lib.runtime.CommandEnvironment;
2730
import io.grpc.ClientInterceptor;
2831
import io.grpc.ManagedChannel;
2932
import io.grpc.Metadata;
33+
import io.grpc.auth.MoreCallCredentials;
3034
import io.grpc.stub.MetadataUtils;
3135
import java.io.IOException;
3236
import java.util.Map;
@@ -70,15 +74,28 @@ protected Class<BuildEventServiceOptions> optionsClass() {
7074

7175
@Override
7276
protected BuildEventServiceClient getBesClient(
73-
BuildEventServiceOptions besOptions, AuthAndTLSOptions authAndTLSOptions) throws IOException {
77+
CommandEnvironment env,
78+
BuildEventServiceOptions besOptions,
79+
AuthAndTLSOptions authAndTLSOptions)
80+
throws IOException {
7481
BackendConfig newConfig = BackendConfig.create(besOptions, authAndTLSOptions);
7582
if (client == null || !Objects.equals(config, newConfig)) {
7683
clearBesClient();
84+
Preconditions.checkState(config == null);
85+
Preconditions.checkState(client == null);
86+
87+
Credentials credentials =
88+
GoogleAuthUtils.newCredentials(
89+
env.getReporter(),
90+
env.getClientEnv(),
91+
env.getRuntime().getFileSystem(),
92+
newConfig.authAndTLSOptions());
93+
7794
config = newConfig;
7895
client =
7996
new BuildEventServiceGrpcClient(
8097
newGrpcChannel(config),
81-
GoogleAuthUtils.newCallCredentials(config.authAndTLSOptions()),
98+
credentials != null ? MoreCallCredentials.from(credentials) : null,
8299
makeGrpcInterceptor(config));
83100
}
84101
return client;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ private BuildEventServiceTransport createBesTransport(
677677

678678
final BuildEventServiceClient besClient;
679679
try {
680-
besClient = getBesClient(besOptions, authTlsOptions);
680+
besClient = getBesClient(cmdEnv, besOptions, authTlsOptions);
681681
} catch (IOException | OptionsParsingException e) {
682682
reportError(
683683
reporter,
@@ -822,7 +822,7 @@ private static AbruptExitException createAbruptExitException(
822822
protected abstract Class<BESOptionsT> optionsClass();
823823

824824
protected abstract BuildEventServiceClient getBesClient(
825-
BESOptionsT besOptions, AuthAndTLSOptions authAndTLSOptions)
825+
CommandEnvironment env, BESOptionsT besOptions, AuthAndTLSOptions authAndTLSOptions)
826826
throws IOException, OptionsParsingException;
827827

828828
protected abstract void clearBesClient();

src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java

+29-91
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@
4949
import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions.UnresolvedScopedCredentialHelper;
5050
import com.google.devtools.build.lib.authandtls.CallCredentialsProvider;
5151
import com.google.devtools.build.lib.authandtls.GoogleAuthUtils;
52-
import com.google.devtools.build.lib.authandtls.Netrc;
53-
import com.google.devtools.build.lib.authandtls.NetrcCredentials;
54-
import com.google.devtools.build.lib.authandtls.NetrcParser;
5552
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperEnvironment;
5653
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialHelperProvider;
5754
import com.google.devtools.build.lib.bazel.repository.downloader.Downloader;
@@ -1052,94 +1049,6 @@ RemoteActionContextProvider getActionContextProvider() {
10521049
return actionContextProvider;
10531050
}
10541051

1055-
/**
1056-
* Create a new {@link Credentials} object by parsing the .netrc file with following order to
1057-
* search it:
1058-
*
1059-
* <ol>
1060-
* <li>If environment variable $NETRC exists, use it as the path to the .netrc file
1061-
* <li>Fallback to $HOME/.netrc
1062-
* </ol>
1063-
*
1064-
* @return the {@link Credentials} object or {@code null} if there is no .netrc file.
1065-
* @throws IOException in case the credentials can't be constructed.
1066-
*/
1067-
@VisibleForTesting
1068-
static Credentials newCredentialsFromNetrc(Map<String, String> clientEnv, FileSystem fileSystem)
1069-
throws IOException {
1070-
String netrcFileString =
1071-
Optional.ofNullable(clientEnv.get("NETRC"))
1072-
.orElseGet(
1073-
() ->
1074-
Optional.ofNullable(clientEnv.get("HOME"))
1075-
.map(home -> home + "/.netrc")
1076-
.orElse(null));
1077-
if (netrcFileString == null) {
1078-
return null;
1079-
}
1080-
1081-
Path netrcFile = fileSystem.getPath(netrcFileString);
1082-
if (netrcFile.exists()) {
1083-
try {
1084-
Netrc netrc = NetrcParser.parseAndClose(netrcFile.getInputStream());
1085-
return new NetrcCredentials(netrc);
1086-
} catch (IOException e) {
1087-
throw new IOException(
1088-
"Failed to parse " + netrcFile.getPathString() + ": " + e.getMessage(), e);
1089-
}
1090-
} else {
1091-
return null;
1092-
}
1093-
}
1094-
1095-
/**
1096-
* Create a new {@link Credentials} with following order:
1097-
*
1098-
* <ol>
1099-
* <li>If authentication enabled by flags, use it to create credentials
1100-
* <li>Use .netrc to provide credentials if exists
1101-
* <li>Otherwise, return {@code null}
1102-
* </ol>
1103-
*
1104-
* @throws IOException in case the credentials can't be constructed.
1105-
*/
1106-
@VisibleForTesting
1107-
static Credentials newCredentials(
1108-
Map<String, String> clientEnv,
1109-
FileSystem fileSystem,
1110-
Reporter reporter,
1111-
AuthAndTLSOptions authAndTlsOptions,
1112-
RemoteOptions remoteOptions)
1113-
throws IOException {
1114-
Credentials creds = GoogleAuthUtils.newCredentials(authAndTlsOptions);
1115-
1116-
// Fallback to .netrc if it exists
1117-
if (creds == null) {
1118-
try {
1119-
creds = newCredentialsFromNetrc(clientEnv, fileSystem);
1120-
} catch (IOException e) {
1121-
reporter.handle(Event.warn(e.getMessage()));
1122-
}
1123-
1124-
try {
1125-
if (creds != null
1126-
&& remoteOptions.remoteCache != null
1127-
&& Ascii.toLowerCase(remoteOptions.remoteCache).startsWith("http://")
1128-
&& !creds.getRequestMetadata(new URI(remoteOptions.remoteCache)).isEmpty()) {
1129-
reporter.handle(
1130-
Event.warn(
1131-
"Username and password from .netrc is transmitted in plaintext to "
1132-
+ remoteOptions.remoteCache
1133-
+ ". Please consider using an HTTPS endpoint."));
1134-
}
1135-
} catch (URISyntaxException e) {
1136-
throw new IOException(e.getMessage(), e);
1137-
}
1138-
}
1139-
1140-
return creds;
1141-
}
1142-
11431052
@VisibleForTesting
11441053
static CredentialHelperProvider newCredentialHelperProvider(
11451054
CredentialHelperEnvironment environment,
@@ -1163,6 +1072,35 @@ static CredentialHelperProvider newCredentialHelperProvider(
11631072
return builder.build();
11641073
}
11651074

1075+
static Credentials newCredentials(
1076+
Map<String, String> clientEnv,
1077+
FileSystem fileSystem,
1078+
Reporter reporter,
1079+
AuthAndTLSOptions authAndTlsOptions,
1080+
RemoteOptions remoteOptions)
1081+
throws IOException {
1082+
Credentials credentials =
1083+
GoogleAuthUtils.newCredentials(reporter, clientEnv, fileSystem, authAndTlsOptions);
1084+
1085+
try {
1086+
if (credentials != null
1087+
&& remoteOptions.remoteCache != null
1088+
&& Ascii.toLowerCase(remoteOptions.remoteCache).startsWith("http://")
1089+
&& !credentials.getRequestMetadata(new URI(remoteOptions.remoteCache)).isEmpty()) {
1090+
// TODO(yannic): Make this a error aborting the build.
1091+
reporter.handle(
1092+
Event.warn(
1093+
"Credentials are transmitted in plaintext to "
1094+
+ remoteOptions.remoteCache
1095+
+ ". Please consider using an HTTPS endpoint."));
1096+
}
1097+
} catch (URISyntaxException e) {
1098+
throw new IOException(e.getMessage(), e);
1099+
}
1100+
1101+
return credentials;
1102+
}
1103+
11661104
@VisibleForTesting
11671105
@AutoValue
11681106
abstract static class ScopedCredentialHelper {

0 commit comments

Comments
 (0)