Set up routing to gRPC services
In this guide, you learn how to expose a gRPC Upstream through a Gloo Gateway Virtual Service, and connect to it with a gRPC client. Then, you explore how to secure the communication between the gRPC client and the Envoy proxy by using TLS certificates.
The following tasks are included in this guide:
- Step 1: Deploy the demo gRPC store service and set up routing
- Step 2: Validate connectivity with a gRPC client
- Step 3: Secure the gRPC app.
Before you begin
Make sure to complete the following tasks before you get started with this guide.
- Create or use an existing Kubernetes cluster.
- Install Gloo Gateway 1.14 or later.
- Install
grpcurlto act as the gRPC client. - Install
opensslto generate self-signed TLS certificates. For example, to installopensslon a Mac, runbrew install openssl.
Step 1: Deploy the demo gRPC store service and set up routing
Use the gRPC Store app to explore how to set up routing to gRPC services.
-
Create the deployment and expose the deployment with a Kubernetes service.
kubectl create deployment grpcstore-demo --image=docker.io/soloio/grpcstore-demo kubectl expose deployment grpcstore-demo --port 80 --target-port=8080 -
Verify that Gloo Gateway automatically discovered the gRPC app and created an upstream for it.
kubectl get upstream -n gloo-system default-grpcstore-demo-80 -
Enable Gloo Gateway function discovery (FDS) so the proto descriptor can be found. The proto descriptor binary includes the gRPC functions that are available in the store service as well as any HTTP mappings that were added for HTTP to gRPC transcoding. For more information, see gRPC transcoding.
kubectl label upstream -n gloo-system default-grpcstore-demo-80 discovery.solo.io/function_discovery=enabled -
Get the upstream for the gRPC store and verify that the proto descriptor was added to the YAMl file.
kubectl get upstream -n gloo-system default-grpcstore-demo-80 -o yamlExample output:
The proto descriptor fields are truncated for brevity.
apiVersion: gloo.solo.io/v1 kind: Upstream metadata: annotations: cloud.google.com/neg: '{"ingress":true}' creationTimestamp: "2023-03-06T16:47:54Z" generation: 4 labels: discovered_by: kubernetesplugin discovery.solo.io/function_discovery: enabled name: default-grpcstore-demo-80 namespace: gloo-system resourceVersion: "10533" uid: bee3ec08-a2c1-44c5-a632-ec53f0113f8c spec: discoveryMetadata: labels: app: grpcstore-demo kube: selector: app: grpcstore-demo serviceName: grpcstore-demo serviceNamespace: default servicePort: 80 serviceSpec: grpcJsonTranscoder: protoDescriptorBin: Cqw...90bzM= # truncated services: - solo.examples.v1.StoreService status: statuses: gloo-system: reportedBy: gloo state: 1 details: fields: functionNames: structValue: fields: solo.examples.v1.StoreService: listValue: values: - stringValue: CreateItem - stringValue: ListItems - stringValue: DeleteItem - stringValue: GetItem -
Optional: Decode the
spec.kube.serviceSpec.grpcJsonTanscoderproto descriptor field. Note that the field is truncated in the example command. Make sure to add the entirespec.kube.serviceSpec.grpcJsonTanscodervalue to this command.echo "Cqw ... 90bzM=" | base64 -d -
Change the communication protocol that the Envoy proxy uses to HTTP/2. This step is required so that incoming requests from gRPC clients can be forwarded to the gRPC app. You can choose between the following two options:
- Add the
gloo.solo.io/h2_service: trueannotation to the gRPC service. - Name the port for the gRPC service one of the following:
grpc,http2, orh2.
-
Get the YAML file for the current gRPC service and save it to a local file.
kubectl get service grpcstore-demo -o yaml > grpc-service.yaml -
Open the file and either add the annotation or port name. The following example changes the name of the
grpcservice port.spec: clusterIP: 10.101.199.96 ports: - name: grpc port: 80 protocol: TCP targetPort: 8080 -
Wait a few seconds and then verify that the
useHttp2: truevalue was added to your upstream.kubectl get upstream -n gloo-system default-grpcstore-demo-80 -o yamlExample output:
... services: - solo.examples.v1.StoreService useHttp2: true
- Add the
-
Create the virtual service so that you can route incoming requests to the gRPC store app. The virtual service assumes that you use the
gloo-systemnamespace for your Gloo Gateway installation. In this configuration, the prefix/is matched for all domains.kubectl apply -f- <<EOF apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: grpc namespace: gloo-system spec: virtualHost: routes: - matchers: - prefix: / routeAction: single: upstream: name: default-grpcstore-demo-80 namespace: gloo-system EOF
Step 2: Validate connectivity with a gRPC client
To test connectivity to the gRPC app, you use the grpcurl utility. Because the connection to the app is not secured via TLS, you must use the -plaintext option in your command to allow unencrypted traffic to the app. You learn how to secure the communication to the app via TLS later in this guide.
-
Send a request to your gRPC app. The
grpcurlutility requires a port number to be sent as part of the request. Because the store service has Server Reflection enabled, you do not have to specify a proto source file forgrpcurlto use with the request. Thelistargument retrieves all the services that are availble in the gRPC app.grpcurl -plaintext $(glooctl proxy address --port http) listExample output:
grpc.reflection.v1alpha.ServerReflection solo.examples.v1.StoreService -
Describe the store service to get a list of methods that are available in the app.
grpcurl -plaintext $(glooctl proxy address --port http) describe solo.examples.v1.StoreServiceExample output:
solo.examples.v1.StoreService is a service: service StoreService { rpc CreateItem ( .solo.examples.v1.CreateItemRequest ) returns ( .solo.examples.v1.CreateItemResponse ); rpc DeleteItem ( .solo.examples.v1.DeleteItemRequest ) returns ( .solo.examples.v1.DeleteItemResponse ); rpc GetItem ( .solo.examples.v1.GetItemRequest ) returns ( .solo.examples.v1.GetItemResponse ); rpc ListItems ( .solo.examples.v1.ListItemsRequest ) returns ( .solo.examples.v1.ListItemsResponse ); } -
You can continue to describe the methods and responses that were returned in the previous command. For example, to get the details for the
solo.examples.v1.CreateItemRequestmethod, run the following command.grpcurl -plaintext $(glooctl proxy address --port http) describe solo.examples.v1.CreateItemRequestExample output:
solo.examples.v1.CreateItemRequest is a message: message CreateItemRequest { .solo.examples.v1.Item item = 1; } -
Use the
CreateItemmethod to add an item to the store app.grpcurl -plaintext -d '{"item":{"name":"item1"}}' $(glooctl proxy address --port http) solo.examples.v1.StoreService/CreateItemExample output:
{ "item": { "name": "item1" } } -
Use the
ListItemsto retrieve all the items that are available in the store.grpcurl -plaintext $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItemsExample output:
{ "items": [ { "name": "item1" } ] }
Step 3: Secure the gRPC app
Enable encryption between the gRPC client and the Envoy proxy on a specific domain.
-
Edit the virtual service that you created earlier and add a
domainentry to thevirtualHostconfiguration. Instead of allowing requests from any domain, the gRPC store app now can receive requests from only thestore.example.comdomain.kubectl edit vs grpc -n gloo-systemAdd the domain as shown in the highlighted lines.
spec: virtualHost: domains: - store.example.com routes: - matchers: - prefix: / routeAction: single: upstream: name: default-grpcstore-demo-80 namespace: gloo-systemIf you want to use a port number other than 443, you must append the port to the domain. For more information, see https://github.com/solo-io/gloo/issues/3505.
-
Send a request to list the items in the store. Because the app is now configured to only receive incoming requests on the
store.example.comdomain, you see an error from the Envoy proxy. Note that the error message is not strictly true, but rather is the best that Envoy can figure out because the gRPC request was sent without anauthorityheader (the equivalent of an HTTP host header incurl). Instead, Envoy used the IP address that was returned with theglooctl proxy addresscommand as the host name.grpcurl -plaintext $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItemsExample output:
Error invoking method "solo.examples.v1.StoreService/ListItems": failed to query for service descriptor "solo.examples.v1.StoreService": server does not support the reflection API -
Add the
authorityflag to the request. This time the request succeeds.grpcurl -plaintext -authority store.example.com $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItemsExample output:
{ "items": [ { "name": "item1" } ] }In this example, the authority header is specified alongside the public IP address of the Envoy proxy in the formatX.X.X.X:80. If you wanted to provide the domain and port directly, such as withstore.example.com:80, you still need to specify the authority header to avoid issues as Envoy uses the domain name and the port as the host header. Becausestore.example.com:80does not matchstore.example.com, you see the same error as if no domain was provided. You can avoid this error by specifying the authority header explicitly. If you cannot specify the authority header, you can update the domain match on the Virtual Service to usestore.example.com*instead. -
Create a self-signed TLS certificate for your domain. Note that self-signed certificates are not a recommended security practice for production. If you plan to use a gRPC app in production, create certificates that are signed by a trusted public or private certificate authority.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt -subj "/CN=store.example.com" -
Store the certificate in a Kubernetes secret.
kubectl create secret tls grpc-tls --key tls.key \ --cert tls.crt --namespace gloo-system -
Configure the Virtual Service to use this certificate to authenticate gRPC clients.
glooctl edit virtualservice --name grpc --namespace gloo-system \ --ssl-secret-name grpc-tls --ssl-secret-namespace gloo-system -
Verify that the SSL configuration was added to the Virtual Service.
glooctl get virtualservice grpc -o kube-yamlapiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: grpc namespace: gloo-system spec: sslConfig: secretRef: name: grpc-tls namespace: gloo-system virtualHost: domains: - store.example.com routes: - matchers: - prefix: / routeAction: single: upstream: name: default-grpcstore-demo-80 namespace: gloo-system status: reportedBy: gateway state: 1 subresourceStatuses: '*v1.Proxy.gloo-system.gateway-proxy': reportedBy: gloo state: 1 -
Send another request. Update the
grpcurlcommand to use the-insecureflag instead of the-plaintextflag. You also need to change the port from 80 (http) to 443 (https). Note that if you have a certificate that is trusted by thegrpcurlclient, you can skip the-insecureflag.grpcurl -insecure -authority store.example.com $(glooctl proxy address --port https) solo.examples.v1.StoreService/ListItemsExample output:
{ "items": [ { "name": "item1" } ] }
Summary and next steps
Excellent! In this guide, you explored how to connect to a gRPC Upstream from a gRPC client by using Gloo Gateway. You also learned how to limit routing to certain domains and how to secure the connection between the gRPC client and your upstream with TLS certificates.
To learn how to connect to a gRPC upstream through Gloo Gateway by using a REST API, check out the Transcode HTTP requests to gRPC guide. For more information about how to further secure your Gloo Gateway deployment, see the Network Encryption guides.