【问题标题】:Sending Proxy protocol to Server that doesn't support Proxy Protocol向不支持代理协议的服务器发送代理协议
【发布时间】:2018-07-01 20:32:59
【问题描述】:

我有一个使用反向代理的 HTTP 加速解决方案。为了向服务器提供客户端 IP,我尝试在我的 HTTP 加速解决方案中添加代理协议支持。虽然由于 HTTP 加速解决方案端点本身不能成为我的服务器的网关,但我需要在服务器中添加代理协议支持或在它们前面添加 HA 代理。

目前用于代理协议状态:

接收器可以配置为支持协议的版本 1 和版本 2。识别协议版本很容易:

- if the incoming byte count is 16 or above and the 13 first bytes match
  the protocol signature block followed by the protocol version 2 :

       \x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02

- otherwise, if the incoming byte count is 8 or above, and the 5 first
  characters match the US-ASCII representation of "PROXY" then the protocol
  must be parsed as version 1 :

       \x50\x52\x4F\x58\x59

- otherwise the protocol is not covered by this specification and the
  connection must be dropped.

我的服务器主要是支持代理协议的nginx。虽然我的 http 服务器很少不支持代理协议

我的问题是如果服务器不支持该协议,他们是否可以正确解析包含代理协议标头的 TCP/HTTP 有效负载?

同样,我想知道为什么代理协议规范不将其作为 TCP/Ip 标头选项: 这样,不理解该选项的服务器将忽略使其对不支持它的服务器完全透明。 而那些支持该协议的服务器将丢弃没有代理协议选项的数据包

【问题讨论】:

    标签: reverse-proxy haproxy http-proxy clientip proxy-protocol


    【解决方案1】:

    我的问题是如果服务器不支持该协议,他们是否可以正确解析包含代理协议标头的 TCP/HTTP 有效负载?

    不,不支持代理协议的服务器将无法将请求解析为 HTTP,因为它不是以有效的 HTTP 方法开头。

    对于不支持代理协议的后端服务器,只需省略 send-proxy 指令即可。请记住,您将丢失有关客户端的信息,除非您将它们传递到诸如 X-Forwarded-For 之类的标头中。

    【讨论】:

      【解决方案2】:

      这是代理协议信息的示例:

      如果服务器不支持该协议,是否可以正确解析包含代理协议标头的 TCP/HTTP 负载?

      这取决于服务器实现

      例如,我尝试向 python 的 HTTPServer 发送代理协议信息,它抛出了 400 错误代码。这是解析标头的服务器的实现:

      def parse_request(self):
          """Parse a request (internal).
      
          The request should be stored in self.raw_requestline; the results
          are in self.command, self.path, self.request_version and
          self.headers.
      
          Return True for success, False for failure; on failure, an
          error is sent back.
      
          """
          self.command = None  # set in case of error on the first line
          self.request_version = version = self.default_request_version
          self.close_connection = 1
          requestline = self.raw_requestline
          requestline = requestline.rstrip('\r\n')
          self.requestline = requestline
          words = requestline.split()
          if len(words) == 3:
              command, path, version = words
              if version[:5] != 'HTTP/':
                  self.send_error(400, "Bad request version (%r)" % version)
                  return False
              try:
                  base_version_number = version.split('/', 1)[1]
                  version_number = base_version_number.split(".")
                  # RFC 2145 section 3.1 says there can be only one "." and
                  #   - major and minor numbers MUST be treated as
                  #      separate integers;
                  #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
                  #      turn is lower than HTTP/12.3;
                  #   - Leading zeros MUST be ignored by recipients.
                  if len(version_number) != 2:
                      raise ValueError
                  version_number = int(version_number[0]), int(version_number[1])
              except (ValueError, IndexError):
                  self.send_error(400, "Bad request version (%r)" % version)
                  return False
              if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
                  self.close_connection = 0
              if version_number >= (2, 0):
                  self.send_error(505,
                            "Invalid HTTP Version (%s)" % base_version_number)
                  return False
          elif len(words) == 2:
              command, path = words
              self.close_connection = 1
              if command != 'GET':
                  self.send_error(400,
                                  "Bad HTTP/0.9 request type (%r)" % command)
                  return False
          elif not words:
              return False
          else:
              self.send_error(400, "Bad request syntax (%r)" % requestline)
              return False
          self.command, self.path, self.request_version = command, path, version
      
          # Examine the headers and look for a Connection directive
          self.headers = self.MessageClass(self.rfile, 0)
      
          conntype = self.headers.get('Connection', "")
          if conntype.lower() == 'close':
              self.close_connection = 1
          elif (conntype.lower() == 'keep-alive' and
                self.protocol_version >= "HTTP/1.1"):
              self.close_connection = 0
          return True
      

      注意一个代理协议有效负载例如:

      代理 TCP4 192.168.73.178 192.168.73.185 52406 80

      包含 6 个单词。如果标头有 3,2 或没有单词,这个特定的服务器实现会处理请求。否则会抛出 400 错误代码。

      else:
          self.send_error(400, "Bad request syntax (%r)" % requestline)
          return False
      

      而当我尝试将相同的信息发送到具有相同 HAProxy 配置的 tomcat 服务器时,它只是忽略了有效负载。

      一般来说,除非我知道它能够明确地处理它,否则我会谨慎行事并且不发送代理信息。

      【讨论】:

        猜你喜欢
        • 2016-03-03
        • 2019-03-27
        • 2018-10-21
        • 1970-01-01
        • 2011-11-11
        • 2018-01-05
        • 2018-06-21
        • 2022-06-15
        • 1970-01-01
        相关资源
        最近更新 更多