diff options
author | Eric Anderson <ejona@google.com> | 2018-05-21 16:51:34 -0700 |
---|---|---|
committer | Eric Anderson <ejona@google.com> | 2018-05-24 09:51:45 -0700 |
commit | 8e9d4cbe5cb455315e55846e91706cccecb4b577 (patch) | |
tree | cc7b6a352ed8ca40c483f642dcb10b1842d9d16f /auth | |
parent | e41e0547769f7ac1b56b9200e9000dc1282f9f08 (diff) | |
download | grpc-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.java | 34 | ||||
-rw-r--r-- | auth/src/test/java/io/grpc/auth/GoogleAuthLibraryCallCredentialsTest.java | 46 |
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); |