0
votes

I have an application which I need to restrict to a handful of IPs. I can write a middleware and return if request IP is not from allowed list, however I would like this process to be as efficient as possible. I.e. I would like to drop the connection as early as possible. What is the earliest stage I can drop connection, preferably with an HTTP response. I do not have control on host firewall or border firewall to filter traffic, and again, I won't be able to provide an HTTP response, even if I had control of firewall.

Also I would prefer if I could get a description of a life cycle of an HTTP request in gin.

2

2 Answers

5
votes

Add a middleware as Lansana described.

It's important that you declare it as early in the chain as possible.

r := gin.New()

whitelist := make(map[string]bool)
whitelist["127.0.0.1"] = true

r.Use(middleware.IPWhiteList(whitelist))

I'd write the middleware like this, if you're not in the whitelist, return an error that is appropriate, in the following snippet i'm returning a JSON error.

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
        }
    }
}
4
votes

Have you read the Gin documentation?

You can add middleware based on the examples in this section. Use router.Use() to chain middleware. If you want it to be as efficient as possible, make it the first middleware in the chain, and use a data structure that allows O(1) read access to the IP's that are allowed.

For instance:

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")
}

The implementation of middleware.IPWhitelist can look something like this:

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
}