1

How can I get the client IP address in Go gRPC from a load balancer that adds x-real-ip and x-forward-for to the header?

The "peer" lib is giving me the only load balancer ip. (from this answer: Get client's IP address from load balance server by X-Forwarded-For)

Just to make it clear: The context where I need to grab this information is inside the GRPC server handler. It may be in an interceptor or inside the request handler:

example:

func (s *myservice) MethodName(ctx context.Context, request *pb.MethodRequest) (*pb.MethodResponse, error) {

    // How to get ip from ctx ? 
    // I have tryied peer but it gives me the loadbalancer ip
    peer, ok := peer.FromContext(ctx)

    return &pb.MethodResponse{Pong: "ok"}, nil
}

It would be ok if I could get this from the interceptor as well... either way works for me.

func (a *MyInterceptor) Unary() grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (interface{}, error) {
        log.Debug("--> My interceptor: " + info.FullMethod)

        
        // How to get ip from ctx ? 
        
        return handler(ctx, req)
    }
}

PS: I'm using Kong as load balancer. https://docs.konghq.com/0.14.x/loadbalancing/

Totalys
  • 445
  • 2
  • 10
  • 25

2 Answers2

1

Looking at the loadbalancer docs you can see that there is a header set of X-Real-IP https://docs.konghq.com/0.14.x/configuration/#real_ip_header

This should be returned in the metadata of the ctx. I can't test this unfortunately, but let me know if it works

func (a *MyInterceptor) Unary() grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (interface{}, error) {
        log.Debug("--> My interceptor: " + info.FullMethod)

        
        // How to get ip from ctx ?
        var realIP string
        m, ok := metadata.FromIncomingContext(ctx)
        if ok {
            realIP := md.Get("X-Real-IP") 
        }
        // do what you need with realIP
        return handler(ctx, req)
    }
}
Chris Townsend
  • 2,586
  • 2
  • 21
  • 41
0

You can read the headers from the request object.

func handler(w http.ResponseWriter, r *http.Request) {
    realIP := r.Header.Get("x-real-ip")
    forwardFor := r.Header.Get("x-forward-for");

    // ...
}
Matt
  • 3,677
  • 1
  • 14
  • 24
  • Please, take a look at my update. Is there a way to get the ip from that context? If not, where does this code fits inside the grpc context? tks – Totalys Jan 27 '21 at 18:16
  • You can add anything to the context. The headers won't exist in the context by default, but you could put them there in the request handler. At the end of the day, the headers you want are on the request object, so you need to get them in the request handler. – Matt Jan 28 '21 at 02:31
  • I still don't get how to use this function inside the gRPC context. Is there a way to add this handler manually in the grpc server? Because Header is not exposed this way in the grpc. At least I'm not aware how to access it. All I have is the grpc metadata. – Totalys Jan 28 '21 at 03:43
  • 1
    Does [this](https://stackoverflow.com/questions/57686963/how-to-access-request-headers-in-grpc-service-proxied-by-grpc-gateway-in-golang) help? You can ignore my answer, it relates to a HTTP request, but you won't have access to the same kind of object. – Matt Jan 28 '21 at 03:49
  • Thank you! I guess I'll just need to inspect the metadata. – Totalys Jan 28 '21 at 04:11