Home | History | Annotate | Download | only in grpc-grpc-java
      1 # Authentication
      2 
      3 gRPC supports a number of different mechanisms for asserting identity between an client and server. This document provides code samples demonstrating how to provide SSL/TLS encryption support and identity assertions in Java, as well as passing OAuth2 tokens to services that support it.
      4 
      5 # Transport Security (TLS)
      6 
      7 HTTP/2 over TLS mandates the use of [ALPN](https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05) to negotiate the use of the h2 protocol. ALPN is a fairly new standard and (where possible) gRPC also supports protocol negotiation via [NPN](https://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04) for systems that do not yet support ALPN.
      8 
      9 On Android, use the [Play Services Provider](#tls-on-android). For non-Android systems, use [OpenSSL](#tls-with-openssl).
     10 
     11 ## TLS on Android
     12 
     13 On Android we recommend the use of the [Play Services Dynamic Security
     14 Provider](https://www.appfoundry.be/blog/2014/11/18/Google-Play-Services-Dynamic-Security-Provider/)
     15 to ensure your application has an up-to-date OpenSSL library with the necessary
     16 ciper-suites and a reliable ALPN implementation. This requires [updating the
     17 security provider at
     18 runtime](https://developer.android.com/training/articles/security-gms-provider.html).
     19 
     20 Although ALPN mostly works on newer Android releases (especially since 5.0),
     21 there are bugs and discovered security vulnerabilities that are only fixed by
     22 upgrading the security provider. Thus, we recommend using the Play Service
     23 Dynamic Security Provider for all Android versions.
     24 
     25 *Note: The Dynamic Security Provider must be installed **before** creating a gRPC OkHttp channel. gRPC's OkHttpProtocolNegotiator statically initializes the security protocol(s) available to gRPC, which means that changes to the security provider after the first channel is created will not be picked up by gRPC.*
     26 
     27 ### Bundling Conscrypt
     28 
     29 If depending on Play Services is not an option for your app, then you may bundle
     30 [Conscrypt](https://conscrypt.org) with your application. Binaries are available
     31 on [Maven
     32 Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.conscrypt%20a%3Aconscrypt-android).
     33 
     34 Like the Play Services Dynamic Security Provider, you must still "install"
     35 Conscrypt before use.
     36 
     37 ```java
     38 import org.conscrypt.Conscrypt;
     39 import java.security.Security;
     40 ...
     41 
     42 Security.insertProviderAt(Conscrypt.newProvider(), 1);
     43 ```
     44 
     45 ## TLS with OpenSSL
     46 
     47 This is currently the recommended approach for using gRPC over TLS (on non-Android systems).
     48 
     49 The main benefits of using OpenSSL are:
     50 
     51 1. **Speed**: In local testing, we've seen performance improvements of 3x over the JDK. GCM, which is used by the only cipher suite required by the HTTP/2 spec, is 10-500x faster.
     52 2. **Ciphers**: OpenSSL has its own ciphers and is not dependent on the limitations of the JDK. This allows supporting GCM on Java 7.
     53 3. **ALPN to NPN Fallback**: if the remote endpoint doesn't support ALPN.
     54 4. **Version Independence**: does not require using a different library version depending on the JDK update.
     55 
     56 Support for OpenSSL is only provided for the Netty transport via [netty-tcnative](https://github.com/netty/netty-tcnative), which is a fork of
     57 [Apache Tomcat's tcnative](http://tomcat.apache.org/native-doc/), a JNI wrapper around OpenSSL.
     58 
     59 ### OpenSSL: Dynamic vs Static (which to use?)
     60 
     61 As of version `1.1.33.Fork14`, netty-tcnative provides two options for usage: statically or dynamically linked. For simplification of initial setup,
     62 we recommend that users first look at `netty-tcnative-boringssl-static`, which is statically linked against BoringSSL and Apache APR. Using this artifact requires no extra installation and guarantees that ALPN and the ciphers required for
     63 HTTP/2 are available. In addition, starting with `1.1.33.Fork16` binaries for
     64 all supported platforms can be included at compile time and the correct binary
     65 for the platform can be selected at runtime.
     66 
     67 Production systems, however, may require an easy upgrade path for OpenSSL security patches. In this case, relying on the statically linked artifact also implies waiting for the Netty team
     68 to release the new artifact to Maven Central, which can take some time. A better solution in this case is to use the dynamically linked `netty-tcnative` artifact, which allows the site administrator
     69 to easily upgrade OpenSSL in the standard way (e.g. apt-get) without relying on any new builds from Netty.
     70 
     71 ### OpenSSL: Statically Linked (netty-tcnative-boringssl-static)
     72 
     73 This is the simplest way to configure the Netty transport for OpenSSL. You just need to add the appropriate `netty-tcnative-boringssl-static` artifact to your application's classpath.
     74 
     75 Artifacts are available on [Maven Central](http://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) for the following platforms:
     76 
     77 Maven Classifier | Description
     78 ---------------- | -----------
     79 windows-x86_64 | Windows distribution
     80 osx-x86_64 | Mac distribution
     81 linux-x86_64 | Linux distribution
     82 
     83 ##### Getting netty-tcnative-boringssl-static from Maven
     84 
     85 In Maven, you can use the [os-maven-plugin](https://github.com/trustin/os-maven-plugin) to help simplify the dependency.
     86 
     87 ```xml
     88 <project>
     89   <dependencies>
     90     <dependency>
     91       <groupId>io.netty</groupId>
     92       <artifactId>netty-tcnative-boringssl-static</artifactId>
     93       <version>2.0.7.Final</version>
     94     </dependency>
     95   </dependencies>
     96 </project>
     97 ```
     98 
     99 ##### Getting netty-tcnative-boringssl-static from Gradle
    100 
    101 Gradle you can use the [osdetector-gradle-plugin](https://github.com/google/osdetector-gradle-plugin), which is a wrapper around the os-maven-plugin.
    102 
    103 ```gradle
    104 buildscript {
    105   repositories {
    106     mavenCentral()
    107   }
    108 }
    109 
    110 dependencies {
    111     compile 'io.netty:netty-tcnative-boringssl-static:2.0.7.Final'
    112 }
    113 ```
    114 
    115 ### OpenSSL: Dynamically Linked (netty-tcnative)
    116 
    117 If for any reason you need to dynamically link against OpenSSL (e.g. you need control over the version of OpenSSL), you can instead use the `netty-tcnative` artifact.
    118 
    119 Requirements:
    120 
    121 1. [OpenSSL](https://www.openssl.org/) version >= 1.0.2 for ALPN support, or version >= 1.0.1 for NPN.
    122 2. [Apache APR library (libapr-1)](https://apr.apache.org/) version >= 1.5.2.
    123 3. [netty-tcnative](https://github.com/netty/netty-tcnative) version >= 1.1.33.Fork7 must be on classpath. Prior versions only supported NPN and only Fedora-derivatives were supported for Linux.
    124 
    125 Artifacts are available on [Maven Central](http://repo1.maven.org/maven2/io/netty/netty-tcnative/) for the following platforms:
    126 
    127 Classifier | Description
    128 ---------------- | -----------
    129 windows-x86_64 | Windows distribution
    130 osx-x86_64 | Mac distribution
    131 linux-x86_64 | Used for non-Fedora derivatives of Linux
    132 linux-x86_64-fedora | Used for Fedora derivatives
    133 
    134 On Linux it should be noted that OpenSSL uses a different soname for Fedora derivatives than other Linux releases. To work around this limitation, netty-tcnative deploys two separate versions for linux.
    135 
    136 ##### Getting netty-tcnative from Maven
    137 
    138 In Maven, you can use the [os-maven-plugin](https://github.com/trustin/os-maven-plugin) to help simplify the dependency.
    139 
    140 ```xml
    141 <project>
    142   <dependencies>
    143     <dependency>
    144       <groupId>io.netty</groupId>
    145       <artifactId>netty-tcnative</artifactId>
    146       <version>2.0.7.Final</version>
    147       <classifier>${tcnative.classifier}</classifier>
    148     </dependency>
    149   </dependencies>
    150 
    151   <build>
    152     <extensions>
    153       <!-- Use os-maven-plugin to initialize the "os.detected" properties -->
    154       <extension>
    155         <groupId>kr.motd.maven</groupId>
    156         <artifactId>os-maven-plugin</artifactId>
    157         <version>1.5.0.Final</version>
    158       </extension>
    159     </extensions>
    160     <plugins>
    161       <!-- Use Ant to configure the appropriate "tcnative.classifier" property -->
    162       <plugin>
    163         <groupId>org.apache.maven.plugins</groupId>
    164         <artifactId>maven-antrun-plugin</artifactId>
    165         <executions>
    166           <execution>
    167             <phase>initialize</phase>
    168             <configuration>
    169               <exportAntProperties>true</exportAntProperties>
    170               <target>
    171                 <condition property="tcnative.classifier"
    172                            value="${os.detected.classifier}-fedora"
    173                            else="${os.detected.classifier}">
    174                   <isset property="os.detected.release.fedora"/>
    175                 </condition>
    176               </target>
    177             </configuration>
    178             <goals>
    179               <goal>run</goal>
    180             </goals>
    181           </execution>
    182         </executions>
    183       </plugin>
    184     </plugins>
    185   </build>
    186 </project>
    187 ```
    188 
    189 ##### Getting netty-tcnative from Gradle
    190 
    191 Gradle you can use the [osdetector-gradle-plugin](https://github.com/google/osdetector-gradle-plugin), which is a wrapper around the os-maven-plugin.
    192 
    193 ```gradle
    194 buildscript {
    195   repositories {
    196     mavenCentral()
    197   }
    198   dependencies {
    199     classpath 'com.google.gradle:osdetector-gradle-plugin:1.4.0'
    200   }
    201 }
    202 
    203 // Use the osdetector-gradle-plugin
    204 apply plugin: "com.google.osdetector"
    205 
    206 def tcnative_classifier = osdetector.classifier;
    207 // Fedora variants use a different soname for OpenSSL than other linux distributions
    208 // (see http://netty.io/wiki/forked-tomcat-native.html).
    209 if (osdetector.os == "linux" && osdetector.release.isLike("fedora")) {
    210   tcnative_classifier += "-fedora";
    211 }
    212 
    213 dependencies {
    214     compile 'io.netty:netty-tcnative:2.0.7.Final:' + tcnative_classifier
    215 }
    216 ```
    217 
    218 ## TLS with JDK (Jetty ALPN/NPN)
    219 
    220 **WARNING: DON'T DO THIS!!**
    221 
    222 *For non-Android systems, the recommended approach is to use [OpenSSL](#tls-with-openssl). Using the JDK for ALPN is generally much slower and may not support the necessary ciphers for HTTP2.*
    223 
    224 *Jetty ALPN brings its own baggage in that the Java bootclasspath needs to be modified, which may not be an option for some environments. In addition, a specific version of Jetty ALPN has to be used for a given version of the JRE. If the versions don't match the negotiation will fail, but you won't really know why. And since there is such a tight coupling between Jetty ALPN and the JRE, there are no guarantees that Jetty ALPN will support every JRE out in the wild.*
    225 
    226 *The moral of the story is: Don't use the JDK for ALPN!  But if you absolutely have to, here's how you do it... :)*
    227 
    228 ---
    229 
    230 If not using the Netty transport (or you are unable to use OpenSSL for some reason) another alternative is to use the JDK for TLS.
    231 
    232 No standard Java release has built-in support for ALPN today ([there is a tracking issue](https://bugs.openjdk.java.net/browse/JDK-8051498) so go upvote it!) so we need to use the [Jetty-ALPN](https://github.com/jetty-project/jetty-alpn) (or [Jetty-NPN](https://github.com/jetty-project/jetty-npn) if on Java < 8) bootclasspath extension for OpenJDK. To do this, add an `Xbootclasspath` JVM option referencing the path to the Jetty `alpn-boot` jar.
    233 
    234 ```sh
    235 java -Xbootclasspath/p:/path/to/jetty/alpn/extension.jar ...
    236 ```
    237 
    238 Note that you must use the [release of the Jetty-ALPN jar](http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-versions) specific to the version of Java you are using. However, you can use the JVM agent [Jetty-ALPN-Agent](https://github.com/jetty-project/jetty-alpn-agent) to load the correct Jetty `alpn-boot` jar file for the current Java version. To do this, instead of adding an `Xbootclasspath` option, add a `javaagent` JVM option referencing the path to the Jetty `alpn-agent` jar.
    239 
    240 ```sh
    241 java -javaagent:/path/to/jetty-alpn-agent.jar ...
    242 ```
    243 
    244 ### JDK Ciphers
    245 
    246 Java 7 does not support [the cipher suites recommended](https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-9.2.2) by the HTTP2 specification. To address this we suggest servers use Java 8 where possible or use an alternative JCE implementation such as [Bouncy Castle](https://www.bouncycastle.org/java.html). If this is not practical it is possible to use other ciphers but you need to ensure that the services you intend to call have [allowed out-of-spec ciphers](https://github.com/grpc/grpc/issues/681) and have evaluated the security risks of doing so.
    247 
    248 Users should be aware that GCM is [_very_ slow (1 MB/s)](https://bugzilla.redhat.com/show_bug.cgi?id=1135504) before Java 8u60. With Java 8u60 GCM is 10x faster (10-20 MB/s), but that is still slow compared to OpenSSL (~200 MB/s), especially with AES-NI support (~1 GB/s). GCM cipher suites are the only suites available that comply with HTTP2's cipher requirements.
    249 
    250 ### Configuring Jetty ALPN in Web Containers
    251 
    252 Some web containers, such as [Jetty](http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html) restrict access to server classes for web applications. A gRPC client running within such a container must be properly configured to allow access to the ALPN classes. In Jetty, this is done by including a `WEB-INF/jetty-env.xml` file containing the following:
    253 
    254 ```xml
    255 <?xml version="1.0"  encoding="ISO-8859-1"?>
    256 <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
    257 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
    258     <!-- Must be done in jetty-env.xml, since jetty-web.xml is loaded too late.   -->
    259     <!-- Removing ALPN from the blacklisted server classes (using "-" to remove). -->
    260     <!-- Must prepend to the blacklist since order matters.                       -->
    261     <Call name="prependServerClass">
    262         <Arg>-org.eclipse.jetty.alpn.</Arg>
    263     </Call>
    264 </Configure>
    265 ```
    266 ## Enabling TLS on a server
    267 
    268 To use TLS on the server, a certificate chain and private key need to be
    269 specified in PEM format. The standard TLS port is 443, but we use 8443 below to
    270 avoid needing extra permissions from the OS.
    271 
    272 ```java
    273 Server server = ServerBuilder.forPort(8443)
    274     // Enable TLS
    275     .useTransportSecurity(certChainFile, privateKeyFile)
    276     .addService(serviceImplementation)
    277     .build();
    278 server.start();
    279 ```
    280 
    281 If the issuing certificate authority is not known to the client then a properly
    282 configured SslContext or SSLSocketFactory should be provided to the
    283 NettyChannelBuilder or OkHttpChannelBuilder, respectively.
    284 
    285 ## Mutual TLS
    286 
    287 [Mutual authentication][] (or "client-side authentication") configuration is similar to the server by providing truststores, a client certificate and private key to the client channel.  The server must also be configured to request a certificate from clients, as well as truststores for which client certificates it should allow.
    288 
    289 ```java
    290 Server server = NettyServerBuilder.forPort(8443)
    291     .sslContext(GrpcSslContexts.forServer(certChainFile, privateKeyFile)
    292         .trustManager(clientCAsFile)
    293         .clientAuth(ClientAuth.REQUIRE)
    294         .build());
    295 ```
    296 
    297 Negotiated client certificates are available in the SSLSession, which is found in the `TRANSPORT_ATTR_SSL_SESSION` attribute of <a href="https://github.com/grpc/grpc-java/blob/master/core/src/main/java/io/grpc/Grpc.java">Grpc</a>.  A server interceptor can provide details in the current Context.
    298 
    299 ```java
    300 public final static Context.Key<SSLSession> SSL_SESSION_CONTEXT = Context.key("SSLSession");
    301 
    302 @Override
    303 public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<RespT> call, 
    304     Metadata headers, ServerCallHandler<ReqT, RespT> next) {
    305     SSLSession sslSession = call.attributes().get(Grpc.TRANSPORT_ATTR_SSL_SESSION);
    306     if (sslSession == null) {
    307         return next.startCall(call, headers)
    308     }
    309     return Contexts.interceptCall(
    310         Context.current().withValue(SSL_SESSION_CONTEXT, clientContext), call, headers, next);
    311 }
    312 ```
    313 
    314 [Mutual authentication]: http://en.wikipedia.org/wiki/Transport_Layer_Security#Client-authenticated_TLS_handshake
    315 
    316 ## Troubleshooting
    317 
    318 If you received an error message "ALPN is not configured properly" or "Jetty ALPN/NPN has not been properly configured", it most likely means that:
    319  - ALPN related dependencies are either not present in the classpath
    320  - or that there is a classpath conflict
    321  - or that a wrong version is used due to dependency management
    322  - or you are on an unsupported platform (e.g., 32-bit OS, Alpine with `musl` libc). See [Transport Security](#transport-security-tls) for supported platforms.
    323 
    324 ### Netty
    325 If you aren't using gRPC on Android devices, you are most likely using `grpc-netty` transport.
    326 
    327 If you are developing for Android and have a dependency on `grpc-netty`, you should remove it as `grpc-netty` is unsupported on Android. Use `grpc-okhttp` instead.
    328 
    329 If you are on a 32-bit operating system, or not on a [Transport Security supported platform](#transport-security-tls), you should use Jetty ALPN (and beware of potential issues), or you'll need to build your own 32-bit version of `netty-tcnative`.
    330 
    331 If you are using `musl` libc (e.g., with Alpine Linux), then
    332 `netty-tcnative-boringssl-static` won't work. There are several alternatives:
    333  - Use [netty-tcnative-alpine](https://github.com/pires/netty-tcnative-alpine)
    334  - Use a distribution with `glibc`
    335 
    336 If you are running inside of an embedded Tomcat runtime (e.g., Spring Boot),
    337 then some versions of `netty-tcnative-boringssl-static` will have conflicts and
    338 won't work. You must use gRPC 1.4.0 or later.
    339 
    340 Most dependency versioning problems can be solved by using
    341 `io.grpc:grpc-netty-shaded` instead of `io.grpc:grpc-netty`, although this also
    342 limits your usage of the Netty-specific APIs. `io.grpc:grpc-netty-shaded`
    343 includes the proper version of Netty and `netty-tcnative-boringssl-static` in a
    344 way that won't conflict with other Netty usages.
    345 
    346 Find the dependency tree (e.g., `mvn dependency:tree`), and look for versions of:
    347  - `io.grpc:grpc-netty`
    348  - `io.netty:netty-handler` (really, make sure all of io.netty except for
    349    netty-tcnative has the same version)
    350  - `io.netty:netty-tcnative-boringssl-static:jar` 
    351 
    352 If `netty-tcnative-boringssl-static` is missing, then you either need to add it as a dependency, or use alternative methods of providing ALPN capability by reading the *Transport Security (TLS)* section carefully.
    353 
    354 If you have both `netty-handler` and `netty-tcnative-boringssl-static` dependencies, then check the versions carefully. These versions could've been overridden by dependency management from another BOM. You would receive the "ALPN is not configured properly" exception if you are using incompatible versions.
    355 
    356 If you have other `netty` dependencies, such as `netty-all`, that are pulled in from other libraries, then ultimately you should make sure only one `netty` dependency is used to avoid classpath conflict. The easiest way is to exclude transitive Netty dependencies from all the immediate dependencies, e.g., in Maven use `<exclusions>`, and then add an explict Netty dependency in your project along with the corresponding `tcnative` versions. See the versions table below.
    357 
    358 If you are running in a runtime environment that also uses Netty (e.g., Hadoop, Spark, Spring Boot 2) and you have no control over the Netty version at all, then you should use a shaded gRPC Netty dependency to avoid classpath conflicts with other Netty versions in runtime the classpath:
    359  - Remove `io.grpc:grpc-netty` dependency
    360  - Add `io.grpc:grpc-netty-shaded` dependency
    361 
    362 Below are known to work version combinations:
    363 
    364 grpc-netty version | netty-handler version | netty-tcnative-boringssl-static version
    365 ------------------ | --------------------- | ---------------------------------------
    366 1.0.0-1.0.1        | 4.1.3.Final           | 1.1.33.Fork19
    367 1.0.2-1.0.3        | 4.1.6.Final           | 1.1.33.Fork23
    368 1.1.x-1.3.x        | 4.1.8.Final           | 1.1.33.Fork26
    369 1.4.x              | 4.1.11.Final          | 2.0.1.Final
    370 1.5.x              | 4.1.12.Final          | 2.0.5.Final
    371 1.6.x              | 4.1.14.Final          | 2.0.5.Final
    372 1.7.x-1.8.x        | 4.1.16.Final          | 2.0.6.Final
    373 1.9.x-1.10.x       | 4.1.17.Final          | 2.0.7.Final
    374 1.11.x-1.12.x      | 4.1.22.Final          | 2.0.7.Final
    375 1.13.x             | 4.1.25.Final          | 2.0.8.Final
    376 1.14.x-            | 4.1.27.Final          | 2.0.12.Final
    377 
    378 _(grpc-netty-shaded avoids issues with keeping these versions in sync.)_
    379 
    380 ### OkHttp
    381 If you are using gRPC on Android devices, you are most likely using `grpc-okhttp` transport.
    382 
    383 Find the dependency tree (e.g., `mvn dependency:tree`), and look for versions of:
    384  - `io.grpc:grpc-okhttp`
    385  - `com.squareup.okhttp:okhttp`
    386 
    387 If you don't have `grpc-okhttp`, you should add it as a dependency.
    388 
    389 If you have both `io.grpc:grpc-netty` and `io.grpc:grpc-okhttp`, you may also have issues. Remove `grpc-netty` if you are on Android.
    390 
    391 If you have `okhttp` version below 2.5.0, then it may not work with gRPC.
    392 
    393 It is OK to have both `okhttp` 2.x and 3.x since they have different group name and under different packages.
    394 
    395 # gRPC over plaintext
    396 
    397 An option is provided to use gRPC over plaintext without TLS. While this is convenient for testing environments, users must be aware of the security risks of doing so for real production systems.
    398 
    399 # Using OAuth2
    400 
    401 The following code snippet shows how you can call the Google Cloud PubSub API using gRPC with a service account. The credentials are loaded from a key stored in a well-known location or by detecting that the application is running in an environment that can provide one automatically, e.g. Google Compute Engine. While this example is specific to Google and it's services, similar patterns can be followed for other service providers.
    402 
    403 ```java
    404 // Create a channel to the test service.
    405 ManagedChannel channel = ManagedChannelBuilder.forTarget("pubsub.googleapis.com")
    406     .build();
    407 // Get the default credentials from the environment
    408 GoogleCredentials creds = GoogleCredentials.getApplicationDefault();
    409 // Down-scope the credential to just the scopes required by the service
    410 creds = creds.createScoped(Arrays.asList("https://www.googleapis.com/auth/pubsub"));
    411 // Create an instance of {@link io.grpc.CallCredentials}
    412 CallCredentials callCreds = MoreCallCredentials.from(creds);
    413 // Create a stub with credential
    414 PublisherGrpc.PublisherBlockingStub publisherStub =
    415     PublisherGrpc.newBlockingStub(channel).withCallCredentials(callCreds);
    416 publisherStub.publish(someMessage);
    417 ```
    418