golang grpc 获取 remote-ip 和 real-ip

rfyiamcool2020 年 5 月 29 日

前言:

grpc-go 提供了 peer 库可以获取客户端地址,如果无网关及代理的情况下,通过 peer 是可以直接拿到对端的 ip 地址,反之有网关代理,那么拿到的地址非真实客户端,而是对端的直连地址。

该文章原文地址 http://xiaorui.cc/archives/6892

实现:

无网关代理时,可使用 GetPeerAddr() 获取。如有代理需要在代理转发时把代理对端的 ip 给塞到 header 的 x-real-ip 头里,这个过程其实跟 nginx 操作 X-Real-IP 和 X-Forwarded-For 一样的。可做 grpc 网关的服务有很多,比如 nginx、traefik、envoy。下面拿 nginx grpc 配置为例。

  1. // xiaorui.cc
  2. server {
  3. listen 9099 http2;
  4. access_log /var/log/nginx/xiaorui.cc.log;
  5. location / {
  6. grpc_pass grpc://127.0.0.1:9091;
  7. grpc_set_header X-Real-IP $remote_addr;
  8. }
  9. }

Go

Copy

使用 metadata 从 header 里获取 x-real-ip。

  1. // xiaorui.cc
  2. // GetRealAddr get real client ip
  3. func GetRealAddr(ctx context.Context) string {
  4. md, ok := metadata.FromIncomingContext(ctx)
  5. if !ok {
  6. return ""
  7. }
  8. rips := md.Get("x-real-ip")
  9. if len(rips) == 0 {
  10. return ""
  11. }
  12. return rips[0]
  13. }
  14. // GetPeerAddr get peer addr
  15. func GetPeerAddr(ctx context.Context) string {
  16. var addr string
  17. if pr, ok := peer.FromContext(ctx); ok {
  18. if tcpAddr, ok := pr.Addr.(*net.TCPAddr); ok {
  19. addr = tcpAddr.IP.String()
  20. } else {
  21. addr = pr.Addr.String()
  22. }
  23. }
  24. return addr
  25. }

Go

Copy

总结:

x-forwared-for 在 web 场景下很常见,grpc 下想不到场景。 😅 通常为了优化 web 服务,加入多级缓存及负载均衡,这些节点都有可能追加 x-forwared-for 头。
http://xiaorui.cc/archives/6892