To be Reviewed By: March 11, 2020

Authors: Dan Smith, Ernie Burghardt

Status: Draft | Discussion | Active | Dropped | Superseded

Superseded by: N/A

Related: GEODE-7852

Problem

Users would like to be able to put Geode servers behind a proxy. This would allow users to avoid having to allocate public IP addresses for all of the servers, as well as being able to control and monitor access to the servers.

Currently, this is not very well supported. Geode clients discover servers using locators. It is possible to set the hostname-for-clients on all servers to point to the proxy server, but this is not ideal. Geode clients rely on being able to connect to specific  servers for features like PR single hop and client server queues. This means that the clients need a protocol that will allow them to tell the proxy which server they connect to.

Anti-Goals

  • It is an anti-goal to deal with configuring the proxy itself.
  • It is an anti-goal to support all different kinds of proxies. In the future we may consider supporting other types of proxies, but this proposal is to support one specific type - a SNI proxy.
  • We're not recommending any specific SNI proxy
  • This proposal does not include supporting a proxy for WAN or P2P connectivity. In the future we may add support for that as well.

Solution

We will add a configuration option to the client to set a SNI proxy for the client to connect to the servers through. When this option is set, all connections from the client to the locator or the servers will use this SNI proxy. When configuring a Pool, the names of the locators or the names of the servers provided in the pool configuration will be passed as the Server Name Indicator field when the client connects to the proxy. The proxy must be able to resolve these names and connect to the correct locator or server.

This setting ideally should be at a pool level, because different pools may need to use different proxies. Therefore we propose the following pool configuration option:

PoolFactory {    
  /**    
   * Set the proxy to be used when making connections from this pool.
   * @param proxyAddress is host and port formatted like the authority part of a URL
   * but without any userinfo part. See RFC 3986 for details. Examples:
   *
   *  "foo.bar.com:1234" is a fully-qualified domain name and port
   *  "1.2.3.4:567" is an IPv4 address and port
   *  "[1:2::1]:80" is an IPv6 address and port
   */
  setProxy(ProxyType type, String proxyAddress)
}

ClientCacheFactory {
  /**
   * (see description of proxyAddress above)
   */
  setPoolProxy(ProxyType type, String proxyAddress)
}

enum ProxyType {
   /**        
    * Use a SNI proxy. With an SNI proxy, the proxyAddress field should be specified as host:port        
    */   
   SNI; 
}


The proxyAddress parameter will be in the familiar URL format[1][2], specifically the authority part of a URL without any userinfo part.

Performance Impact

Connecting through a proxy may impact the performance of client/server messaging, but it is up to the user to decide if they want to use this feature or not. SNI proxies do require the use of TLS, which also adds overhead.

Backwards Compatibility and Upgrade Path

This is a client side setting, so there should be no backwards compatibility or upgrade concerns.

Prior Art

Alternative ways to introduce a proxy would be:

  • Set the hostname-for-clients to point to the proxy. However this does not let the client connect to specific servers, because from the client's point of view there is only one server (the proxy). This makes client single hop no longer functional. It also makes it hard for the client to maintain redundancy of server to client queues, for subscriptions.
  • Use a SOCKS5 proxy instead.


Geode also added support to set the SNI field in the client hello as part of GEODE-7414. With those changes, a user can provide a SSLParameterExtension callback that can modify any of the SSLParameters, including the SNI server name. If a proxy of type SNI is set and the SSLParameterExtension is also set, the SSLParameterExtension will run after geode has set the SNI name, and can potential modify it.

FAQ

Is this platform specific?

  • No this feature is intended to be platform agnostic.

Does this require a firewall?

  • This is an implementation detail that will be left to the users discretion.


Errata

What are minor adjustments that had to be made to the proposal since it was approved?


References

[1] Description of URL syntax from Wikipedia article on URLs https://en.wikipedia.org/wiki/URL#Syntax

[2] RFC 3986 sections 3.2.2 and 3.2.3 describing host and port syntax in URLs https://tools.ietf.org/html/rfc3986#section-3.2.2

  • No labels

6 Comments

  1. setProxy(ProxyType type, String proxyAddress)
    Consider void setProxy(URL proxy) since you are taking a schema and address here as two parts that is what URL/URI encodes. Otherwise consider void setProxy(ProxyType, InetSocketAddress proxyAddress) for stronger typing of the proxyAddress argument. 
    1. I would lean towards the more specific option setProxy(ProxyType, String host, int port) rather than a URI.  With a URI we will have ignore or reject most of the components (path, query, fragment, etc.) and provide our own custom scheme. That's extra validation and potential for user confusion we could avoid with a more strongly typed API.


      BTW - InetSocketAddress and URL are not so great for configuration options, because they imply some networking side affects like hostname resolution.

      1. InetSocketAddress doesn't require hostname resolution, though eventually it will have to. Use InetSocketAddress.createUnresolved() if you want. The point is to use a strong type for the parameters.

        If something other then host:port is going to be encoded then some other strong type should be used. Leaving it as some arbitrary string doesn't get the compiler an opportunity to catch mistakes in the format. If you think we will need that now or in the future then:

        void setProxy(ProxyType type, InetSocketAddress address, ProxyConfig config);
        interface ProxyConfig {}
        class SniProxyConfig {
        public SniProxyConfig (String aParam) {...}
        }
        pool.setProxy(ProxyType.SNI, InetSocketAddress.createUnresolved("proxy", 1234), new SniProxyConfig("something interesting"));


        For that matter we could just dump the type for explicit ProxyConfig objects.

        void setProxy(ProxyConfig config);
        interface ProxyConfig {}
        class SniProxyConfig {
        public SniProxyConfig (String hostname, short port port, String aParam) {...}
        }
        pool.setProxy(new SniProxyConfig("proxy", 1234, "something interesting"));


  2. What is the relationship between the hostname and the proxyHostname. Do we still set both?

    Would it make sense to actually only have one hostName setting?

    1. Not sure I understand this question. There is only one hostname setting for the proxy server.

      1. Please ignore... I forgot that I had posted this... for some strange reason I was stuck on the server side... This is client-side only...