限制来自本地子网的客户端对GO Web服务的访问

问题描述:

I'm writing a small web service in GO using just the GO http package. I want to restrict access to the web service to clients on the local subnets (127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16).

I tried using a subnet mask as addr argument to ListenAndServe but it exits with a "no such host" error.

EDIT:

This is the solution I came up with the help of @RickA and @Dewy Broto.

func JustLocal(handler http.Handler) http.Handler {
    var local_subnets []*net.IPNet
    local_subnet_s := []string{"127.0.0.1/31", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}

    for _,net_s := range local_subnet_s {
        _, n, _ := net.ParseCIDR(net_s)
        local_subnets = append(local_subnets, n)
    }

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Println(r.RemoteAddr)
        remote_ip := net.ParseIP(strings.Split(r.RemoteAddr, ":")[0])
        fmt.Println(remote_ip)

        local := false
        for _, local_subnet := range local_subnets {
            fmt.Println(local_subnet, remote_ip)
            if local_subnet.Contains(remote_ip) {
                local = true
                break
            }
        }
        if !local {
            http.Error(w, "go away", 403)
            return
        }
        handler.ServeHTTP(w, r)
        return
    })
}

It's a bit raw around the edges but it works as far as I could tell. Thanks for all the help!

我仅使用GO http包在GO中编写了一个小型Web服务。 我想将对Web服务的访问限制为本地子网上的客户端(127.0.0.1、10.0.0.0 / 8、172.16.0.0 / 12、192.168.0.0 / 16)。 p>

我尝试将子网掩码用作 ListenAndServe 的addr参数,但它以“否”退出 这样的主机”错误。 p>

编辑: p>

这是我在@RickA和@Dewy Broto的帮助下提出的解决方案。 p >

  func JustLocal(handler http.Handler)http.Handler {
 var local_subnets [] * net.IPNet 
 local_subnet_s:= [] string {“ 127.0.0.1/31”,  _,net_s的“ 10.0.0.0/8"、"172.16.0.0/12"、"192.168.0.0/16"}

:=范围local_subnet_s {
 _,n,_:= net.ParseCIDR(  net_s)
 local_subnets = append(local_subnets,n)
} 
 
返回http.HandlerFunc(func(w http.ResponseWriter,r * http.Request){
 fmt.Println(r.RemoteAddr)
  remote_ip:= net.ParseIP(strings.Split(r.RemoteAddr,“:”)[0])
 fmt.Println(remote_ip)
 
本地 := false 
表示_,local_subnet:=范围local_subnets {
 fmt.Println(local_subnet,remote_ip)
 if local_subnet.Contains(remote_ip){
 local = true 
 break 
} 
} 
  if!local {
 http.Error(w,“ go away”,403)
返回
} 
处理函数。ServeHTTP(w,r)
 return 
})
} 
  code  >  pre> 
 
 

边缘有点生,但据我所知它仍然有效。 感谢您的所有帮助! p> div>

Write a handler that checks to see if the client's IP address is allowed before delegating to another handler:

type authorizationHandler struct {
    nets []*net.IPNet
    h handler
}

func (ah *authorizationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ok := false
    ip := net.ParseIP(r.RemoteAddr)
    for _, n := range ah.nets {
       if n.Contains(ip) {
         ok := true
         break
       }
    }
    if !ok {
       http.Error(w, "go away", 403)
       return
    }
    ah.h(w, r)
}

Use this handler to wrap your the root handler:

err := ListenAndServe(addr, 
          &authorizationHandler{nets: allowedNets, h:rootHandler))

Note: I typed in the above code without compiling or testing it. Some tweaks are probably required around parsing and testing the remote address.

The address in ListenAndServe is the address your server binds to on the machine where it is running. It is not some sort of ip whitelist thingy. This allows you to specify on what (network)interface you want to bind the server.

So if this machine has a ethernet card (ifconfig) that has the ipadress 10.0.100.100 you can bind ListenAndServe to 10.0.100.100:8123. The ip-less version :8123 binds the server to all available interfaces. eg. [::]:8123 (netstat -l), and is thus reachable from outside.

To get what you want you have a couple of options:

  • Put the machine in a private subnet that you trust and bind ListenAndServe to the ip of that subnet on your machine. Thus it should only be routable from within that subnet.
  • Use a webserver in front of this server, have it pass requests to it based on its ip acl (ex. nginx)
  • Implement your own ip(range) filtering in the go server code (eg. use request.URL and test that against your whitelist)

You can listen on multiple ports / hosts, you have to call ListenAndServe multiple times.

func main() {
    addrs := []string{"127.0.0.1:8080", "10.0.0.1:8080", "172.16.0.1:8080", "192.168.0.1:8080", "192.168.1.1:8080"}
    errs := make(chan error)
    for _, addr := range addrs {
        go func(addr string) {
            errs <- http.ListenAndServe(addr, nil) //or replace the nil with your mux
        }(addr)
    }
    i := len(addrs)
    for err := range errs {
        fmt.Println(err)
        if i = i - 1; i == 0 {
            break
        }
    }
}

Or you could use nging and just make your go server listen on 127.0.0.1.