在Go上编写Grafana反向代理


我确实想将文章命名为“ 3行中的Go上的代理服务”,但我对此表示赞赏。


实际上,主要逻辑可以分为三行。 对于不耐烦的人和想看本质的人:


proxy := httputil.NewSingleHostReverseProxy(url) r.Header.Set(header, value) proxy.ServeHTTP(w, r) 

对于刚接触Golang语言的人以及需要在尽可能短的时间内创建反向代理的人来说,这下的故事更为详尽。


让我们弄清楚为什么需要代理服务,如何实现代理服务以及标准库的功能。


反向代理


反向代理是一种代理服务器,它从客户端接收请求,将其重定向到一个或多个服务器,然后将响应转发回去。


反向代理的一个显着特征是它是将用户连接到服务器的入口点,代理本身通过​​业务逻辑与服务器连接。 它确定客户端请求将发送到哪些服务器。 在代理后面建立网络的逻辑对用户而言仍然是隐藏的。



反向代理


为了进行比较,常规代理将其客户端连接到他们需要的任何服务器。 在这种情况下,代理位于用户面前,并且只是执行请求的中介。



普通代理(转发代理)


为什么使用
反向代理概念可以应用于各种情况:
-负载平衡,
-A / B测试
-资源缓存,
-压缩请求数据,
-流量过滤
-授权。


当然,范围不限于这六点。 在代理之前和之后都可能处理请求的事实为创造力提供了很大的空间。 在本文中,我们将讨论使用反向代理进行授权。


挑战赛


我们正在开发一个VMmanager 6虚拟化控制面板,一天下来,我们决定让用户在监视和可视化这些群集方面拥有更多的自由。 为此,他们选择了Grafana


为了让Grafana处理我们的数据,必须配置授权。 即使不是三个“ buts”,这样做也不难。


  1. 我们已经有一个入口点-授权服务。
  2. 我们不想启动并授权Grafana中的用户。
  3. 我们不想让用户直接访问Grafana。

为了符合条件,我们决定将Grafana放在内部网络上并编写一个反向代理。 只有在将请求转移到Grafana之后,他才会检查授权服务中的权利。


主意


主要思想是将Grafana中的授权责任转移到反向代理服务器( 官方文档 )。 如果Grafana包含特定的标头,它将接受授权的任何请求。 在替换此标题之前,我们必须确保当前用户有权使用Grafana。



呼叫链“ Grafana代理或往返”


实作


主要功能是相当标准的。 我们启动http服务器,它将接受4000端口上的连接,并处理将与之建立连接的任何“ /”地址。


 func main() { http.HandleFunc("/", handlerProxy) if err := http.ListenAndServe(":4000", nil); err != nil { panic(err) } } 

大部分工作发生在请求处理程序中。


[完整的代码被删减]
 func handlerProxy(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL.Host) if strings.HasPrefix(r.URL.String(), "/api") { //     } url, err := url.Parse(fmt.Sprintf("http://%s/", grafanaHost)) if err != nil { SendJSONError(w, err.Error()) return } proxy := httputil.NewSingleHostReverseProxy(url) fmt.Println(r.URL.Host) r.Header.Set(grafanaHeader, grafanaUser) proxy.ServeHTTP(w, r) } 

让我们浏览一下参数。 我将示例中的主要变量放入常量中:


 grafanaUser = "admin" //,      grafanaHost = "grafana:3000" //  grafana grafanaHeader = "X-GRAFANA-AUTH" //Header,      

例如,这足够了;实际上,您可能需要预设这些值。 您可以将它们的代理作为命令行参数传递,然后使用标志或更多高级软件包来解析它们。 容器环境还经常使用环境变量来配置服务, os.Getenv将一路为您提供帮助。


接下来是授权检查:


 if strings.HasPrefix(r.URL.String(), "/api") { err := CheckRights(r.Header) if err != nil { SendJSONError(w, err.Error()) return } } 

如果请求发送到grafana.host/api,我们将检查当前用户的权限以使用Grafana。 必须进行验证,以确保对于JS脚本或PNG图标的每个GET请求都不会干扰授权点。 我们将代理静态内容,而无需进行其他检查。 为此,我们将带有包含用户会话的标头的映射传递给授权服务。 这可能是常规的GET请求。 授权服务设备在这里无关紧要。 如果授权数据不合适,请关闭连接,并返回错误。


检查之后,我们形成基本路径的对象:


 url, err := url.Parse(fmt.Sprintf("http://%s/", grafanaHost)) 

使用扩展http包的标准httputil包,我们形成ReverseProxy对象。


 proxy := httputil.NewSingleHostReverseProxy(url) 

ReverseProxy是一个请求处理程序,它将接受传入的请求,将其发送到Grafana并将响应传递回客户端。


它将所有请求转发到地址“基本路径+请求的URL”。 如果用户到达地址代理:4000 / api / something,他的请求将被转换为grafana:3000 / api / something,其中grafana:3000是传递给NewSingleHostReverseProxy的基本路径,而/ api / something是传入请求。


添加Grafana的授权标头并调用ServeHTTP方法,该方法将完成大部分请求处理。


 r.Header.Set(grafanaHeader, grafanaUser) proxy.ServeHTTP(w, r) 

在后台,ServeHTTP做了很多工作,例如,处理X-Forwarded-For标头或101服务器对协议更改的响应。 该方法的主要工作是将请求发送到组合地址,并将接收到的答案传输到ResponseWriter。



结果


所有代码都可以在github上找到


检查一下


使用Docker模拟我们的系统。 让我们在一个网络中创建两个容器-proxy和Grafana。 我们不会创建授权点,我们认为授权点总是肯定的。 Grafana容器将脱机不可用,代理容器在特定端口上可用。


创建一个网络:


 docker network create --driver=bridge --subnet=192.168.0.0/16 gnet 

通过标头使用配置的授权模式提升Grafana容器:


 docker run -d --name=grafana --network=gnet -e "GF_AUTH_PROXY_ENABLED=true" -e "GF_AUTH_PROXY_HEADER_NAME=X-GRAFANA-AUTH" grafana/grafana 

请注意,这是演示而不是最终配置。 至少必须设置管理员密码,并禁止自动用户注册。


提升反向代理:


 docker run -d --name proxy -p 4000:4000 --network=gnet grafana_proxy:latest 

在浏览器中,转到localhost:4000。


太好了,我们面前有授权的Grafana。


github上有用于使用代理构建容器的Dockerfile以及有关提升容器的更多详细说明。

Source: https://habr.com/ru/post/zh-CN480446/


All Articles