Écriture du proxy inverse Grafana sur Go


Je voulais vraiment nommer l'article «Proxy-service on Go en 3 lignes», mais je suis au-dessus.


En fait, c'est que la logique principale peut tenir sur trois lignes. Pour les impatients et ceux qui veulent voir l'essence:


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

Under the cat est une histoire plus détaillée pour les nouveaux arrivants dans la langue Golang et ceux qui ont besoin de créer un proxy inverse dans les plus brefs délais.


Voyons pourquoi un service proxy est nécessaire, comment l'implémenter et ce qui se trouve sous le capot de la bibliothèque standard.


Proxy inverse


Un proxy inverse est un type de serveur proxy qui reçoit une demande d'un client, la redirige vers un ou plusieurs serveurs et retransmet la réponse.


Une caractéristique distinctive du proxy inverse est qu'il est le point d'entrée pour connecter l'utilisateur aux serveurs avec lesquels le proxy lui-même est connecté par la logique métier. Il détermine vers quels serveurs la demande du client sera envoyée. La logique de construction d'un réseau derrière le proxy reste cachée à l'utilisateur.



Proxy inverse


À titre de comparaison, un proxy ordinaire connecte ses clients à n'importe quel serveur dont ils ont besoin. Dans ce cas, le proxy est devant l'utilisateur et n'est qu'un intermédiaire dans l'exécution de la demande.



Proxy normal (proxy direct)


Pourquoi utiliser
Le concept de proxy inverse peut être appliqué dans diverses situations:
- équilibrage de charge,
- Tests A / B
- mise en cache des ressources,
- compression des données de demande,
- filtrage du trafic,
- autorisation.


Bien entendu, la portée ne se limite pas à ces six points. Le fait de la possibilité de traiter la demande avant et après procuration donne beaucoup de place à la créativité. Dans cet article, nous discuterons de l'utilisation du proxy inverse pour l'autorisation.


Défi


Nous développons un panneau de contrôle de virtualisation VMmanager 6. Un beau jour, nous avons décidé de donner aux utilisateurs plus de liberté dans la surveillance et la visualisation de ces clusters. À ces fins, ils ont choisi Grafana .


Pour que Grafana fonctionne avec nos données, il a fallu configurer l'autorisation. Ce n'est pas difficile à faire, sinon pour trois «mais».


  1. Nous avons déjà un point d'entrée unique - un service d'autorisation.
  2. Nous ne voulons pas démarrer et autoriser des utilisateurs dans Grafana.
  3. Nous ne voulons pas donner aux utilisateurs un accès direct à Grafana.

Pour respecter les conditions, nous avons décidé de mettre Grafana sur le réseau interne et d'écrire un proxy inverse. Il vérifiera les droits dans le service d'autorisation et ce n'est qu'après avoir transféré la demande à Grafana.


Idée


L'idée principale est de transférer la responsabilité de l'autorisation dans Grafana au serveur proxy inverse ( documentation officielle ). Grafana acceptera toute demande autorisée si elle contient un en-tête spécifique. Avant de remplacer cette rubrique, nous devons nous assurer que l'utilisateur actuel a le droit de travailler avec Grafana.



Chaîne d'appels "Grafana-proxy, ou aller-retour"


Implémentation


La fonction principale est assez standard. Nous démarrons le serveur http, qui acceptera les connexions sur le port 4000 et traitera toute adresse «/» avec laquelle la connexion aura lieu.


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

La majeure partie du travail se produit dans le gestionnaire de demandes.


[code complet sous coupe]
 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) } 

Passons en revue les paramètres. Les principales variables de l'exemple que je mets en constantes:


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

Par exemple, cela suffit; en pratique, vous devrez peut-être prédéfinir ces valeurs. Vous pouvez leur passer des proxys en tant que paramètres de ligne de commande, puis utiliser des indicateurs ou des packages plus avancés pour les analyser. L'environnement de conteneur utilise également souvent des variables d'environnement pour configurer les services, os.Getenv vous aidera tout au long du processus.


Vient ensuite le contrôle d'autorisation:


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

Si la demande est envoyée à grafana.host/api, nous vérifions les droits de l'utilisateur actuel à utiliser Grafana. La vérification est nécessaire pour que pour chaque requête GET d'un script JS ou d'une icône PNG ne dérange pas le point d'autorisation. Nous procurons des contenus statiques sans vérification supplémentaire. Pour ce faire, nous transmettons la carte avec les en-têtes qui contiennent la session utilisateur au service d'autorisation. Cela peut être une demande GET régulière. Le dispositif de service d'autorisation n'a pas d'importance ici. Si les données d'autorisation ne conviennent pas, fermez la connexion en renvoyant une erreur.


Après les vérifications, nous formons l'objet du chemin de base:


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

En utilisant le package httputil standard, qui étend le package http, nous formons l'objet ReverseProxy.


 proxy := httputil.NewSingleHostReverseProxy(url) 

ReverseProxy est un gestionnaire de demandes qui acceptera une demande entrante, l'enverra à Grafana et transmettra la réponse au client.


Il transmettra toutes les demandes à l'adresse «chemin de base + URL demandée». Si l'utilisateur est arrivé à l'adresse proxy: 4000 / api / quelque chose, sa demande sera transformée en grafana: 3000 / api / quelque chose, où grafana: 3000 est le chemin de base transmis à NewSingleHostReverseProxy et / api / quelque chose est la demande entrante.


Ajoutez l'en-tête d'autorisation pour Grafana et appelez la méthode ServeHTTP, qui effectuera l'essentiel du traitement des demandes.


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

Sous le capot, ServeHTTP fait un peu de travail, par exemple, traite l'en-tête X-Forwarded-For ou la réponse du serveur 101 à un changement de protocole. Le travail principal de la méthode consiste à envoyer une demande à une adresse composite et à transférer la réponse reçue à ResponseWriter.



Résultat


Tout le code est disponible sur github .


Vérifier


Émulez notre système à l'aide de Docker. Créons deux conteneurs - proxy et Grafana dans un même réseau. Nous ne créerons pas de point d'autorisation, nous pensons qu'il répond toujours par l'affirmative. Le conteneur Grafana ne sera pas disponible hors ligne, le conteneur proxy est disponible sur un port spécifique.


Créez un réseau:


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

Soulevez le conteneur Grafana avec le mode d'autorisation configuré via l'en-tête:


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

Veuillez noter qu'il s'agit d'une configuration de démonstration et non finale. Au minimum, vous devez définir un mot de passe administrateur et interdire l'enregistrement automatique des utilisateurs.


Relevez le proxy inverse:


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

Dans le navigateur, accédez à localhost: 4000.


Super, nous avons un Grafana autorisé devant nous.


Dockerfile pour la construction d'un conteneur avec un proxy et des instructions plus détaillées sur le levage des conteneurs sont sur github .

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


All Articles