JeetKunDoug commented on code in PR #3638: URL: https://github.com/apache/cassandra/pull/3638#discussion_r1846772307
########## src/java/org/apache/cassandra/utils/RMICloseableServerSocketFactory.java: ########## @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.utils; + +import java.io.IOException; +import java.rmi.server.RMIServerSocketFactory; + +/** + * This represents closeable RMI Server Socket factory. + */ +public interface RMICloseableServerSocketFactory extends RMIServerSocketFactory Review Comment: This is essentially: ```suggestion public interface RMICloseableServerSocketFactory extends RMIServerSocketFactory, AutoCloseable ``` I am trying to figure out if there's a reason we _wouldn't_ want to also extend `AutoCloseable` rather than adding a custom `close()` method... while I can't think of a reason why you'd want to use one in a "try-with-resources" block at this time, it also doesn't hurt anything to _allow it_ in the future, and by not doing it now we're artificially limiting the ability to do so. Also, there are some frameworks that can check if something is AutoCloseable and close it on shutdown/cleanup (again, nothing today seems to do this but it's possible we may want to some day). ########## src/java/org/apache/cassandra/utils/RMICloseableServerSocketFactory.java: ########## @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.utils; + +import java.io.IOException; +import java.rmi.server.RMIServerSocketFactory; + +/** + * This represents closeable RMI Server Socket factory. + */ +public interface RMICloseableServerSocketFactory extends RMIServerSocketFactory Review Comment: FYI - this would go for the Client socket factory as well. ########## src/java/org/apache/cassandra/config/DatabaseDescriptor.java: ########## @@ -1306,7 +1309,14 @@ public static void applySslContext() { SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, REQUIRED, true); SSLFactory.validateSslContext("Native transport", conf.client_encryption_options, conf.client_encryption_options.getClientAuth(), true); + // For JMX SSL the validation is pretty much the same as the Native transport + SSLFactory.validateSslContext("JMX transport", conf.jmx_encryption_options, conf.jmx_encryption_options.getClientAuth(), true); SSLFactory.initHotReloading(conf.server_encryption_options, conf.client_encryption_options, false); + /* + For JMX SSL, the hot reloading of the SSLContext is out of scope for CASSANDRA-18508. Review Comment: Did we create a JIRA to track this, as the lack of hot-reloading of SSL certificates is going to be a real issue for folks who use short-lived SSL certs. ########## test/distributed/org/apache/cassandra/distributed/impl/CollectingSslRMIServerSocketFactoryImpl.java: ########## @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.distributed.impl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import org.apache.cassandra.utils.RMICloseableServerSocketFactory; + + +/** + * This class is used to keep track of SSL based RMI servers created during a cluster creation to + * later close the sockets, which would otherwise be left with a thread running waiting for + * connections that would never show up as the server was otherwise closed. + */ +class CollectingSslRMIServerSocketFactoryImpl implements RMICloseableServerSocketFactory +{ + private final InetAddress bindAddress; + private final String[] enabledCipherSuites; + private final String[] enabledProtocols; + private final boolean needClientAuth; + private SSLContext sslContext; Review Comment: If we change this field to be: ```suggestion private final SSLSocketFactory sslSocketFactory; ``` We can then initialize it using the `sslContext` passed in to the constructor that uses it, _or_ use `getDefaultSSLSocketFactory` in the other, which gets rid of the ternary operator in `createSslServerSocket`. Do you envision a need to hold on to the `sslContext` itself, or could we do this in stead? ########## test/distributed/org/apache/cassandra/distributed/impl/IsolatedJmx.java: ########## @@ -137,6 +143,50 @@ public void startJmx() { } } + /** + * Builds {@code EncryptionOptions} from the map based SSL configuration properties. + * @param encryptionOptionsMap of SSL configuration properties + * @return EncryptionOptions built object + */ + @SuppressWarnings("unchecked") + private EncryptionOptions getJmxEncryptionOptions(Map<String,Object> encryptionOptionsMap) + { + if (encryptionOptionsMap != null) + { + EncryptionOptions jmxEncryptionOptions = new EncryptionOptions(); + String[] cipherSuitesArray = (String[])encryptionOptionsMap.get(EncryptionOptions.ConfigKey.CIPHER_SUITES.toString()); + if (cipherSuitesArray != null ) + { + jmxEncryptionOptions = jmxEncryptionOptions.withCipherSuites(cipherSuitesArray); + } + List<String> acceptedProtocols = (List<String>)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ACCEPTED_PROTOCOLS.toString()); + if (acceptedProtocols != null ) + { + jmxEncryptionOptions = jmxEncryptionOptions.withAcceptedProtocols(acceptedProtocols); + } + + Boolean requireClientAuthValue = (Boolean) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.REQUIRE_CLIENT_AUTH.toString()); + EncryptionOptions.ClientAuth requireClientAuth = requireClientAuthValue == null ? + EncryptionOptions.ClientAuth.NOT_REQUIRED : + EncryptionOptions.ClientAuth.from(String.valueOf(requireClientAuthValue)); + Object enabledOption = encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString()); + boolean enabled = enabledOption != null ? (Boolean)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString()) : false; + + //CASSANDRA-18508 NOTE- We do not populate sslContextFactory configuration here for tests, it could be enhanced + jmxEncryptionOptions = jmxEncryptionOptions + .withKeyStore((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE.toString())) + .withKeyStorePassword((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE_PASSWORD.toString())) + .withTrustStore((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE.toString())) + .withTrustStorePassword((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE_PASSWORD.toString())) + .withRequireClientAuth(requireClientAuth) + .withEnabled(enabled); + return jmxEncryptionOptions; + } else + { + return null; + } Review Comment: I tend to try to invert these kinds of things, which generally allows a quick return on `null` and then you don't actually need the extra indentation/else statement: ```suggestion private EncryptionOptions getJmxEncryptionOptions(Map<String,Object> encryptionOptionsMap) { if (encryptionOptionsMap == null) { return null; } EncryptionOptions jmxEncryptionOptions = new EncryptionOptions(); String[] cipherSuitesArray = (String[])encryptionOptionsMap.get(EncryptionOptions.ConfigKey.CIPHER_SUITES.toString()); if (cipherSuitesArray != null ) { jmxEncryptionOptions = jmxEncryptionOptions.withCipherSuites(cipherSuitesArray); } List<String> acceptedProtocols = (List<String>)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ACCEPTED_PROTOCOLS.toString()); if (acceptedProtocols != null ) { jmxEncryptionOptions = jmxEncryptionOptions.withAcceptedProtocols(acceptedProtocols); } Boolean requireClientAuthValue = (Boolean) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.REQUIRE_CLIENT_AUTH.toString()); EncryptionOptions.ClientAuth requireClientAuth = requireClientAuthValue == null ? EncryptionOptions.ClientAuth.NOT_REQUIRED : EncryptionOptions.ClientAuth.from(String.valueOf(requireClientAuthValue)); Object enabledOption = encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString()); boolean enabled = enabledOption != null ? (Boolean)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString()) : false; //CASSANDRA-18508 NOTE- We do not populate sslContextFactory configuration here for tests, it could be enhanced jmxEncryptionOptions = jmxEncryptionOptions .withKeyStore((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE.toString())) .withKeyStorePassword((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE_PASSWORD.toString())) .withTrustStore((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE.toString())) .withTrustStorePassword((String)encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE_PASSWORD.toString())) .withRequireClientAuth(requireClientAuth) .withEnabled(enabled); return jmxEncryptionOptions; } ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]

