aboutsummaryrefslogtreecommitdiff
path: root/auth
diff options
context:
space:
mode:
authorEric Anderson <ejona@google.com>2018-05-21 16:51:34 -0700
committerEric Anderson <ejona@google.com>2018-05-24 09:51:45 -0700
commit8e9d4cbe5cb455315e55846e91706cccecb4b577 (patch)
treecc7b6a352ed8ca40c483f642dcb10b1842d9d16f /auth
parente41e0547769f7ac1b56b9200e9000dc1282f9f08 (diff)
downloadgrpc-grpc-java-8e9d4cbe5cb455315e55846e91706cccecb4b577.tar.gz
auth: Require PRIVACY_AND_INTEGRITY for GoogleCredentials
This keeps them more secure. Other types of creds are left as-is, since we don't quite know if it makes sense to have a similar restriction. (It likely does make sense, but this is a more precise change for our needs.)
Diffstat (limited to 'auth')
-rw-r--r--auth/src/main/java/io/grpc/auth/GoogleAuthLibraryCallCredentials.java34
-rw-r--r--auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java46
2 files changed, 79 insertions, 1 deletions
diff --git a/auth/src/main/java/io/grpc/auth/GoogleAuthLibraryCallCredentials.java b/auth/src/main/java/io/grpc/auth/GoogleAuthLibraryCallCredentials.java
index 4117677cb..51b29a90e 100644
--- a/auth/src/main/java/io/grpc/auth/GoogleAuthLibraryCallCredentials.java
+++ b/auth/src/main/java/io/grpc/auth/GoogleAuthLibraryCallCredentials.java
@@ -26,6 +26,7 @@ import io.grpc.Attributes;
import io.grpc.CallCredentials;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
+import io.grpc.SecurityLevel;
import io.grpc.Status;
import io.grpc.StatusException;
import java.io.IOException;
@@ -51,7 +52,10 @@ final class GoogleAuthLibraryCallCredentials implements CallCredentials {
= Logger.getLogger(GoogleAuthLibraryCallCredentials.class.getName());
private static final JwtHelper jwtHelper
= createJwtHelperOrNull(GoogleAuthLibraryCallCredentials.class.getClassLoader());
+ private static final Class<? extends Credentials> googleCredentialsClass
+ = loadGoogleCredentialsClass();
+ private final boolean requirePrivacy;
@VisibleForTesting
final Credentials creds;
@@ -65,9 +69,18 @@ final class GoogleAuthLibraryCallCredentials implements CallCredentials {
@VisibleForTesting
GoogleAuthLibraryCallCredentials(Credentials creds, JwtHelper jwtHelper) {
checkNotNull(creds, "creds");
+ boolean requirePrivacy = false;
+ if (googleCredentialsClass != null) {
+ // All GoogleCredentials instances are bearer tokens and should only be used on private
+ // channels. This catches all return values from GoogleCredentials.getApplicationDefault().
+ // This should be checked before upgrading the Service Account to JWT, as JWT is also a bearer
+ // token.
+ requirePrivacy = googleCredentialsClass.isInstance(creds);
+ }
if (jwtHelper != null) {
creds = jwtHelper.tryServiceAccountToJwt(creds);
}
+ this.requirePrivacy = requirePrivacy;
this.creds = creds;
}
@@ -77,6 +90,14 @@ final class GoogleAuthLibraryCallCredentials implements CallCredentials {
@Override
public void applyRequestMetadata(MethodDescriptor<?, ?> method, Attributes attrs,
Executor appExecutor, final MetadataApplier applier) {
+ SecurityLevel security = checkNotNull(attrs.get(ATTR_SECURITY_LEVEL), "securityLevel");
+ if (requirePrivacy && security != SecurityLevel.PRIVACY_AND_INTEGRITY) {
+ applier.fail(Status.UNAUTHENTICATED
+ .withDescription("Credentials require channel with PRIVACY_AND_INTEGRITY security level. "
+ + "Observed security level: " + security));
+ return;
+ }
+
String authority = checkNotNull(attrs.get(ATTR_AUTHORITY), "authority");
final URI uri;
try {
@@ -214,6 +235,19 @@ final class GoogleAuthLibraryCallCredentials implements CallCredentials {
return null;
}
+ @Nullable
+ private static Class<? extends Credentials> loadGoogleCredentialsClass() {
+ Class<?> rawGoogleCredentialsClass;
+ try {
+ // Can't use a loader as it disables ProGuard's reference detection and would fail to rename
+ // this reference. Unfortunately this will initialize the class.
+ rawGoogleCredentialsClass = Class.forName("com.google.auth.oauth2.GoogleCredentials");
+ } catch (ClassNotFoundException ex) {
+ return null;
+ }
+ return rawGoogleCredentialsClass.asSubclass(Credentials.class);
+ }
+
@VisibleForTesting
static class JwtHelper {
private final Class<? extends Credentials> serviceAccountClass;
diff --git a/auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java b/auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java
index 53a5859b1..5104ceaa2 100644
--- a/auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java
+++ b/auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import com.google.auth.Credentials;
import com.google.auth.RequestMetadataCallback;
import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.OAuth2Credentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.common.collect.Iterables;
@@ -254,10 +255,14 @@ public class GoogleAuthLibraryCallCredentialsTest {
return token;
}
};
+ // Security level should not impact non-GoogleCredentials
+ Attributes securityNone = attrs.toBuilder()
+ .set(CallCredentials.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
+ .build();
GoogleAuthLibraryCallCredentials callCredentials =
new GoogleAuthLibraryCallCredentials(credentials);
- callCredentials.applyRequestMetadata(method, attrs, executor, applier);
+ callCredentials.applyRequestMetadata(method, securityNone, executor, applier);
assertEquals(1, runPendingRunnables());
verify(applier).apply(headersCaptor.capture());
@@ -268,6 +273,45 @@ public class GoogleAuthLibraryCallCredentialsTest {
}
@Test
+ public void googleCredential_privacyAndIntegrityAllowed() {
+ final AccessToken token = new AccessToken("allyourbase", new Date(Long.MAX_VALUE));
+ final Credentials credentials = GoogleCredentials.create(token);
+ Attributes privacy = attrs.toBuilder()
+ .set(CallCredentials.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
+ .build();
+
+ GoogleAuthLibraryCallCredentials callCredentials =
+ new GoogleAuthLibraryCallCredentials(credentials);
+ callCredentials.applyRequestMetadata(method, privacy, executor, applier);
+ runPendingRunnables();
+
+ verify(applier).apply(headersCaptor.capture());
+ Metadata headers = headersCaptor.getValue();
+ Iterable<String> authorization = headers.getAll(AUTHORIZATION);
+ assertArrayEquals(new String[]{"Bearer allyourbase"},
+ Iterables.toArray(authorization, String.class));
+ }
+
+ @Test
+ public void googleCredential_integrityDenied() {
+ final AccessToken token = new AccessToken("allyourbase", new Date(Long.MAX_VALUE));
+ final Credentials credentials = GoogleCredentials.create(token);
+ // Anything less than PRIVACY_AND_INTEGRITY should fail
+ Attributes integrity = attrs.toBuilder()
+ .set(CallCredentials.ATTR_SECURITY_LEVEL, SecurityLevel.INTEGRITY)
+ .build();
+
+ GoogleAuthLibraryCallCredentials callCredentials =
+ new GoogleAuthLibraryCallCredentials(credentials);
+ callCredentials.applyRequestMetadata(method, integrity, executor, applier);
+ runPendingRunnables();
+
+ verify(applier).fail(statusCaptor.capture());
+ Status status = statusCaptor.getValue();
+ assertEquals(Status.Code.UNAUTHENTICATED, status.getCode());
+ }
+
+ @Test
public void serviceUri() throws Exception {
GoogleAuthLibraryCallCredentials callCredentials =
new GoogleAuthLibraryCallCredentials(credentials);