
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".
- Já temos um único ponto de entrada - um serviço de autorização.
- Não queremos iniciar e autorizar usuários no Grafana.
- 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") {
Vamos analisar os parâmetros. As principais variáveis no exemplo que coloco em constantes:
grafanaUser = "admin"
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 .