Jetty
  1. Jetty
  2. JETTY-558

Jetty behind a reverse proxy and request.getServerName()

    Details

    • Type: New Feature New Feature
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 7.0.0pre1, 6.1.10
    • Component/s: HTTP
    • Labels:
      None
    • Environment:
      Jetty behind a reverse proxy (especially Apache HTTPD with mod_proxy_http for example)
    • Testcase included:
      yes
    • Patch Submitted:
      Yes
    • Number of attachments :
      4

      Description

      When Jetty is behind a reverse proxy it is not possible to retrieve the following properties of the original request :

      • getServerName()
      • getServerPort()
      • getRemoteAddr()

      This is typically the case when you use a ProxyPass directive with Apache HTTPD and mod_proxy_http.

      Tomcat use proxyName and proxyPort for retrieving original host and port:
      http://tomcat.apache.org/tomcat-5.5-doc/proxy-howto.html

      So i have created a Handler for wrapping the request and providing these original properties when possible.

      This handler can be used in two different ways:

      • like Tomcat if at least the serverName property is set
      • mod_proxy_http if the serverName property is not set and then headers from reverse proxy are used

      By default mod_proxy_http headers are used:
      http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#x-headers

        Activity

        Hide
        Greg Wilkins added a comment -

        Sébastien,
        this has been something that we have been meaning to support out-of-the-box - but have not got around to it,as it has been easy to do on a case by case basis.

        So I would like to include support for this. However, may I suggest a better way to implement it than a handler, because wrapping requests is expensive and can cause other issues.

        If you put the handling in AbstractConnector.customize(Endpoint,Request), then that is called for every request
        and you are able to directly call request.setServerName(String) and request.setServerPort.

        It would be good to protect this handling with a boolean, as looking for headers is also a little expensive and so it would be best not to check for the headers unless the connector was configured with the boolean true (setCheckingProxyHeaders(true) or connector.setServerName to have a defined server.

        So it would be great if you could morph you patch to that.... otherwise I'll try to get to this later in the month.

        Show
        Greg Wilkins added a comment - Sébastien, this has been something that we have been meaning to support out-of-the-box - but have not got around to it,as it has been easy to do on a case by case basis. So I would like to include support for this. However, may I suggest a better way to implement it than a handler, because wrapping requests is expensive and can cause other issues. If you put the handling in AbstractConnector.customize(Endpoint,Request), then that is called for every request and you are able to directly call request.setServerName(String) and request.setServerPort. It would be good to protect this handling with a boolean, as looking for headers is also a little expensive and so it would be best not to check for the headers unless the connector was configured with the boolean true (setCheckingProxyHeaders(true) or connector.setServerName to have a defined server. So it would be great if you could morph you patch to that.... otherwise I'll try to get to this later in the month.
        Hide
        Sebastien Launay added a comment -

        I have changed the processing from request wrapping to change in the AbstractConnector.
        But i am blocked because of this problems:

        • i do not know how to retrieve HttpFields of the current connection from the EndPoint object.
        • i was forced to touch the Request object in order to change remote address and remote host.

        So here is my first try, let me know how to resolve this and if i am on the right path .

        Show
        Sebastien Launay added a comment - I have changed the processing from request wrapping to change in the AbstractConnector . But i am blocked because of this problems: i do not know how to retrieve HttpFields of the current connection from the EndPoint object. i was forced to touch the Request object in order to change remote address and remote host. So here is my first try, let me know how to resolve this and if i am on the right path .
        Hide
        Greg Wilkins added a comment -

        Sébastien,

        this looks pretty good.

        There is not issue with accessing the request object our updating it.
        The simplest way to get to the fields is to call request.getHeader("foo");

        To get the HttpConnection, you can just use Request.getConnection() !-)

        For handling exceptions, it's probably best to do something like:

        catch (NumberFormatException e)

        { thow new HttpException(400,"Bad Request X-Forwarded-Host",e); }

        If you want to patch this again... great, else I'm happy to take this from here.
        Either way, thanks for this!

        Show
        Greg Wilkins added a comment - Sébastien, this looks pretty good. There is not issue with accessing the request object our updating it. The simplest way to get to the fields is to call request.getHeader("foo"); To get the HttpConnection, you can just use Request.getConnection() !-) For handling exceptions, it's probably best to do something like: catch (NumberFormatException e) { thow new HttpException(400,"Bad Request X-Forwarded-Host",e); } If you want to patch this again... great, else I'm happy to take this from here. Either way, thanks for this!
        Hide
        Sebastien Launay added a comment -

        The new patch with your advices.

        I added the following modifications:

        • i still use HttpFields for retrieving because i need to override the Host header (maybe this a little too violent?).
        • the default headers are those of mod_proxy_http but this can be tuned.
        • new test case
        Show
        Sebastien Launay added a comment - The new patch with your advices. I added the following modifications: i still use HttpFields for retrieving because i need to override the Host header (maybe this a little too violent?). the default headers are those of mod_proxy_http but this can be tuned. new test case
        Hide
        Greg Wilkins added a comment -

        Don't worry about the brute force. It is a stupid thing for a proxy to do to replace the Host header in the first place and use the X-Forwarded-Host header. The Host header is meant to be exactly what the client think the server is, so the proxy should not have changed it and I'm happy to brute force it back.

        I'm a bit concerned however about X-Forwarded-For. I think it can be mutli-valued: http://en.wikipedia.org/wiki/X-Forwarded-For
        So a bit more processing is required I think to get the last IP address in that chain.

        Note also, that if you can't (or don't try) to resolve the RemoteHost name, then the remoteHost should be set to the IP address.

        again... I'm happy to take it from here... but would also welcome your continued contribution.

        cheers

        Show
        Greg Wilkins added a comment - Don't worry about the brute force. It is a stupid thing for a proxy to do to replace the Host header in the first place and use the X-Forwarded-Host header. The Host header is meant to be exactly what the client think the server is, so the proxy should not have changed it and I'm happy to brute force it back. I'm a bit concerned however about X-Forwarded-For. I think it can be mutli-valued: http://en.wikipedia.org/wiki/X-Forwarded-For So a bit more processing is required I think to get the last IP address in that chain. Note also, that if you can't (or don't try) to resolve the RemoteHost name, then the remoteHost should be set to the IP address. again... I'm happy to take it from here... but would also welcome your continued contribution. cheers
        Hide
        Sebastien Launay added a comment -

        New patch with the following modifications:

        • use left-most value from all headers
          i could not find any example for X-Forwarded-Host and X-Forwarded-Server but
          http://httpd.apache.org/docs/trunk/mod/mod_proxy.html#x-headers says it can be multivalued...
        • use remote addr as remote host if the header is provided and useDNS is false
        • update test case with such cases
        Show
        Sebastien Launay added a comment - New patch with the following modifications: use left-most value from all headers i could not find any example for X-Forwarded-Host and X-Forwarded-Server but http://httpd.apache.org/docs/trunk/mod/mod_proxy.html#x-headers says it can be multivalued... use remote addr as remote host if the header is provided and useDNS is false update test case with such cases
        Hide
        Greg Wilkins added a comment -

        applied with some minor name changes and some optimizations.

        Show
        Greg Wilkins added a comment - applied with some minor name changes and some optimizations.
        Hide
        Sebastien Launay added a comment -

        Thanks i am looking forward to update Jetty in my projects instead of coming each times with a custom jar .

        Show
        Sebastien Launay added a comment - Thanks i am looking forward to update Jetty in my projects instead of coming each times with a custom jar .
        Hide
        Sebastien Launay added a comment -
        Show
        Sebastien Launay added a comment - Works perfectly. Documentation on this feature has been added: http://docs.codehaus.org/display/JETTY/Configuring+Connectors http://docs.codehaus.org/display/JETTY/Configuring+mod_proxy

          People

          • Assignee:
            Greg Wilkins
            Reporter:
            Sebastien Launay
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: