🙄

ginのリクエスト元のIPアドレス取得ソースコードを読んでみました

2022/11/29に公開

背景

リクエストが来る時に、リクエスト元のIPアドレスをみて、アクセスを制限させる要望はよくあると思います。
ginは一応そのIPの取り方をAPIとして用意されてます、今回そのAPIの中身を読んでみました。

ginのバージョン

github.com/gin-gonic/gin v1.8.1

API

まずgin.Contextの中に②種類のAPIが存在しています

  • RemoteIP: 直接繋がるリクエスト先のIPを取っています。
  • ClientIP: HTTPヘッダー情報からIP情報を解析しています。

1. RemoteIP####

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L807

実質はgin.Context.Request.RemoteAddrの価を取り出しています。

この値はginが依存しているnet/httpパッケージから取ってきています。

https://github.com/golang/go/blob/master/src/net/http/server.go#L1847

さらにnetパッケージから取ってきています。

https://github.com/golang/go/blob/master/src/net/net.go#L227

この方法を使えば、直接ginが搭載しているサーバーと繋がるエンドユーザーのIPを取れますが、実際にdeployされる環境では、エンドユーザーとginのサーバーの間には複数proxyサーバーが存在する可能性は高いです。
そのため、エンドユーザーのIPを取るためには、このAPIは活用できない可能性は結構たかいです。

このAPIを使うなら、取れるのはginのサーバーと直接繋がる一個前のproxyサーバーのIPアドレスになります。

2. ClientIP

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L769

この方法を使えば、うまく設定すれば、エンドユーザーのIPアドレスを取れます。

まず中を見てみましょう

2-1. 一個前のproxyサーバーのIPアドレスを取ります

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L789

2-2. 一個前のproxyサーバーは信頼するべきかどうかを判断します

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L793-L795

信頼するリストがあります。それはmain関数が走る時に、設定するものです。
gin1.8ではproxyサーバーのIPアドレスを信頼リストに登録する必要があります。

2-2-1. 一個前のproxyサーバーは信頼するものではないなら、そのまま一個前のproxyサーバーのIPアドレスを返します

2-2-2. 一個前のproxyサーバーは信頼するものなら

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L795

2-3. httpヘッダーにある情報を取り出して、検証します

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/context.go#L797

デフォルトはこちらのヘッダーを取り出しています。

https://github.com/gin-gonic/gin/blob/master/gin.go#L192

validationの中身を見てみると

https://github.com/gin-gonic/gin/blob/80cd679c43a3d6ed03957b6a3614f97d0af0751c/gin.go#L454-L473

ここに来るheaderは実際にこのような価になっています

142.222.149.55 ,172.16.25.116

,を区切って、配列にして、後ろから見ていますよ。

for i := len(items) - 1; i >= 0; i-- {

そして、最初のelementもしくは、信頼リストに入ってないIPを見つけたら、すぐ返します。

if (i == 0) || (!engine.isTrustedProxy(ip)) {
			return ipStr, true
		}

実際のイメージ

proxyサーバーは受け取ったリクエストのX-Forwarded-Forにリクエスト元のRemoteAddrを後ろに足して、次の先に投げています。

Discussion