How to listen on multiple grpc ports
The context I am exploring grpc-spring-boot-starter and like to find out how to support 2 ports. For example, I'd like to have 1 port running on 9090 as external port and 9091 as internal port, so that users can have different scope of access to my api. for spring restful, the underlying container, like tomcat or undertow, it can support such multiple ports within 1 spring process.
i searched your doc, but can't find such configuration to allow it. is it doable?
The question
Stacktraces and logs
The application's environment
Which versions do you use?
- Spring (boot): latest
- grpc-java:
- grpc-spring-boot-starter: latest
- java: version + architecture (64bit?) 1.8
- Other relevant libraries...
Additional information
- Did it ever work before?
- How can we reproduce it?
- Do you have a demo?
Variant 1 - two ports same handler (both ports can connect to the same services, but they are separated by user/ip/port permissions):
First you need a GrpcServerConfigurer (Docs), there you can add the additional port. (Note: By the default variant is the shaded netty)
You will need something like this to get the actual port the user connected to (probably using a different key): https://github.com/yidongnan/grpc-spring-boot-starter/blob/7ce76713a562c4f8ad907c89d4239eabda32ee7f/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/security/authentication/SSLContextGrpcAuthenticationReader.java#L45 If your authentication reader does not add any details itself this library will attach the details by default: https://github.com/yidongnan/grpc-spring-boot-starter/blob/7ce76713a562c4f8ad907c89d4239eabda32ee7f/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/security/interceptors/DefaultAuthenticatingServerInterceptor.java#L104
Your Authenticationmanager/provider must convert these to actual permissions/roles though.
Variant 2 - two ports, separated handlers (the normal server doesn't know about the management services and vice versa):
You have to create two GrpcServerFactories and Lifecycle instances and somewhat control which service is added where. Basically this code just twice:
https://github.com/yidongnan/grpc-spring-boot-starter/blob/7ce76713a562c4f8ad907c89d4239eabda32ee7f/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/autoconfigure/GrpcServerFactoryAutoConfiguration.java#L56-L98
One of them probably with an overwritten port/address method: https://github.com/yidongnan/grpc-spring-boot-starter/blob/7ce76713a562c4f8ad907c89d4239eabda32ee7f/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/serverfactory/ShadedNettyGrpcServerFactory.java#L71
Sorry, I was on vacation and also dragged to other project after back and hence the delay.
Thanks a lot for the response. I tried Variant 1 and able to setup 2 ports. However, I can't get the actual port that the request comes in. I have different apis that i wish to map to different port in order to control usage, like internal users vs external. If coming from a wrong port, i'd simply return an error. How do I prepare ServerCall? via interceptor?
However, I can't get the actual port that the request comes in.
You can via:
io.grpc.Grpc.TRANSPORT_ATTR_LOCAL_ADDR
If you are lucky, these attributes are still available in your authentication instance:
https://github.com/yidongnan/grpc-spring-boot-starter/blob/2cc8fa9d9b39dc1093dbcd2a0090b5ef6e075d67/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/security/interceptors/DefaultAuthenticatingServerInterceptor.java#L104
Then you can use the https://github.com/yidongnan/grpc-spring-boot-starter/blob/2cc8fa9d9b39dc1093dbcd2a0090b5ef6e075d67/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/security/check/ManualGrpcSecurityMetadataSource.java#L46
and configure the methods according to your needs e.g. set(x, auth -> auth.getUserDetails.get(TRANSPORT_ATTR_LOCAL_ADDR).toString().equals("xyz"))
(Not tested)
I also might have found a way to add this feature to this library in the future, but that requires some changes to the AuthorizationCheckingServerInterceptor, AccessPredicates and GrpcSecurityMetadataSource and might take a while to be released. (Basically include the original server call/metadata in the access check)
If not, you have to write your own ServerInterceptor and compare the ServerCall.getAttributes/the actual port with the method descriptor.
Hi there, thanks a lot for the suggestions. I took the ServerInterceptor approach and it works. Below is my code
@GrpcGlobalServerInterceptor public class MyInterceptor implements ServerInterceptor {
@Override public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata headers, ServerCallHandler<ReqT, RespT> next)
{ Context ctx = Context.current().; System.out.println("incoming port = " + (InetSocketAddress)serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_LOCAL_ADDR)).getPort()); return Contexts.interceptCall(ctx, serverCall, headers, next); } }
FYI: I created a PR that grants the developer access to the ServerCall during authorization checks (e.g. the client address):
https://github.com/yidongnan/grpc-spring-boot-starter/pull/742
This PR can be considered BREAKING if you use our grpc authorization classes for more than just configuration.
If you just use it for simple configuration, then you don't have to change anything.
The enhanced security support (for multiple server ports) was added in #742.