英文:
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
将服务器绑定到所有可用的接口。例如 [::]:8123
(netstat -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{"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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论