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
grpcurl
to act as the gRPC client. - Install
openssl
to generate self-signed TLS certificates. For example, to installopenssl
on 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 yaml
Example 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.grpcJsonTanscoder
proto descriptor field. Note that the field is truncated in the example command. Make sure to add the entirespec.kube.serviceSpec.grpcJsonTanscoder
value 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: true
annotation 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
grpc
service 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: true
value was added to your upstream.kubectl get upstream -n gloo-system default-grpcstore-demo-80 -o yaml
Example 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-system
namespace 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
grpcurl
utility 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 forgrpcurl
to use with the request. Thelist
argument retrieves all the services that are availble in the gRPC app.grpcurl -plaintext $(glooctl proxy address --port http) list
Example 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.StoreService
Example 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.CreateItemRequest
method, run the following command.grpcurl -plaintext $(glooctl proxy address --port http) describe solo.examples.v1.CreateItemRequest
Example output:
solo.examples.v1.CreateItemRequest is a message: message CreateItemRequest { .solo.examples.v1.Item item = 1; }
-
Use the
CreateItem
method to add an item to the store app.grpcurl -plaintext -d '{"item":{"name":"item1"}}' $(glooctl proxy address --port http) solo.examples.v1.StoreService/CreateItem
Example output:
{ "item": { "name": "item1" } }
-
Use the
ListItems
to retrieve all the items that are available in the store.grpcurl -plaintext $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItems
Example 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
domain
entry to thevirtualHost
configuration. Instead of allowing requests from any domain, the gRPC store app now can receive requests from only thestore.example.com
domain.kubectl edit vs grpc -n gloo-system
Add 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-system
If 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.com
domain, 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 anauthority
header (the equivalent of an HTTP host header incurl
). Instead, Envoy used the IP address that was returned with theglooctl proxy address
command as the host name.grpcurl -plaintext $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItems
Example 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
authority
flag to the request. This time the request succeeds.grpcurl -plaintext -authority store.example.com $(glooctl proxy address --port http) solo.examples.v1.StoreService/ListItems
Example 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:80
does 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-yaml
apiVersion: 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
grpcurl
command to use the-insecure
flag instead of the-plaintext
flag. You also need to change the port from 80 (http) to 443 (https). Note that if you have a certificate that is trusted by thegrpcurl
client, you can skip the-insecure
flag.grpcurl -insecure -authority store.example.com $(glooctl proxy address --port https) solo.examples.v1.StoreService/ListItems
Example 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.