Search code examples
httpnginxhttpsgrpcgrpc-python

Redirect gRPC traffic using nginx from HTTPS to HTTP


I am planning to redirect HTTPS and HTTP gRPC traffic using nginx for a special use case. I am being able to recreate the problem using a hello world example. The main documentation I have used are [Introducing gRPC Support with NGINX 1.13.10][1] and [Nginx as Reverse Proxy with GRPC][2].

Firstly, I created certificate files for the ssl connection using

openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt -subj '/CN=localhost'

When I follow the article, I am being able to successfully route traffic from a secure grpc client to a secure grpc server. However, my use case needs to forward traffic from a secure nginx port to an insecure grpc server. The client, nginx.conf and server code attached below.

nginx.conf (Needs to reroute traffic to an insecure port)

upstream dev {
 server localhost:1338;
}

server {
listen 1449 ssl http2;
ssl_certificate /ssl/server.crt;  #Enter you certificate location 
ssl_certificate_key /ssl/server.key;

location /helloworld.Greeter {
 grpc_pass grpcs://dev;

 }
}

client.py (Includes ssl certificate to hit nginx secure endpoint)


from __future__ import print_function
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.

    host = 'localhost'
    port = 1449 

    with open('/home/ubuntu/Documents/ludex_repos/nginx-grpc/server.crt', 'rb') as f:
        trusted_certs = f.read()

    credentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs) 
    with grpc.secure_channel(f'{host}:{port}', credentials) as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
    print(f"========================Greeter client received: {response.message}===============================")


if __name__ == '__main__':
    logging.basicConfig()
    run()

server.py (Has insecure port)


from concurrent import futures
import time
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    port = '1338'

    with open('/ssl/server.key', 'rb') as f:
        private_key = f.read()
    with open('/ssl/server.crt', 'rb') as f:
        certificate_chain = f.read()

    server_credentials = grpc.ssl_server_credentials(((private_key, certificate_chain,),))

    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)

    **If I change this to a secure port then it routes traffic correctly via nginx**

    #server.add_secure_port('[::]:'+port, server_credentials)
    server.add_insecure_port('[::]:'+port)

    print("Server Started...")
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    logging.basicConfig()
    serve()

Secure to secure response

========================Greeter client received: Hello, you!===============================

Secure to insecure response

Traceback (most recent call last):
  File "greeter_client.py", line 45, in <module>
    run()
  File "greeter_client.py", line 39, in run
    response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
  File "/home/ubuntu/anaconda3/envs/fp/lib/python3.8/site-packages/grpc/_channel.py", line 946, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/home/ubuntu/anaconda3/envs/fp/lib/python3.8/site-packages/grpc/_channel.py", line 849, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
        status = StatusCode.UNAVAILABLE
        details = "Received http2 header with status: 502"
        debug_error_string = "{"created":"@1641485952.541123035","description":"Received http2 :status header with non-200 OK status","file":"src/core/ext/filters/http/client/http_client_filter.cc","file_line":132,"grpc_message":"Received http2 header with status: 502","grpc_status":14,"value":"502"}"
>

I understand a reverse proxy is possible and I've seen examples forwarding traffic from https to http using webpages but I'm not sure if it is possible to do it with gRPC traffic? [1]: https://www.nginx.com/blog/nginx-1-13-10-grpc/ [2]: https://medium.com/nirman-tech-blog/nginx-as-reverse-proxy-with-grpc-820d35642bff


Solution

  • Try using grpc_pass grpc://... instead of grpcs://...

    This updated blog post might help: https://www.nginx.com/blog/deploying-nginx-plus-as-an-api-gateway-part-3-publishing-grpc-services/