1. Overview
In this tutorial, we’ll explore the role of ALTS (Application Layer Transport Security) in gRPC applications. As we know, ensuring authentication and data security is difficult but essential in a distributed architecture.
ALTS is a custom built-in mutual authentication and transport encryption solution from Google that is available exclusively in Google’s cloud infrastructure. ALTS simplifies authentication and data encryption between gRPC services and can be enabled with minimal code changes. Hence, it’s popular among developers as they can focus more on writing business logic.
2. Key Differences Between ALTS and TLS
ALTS is similar to TLS but has a different trust model optimized for Google’s infrastructure. Let’s quickly take a look at the key differences between them:
Features | ALTS | TLS |
---|---|---|
Trust Model | Identity-based relying on GCP IAM Service Accounts | Certificate-based, requires certificate management, including renewal and revocation |
Design | Simpler | Complex |
Usage Context | Used for securing gRPC services running on Google data centers | Used for securing Web browsing (HTTPS), email, instant messaging, VoIP, etc. |
Message Serialization | Uses Protocol Buffers | Uses X.509 certificates encoded with ASN.1 |
Performance | Designed for general use | Optimized for low-latency, high-throughput communications in Google’s data centers |
3. Sample Application Using ALTS
The ALTS feature is enabled by default on the Google Cloud Platform (GCP). It uses GCP service accounts to secure RPC calls between gRPC services. Specifically, it runs on Google Compute Engine or Kubernetes Engine (GKE) within Google’s infrastructure.
Let’s assume there’s an Operation Theater (OT) booking system in a hospital that consists of a front-end and a backend service:
The OT Booking system comprises two services running in the Google Cloud Platform (GCP). A front-end service makes remote procedure calls to the backend service. We’ll develop the services using the gRPC framework. Considering the sensitive nature of the data, we’ll utilize the built-in ALTS feature in GCP to enable authentication and encryption for the transit data.
First, let’s define the protobuf ot_booking.proto file:
syntax = "proto3";
package otbooking;
option java_multiple_files = true;
option java_package = "com.baeldung.grpc.alts.otbooking";
service OtBookingService {
rpc getBookingInfo(BookingRequest) returns (BookingResponse) {}
}
message BookingRequest {
string patientID = 1;
string doctorID = 2;
string description = 3;
}
message BookingResponse {
string bookingDate = 1;
string condition = 2;
}
Basically, we declared a service OtBookingService with the RPC getBookingInfo(), and two DTOs BookingRequest and BookingResponse in the protobuf file.
Next, let’s have a look at the important classes of this application:
The Maven plugin compiles the protobuf file and auto-generates some classes such as OtBookingServiceGrpc, OtBookingServiceImplBase, BookingRequest, and BookingResponse. We’ll use the gRPC library class AltsChannelBuilder to enable ALTS to create the ManagedChannel object on the client side. Finally, we’ll use OtBookingServiceGrpc to generate the OtBookingServiceBlockingStub to call the RPC getBookingInfo() method running on the server side.
Like AltsChannelBuilder, the AltsServerBuilder class helps enable ALTS on the server side. We register the interceptor ClientAuthInterceptor to help authenticate the client. Finally, we register the OtBookingService to the io.grpc.Server object and then start the service.
Furthermore, we’ll discuss the implementation in the next section.
4. Application Implementation Using ALTS
Let’s implement the classes we discussed earlier. Then, we’ll demonstrate by running the services on the GCP virtual machines.
4.1. Prerequisite
Since ALTS is a built-in feature in GCP, we’ll have to provision a few cloud resources for running the sample application.
First, we’ll create two IAM service accounts to associate them with the front-end and back-end servers respectively:
Then, we’ll create two virtual machines hosting the front-end and back-end services respectively:
The virtual machine prod-booking-client-vm is associated with prod-ot-booking-client-svc service account. Similarly, prod-booking-service-vm is associated with prod-ot-booking-svc service account. The service accounts serve as the servers’ identities and ALTS uses them for authorization and encryption.
4.2. Implementation
Let’s first start with an entry into the pom.xml file to resolve the Maven dependency:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-alts</artifactId>
<version>1.63.0</version>
</dependency>
Then, we’ll implement the backend, starting with the AltsBookingServer class:
public class AltsOtBookingServer {
public static void main(String[] args) throws IOException, InterruptedException {
final String CLIENT_SERVICE_ACCOUNT = args[0];
Server server = AltsServerBuilder.forPort(8080)
.intercept(new ClientAuthInterceptor(CLIENT_SERVICE_ACCOUNT))
.addService(new OtBookingService())
.build();
server.start();
server.awaitTermination();
}
}
gRPC provides a special class AltsServerBuilder for configuring the server in ALTS mode. We’ve registered the ClientAuthInterceptor on the server to intercept all the RPCs before they hit the endpoints in the OtBookingService class.
Let’s take a look at the ClientAuthInterceptor class:
public class ClientAuthInterceptor implements ServerInterceptor {
String clientServiceAccount = null;
public ClientAuthInterceptor(String clientServiceAccount) {
this.clientServiceAccount = clientServiceAccount;
}
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
ServerCallHandler<ReqT, RespT> serverCallHandler) {
Status status = AuthorizationUtil.clientAuthorizationCheck(serverCall,
Lists.newArrayList(this.clientServiceAccount));
if (!status.isOk()) {
serverCall.close(status, new Metadata());
}
return serverCallHandler.startCall(serverCall, metadata);
}
}
All the RPCs hit the intercept() method in ClientAuthInterceptor. Then, we invoke the clientAuthorizationCheck() method of the gRPC library class AuthorizationUtil to authorize the client service account. Finally, the RPC moves further only when the authorization is successful.
Next, let’s take a look at the front-end service:
public class AltsOtBookingClient {
public static void main(String[] args) {
final String SERVER_ADDRESS = args[0];
final String SERVER_ADDRESS_SERVICE_ACCOUNT = args[1];
ManagedChannel managedChannel = AltsChannelBuilder.forTarget(SERVER_ADDRESS)
.addTargetServiceAccount(SERVER_ADDRESS_SERVICE_ACCOUNT)
.build();
OtBookingServiceGrpc.OtBookingServiceBlockingStub OTBookingServiceStub = OtBookingServiceGrpc
.newBlockingStub(managedChannel);
BookingResponse bookingResponse = OTBookingServiceStub.getBookingInfo(BookingRequest.newBuilder()
.setPatientID("PT-1204")
.setDoctorID("DC-3904")
.build());
managedChannel.shutdown();
}
}
Similar to AltsServerBuilder, gRPC offers an AltsChannelBuilder class for enabling ALTS on the client side. We can call the addTargetServiceAccount() method multiple times to add more than one potential target service account. Further, we initiate the RPC by calling the getBookingInfo() method on the stub.
The same service account can be associated with multiple virtual machines. Hence, it provides a certain degree of flexibility and agility to scale the services horizontally.
4.3. Run on Google Compute Engine
Let’s login to both servers and then clone the GitHub repository hosting the source code of the demo gRPC service:
git clone https://github.com/eugenp/tutorials.git
After cloning, we’ll compile the code in the tutorials/grpc directory:
mvn clean compile
Post successful compilation, we’ll start the backend service in prod-booking-service-vm:
mvn exec: java -Dexec.mainClass="com.baeldung.grpc.alts.server.AltsOtBookingServer" \
-Dexec.arguments="prod-ot-booking-client-svc@grpc-alts-demo.iam.gserviceaccount.com"
We ran the AltsOtBookingServer class with the service account of the front-end client as an argument.
Once the service is up and running, we’ll initiate an RPC from the front-end service running on the virtual machine prod-booking-client-vm:
mvn exec:java -Dexec.mainClass="com.baeldung.grpc.alts.client.AltsOtBookingClient" \
-Dexec.arguments="10.128.0.2:8080,prod-ot-booking-svc@grpc-alts-demo.iam.gserviceaccount.com"
We ran the AltsOtBookingClient class with two arguments. The first argument is the target server where the backend service is running and the second is the service account associated with the backend server.
The command runs successfully and the service returns a response after authenticating the client:
Let’s suppose we disable the client service account:
As a result, the ALTS prevents the RPC from reaching the backend service:
The RPC fails with the status UNAVAILABLE.
Now, let’s disable the service account of the backend server:
Surprisingly, the RPC goes through but after restarting the servers it fails like the earlier scenario:
It seems that ALTS was caching the service account status earlier, but after the server restart, the RPC failed with the status UNKNOWN.
5. Conclusion
In this article, we delved into the gRPC Java library supporting ALTS. With minimal code, ALTS can be enabled in gRPC services. It also provides greater flexibility in controlling the authorization of gRPC services with the help of GCP IAM service accounts.
However, it works only in GCP infrastructure as it’s provided out of the box. Hence, to run gRPC services outside of GCP infrastructure, TLS support in gRPC is crucial and must be manually configured.
As usual, the code used here is available over on GitHub.