-
Notifications
You must be signed in to change notification settings - Fork 461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WFCORE-6384] When shuting down to perform an installation CLI is dis… #5537
Conversation
|
||
// check the presence of a client marker in our server installation and if so, returns its value so the client | ||
// can detect whether he was launched from the same installation dir. | ||
final Path cliMarker = environment.getHomeDir().toPath().resolve("bin").resolve("cli-marker"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test that the Files.exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can do it without checking the existence since we can build Path objects for unknown files since Path doesn't access to the filesystem, do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I agree, the exception will get thrown when creating the BufferedReader, but this exception is then catch. Can be cleaner to check the existence of the file prior to create the BufferedReader.
// check the presence of a client marker in our server installation and if so, returns its value so the client | ||
// can detect whether he was launched from the same installation dir. | ||
final Path cliMarker = environment.getHomeDir().toPath().resolve("bin").resolve("cli-marker"); | ||
try (BufferedReader reader = new BufferedReader(new FileReader(cliMarker.toFile()))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test that the Files.exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the file doesn't exist, it is captured on the catch block which is explicitly ignored
if (clientMarker != null) { | ||
final String jbossHome = WildFlySecurityManager.getEnvPropertyPrivileged("JBOSS_HOME", null); | ||
final String clientMarkerData = WildFlySecurityManager.getPropertyPrivileged(Util.CLI_MARKER_VALUE, null); | ||
if (jbossHome != null && clientMarkerData != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why we need to check for the presence of JBOSS_HOME env variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, it was left over from a previous approach, we can remove the JBOSS_HOME check here
WildFlySecurityManager.setPropertyPrivileged(Util.CLI_MARKER_VALUE, data); | ||
} | ||
} catch (Throwable e) { | ||
// explicitly ignore this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have a warning displayed. I imagine the case where part of the JBOSS_HOME sub directories would be readonly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is explicitly ignored because of the read-only use case (or any other exception).
We could log a WARN (we should move the method invocation after the one that setups the logging) but I considered it as a corner case that could go silently; if that occurs the logic will assume we are using a local CLI instance and it will get closed when the update is in place just because we won't be able to detect whether it is local.
This won't affect the update/rollback process, it is just that if your user is running on a read-only mode over the JBOSS_HOME/bin directory and you are using a non-local CLI instance your CLI instance will be closed with the aim to ensure you will be using the latest jboss-modules.jar file after the update, although for this case, it wouldn't be necessary to close it.
We can add a WARN, but, shouldn't it be just noise since it is a very particular case only related to the shutdown when performing an installation? WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or at the very least is any logging possible? Catch blocks that drop the Throwable somehow end up being the place you need more information from when remote debugging an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, yes, a debug log could be useful for the future, I'm going to add it
*/ | ||
private static void createClientMarker() { | ||
try { | ||
final String jbossHome = WildFlySecurityManager.getEnvPropertyPrivileged("JBOSS_HOME", null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure we need the SecurityManager wrappers here, in this case as this is the entry point there should not be other protection domains on the call stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably incorrectly tend to use WildFlySecurityManager to ensure we can load a system property or env variables regardless of the current user permissions if, in this case, the CLI is running with the Security Manager enabled.
Is this incorrect in this case? Notice the JBOSS_HOME is exported by us in the jboss-cli scripts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you maybe suggesting just using a privileged block instead of the WildFlySecurityManager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am suggesting neither, just access the property directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot the CLI be executed with the security manager enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The security manager being possibly enabled is never a reason to add a doPrivileged.
When you add a doPrivileged call what you are effectively doing is dropping the protection domains of any callers currently on the call stack and setting up the permission check to be in the context of the protection domain of the class that is calling the method with the permissions check.
As this is a main method / entry point - what callers are being dropped from the call stack and what measures have been put in place to verify they can safely call this method.
The way these properties are being used the safety is probably fine but what callers are being dropped from the call stack?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is a main method / entry point - what callers are being dropped from the call stack and what measures have been put in place to verify they can safely call this method.
I just wanted to avoid the need to require specific permission to load an environment variable that is indeed configured by the launch script. We know that this is safe to do because it is the launch script the one that invokes this Main class.
The way these properties are being used the safety is probably fine but what callers are being dropped from the call stack?
Ok, so the point is that since it is a main class, it can be invoked by anyone, and in that case, if it is invoked by something that is not controlled by us (e.x. something different than our launch script) we want to ensure the caller has been granted with the required permission.
In such a case, then it is better to ensure that any caller has been granted the expected permission, including if it is invoked from our script.
Ok, I'm going to remove the privileged block and force any caller to have the expected permission since we can't ensure what is the caller of this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@darranl I've just noticed that in this class we are already reading/setting system properties under a privileged block to configure the logs:
https://github.com/wildfly/wildfly-core/blob/main/cli/src/main/java/org/jboss/as/cli/CommandLineMain.java#L101-L106, if we apply the same reasoning to them, those privileged blocks should be also removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wanted to avoid the need to require specific permission to load an environment variable that is indeed configured by the launch script. We know that this is safe to do because it is the launch script the one that invokes this Main class.
This is not the purpose of a doPrivileged call, it is not possible to prevent the permission check. The ProtectionDomain for the CommandLineMain class will still need the relevant permissions to be granted.
When a permission check is performed it loops through the PermissionDomains of all classes on the call stack, and each and every one of those must be granted that permission or the check will fail. What doPrivileged does is it says ignore the classes that called me but it does not get away from the "me" class being required the permission.
If this class is always intended to be called as the main entry point to the program it does not need any doPrivileged calls as it will always be the starting point of the call stack so there will be no protection domains to drop.
On the other hand if this is expected that other classes from other protection domains will call this class then this class should only be using doPrivileged if those are trusted called / it has been designed so the doPrivileged calls can not be abused.
TBH this pattern that creeps in where if there is going to be a permission check a doPrivileged is added is one of the reasons that adds to the justification of the removal of the security manager in Java.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm starting to understand the tricky details of the security manager when, for good reasons, it was going to be removed :)...never is late, even if it going to be an obsolete knowledge
final String jbossHome = WildFlySecurityManager.getEnvPropertyPrivileged("JBOSS_HOME", null); | ||
if (jbossHome != null) { | ||
final Path cliMarkerPath = Paths.get(jbossHome).resolve("bin").resolve(Util.CLI_MARKER); | ||
Files.deleteIfExists(cliMarkerPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if multiple instances of the CLI are running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RFE contemplates this situation where we have stated that is the user the one who has to ensure no other processes are launched when they are updating the server.
In this case, the marker value returned by the mgnt Operation won't match the current CLI instance, so it will assume it is not a local instance and won't be automatically closed.
The user will end up in the same situation as the current Patch tool, where an update/patch can update the underlying jboss-modules.jar. Since the client CLI session is not automatically closed, the user will be using an obsolete CLI process launched from a patched jboss-modules.jar. Probably he will get errors using the CLI and he will be forced to close it and open it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more sophisticated approaches but I guess out of the scope of the first release, for example, a kind of watcher service over the boss-modules.jar resource used by the CLI instance and if it is modified, close the client. Or use a kind of file lock that allows us to wait on the mgmt operation side if the client has been closed before shutdown.
I confirm that the original issue described in https://issues.redhat.com/browse/WFCORE-6384 is fixed by this MR! Thank you! |
*/ | ||
private static void createClientMarker() { | ||
try { | ||
final String jbossHome = System.getenv("JBOSS_HOME"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yersan when CLI is started as a fat jar (bin/client/jboss-cli-client.jar), we don't have this env set. I am wandering if we should attempt to find the jar location (class.getProtectionDomain().getCodeSource().getLocation()) and determinate if that is inside an installation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfdenise We should be fine there since Installation Manager high level commands are not available for the CLI if the CLI is not running on a modular environment.
Indeed, none of the extension commands are available when running on a non-modular environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
ctx.printLine("The JBoss CLI session will be closed automatically to allow the server be updated. Once the server has been restarted, you can relaunch the JBoss CLI session.", false); | ||
try { | ||
TimeUnit.SECONDS.sleep(3); | ||
} catch (InterruptedException e) { | ||
// Ignored | ||
} | ||
|
||
// We are using a CLI which was launched from the server installation we have requested to be updated. | ||
// In order to prevent keeping using a jboss-modules.jar that could have been updated, we finish the CLI process | ||
// Once the server has been restarted the user will launch again the CLI that will use the most recent updates | ||
ctx.terminateSession(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't you return after this call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never found an issue, but yes, looks correct returning here.
…connected from DC Jira issue: https://issues.redhat.com/browse/WFCORE-6384
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably still needs review from @jfdenise but no objections from me.
Thanks @jfdenise / @darranl / @marekkopecky |
…connected from DC
Jira issue: https://issues.redhat.com/browse/WFCORE-6384
Instead of comparing Paths to understand whether we are using a local CLI client instance when performing an installation, now we use a CLI client marker file. The content of this client marker file is returned by the
shutdown --perform-installation
management operation. If the content doesn't match, we are not using a local CLI instance. Otherwise, we use the most restrictive approach assuming assuming the CLI is local.