【问题标题】:How to implement efficient IP whitelist in gin-gonic/gin middleware如何在gin-gonic/gin中间件中实现高效的IP白名单
【发布时间】:2018-04-02 17:46:38
【问题描述】:

我有一个应用程序需要限制为少数 IP。如果请求 IP 不在允许列表中,我可以编写一个中间件并返回,但是我希望这个过程尽可能高效。 IE。我想尽早断开连接。我可以断开连接的最早阶段是什么,最好是使用 HTTP 响应。 我无法控制主机防火墙或边界防火墙来过滤流量,而且即使我控制了防火墙,我也无法提供 HTTP 响应。

如果我能在 gin 中获得关于 HTTP 请求生命周期的描述,我更愿意。

【问题讨论】:

    标签: go middleware go-gin


    【解决方案1】:

    添加一个如 Lansana 所述的中间件。

    尽早在链中声明它很重要。

    r := gin.New()
    
    whitelist := make(map[string]bool)
    whitelist["127.0.0.1"] = true
    
    r.Use(middleware.IPWhiteList(whitelist))
    

    我会这样写中间件,如果你不在白名单中,返回一个合适的错误,在下面的sn-p中我返回一个JSON错误。

    package middleware
    
    import (
        "log"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func IPWhiteList(whitelist map[string]bool) gin.HandlerFunc {
        return func(c *gin.Context) {
            if !whitelist[c.ClientIP()] {
                c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
                    "status":  http.StatusForbidden,
                    "message": "Permission denied",
                })
                return
            }
        }
    }
    

    【讨论】:

    • 感谢您的回复。请检查我上面的评论,任何信息都会非常有用,特别是 gin 中的 HTTP 请求生命周期。
    【解决方案2】:

    你读过Gin documentation吗?

    您可以根据this section 中的示例添加中间件。使用router.Use() 链接中间件。如果您希望它尽可能高效,请将其设置为链中的第一个中间件,并使用允许 O(1) 对允许的 IP 进行读取访问的数据结构。

    例如:

    var myWhiteList map[string]bool = map[string]bool{
        "1.2.3.4": true,
        "4.3.2.1": true,
    }
    
    func main() {
        // Creates a router without any middleware by default
        r := gin.New()
    
        // Add whitelist middleware
        r.Use(middleware.IPWhitelist(myWhiteList))
    
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }
    

    middleware.IPWhitelist 的实现可能如下所示:

    func IPWhiteList(whitelist map[string]bool) func(http.Handler) http.Handler {
        f := func(h http.Handler) http.Handler {
            fn := func(w http.ResponseWriter, r *http.Request) {
                // Get IP of this request
                ip := doSomething()
    
                // If the IP isn't in the whitelist, forbid the request.
                if !whitelist[ip] {
                    w.Header().Set("Content-Type", "text/plain")
                    w.WriteHeader(http.StatusForbidden)
                    w.Write([]byte("."))
                    return
                }
    
                h.ServeHTTP(w, r)
            }
    
            return http.HandlerFunc(fn)
        }
    
        return f
    }
    

    【讨论】:

    • 我应该更详细一点:只要建立 TCP 连接,remoteAddr 就可用,但是,我的假设是,只有当客户端完成发送完整的 HTTP 请求时,第一个中间件才会执行。如果客户端发布文件,我的第一个具有 O(1) 查找的中间件将在几毫秒、几 MB 的数据传输和至少几千个 CPU 周期后执行。所以我的问题是中间件可以控制请求处理的最早阶段是什么,并且可以拒绝请求,最好使用有效的 HTTP 响应代码,从而节省上述资源
    • 或者Expect header应该如何处理? developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect
    • 我不明白你的问题。你能详细说明一下吗?如果你想获取 RemoteAddr 或通过其他方式获取 IP,请在我在上面的中间件中添加的 doSomething() 函数中进行。您可以在此处获取 IP 地址或唯一请求 ID,但您想这样做。
    • doSomething() 在客户端完成发送所有数据(请求标头+请求正文)后调用。我希望能够在收到请求标头之后但在传输请求正文之前决定是否要处理请求。这将节省大量时间和其他资源。我希望问题现在很清楚。非常感谢您的帮助。
    • 我认为 Golang 不是解决该问题的正确工具。您应该在 DNS 级别执行此操作,可能是在防火墙或负载均衡器中,而不是在您的 Go 代码中。如果您的意图是防止 DDoS,那么您绝对不想在 Go 中这样做。无论您添加哪种中间件,所要做的就是完全淹没您的管道,并且您的服务器仍将停止运行。研究类似 CloudFlare 的东西;它们是专门为防止 DDoS 而构建的,因为它们将在您的服务前充当代理,并防止任何恶意请求到达您的服务。
    猜你喜欢
    • 2018-11-07
    • 1970-01-01
    • 2021-06-13
    • 2015-09-04
    • 2017-08-03
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多