Gravando o proxy reverso Grafana em Go


Eu realmente queria nomear o artigo "Serviço proxy em Go em 3 linhas", mas estou acima disso.


De fato, a lógica principal pode caber em três linhas. Para impacientes e aqueles que querem ver a essência:


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

Sob o gato, há uma história mais detalhada para iniciantes no idioma Golang e para aqueles que precisam criar um proxy reverso no menor tempo possível.


Vamos descobrir por que um serviço de proxy é necessário, como implementá-lo e o que está sob o capô da biblioteca padrão.


Proxy reverso


Um proxy reverso é um tipo de servidor proxy que recebe uma solicitação de um cliente, a redireciona para um ou mais servidores e encaminha a resposta de volta.


Uma característica distinta do proxy reverso é que ele é o ponto de entrada para conectar o usuário aos servidores com os quais o próprio proxy está conectado pela lógica de negócios. Determina para quais servidores a solicitação do cliente será enviada. A lógica de construir uma rede atrás do proxy permanece oculta ao usuário.



Proxy Reverso


Para comparação, um proxy comum conecta seus clientes a qualquer servidor necessário. Nesse caso, o proxy está na frente do usuário e é simplesmente um intermediário na execução da solicitação.



Proxy normal (Proxy de encaminhamento)


Por que usar
O conceito de proxy reverso pode ser aplicado em várias situações:
- balanceamento de carga,
- Teste A / B
- cache de recursos,
- compressão dos dados solicitados,
- filtragem de tráfego,
- autorização.


Obviamente, o escopo não se limita a esses seis pontos. O fato da possibilidade de processar a solicitação antes e depois do proxy fornece muito espaço para criatividade. Neste artigo, discutiremos o uso do proxy reverso para autorização.


Desafio


Estamos desenvolvendo um painel de controle de virtualização VMmanager 6. Um belo dia, decidimos dar aos usuários mais liberdade no monitoramento e visualização desses clusters. Para esses fins, eles escolheram Grafana .


Para o Grafana trabalhar com nossos dados, foi necessário configurar a autorização. Não é difícil fazer isso, se não por três "buts".


  1. Já temos um único ponto de entrada - um serviço de autorização.
  2. Não queremos iniciar e autorizar usuários no Grafana.
  3. Não queremos dar aos usuários acesso ao Grafana diretamente.

Para cumprir as condições, decidimos colocar o Grafana na rede interna e escrever um proxy reverso. Ele verificará os direitos no serviço de autorização e somente depois disso transferirá a solicitação para a Grafana.


Idéia


A idéia principal é transferir a responsabilidade pela autorização no Grafana para o servidor proxy reverso ( documentação oficial ). A Grafana aceitará qualquer solicitação autorizada se ela contiver um cabeçalho específico. Antes de substituir esse cabeçalho, devemos garantir que o usuário atual tenha direitos para trabalhar com a Grafana.



Cadeia de chamadas "Grafana-proxy, ou Ida e Volta"


Implementação


A função principal é bastante padrão. Iniciamos o servidor http, que aceita conexões na porta 4000 e processa qualquer endereço "/" com o qual a conexão ocorrerá.


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

A maior parte do trabalho acontece no manipulador de solicitações.


[código completo em corte]
 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) } 

Vamos analisar os parâmetros. As principais variáveis ​​no exemplo que coloco em constantes:


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

Por exemplo, isso é suficiente; na prática, pode ser necessário predefinir esses valores. Você pode transmitir a eles proxies como parâmetros de linha de comando e, em seguida, usar sinalizadores ou pacotes mais avançados para analisá-los. O ambiente de contêiner também costuma usar variáveis ​​de ambiente para configurar serviços, o os.Getenv o ajudará ao longo do caminho.


A seguir está a verificação de autorização:


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

Se a solicitação for para grafana.host/api, verificamos os direitos do usuário atual de usar o Grafana. A verificação é necessária para que, para cada solicitação GET de um script JS ou ícone PNG, não perturbe o ponto de autorização. Proxy de conteúdo estático sem verificações adicionais. Para fazer isso, passamos o mapa com os cabeçalhos que contêm a sessão do usuário para o serviço de autorização. Esta pode ser uma solicitação GET regular. O dispositivo de serviço de autorização não importa aqui. Se os dados da autorização não forem adequados, feche a conexão, retornando um erro.


Após as verificações, formamos o objeto do caminho base:


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

Usando o pacote padrão enableputil , que estende o pacote http, formamos o objeto ReverseProxy.


 proxy := httputil.NewSingleHostReverseProxy(url) 

ReverseProxy é um manipulador de solicitações que aceita uma solicitação recebida, a envia para a Grafana e passa a resposta de volta ao cliente.


Ele encaminhará todas as solicitações para o endereço "caminho base + URL solicitado". Se o usuário acessar o proxy de endereço: 4000 / api / something, sua solicitação será transformada em grafana: 3000 / api / something, em que grafana: 3000 é o caminho base passado para NewSingleHostReverseProxy e / api / something é a solicitação de entrada.


Adicione o cabeçalho de autorização do Grafana e chame o método ServeHTTP, que fará a maior parte do processamento da solicitação.


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

Sob o capô, o ServeHTTP trabalha bastante, por exemplo, processa o cabeçalho X-Forwarded-For ou a resposta do servidor 101 a uma alteração de protocolo. O principal trabalho do método é enviar uma solicitação para um endereço composto e transferir a resposta recebida para o ResponseWriter.



Resultado


Todo o código está disponível no github .


Verifique


Emule nosso sistema usando o Docker. Vamos criar dois contêineres - proxy e Grafana em uma rede. Não criaremos um ponto de autorização, acreditamos que ele sempre responde afirmativamente. O contêiner Grafana estará indisponível offline, o contêiner proxy está disponível em uma porta específica.


Crie uma rede:


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

Levante o contêiner Grafana com o modo de autorização configurado através do cabeçalho:


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

Observe que esta é uma demonstração e não uma configuração final. No mínimo, você deve definir uma senha de administrador e proibir o registro automático do usuário.


Aumente o proxy reverso:


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

No navegador, vá para localhost: 4000.


Ótimo, temos uma Grafana autorizada à nossa frente.


O Dockerfile para construir um contêiner com um proxy e instruções mais detalhadas sobre como levantar contêineres estão no github .

Source: https://habr.com/ru/post/pt480446/


All Articles