HAProxy CORS OPTIONS header intercept setup

avec ma configuration NGinx j'ai été capable d'intercepter les requêtes D'OPTIONS depuis ajax avant le vol et de répondre avec les en-têtes CORS corrects et une réponse de 200 pour que la requête puisse continuer. J'essaie de consolider mes mandataires frontend en HAProxy et j'ai du mal à faire fonctionner cette pièce du puzzle.

mon problème particulier est que même si je suis capable d'ajouter les bonnes options CORS lorsqu'il y a un serveur capable de répondre correctement à une requête D'OPTIONS, quelques-uns des les endos ne peuvent pas traiter / répondre avec une erreur 405 lorsque la demande avant le vol est émise. Mon haproxy.cfg inclus les lignes suivantes pour ajouter les en-têtes:

capture request header origin len 128
http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Credentials: true if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Origin, User-Agent, If-Modified-Since, Cache-Control, Accept if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS if { capture.req.hdr(0) -m found }
rspadd Access-Control-Max-Age: 1728000 if { capture.req.hdr(0) -m found }

La solution:

Comment envoyer une réponse avec HAProxy sans passer la demande aux serveurs web fonctionne lorsque vous définissez tous les en-têtes corrects à partir de la demande d'un client, mais n'est pas dynamique, ce qui n'est pas une solution idéale.

Toute aide serait apprécié!

13
demandé sur Community 2015-09-24 00:09:17

3 réponses

Vous pouvez utiliser Lua, mais vous devez vous assurer que HAproxy est construit avec USE_LUA en cochant haproxy -vv.

il s'agit d'une configuration d'exemple, Je ne l'ai pas essayé moi-même, mais elle vous donnera une idée de ce que vous pouvez faire:

# haproxy.cfg

global
    lua-load cors.lua

frontend foo
    ...
    http-request use-service lua.cors-response if METH_OPTIONS { req.hdr(origin) -m found } { ... }

# cors.lua
core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Origin, User-Agent, If-Modified-Since, Cache-Control, Accept")
    applet:add_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)
5
répondu anine.io 2016-07-05 02:51:34

basé sur le grande réponse de anine.io j'ai trouvé la solution suivante qui permet de définir une liste de permis origines et il ajoute également l'absence de Acccess-Control-Allow-Origin en-tête pour toutes les requêtes HTTP. La réponse d'anine.io n'a montré que le CORS avant le vol, mais n'a pas tenu compte des demandes normales.

haproxy.cfg charge cors.lua fichier (adapter le chemin si nécessaire) dans la section globale

global
    lua-load /usr/local/etc/haproxy/cors.lua

ajouter la configuration CORS à votre frontend définition

frontend http-in
    # CORS configuration
    # capture origin HTTP header
    capture request header origin len 128
    # add Access-Control-Allow-Origin HTTP header to response if origin matches the list of allowed URLs
    http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if !METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }
    # if a preflight request is made, use CORS preflight backend
    http-request use-service lua.cors-response if METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }

Créer un fichier appelé cors.lua et le stocker sous le chemin que vous avez spécifié ci-dessus. Le fichier contient le CORS preflight et s'il n'y a pas de bonne raison, ne restreignez pas les méthodes ou les en-têtes car vous devrez inclure toutes les restrictions concernant les méthodes ou les en-têtes dans les ACLs définis dans la configuration de CORS en haproxy.conf. Remarque: actuellement, les navigateurs ne supportent pas les caractères génériques * pour les Access-Control-Allow-Methods en-tête.cors.lua le fichier doit contenir les éléments suivants contenu

core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "*")
    applet:add_header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)

Créer un fichier appelé cors-origins.lst et stockez-le sous le chemin que vous avez spécifié ci-dessus dans la configuration de CORS. Le fichier doit contenir des expressions régulières (ou simplement des chaînes simples). Si le client envoie un en-tête Origin, il sera validé par rapport à ces expressions régulières et seulement si elles correspondent, le CORS Préflight de cors.lua seront retournés (pour HTTP OPTIONS demandes) ou Access-Control-Allow-Origin avec la valeur de l'en-tête d'origine du client la demande sera ajouté à la réponse. Un exemple de contenu de cors-origins.lst peut être

example.com
localhost.*
.*\.mydomain\.com:[8080|8443]

tester la configuration avec http://test-cors.org/. Pour les requêtes GET, il ne doit pas y avoir de vol CORS. Pour les requêtes autres que GET, le client doit d'abord effectuer une requête CORS Preflight (par exemple un appel D'OPTIONS HTTP) pour vérifier si la méthode, les en-têtes et l'autorisation sont autorisés.

Voir contrôle D'accès HTTP (CORS) pour plus de détails concernant CORS.

4
répondu Florian Feldhaus 2017-09-06 23:02:52

je veux lancer ma propre réponse dans le ring ici. Nous avons connu une certaine douleur, pour arriver à une configuration de travail. Les autres réponses à cette question ont été très utiles, mais n'ont pas fourni une configuration entièrement fonctionnelle pour nous.

nous voulions autoriser n'importe quelle origine. Si vous avez besoin d'origines à liste blanche, voir le réponse de @Florian Feldhaus pour une astuce regex utile. Au lieu d'utiliser une liste blanche, on renvoie l'en-tête location:

http-request set-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }

nous avons aussi eu besoin de définir explicitement Access-Control-Allow-Headers et Access-Control-Expose-Headers. le support du navigateur pour les caractères génériques dans ces en-têtes n'est pas encore tout à fait présent.

alors voici ce que fait notre configuration:

  1. traite les demandes avant vol en utilisant c'scro.lua script
  2. traite les requêtes normales en utilisant http-response set-header ajouter Access-Control-Allow-* les en-têtes
  3. ajuster tune.maxrewrite pour s'adapter à nos en-têtes CORS (qui sont > 1 KB)

Les étapes 1) et 2) sont expliqués dans les autres réponses ici, mais l'étape 3) nous a fallu beaucoup de temps pour comprendre. J'ai documenté la configuration complète et le voyage qui nous a menés là-bas en ce post. Le post contient des liens pour l'essentiel sur github.

2
répondu Johannes Rudolph 2017-09-22 18:01:33