H2o: Bug dans H2O.next.call(env)

Créé le 20 janv. 2020  ·  7Commentaires  ·  Source: h2o/h2o

Je reclasse ce problème en tant que rapport de "bug", car maintenant je l'ai également testé sous Fedora avec les dernières sources.

Pour la démonstration de ce bogue, vous pouvez utiliser ce h2o.conf :

user: www
access-log: /var/log/h2o/h2o-access.log
error-log: /var/log/h2o/h2o-error.log
listen: 8080
hosts:
  "<my-ip-address-here>:8080":
    paths:
      "/dir0":
        mruby.handler: |
          proc {|env|
            resp = H2O.next.call(env)
            resp
          }
        proxy.reverse.url: "https://www.google.com" # this URL is just for testing/demonstration
      "/dir1":
        file.dir: "/usr/local/www/data/testh2o/dir1"
      "/dir2":
        file.dir: "/usr/local/www/data/testh2o/dir2"
      "/":
        file.dir: "/usr/local/www/data/testh2o"

Cela fonctionne : je peux accéder aux fichiers sous dir1, dir2 et dir3. Et quand je demande http://<my-ip-address-here>:8080/dir0 j'obtiens la page d'accueil de Google (juste pour la démo).

Mais ajouter encore un segment /path casse H20.next.call(env) comme décrit ici pour HardenedBSD, FreeBSD.

h2o.conf cassé :

user: www
access-log: /var/log/h2o/h2o-access.log
error-log: /var/log/h2o/h2o-error.log
listen: 8080
hosts:
  "<my-ip-address-here>:8080":
    paths:
      "/dir0":
        mruby.handler: |
          proc {|env|
            resp = H2O.next.call(env)
            # In my production conf I have here code to look for a certain header field.
            # If it finds it, this handler makes a http_request() to a certain URL on another server 
            # in order to trigger emptying a cache. 
            # This bug demo is independent of this code.
            resp
          }
        proxy.reverse.url: "https://www.google.com" # this URL is just for testing/demonstration
      "/dir1":
        file.dir: "/usr/local/www/data/testh2o/dir1"
      "/dir2":
        file.dir: "/usr/local/www/data/testh2o/dir2"
      "/dir3":
        file.dir: "/usr/local/www/data/testh2o/dir3"
      "/":
        file.dir: "/usr/local/www/data/testh2o"

J'obtiens maintenant une erreur de serveur interne. Et le journal des erreurs sur Fedora dit :

[h2o_mruby] in request:/dir0:mruby raised: (eval):28: can't modify `SCRIPT_NAME` with `H2O.next`. Is `H2O.reprocess` what you want? (RuntimeError)

C'est sur une nouvelle Fedora 31, avec H2O construit à partir de la source à b9989220bea9bda30f69083b66166c4c657bdf84 .

Tous les 7 commentaires

Comme le message d'erreur suggérait d'utiliser H2O.reprocess j'ai maintenant essayé cette option. Même résultat : l'ajout de plus de segments de chemin fait planter l'eau.

hosts:
  test.local:
    listen:
      port: 80
      host: 10.0.0.10
    paths:
      "/dir0":
        file.dir: "/usr/local/www/data/testh2o/dir0"
      "/dir1":
        file.dir: "/usr/local/www/data/testh2o/dir1"
      "/wp-admin":
        mruby.handler: |
          proc {|env|
            env['SCRIPT_NAME'] = '/proxy-wp-admin'
            resp = H2O.reprocess.call(env)
            resp
          }
      "/proxy-wp-admin":
        proxy.reverse.url: "https://www.google.com"

Avec cette configuration, l'ouverture de http://test.local/wp-admin dans le navigateur me donne la page d'accueil de Google.

Mais l'ajout d'un segment de chemin supplémentaire, par exemple un /dir2 supplémentaire me donne l'erreur suivante :

Feb  4 17:12:11 web2 h2o[34490]: received fatal signal 11
Feb  4 17:12:11 web2 h2o[34490]: [34493] 0x4cf6a0 <???> at /usr/local/bin/h2o
Feb  4 17:12:11 web2 h2o[34490]: [34493] 0x8018c5946 <pthread_sigmask+0x536> at /lib/libthr.so.3
Feb  4 17:12:11 web2 h2o[34490]: [34493] 0x8018c4eb2 <pthread_getspecific+0xe12> at /lib/libthr.so.3

Y a-t-il vraiment une limite aussi basse sur le nombre de segments de chemin autorisés ??? Ce doit être un bug. Peut-être le même que celui de H2O.next .

Étant donné que les deux, H2O.next et H20.reprocess semblent cassés, j'ai dû explorer une autre solution. J'ai implémenté un gestionnaire de proxy inverse dans mruby, en suivant l' exemple de @kazuho .

Sur mon serveur de test, cela fonctionne. Je vais probablement le déployer en production, bientôt. Peut-être que cela aide quelqu'un d'autre à rencontrer les mêmes problèmes avec les méthodes mentionnées ci-dessus :

proc {|env|
    # copy headers
    headers = {}
    env.each do |key, value|
      if /^HTTP_/.match(key)
        headers[$'] = value
      end
    end
    if env['CONTENT_TYPE']
        headers['CONTENT_TYPE'] = env['CONTENT_TYPE']
    end

    # issue the request
    input = env["rack.input"] ? env["rack.input"] : ""
    if env['QUERY_STRING'].to_s != ''
        uri = env['SCRIPT_NAME'] + env['PATH_INFO'] + '?' + env['QUERY_STRING']
    else
        uri = env['SCRIPT_NAME'] + env['PATH_INFO']
    end
    req = http_request(
      "http://<backend-ip>#{uri}",
      method:  env["REQUEST_METHOD"],
      headers: headers,
      body: input,
    )

    # Extract status, headers and body, so that I can look into the headers
    status, headers, body = req.join

    # actual work done here, if a certain header is present
    [status, headers, body]
}

Pas encore tout à fait fonctionnel :
H2O ou mruby transforme l'en-tête CONTENT_TYPE en HTTP_CONTENT_TYPE , ce qui casse un backend WordPress...

Exemple:
"CONTENT_TYPE"=>"application/x-www-form-urlencoded; charset=UTF-8"
devient
"HTTP_CONTENT_TYPE"=>"application/x-www-form-urlencoded; charset=UTF-8"

Existe-t-il un moyen de le forcer à n'utiliser que CONTENT_TYPE ?

Et tout ça, juste parce que je voulais ajouter un autre /path dans mon h2o.conf...

@utrenkner Désolé d'être en retard. J'espère que https://github.com/h2o/h2o/pull/2254 résoudra le problème. Avec ce PR H2O.next et H2O.reprocess fonctionne bien je pense

H2O ou mruby transforme l'en-tête CONTENT_TYPE d'origine en HTTP_CONTENT_TYPE, ce qui casse un backend WordPress...

J'ai essayé le même gestionnaire mruby mais je n'ai pas pu le reproduire. Je veux dire, j'ai généré un serveur en amont minimal ( echo -ne "HTTP/1.1 200 OK\r\n\r\n" | nc -l 9000 ) et je vois les en-têtes envoyés depuis h2o. Il comprenait CONTENT_TYPE , pas HTTP_CONTENT_TYPE .
BTW, si vous adoptez cette approche http_request, vous devez convertir les traits de soulignement dans les clés de hachage d'en-tête en tirets : c'est-à-dire que vous devez convertir HTTP_X_FOO en X-FOO , et non en X_FOO . Je suppose que c'est la cause de ce problème : l'implémentation de votre serveur ne le reconnaît pas comme content-type tête

@i110 Merci beaucoup ! J'essaierai ton patch demain.

Et merci également d'avoir testé le gestionnaire mruby ci-dessus. En effet, j'avais complètement oublié de changer le trait de soulignement en trait d'union. Avec ce changement, cela fonctionne comme prévu et CONTENT_TYPE reste CONTENT_TYPE !

@i110 Merci beaucoup ! #2254 résout mes problèmes avec à la fois H2O.next et H2O.reprocess . J'ai ajouté encore plus /path segments vraiment . Et, oui, c'est le cas !

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

proyb6 picture proyb6  ·  5Commentaires

utrenkner picture utrenkner  ·  3Commentaires

Jxck picture Jxck  ·  7Commentaires

utrenkner picture utrenkner  ·  5Commentaires

daniel-lucio picture daniel-lucio  ·  5Commentaires