Skip to content
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-75230] Fixes the use of an init script with Windows AMI using a Linux launcher #1046

Merged
merged 4 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 9 additions & 70 deletions src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
*/
package hudson.plugins.ec2.ssh;

import static org.apache.sshd.client.session.ClientSession.REMOTE_COMMAND_WAIT_EVENTS;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.KeyPair;
Expand Down Expand Up @@ -66,19 +64,15 @@
import java.nio.file.attribute.PosixFilePermission;
import java.security.PublicKey;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
Expand Down Expand Up @@ -244,114 +238,59 @@

if (StringUtils.isNotBlank(initScript)
&& !executeRemote(clientSession, "test -e ~/.hudson-run-init", logger)) {
logInfo(computer, listener, "Executing init script");
logInfo(computer, listener, "Upload init script");
scp.upload(
initScript.getBytes(StandardCharsets.UTF_8),
tmpDir + "/init.sh",
List.of(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ),
List.of(
PosixFilePermission.OWNER_READ,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good to me. Just want to understand why all the permissions are enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least, OWNER_WRITE is needed, so that Windows doesn't set the read-only flag. OWNER should suffice, I'm also adding GROUP_XXX and OTHERS_XXX to make sure that the permissions will no longer be a problem.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scp.put(initScript.getBytes(StandardCharsets.UTF_8), "init.sh", tmpDir, "0700")
Before the Mina changes, the script was uploaded with permissions set to 0700, ensuring that only the owner could read, write, and execute it. I guess maintaining the same level of restriction (0700: OWNER_READ, OWNER_WRITE, OWNER_EXECUTE) should be sufficient, as it prevents unintended access by other users.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @Priya-CB , we should keep permissions as they were before this bug was introduced (https://github.com/jenkinsci/ec2-plugin/blob/1797.ve8a_edb_7e5f6a_/src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java#L216), I'd remove GROUP_* and OTHER_*

PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE),
scpTimestamp);

logInfo(computer, listener, "Executing init script");
String initCommand = buildUpCommand(computer, tmpDir + "/init.sh");
try (ClientChannel channel = clientSession.createExecChannel(
initCommand, StandardCharsets.US_ASCII, null, Collections.emptyMap())) {

channel.open().await(timeout);

OutputStream invertedIn = channel.getInvertedIn();
if (invertedIn != null) {
invertedIn.close(); // nothing to write here
}

Collection<ClientChannelEvent> waitMask =
channel.waitFor(REMOTE_COMMAND_WAIT_EVENTS, timeout);

if (waitMask.contains(ClientChannelEvent.TIMEOUT)) {
logWarning(computer, listener, "init script timed out");
return;
}

int exitStatus = waitCompletion(channel, timeout);
if (exitStatus != 0) {
logWarning(computer, listener, "init script failed: exit code=" + exitStatus);
return;
}

InputStream invertedErr = channel.getInvertedErr();
if (invertedErr != null) {
invertedErr.close(); // we are not supposed to get anything from stderr
}
IOUtils.copy(channel.getInvertedOut(), logger);
}
executeRemote(clientSession, initCommand, logger);

logInfo(computer, listener, "Creating ~/.hudson-run-init");
String createHudsonRunInitCommand = buildUpCommand(computer, "touch ~/.hudson-run-init");
try (ClientChannel channel = clientSession.createExecChannel(
createHudsonRunInitCommand, StandardCharsets.US_ASCII, null, Collections.emptyMap())) {
OutputStream invertedIn = channel.getInvertedIn();
if (invertedIn != null) {
invertedIn.close(); // nothing to write here
}
channel.open().await(timeout);

Collection<ClientChannelEvent> waitMask =
channel.waitFor(REMOTE_COMMAND_WAIT_EVENTS, timeout);

if (waitMask.contains(ClientChannelEvent.TIMEOUT)) {
logWarning(computer, listener, "init script timed out");
return;
}

int exitStatus = waitCompletion(channel, timeout);
if (exitStatus != 0) {
logWarning(computer, listener, "init script failed: exit code=" + exitStatus);
return;
}

InputStream invertedErr = channel.getInvertedErr();
if (invertedErr != null) {
invertedErr.close(); // we are not supposed to get anything from stderr
}
IOUtils.copy(channel.getInvertedOut(), logger);
}
executeRemote(clientSession, createHudsonRunInitCommand, logger);
}

try {
Instance nodeInstance = computer.describeInstance();
if (nodeInstance.getInstanceType().equals("mac2.metal")) {
LOGGER.info("Running Command for mac2.metal");
executeRemote(
computer,
clientSession,
javaPath + " -fullversion",
"curl -L -O "
+ CORRETTO_LATEST_URL
+ "/amazon-corretto-11-aarch64-macos-jdk.pkg; sudo installer -pkg amazon-corretto-11-aarch64-macos-jdk.pkg -target /",
logger,
listener);
} else {
executeRemote(
computer,
clientSession,
javaPath + " -fullversion",
"curl -L -O "
+ CORRETTO_LATEST_URL
+ "/amazon-corretto-11-x64-macos-jdk.pkg; sudo installer -pkg amazon-corretto-11-x64-macos-jdk.pkg -target /",
logger,
listener);
}
} catch (InterruptedException ex) {
LOGGER.warning(ex.getMessage());
}

// Always copy so we get the most recent remoting.jar
logInfo(computer, listener, "Copying remoting.jar to: " + tmpDir);
scp.upload(
Jenkins.get().getJnlpJars("remoting.jar").readFully(),
tmpDir + "/remoting.jar",
List.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ),
List.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE),

Check warning on line 293 in src/main/java/hudson/plugins/ec2/ssh/EC2MacLauncher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 241-293 are not covered by tests
scpTimestamp);
}
}
Expand Down
80 changes: 9 additions & 71 deletions src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
*/
package hudson.plugins.ec2.ssh;

import static org.apache.sshd.client.session.ClientSession.REMOTE_COMMAND_WAIT_EVENTS;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.KeyPair;
Expand Down Expand Up @@ -68,19 +66,15 @@
import java.security.PublicKey;
import java.time.Duration;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
Expand Down Expand Up @@ -245,102 +239,46 @@

if (StringUtils.isNotBlank(initScript)
&& !executeRemote(clientSession, "test -e ~/.hudson-run-init", logger)) {
logInfo(computer, listener, "Executing init script");
logInfo(computer, listener, "Upload init script");
scp.upload(
initScript.getBytes(StandardCharsets.UTF_8),
tmpDir + "/init.sh",
List.of(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ),
List.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE),
scpTimestamp);

logInfo(computer, listener, "Executing init script");
String initCommand = buildUpCommand(computer, tmpDir + "/init.sh");
try (ClientChannel channel = clientSession.createExecChannel(
initCommand, StandardCharsets.US_ASCII, null, Collections.emptyMap())) {

channel.open().await(timeout);

OutputStream invertedIn = channel.getInvertedIn();
if (invertedIn != null) {
invertedIn.close(); // nothing to write here
}

Collection<ClientChannelEvent> waitMask =
channel.waitFor(REMOTE_COMMAND_WAIT_EVENTS, timeout);

if (waitMask.contains(ClientChannelEvent.TIMEOUT)) {
logWarning(computer, listener, "init script timed out");
return;
}

int exitStatus = waitCompletion(channel, timeout);
if (exitStatus != 0) {
logWarning(computer, listener, "init script failed: exit code=" + exitStatus);
return;
}

InputStream invertedErr = channel.getInvertedErr();
if (invertedErr != null) {
invertedErr.close(); // we are not supposed to get anything from stderr
}
IOUtils.copy(channel.getInvertedOut(), logger);
}
executeRemote(clientSession, initCommand, logger);

logInfo(computer, listener, "Creating ~/.hudson-run-init");

String createHudsonRunInitCommand = buildUpCommand(computer, "touch ~/.hudson-run-init");
try (ClientChannel channel = clientSession.createExecChannel(
createHudsonRunInitCommand, StandardCharsets.US_ASCII, null, Collections.emptyMap())) {
OutputStream invertedIn = channel.getInvertedIn();
if (invertedIn != null) {
invertedIn.close(); // nothing to write here
}
channel.open().await(timeout);

Collection<ClientChannelEvent> waitMask =
channel.waitFor(REMOTE_COMMAND_WAIT_EVENTS, timeout);

if (waitMask.contains(ClientChannelEvent.TIMEOUT)) {
logWarning(computer, listener, "init script timed out");
return;
}

int exitStatus = waitCompletion(channel, timeout);
if (exitStatus != 0) {
logWarning(computer, listener, "init script failed: exit code=" + exitStatus);
return;
}

InputStream invertedErr = channel.getInvertedErr();
if (invertedErr != null) {
invertedErr.close(); // we are not supposed to get anything from stderr
}
IOUtils.copy(channel.getInvertedOut(), logger);
}
executeRemote(clientSession, createHudsonRunInitCommand, logger);
}

executeRemote(
computer,
clientSession,
javaPath + " -fullversion",
"sudo amazon-linux-extras install java-openjdk11 -y; sudo yum install -y fontconfig java-11-openjdk",
logger,
listener);
executeRemote(
computer,
clientSession,
"which scp",
"sudo yum install -y openssh-clients",
logger,
listener);

// Always copy so we get the most recent remoting.jar
logInfo(computer, listener, "Copying remoting.jar to: " + tmpDir);
scp.upload(
Jenkins.get().getJnlpJars("remoting.jar").readFully(),
tmpDir + "/remoting.jar",
List.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ),
List.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE),

Check warning on line 281 in src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 242-281 are not covered by tests
scpTimestamp);
}
}
Expand Down
Loading