-
Notifications
You must be signed in to change notification settings - Fork 696
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
[JENKINS-75011] Use Apache Mina as ssh transport layer, remove trilead #1022
[JENKINS-75011] Use Apache Mina as ssh transport layer, remove trilead #1022
Conversation
public final String targetHost; | ||
public final int targetPort; | ||
public final String proxyUser; | ||
public final String proxyPass; |
Check warning
Code scanning / Jenkins Security Scan
Jenkins: Plaintext password storage Warning
Without a timestamp, the permissions are ignored
@@ -39,6 +49,8 @@ | |||
public abstract class EC2ComputerLauncher extends ComputerLauncher { | |||
private static final Logger LOGGER = Logger.getLogger(EC2ComputerLauncher.class.getName()); | |||
|
|||
private static final long timeout = Duration.ofSeconds(10).toMillis(); |
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.
Nit: make the constant all-caps
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 didn't want to publish the comment until it was marked as ready for review, I am still learning GitHub.
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.
No problem 🙂
This should use the timeout from the node anyway: 7680d4e
computer, | ||
listener, | ||
"SSH service responded. Waiting " + bootDelay + "ms for service to stabilize"); | ||
Thread.sleep(bootDelay); |
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.
Do we need to check the max delay time so that it is always a realistic value?
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.
Although this suggestion may be worth considering (I leave it to the discretion of the maintainer), I have kept the previous implementation: https://github.com/jmdesprez/ec2-plugin/blob/master/src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java#L186
If requested, I can add this to this PR.
if (initScript != null | ||
&& !initScript.trim().isEmpty() |
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 (initScript != null | |
&& !initScript.trim().isEmpty() | |
if (StringUtils.isEmpty(initScript) |
Since the project already uses Apache Commons, we can utilize the utility method.
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 guess you mean
if (initScript != null | |
&& !initScript.trim().isEmpty() | |
if (StringUtils.isNotEmpty(initScript) |
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.
This is not related to the trilead
migration, but I have included it as it is a minor change: fe9a901
computer, | ||
clientSession, | ||
javaPath + " -fullversion", | ||
"curl -L -O https://corretto.aws/downloads/latest/amazon-corretto-11-aarch64-macos-jdk.pkg; sudo installer -pkg amazon-corretto-11-aarch64-macos-jdk.pkg -target /", |
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.
https://corretto.aws/downloads/latest/
Can we have this in constant as it is used in two places? Also, would allowing configuration using system properties be a good idea?
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.
This is not related to the trilead
migration, but I have included it as it is a minor change: 9a4fc3b
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.
allowing configuration using system properties be a good idea
Although this suggestion may be worth considering (I leave it to the discretion of the maintainer), I have kept the previous implementation.
If requested, I can add this to this PR.
computer, | ||
clientSession, | ||
javaPath + " -fullversion", | ||
"curl -L -O https://corretto.aws/downloads/latest/amazon-corretto-11-x64-macos-jdk.pkg; sudo installer -pkg amazon-corretto-11-x64-macos-jdk.pkg -target /", |
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.
Ditto
try { | ||
session.executeRemoteCommand(command, logger, logger, null); | ||
return true; | ||
} catch (IOException e) { |
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.
Should we log an exception?
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.
Using trilead
, there is no logging when the command fails. So I used the FINE
level: 7ceaeea
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.
This function is used to execute the remote commands like directory creation, should we consider the warning level?
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 that will change the plugin behavior, I would prefer to change that in another PR. Again, if requested, I can add this to this PR.
|
||
final SshClient remotingClient = SshClient.setUpDefaultClient(); | ||
final ClientSession remotingSession = connectToSsh(remotingClient, computer, listener, template); | ||
KeyPair key = computer.getCloud().getKeyPair(); |
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.
Do we need to check the Key length? I assume not, but worth noting.
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 as part of apache mina migration, that could be part of some FIPS compatibility ticket.
import java.security.MessageDigest; | ||
import java.util.logging.Logger; | ||
|
||
public class HostKeyVerifierImpl implements ServerHostKeyVerifier { | ||
public class HostKeyVerifierImpl { |
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.
Should we refactor the class name as it is no longer implementing the Interface?
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 kept the name as this is a public class
.
computer, | ||
listener, | ||
"SSH service responded. Waiting " + bootDelay + "ms for service to stabilize"); | ||
Thread.sleep(bootDelay); |
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.
Same. Should we make a check so that it doesn't wait indefinitely?
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.
Although this suggestion may be worth considering (I leave it to the discretion of the maintainer), I have kept the previous implementation: https://github.com/jmdesprez/ec2-plugin/blob/master/src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java#L188
If requested, I can add this to this PR.
} else if ("EC".equalsIgnoreCase(privateKey.getAlgorithm())) { | ||
ECPrivateKeySpec ecPrivateKeySpec = keyFactory.getKeySpec(privateKey, ECPrivateKeySpec.class); | ||
return keyFactory.generatePublic(ecPrivateKeySpec); | ||
} else { |
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 we only supporting two algorithms?
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 rewrote it in a more robust way. Anyway, I think that this is not used because we get a PrivateKeyInfo
.
4e9d26d
computer, | ||
listener, | ||
"SSH service responded. Waiting " + bootDelay + "ms for service to stabilize"); | ||
Thread.sleep(bootDelay); |
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.
nit: how about using non thread blocking
jenkins.util.Timer.get().schedule(() -> {}, bootDelay, TimeUnit.SECONDS);
instead?
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.
Although this suggestion may be worth considering (I leave it to the discretion of the maintainer), I have kept the previous implementation: https://github.com/jmdesprez/ec2-plugin/blob/master/src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java#L186
If requested, I can add this to this PR.
if (initScript != null | ||
&& !initScript.trim().isEmpty() |
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 guess you mean
if (initScript != null | |
&& !initScript.trim().isEmpty() | |
if (StringUtils.isNotEmpty(initScript) |
if (key == null) { | ||
isAuthenticated = false; | ||
} else { | ||
clientSession.addPublicKeyIdentity(KeyHelper.decodeKeyPair(key.getKeyMaterial(), "")); |
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.
Can we only have unencrypted key pairs? This won't work if we find a PEMEncryptedKeyPair
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.
Why won't this work? If it's related to the empty password, I kept the previous implementation: https://github.com/jmdesprez/ec2-plugin/blob/master/src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java#L410-L411
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.
Lack of knowledge from my side about how AWS keys work.
IIUTC computer.getCloud().getKeyPair()
is returning a KeyPair and then we're adding the public key to the client session with KeyHelper.decodeKeyPair(key.getKeyMaterial(), "")
.
KeyHelper
is checking if the key is encrypted or not https://github.com/jenkinsci/ec2-plugin/pull/1022/files#diff-0a41b9e9dd813646b5762bea12e95c9aca2f70c8387366b1e6e9870add0d0c2aR58 but we're calling this method is always called with empty / null password, so we'll only handle unencrypted keys.
If I have followed the code correctly, this ssh credentials are configured for the cloud, and the should be a username + SSH private key (I gues obtained with aws sts get-access-key-info
).
So, the question is, is this ssh key always unencrypted? AFAIK it is, but I wanted to confirm 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.
Lack of knowledge from my side about how AWS keys work.
Same here
So, the question is, is this ssh key always unencrypted?
Based on previous implementation, the password is hard coded to ""
final ClientSession remotingSession = connectToSsh(remotingClient, computer, listener, template); | ||
KeyPair key = computer.getCloud().getKeyPair(); | ||
if (key != null) { | ||
remotingSession.addPublicKeyIdentity(KeyHelper.decodeKeyPair(key.getKeyMaterial(), "")); |
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.
ditto
|
||
final SshClient remotingClient = SshClient.setUpDefaultClient(); | ||
final ClientSession remotingSession = connectToSsh(remotingClient, computer, listener, template); | ||
KeyPair key = computer.getCloud().getKeyPair(); |
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 as part of apache mina migration, that could be part of some FIPS compatibility ticket.
|
||
if (object instanceof PEMEncryptedKeyPair) { | ||
PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) object) | ||
.decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(password.toCharArray())); |
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.
Might throw a NPE when password
is null
(e.g.: https://github.com/jenkinsci/ec2-plugin/pull/1022/files#diff-1d5f872d1bd42e798ad1690b14df1b4a4f79926b27a7be424148228e9073ac5aR76) let's add a @NonNull
annotation or perform a null check
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.
Added: 9abf818
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.
since the password can be null, it would be better to remove the @NonNull
annotation and add a null check?
PrintStream logger = listener.getLogger(); | ||
EC2AbstractSlave node = computer.getNode(); | ||
SlaveTemplate template = computer.getSlaveTemplate(); | ||
final long timeout = node == null ? 0L : node.getLaunchTimeoutInMillis(); |
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.
It would be cleaner to move the initialization of timeout after the null check for node
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.
Moved: abea87f
try { | ||
session.executeRemoteCommand(command, logger, logger, null); | ||
return true; | ||
} catch (IOException e) { |
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.
This function is used to execute the remote commands like directory creation, should we consider the warning level?
|
||
if (object instanceof PEMEncryptedKeyPair) { | ||
PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) object) | ||
.decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(password.toCharArray())); |
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.
since the password can be null, it would be better to remove the @NonNull
annotation and add a null check?
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.
LGTM
Hi @res0nance ! We are working on being able to install and use the plugin in a FIPS environment, following https://github.com/jenkinsci/jep/blob/bdc28d03ed3202be3f343a927a247341a64b9156/jep/237/README.adoc The problem is that It'd be great if you could make a review! Thanks in advance PS: There's another incoming PR adding some minor validations, but this change is big enough and with enough entity to consider the two of them as separated changes. |
@jmdesprez just a quick note before I open a ticket somewhere appropriate: updating to the release with this change resulted in Agents failing to start.
For comparison, without this change the output looks like this:
Note: in our Cloud Config, we specify "Connect by SSH Process" (I don't think that is the default). |
@mwebber Thanks. I'm investigating the issue. |
PR for JENKINS-75011
Mainly, the changes consist of:
SshClient
where a connection is neededConnection
byClientSession
SCPClient
byCloseableScpClient
try-with-resource
forCloseable
resourcesProxyCONNECTListener
for the http proxy CONNECTPEMParser
because Mina requires aKeyPair
instead of achar[]
Implementation note
I focused on migrating from
trilead
tomina
. I didn't refactor because I didn't want to mix too many things up.Testing done
I create this in draft mode because I'm still doing manual tests.Manual testing has been done with the help of @Priya-CB and @mikecirioli.
What is tested:
What is not tested:
Submitter checklist