Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Optionally detach ssh authenticated git calls from terminal
When I run CredentialsTest from my terminal window on Ubuntu 16.04, the
test fails for ssh keys which need a passphrase.  If I run the tests
from my IDE, or if I prepend "setsid" to the maven command that runs
the tests, the tests pass.

The ssh command called by git seems to require the DISPLAY variable, and
the GIT_SSH variable, and must be detached from the controlling terminal.
If any one of those is missing (at least on Ubuntu 16), the passphrase
prompt will not be answered.

Command line maven builds now include the property:

  org.jenkinsci.plugins.gitclient.CliGitAPIImpl.useSETSID=true

Default runtime value of the property is false. Users will not run
with this change unless they specifically set that property to true.
Most users don't run Jenkins with a controlling terminal attached,
so they don't need the change.

Sets useSETSID=true in surefire target so that command line invocations
of the tests will prefix the "git" command with setsid when it is used
in an ssh private key context.

Allows command line run of CredentialsTest with passphrase protected
private keys.

Setting BatchMode=yes in the ssh command does not have the same result.
The setsid call was the only technique I found that reliably allowed
the ssh call performed by command line git to consistently process the
script defined in the SSH_ASKPASS variable.

See [JENKINS-20879] and [JENKINS-25194] for more details.
  • Loading branch information
MarkEWaite committed Apr 19, 2017
1 parent 35d8608 commit 882ecdf
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
14 changes: 14 additions & 0 deletions pom.xml
Expand Up @@ -253,6 +253,20 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<!-- Prefix git ssh authenticating command line calls with setsid -->
<!-- Detaches git from the controlling terminal of the test runner -->
<!-- For CredentialsTest with passphrase protected private keys -->
<name>org.jenkinsci.plugins.gitclient.CliGitAPIImpl.useSETSID</name>
<value>true</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
Expand Down
46 changes: 45 additions & 1 deletion src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java
Expand Up @@ -66,8 +66,34 @@
public class CliGitAPIImpl extends LegacyCompatibleGitAPIImpl {

private static final boolean acceptSelfSignedCertificates;

/**
* Constant which can block use of setsid in git calls for ssh credentialed operations.
*
* <code>USE_SETSID=Boolean.valueOf(System.getProperty(CliGitAPIImpl.class.getName() + ".useSETSID", "false"))</code>.
*
* Allow ssh authenticated git calls on Unix variants to be preceded
* by setsid so that the git command is run without being associated
* with a terminal. Some docker runtime cases, and some automated test
* cases have shown that some versions of command line git or ssh will
* not allow automatic answers to private key passphrase prompts
* unless there is no controlling terminal associated with the process.
*/
public static final boolean USE_SETSID = Boolean.valueOf(System.getProperty(CliGitAPIImpl.class.getName() + ".useSETSID", "false"));

/**
* CALL_SETSID decides if command line git can use the setsid program
* during ssh based authentication to detach git from its controlling
* terminal.
*
* If the controlling terminal remains attached, then ssh passphrase based
* private keys cannot be decrypted during authentication (at least in some
* ssh configurations).
*/
private static final boolean CALL_SETSID;
static {
acceptSelfSignedCertificates = Boolean.getBoolean(GitClient.class.getName() + ".untrustedSSL");
CALL_SETSID = setsidExists() && USE_SETSID;
}

private static final long serialVersionUID = 1;
Expand Down Expand Up @@ -1791,6 +1817,11 @@ private String launchCommandIn(ArgumentListBuilder args, File workDir, EnvVars e
String command = gitExe + " " + StringUtils.join(args.toCommandArray(), " ");
try {
args.prepend(gitExe);
if (CALL_SETSID && launcher.isUnix() && env.containsKey("GIT_SSH") && env.containsKey("DISPLAY")) {
/* Detach from controlling terminal for git calls with ssh authentication */
/* GIT_SSH won't call the passphrase prompt script unless detached from controlling terminal */
args.prepend("setsid");
}
listener.getLogger().println(" > " + command + (timeout != null ? TIMEOUT_LOG_PREFIX + timeout : ""));
Launcher.ProcStarter p = launcher.launch().cmds(args.toCommandArray()).
envs(environment).stdout(fos).stderr(err);
Expand Down Expand Up @@ -2750,5 +2781,18 @@ private boolean isWindows() {
return File.pathSeparatorChar==';';
}


/* Return true if setsid program exists */
static private boolean setsidExists() {
if (File.pathSeparatorChar == ';') {
return false;
}
String[] prefixes = { "/usr/bin/", "/bin/", "/usr/sbin/", "/sbin/" };
for (String prefix : prefixes) {
File setsidFile = new File(prefix + "setsid");
if (setsidFile.exists()) {
return true;
}
}
return false;
}
}

0 comments on commit 882ecdf

Please sign in to comment.