Bug 60762 - Enhancement: Add support for runtime SNI changes in tomcat-embed
Summary: Enhancement: Add support for runtime SNI changes in tomcat-embed
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 8
Classification: Unclassified
Component: Connectors (show other bugs)
Version: 8.5.x-trunk
Hardware: All All
: P2 enhancement (vote)
Target Milestone: ----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
: 55770 (view as bug list)
Depends on:
Blocks:
 
Reported: 2017-02-21 23:31 UTC by Jesse
Modified: 2017-12-05 21:16 UTC (History)
3 users (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jesse 2017-02-21 23:31:03 UTC
This is an enhancement request, I have put it against Tomcat 8 since that is the latest stable product, if this enhancement is more appropriate for the Tomcat 9 release please move it or let me know and I can re-submit it.

The request is to add public methods for adding/removing/updating SSLHostConfig objects within the AbstractEndpoint.sslHostConfigs map.  Ideally this would be exposed publicly via the Connector class.  I think this is specific to tomcat-embed since I don't believe that runtime changes such as this are possible elsewhere.

For our application I have exposed the functionality needed by extending the NioEndpoint class, this does not consider any of the other nio2/apr/bio connectors, as we only use nio it works for us.  I have tested and been running this way for a while now.

I have also extended the AbstractHttp11JsseProtocol and Connector classes so I can gain access to my implementation of NioEndpoint, this is just for convenience, I think these methods belong publicly exposed on the Connector class (similar to how addSslHostConfig/findSslHostConfigs methods are delegated from Connector all the way to AbstractEndpoint).

The methods I've added to my NioEndpoint implementation:

    // this behaves similar to a Map.put operation, only it is void
    public void addOrUpdateSSLHostConfig(SSLHostConfig config)

    public void removeSSLHostConfig(String sniHostName)


For the nio implementation I had to ensure the ssl contexts were released/created, not sure about how that would work for the other implementations.

Thanks!
Jesse
Comment 1 Christopher Schultz 2017-02-22 04:15:52 UTC
If you call org.apache.tomcat.util.net.AbstractEndpoint.addSslHostConfig(SSLHostConfig) with a hostconfig object with a hostname matching an existing hostconfig, it will be replaced.

Is this not sufficient?

Note that the new SSLHostConfig has no effect unless you bounce the whole connector.
Comment 2 Jesse 2017-02-22 17:47:34 UTC
In version 8.5.9 that we are running it looks like that method calls putIfAbsent against the sslHostConfigs map, throwing an IllegalArgumentException if there is a duplicate SSLHostConfig object for the given key/hostname.  From what I can tell there is no existing public method in 8.5.9 to modify an existing SSLHostConfig once loaded, additionally the methods to do so properly with regard to ssl context release/create are protected.  Please correct me if my understanding of this is wrong.

Also, in the case where an SSL certificate or SSLHostConfig object needs to be removed for any reason, this method would not suffice with either behavior.

When you say that the new SSLHostConfig has no effect without bouncing the whole connector, is this in a newer tomcat version?  Or do you refer to the newly created SSLHostConfig object that we are creating and putting in the sslHostConfigs map?  In the latter case we definitely are seeing the newly created SSLHostConfig object taking effect and new requests to it's hostname are being served the corresponding certificate.
Comment 3 Mark Thomas 2017-03-06 22:08:07 UTC
For anyone looking at implementing this, the following thread will be useful:
http://markmail.org/thread/ox3h7oaqgef3qqyk

The short version is removing an SSLHostConfig for APR/native is going to be  tricky.
Comment 4 Ralf Hauser 2017-07-21 07:41:10 UTC
Might be useful to implement letsencrypt - see also

http://people.apache.org/~schultz/ApacheCon NA 2017/Let's Encrypt Apache Tomcat.pdf
Comment 5 Jesse 2017-07-21 18:23:16 UTC
We have actually implemented letsencrypt in our application.  We allow users to configure new domains that our application will respond to over SSL, and we give them the option to provide their own certificate or the application can generate (and auto-renew) for them via letsencrypt.

I do agree that this request would align well with the needs of tomcat supporting letsencrypt, although my request is actually to provide the public methods so our application can control things as necessary to leverage letsencrypt or user provided certificates at runtime.  Those same methods could likely be used by more generic letsencrypt support as well.
Comment 6 Mark Thomas 2017-09-15 20:12:24 UTC
This has been implemented in 9.0.x for 9.0.0.M28 onwards.

I'll back-port it to 8.5.x once folks have had a chance to test it.
Comment 7 Mark Thomas 2017-11-23 15:10:40 UTC
*** Bug 55770 has been marked as a duplicate of this bug. ***
Comment 8 Mark Thomas 2017-11-23 18:51:28 UTC
This will be available in 8.5.24 onwards.

There are no plans at this time to back-port this to 8.0.x or 7.0.x.
Comment 9 Jesse 2017-12-05 07:03:13 UTC
Hi Mark,

Thanks for your work on this, I see the methods on AbstractEndpoint and I think they are exactly what I need and should allow me to remove my custom implementation.

The only thing holding me back at this point is that I don't know how to get access to the AbstractEndpoint from our application.  We have an instance of Tomcat and can get a Connector using the getConnector method, from there I can get the ProtocolHandler but that does not offer any way to get the endpoint.  I could probably safely cast ProtocolHandler to AbstractProtocol where there is a getEndpoint method, but that method is protected.

Is there an appropriate way for me to get access to the new public methods on AbstractEndpoint?

Thanks again!
Jesse
Comment 10 Mark Thomas 2017-12-05 14:19:23 UTC
Off the top of my head (I can check later if this doesn't work) you should be able to do this via JMX.
Comment 11 Jesse 2017-12-05 18:03:26 UTC
I'd rather not use JMX, I'm not totally familiar with JMX but wouldn't that require me to enable JMX on a port in our application?

If I can use JMX directly via the embedded tomcat without doing any tcp port type access then that would work.

For now I have been able to remove my implementation of methods in NioEndpoint in favor of using the ones you have, but I still have my own extension classes so I can get access to them.

Thanks,
Jesse
Comment 12 Christopher Schultz 2017-12-05 19:16:47 UTC
(In reply to Jesse from comment #11)
> I'd rather not use JMX, I'm not totally familiar with JMX but wouldn't that
> require me to enable JMX on a port in our application?

You can use JMX within the same JVM just via an API interface. No need to expose any ports or anything like that.
Comment 13 Jesse 2017-12-05 20:15:07 UTC
I was able to see many tomcat mbeans using:

ManagementFactory.getPlatformMBeanServer().queryNames(new ObjectName("Tomcat:*"), null)

However I don't see any that look like they'd represent an endpoint in that list.

I also looked for "Catalina:*" but none were found.

Given that I am running this in the same JVM as the Tomcat instance that I want to manage, if I could find an ObjectName that represents the AbstractEndpoint, could I actually get access to the AbstractEndpoint that I want to invoke the add/removeSslHostConfig methods on?  It seems like the JMX interfaces are all for string/serialized communications and I need to pass the SSLHostConfig object that I've created.

Thanks!
Comment 14 Mark Thomas 2017-12-05 20:30:19 UTC
Catalina:type=ThreadPool,name="http-nio-8080"
Comment 15 Jesse 2017-12-05 21:05:39 UTC
That gives an InstanceNotFoundException, I can't find any Catalina:* objects.

Even if that did return an object, could I gain direct access to the AbstractEndpoint instance that I want to call the methods against?

Thanks!
Comment 16 Mark Thomas 2017-12-05 21:16:18 UTC
Please move this to the users list.