blob: 63dda5014b55fd0240aac9864b647300e0e418be [file] [log] [blame]
buchgrfe9ba892017-08-04 15:09:08 +02001// Copyright 2017 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
15package com.google.devtools.build.lib.authandtls;
16
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080017import com.google.auth.Credentials;
buchgrfe9ba892017-08-04 15:09:08 +020018import com.google.auth.oauth2.GoogleCredentials;
19import com.google.common.annotations.VisibleForTesting;
20import com.google.common.base.Preconditions;
21import com.google.common.collect.ImmutableList;
22import io.grpc.CallCredentials;
23import io.grpc.ManagedChannel;
24import io.grpc.auth.MoreCallCredentials;
25import io.grpc.netty.GrpcSslContexts;
26import io.grpc.netty.NegotiationType;
27import io.grpc.netty.NettyChannelBuilder;
28import io.grpc.util.RoundRobinLoadBalancerFactory;
29import io.netty.handler.ssl.SslContext;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileNotFoundException;
33import java.io.IOException;
34import java.io.InputStream;
35import javax.annotation.Nullable;
36
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080037/** Utility methods for using {@link AuthAndTLSOptions} with Google Cloud. */
38public final class GoogleAuthUtils {
buchgrfe9ba892017-08-04 15:09:08 +020039
40 /**
41 * Create a new gRPC {@link ManagedChannel}.
42 *
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080043 * @throws IOException in case the channel can't be constructed.
buchgrfe9ba892017-08-04 15:09:08 +020044 */
45 public static ManagedChannel newChannel(String target, AuthAndTLSOptions options)
46 throws IOException {
47 Preconditions.checkNotNull(target);
48 Preconditions.checkNotNull(options);
49
50 final SslContext sslContext =
51 options.tlsEnabled ? createSSlContext(options.tlsCertificate) : null;
52
53 try {
54 NettyChannelBuilder builder =
55 NettyChannelBuilder.forTarget(target)
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080056 .negotiationType(options.tlsEnabled ? NegotiationType.TLS : NegotiationType.PLAINTEXT)
buchgrfe9ba892017-08-04 15:09:08 +020057 .loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance());
58 if (sslContext != null) {
59 builder.sslContext(sslContext);
60 if (options.tlsAuthorityOverride != null) {
61 builder.overrideAuthority(options.tlsAuthorityOverride);
62 }
63 }
64 return builder.build();
65 } catch (RuntimeException e) {
66 // gRPC might throw all kinds of RuntimeExceptions: StatusRuntimeException,
67 // IllegalStateException, NullPointerException, ...
68 String message = "Failed to connect to '%s': %s";
69 throw new IOException(String.format(message, target, e.getMessage()));
70 }
71 }
72
73 private static SslContext createSSlContext(@Nullable String rootCert) throws IOException {
74 if (rootCert == null) {
75 try {
76 return GrpcSslContexts.forClient().build();
77 } catch (Exception e) {
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080078 String message = "Failed to init TLS infrastructure: " + e.getMessage();
buchgrfe9ba892017-08-04 15:09:08 +020079 throw new IOException(message, e);
80 }
81 } else {
82 try {
83 return GrpcSslContexts.forClient().trustManager(new File(rootCert)).build();
84 } catch (Exception e) {
85 String message = "Failed to init TLS infrastructure using '%s' as root certificate: %s";
86 message = String.format(message, rootCert, e.getMessage());
87 throw new IOException(message, e);
88 }
89 }
90 }
91
92 /**
93 * Create a new {@link CallCredentials} object.
94 *
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080095 * @throws IOException in case the call credentials can't be constructed.
buchgrfe9ba892017-08-04 15:09:08 +020096 */
97 public static CallCredentials newCallCredentials(AuthAndTLSOptions options) throws IOException {
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -080098 Credentials creds = newCredentials(options);
99 if (creds != null) {
100 return MoreCallCredentials.from(creds);
101 }
102 return null;
103 }
104
105 @VisibleForTesting
106 public static CallCredentials newCallCredentials(
107 @Nullable InputStream credentialsFile, @Nullable String authScope) throws IOException {
108 Credentials creds = newCredentials(credentialsFile, authScope);
109 if (creds != null) {
110 return MoreCallCredentials.from(creds);
111 }
112 return null;
113 }
114
115 /**
116 * Create a new {@link Credentials} object.
117 *
118 * @throws IOException in case the credentials can't be constructed.
119 */
120 public static Credentials newCredentials(AuthAndTLSOptions options) throws IOException {
buchgrfe9ba892017-08-04 15:09:08 +0200121 if (!options.authEnabled) {
122 return null;
123 }
124
125 if (options.authCredentials != null) {
126 // Credentials from file
127 try (InputStream authFile = new FileInputStream(options.authCredentials)) {
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800128 return newCredentials(authFile, options.authScope);
buchgrfe9ba892017-08-04 15:09:08 +0200129 } catch (FileNotFoundException e) {
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800130 String message =
131 String.format(
132 "Could not open auth credentials file '%s': %s",
133 options.authCredentials, e.getMessage());
buchgrfe9ba892017-08-04 15:09:08 +0200134 throw new IOException(message, e);
135 }
136 }
137 // Google Application Default Credentials
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800138 return newCredentials(null, options.authScope);
buchgrfe9ba892017-08-04 15:09:08 +0200139 }
140
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800141 private static Credentials newCredentials(
142 @Nullable InputStream credentialsFile, @Nullable String authScope) throws IOException {
buchgrfe9ba892017-08-04 15:09:08 +0200143 try {
144 GoogleCredentials creds =
145 credentialsFile == null
146 ? GoogleCredentials.getApplicationDefault()
147 : GoogleCredentials.fromStream(credentialsFile);
148 if (authScope != null) {
149 creds = creds.createScoped(ImmutableList.of(authScope));
150 }
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800151 return creds;
buchgrfe9ba892017-08-04 15:09:08 +0200152 } catch (IOException e) {
Jakob Buchgraberdd11a0e2017-12-18 04:40:16 -0800153 String message = "Failed to init auth credentials: " + e.getMessage();
buchgrfe9ba892017-08-04 15:09:08 +0200154 throw new IOException(message, e);
155 }
156 }
157}