0時に寝て何度か起きて7時に起きた。寒くて朝起きれなくなってきた。

ローカルネットワークからのリクエストのみを受け付けるミドルウェア

Request 構造体の中にある RemoteAddr を参照すればすぐできるだろ思って、すぐにできた。すぐにできたんだけど、これは実際の運用で使えるものではない。

type LocalNetworkConfig struct {
	Skipper middleware.Skipper
}

func localNetworkWithConfig(cfg LocalNetworkConfig) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if cfg.Skipper(c) {
				return next(c)
			}

			req := c.Request()
			addr, _, err := net.SplitHostPort(req.RemoteAddr)
			if err != nil {
				slog.Error("failed to get remote address",
					"err", err, "addr", addr)
				return echo.ErrForbidden
			}
			ip := net.ParseIP(addr)
			if ip.IsPrivate() || ip.IsLoopback() {
				return next(c)
			}

			slog.Error("requested from an external network", "ip", ip.String())
			return echo.ErrForbidden
		}
	}
}

func NewLocalNetwork() echo.MiddlewareFunc {
	return localNetworkWithConfig(LocalNetworkConfig{
		Skipper: func(c echo.Context) bool { return false },
	})
}

過去にも同じようなことをやらかしたような既視感がある。

golangのnet.Request構造体内にはRemoteAddr属性があり、当然のことながら要求元のリモートアドレスが含まれています。これで仕事は終わりですね?しかし、リバースプロキシやロードバランサーをアプリケーションに使っている場合はそうではありません。これはgoサーバには常に、すべてのリクエストがロードバランサから来ているかのように見えます。これは、これを何らかのスロットリングの指標として使いたいのであれば、最悪なことです。ですから、何らかの形のリバースプロキシの後ろでなく、goサーバーが1つだけ動いているのでない限り、私たちの目的には役に立たないとして、すぐにこれを捨てることができます。

https://husobee.github.io/golang/ip-address/2015/12/17/remote-ip-go.html

途中のロードバランサーやアプリケーションゲートウェイ、リバースプロキシなど、中継するサーバーの ip アドレスに置き換わってしまうため、ネットワークのインフラを管理していないとどこからリクエストされたかを追跡することはできない。X-Real-IPX-Forwarded-For という http ヘッダーに中継したサーバーの ip アドレスを記録するという慣習があるようだけど、これはクライアント側で書き換えできるのでこれ単体でアクセス制御のようなものに使うことはできない。

次に同じ失敗をしないようにここに書いておく。