限制对GO Web服务的访问仅限于本地子网的客户端。

huangapple go评论74阅读模式
英文:

Restrict access to GO webservice to clients from local subnets

问题

我正在使用GO的http包编写一个小型Web服务。我想将对该Web服务的访问限制在本地子网上(127.0.0.1、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)。

我尝试将子网掩码作为addr参数传递给ListenAndServe,但它会出现"no such host"错误。

这是我在@RickA和@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
    })
}

它可能还有些不完善,但据我所知它是有效的。感谢所有的帮助!

英文:

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!

答案1

得分: 3

ListenAndServe中的地址是服务器绑定到运行该服务器的机器上的地址。它不是某种IP白名单的东西。这允许您指定要绑定服务器的(网络)接口。

因此,如果该机器有一个以太网卡(ifconfig)具有IP地址10.0.100.100,您可以将ListenAndServe绑定到10.0.100.100:8123
无IP版本的:8123将服务器绑定到所有可用的接口。例如 [::]:8123netstat -l),因此可以从外部访问。

要实现您想要的效果,您有几个选项:

  • 将机器放在您信任的私有子网中,并将ListenAndServe绑定到该子网在您的机器上的IP上。因此,它只能从该子网内路由。

  • 在此服务器前面使用一个Web服务器,让它根据其IP访问控制列表将请求传递给它(例如nginx)。

  • 在Go服务器代码中实现自己的IP(范围)过滤(例如,使用request.URL并将其与白名单进行测试)。

英文:

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)

答案2

得分: 2

编写一个处理程序,在委派给另一个处理程序之前检查客户端的IP地址是否被允许:

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

使用此处理程序包装您的根处理程序:

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

注意:我在上面的代码中输入时没有进行编译或测试。可能需要对解析和测试远程地址进行一些调整。

英文:

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.

答案3

得分: 0

你可以在多个端口/主机上进行监听,需要多次调用ListenAndServe函数。

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) //或者将nil替换为你的mux
        }(addr)
    }
    i := len(addrs)
    for err := range errs {
        fmt.Println(err)
        if i = i - 1; i == 0 {
            break
        }
    }
}

或者你可以使用nginx,然后让你的Go服务器只监听127.0.0.1

英文:

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

func main() {
	addrs := []string{&quot;127.0.0.1:8080&quot;, &quot;10.0.0.1:8080&quot;, &quot;172.16.0.1:8080&quot;, &quot;192.168.0.1:8080&quot;, &quot;192.168.1.1:8080&quot;}
	errs := make(chan error)
	for _, addr := range addrs {
		go func(addr string) {
			errs &lt;- 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.

huangapple
  • 本文由 发表于 2014年9月30日 17:58:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/26117940.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定