-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Git ssh private key binding(GSoC-21) #1111
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
base: master
Are you sure you want to change the base?
Changes from all commits
e9c550a
4fd5c4c
e212567
78a47fb
818599a
6eeecee
2bc3140
e45ed33
9030a2c
9312a70
a8f38f3
9e17779
b53385b
588c389
d70831c
443c0f7
65624c0
a55e3dd
d38eea0
f57be59
df3d33c
0996235
47588ae
7549c54
d1eeb22
96c0103
d3ee23a
c218b0b
d596240
8922ada
a44b388
a42ac94
598eb80
5c47934
752a94a
035e6c2
dcba7c9
fbba6f9
69fafe5
ee925a7
4a5e7de
63680a0
8c525eb
4ec3a3d
638989b
e830aef
be399fb
73b01ba
7ef7820
b419b4c
9bc6d3a
82022ef
3f82b61
30064ed
319442e
ea86cda
dcc83b3
29cdcd7
e13d669
dc83e1c
583ee18
cbf6215
74500c1
1ff2fcb
fc81ba3
c6d9b3b
607a5ab
1662e96
c0d14aa
99535a8
85dfeed
d7f3b55
2efc015
e9d572d
be8b2e9
a98514f
649cd85
1766ec2
74b4f16
f168ecb
71bd8fb
b6eebfd
b6eeac2
66009a8
06c8073
187b399
e12419b
828cc94
dd86551
cdefb57
1ce0079
0e5b9fb
1354b1f
eefb41d
83171e3
3a238c0
c63559c
4654472
b3b2f69
8396478
b097fb5
0a40c33
1785381
78651ae
825eda8
4a5fbdf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,219 @@ | ||||||
package jenkins.plugins.git; | ||||||
|
||||||
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; | ||||||
import com.cloudbees.plugins.credentials.common.StandardCredentials; | ||||||
import edu.umd.cs.findbugs.annotations.NonNull; | ||||||
import hudson.EnvVars; | ||||||
import hudson.FilePath; | ||||||
import hudson.Launcher; | ||||||
import hudson.model.Run; | ||||||
import hudson.model.TaskListener; | ||||||
import hudson.plugins.git.GitSCM; | ||||||
import hudson.plugins.git.GitTool; | ||||||
import hudson.util.ListBoxModel; | ||||||
import hudson.Extension; | ||||||
import jenkins.model.Jenkins; | ||||||
import org.jenkinsci.Symbol; | ||||||
import org.jenkinsci.plugins.credentialsbinding.BindingDescriptor; | ||||||
import org.jenkinsci.plugins.credentialsbinding.MultiBinding; | ||||||
import org.jenkinsci.plugins.credentialsbinding.impl.AbstractOnDiskBinding; | ||||||
import org.jenkinsci.plugins.credentialsbinding.impl.UnbindableDir; | ||||||
import org.jenkinsci.plugins.gitclient.CliGitAPIImpl; | ||||||
import org.jenkinsci.plugins.gitclient.Git; | ||||||
import org.jenkinsci.plugins.gitclient.GitClient; | ||||||
import org.kohsuke.stapler.DataBoundConstructor; | ||||||
import org.kohsuke.stapler.interceptor.RequirePOST; | ||||||
|
||||||
import javax.annotation.Nullable; | ||||||
import java.io.IOException; | ||||||
import java.util.LinkedHashMap; | ||||||
import java.util.Collections; | ||||||
import java.util.Map; | ||||||
import java.util.List; | ||||||
import java.util.Set; | ||||||
|
||||||
|
||||||
public class GitSSHPrivateKeyBinding extends MultiBinding<SSHUserPrivateKey> implements GitCredentialBindings, SSHKeyUtils { | ||||||
final private String gitToolName; | ||||||
private transient boolean unixNodeType; | ||||||
|
||||||
@DataBoundConstructor | ||||||
public GitSSHPrivateKeyBinding(String gitToolName, String credentialsId) { | ||||||
super(credentialsId); | ||||||
this.gitToolName = gitToolName; | ||||||
//Variables could be added if needed | ||||||
} | ||||||
|
||||||
public String getGitToolName() { | ||||||
return this.gitToolName; | ||||||
} | ||||||
|
||||||
private void setUnixNodeType(boolean value) { | ||||||
this.unixNodeType = value; | ||||||
} | ||||||
|
||||||
@Override | ||||||
public MultiEnvironment bind(@NonNull Run<?, ?> run, @Nullable FilePath filePath, | ||||||
@Nullable Launcher launcher, @NonNull TaskListener taskListener) throws IOException, InterruptedException { | ||||||
final Map<String, String> secretValues = new LinkedHashMap<>(); | ||||||
final Map<String, String> publicValues = new LinkedHashMap<>(); | ||||||
SSHUserPrivateKey credentials = getCredentials(run); | ||||||
GitTool cliGitTool = getCliGitTool(run, this.gitToolName, taskListener); | ||||||
if (cliGitTool != null && filePath != null && launcher != null) { | ||||||
setUnixNodeType(isCurrentNodeOSUnix(launcher)); | ||||||
final UnbindableDir unbindTempDir = UnbindableDir.create(filePath); | ||||||
final GitClient git = getGitClientInstance(cliGitTool.getGitExe(), unbindTempDir.getDirPath(), new EnvVars(), taskListener); | ||||||
final String sshExePath = getSSHPath(git); | ||||||
setGitEnvironmentVariables(git, publicValues); | ||||||
if (isGitVersionAtLeast(git, 2, 3, 0, 0)) { | ||||||
secretValues.put("GIT_SSH_COMMAND", getSSHCmd(credentials, unbindTempDir.getDirPath(), sshExePath)); | ||||||
} else { | ||||||
SSHScriptFile sshScript = new SSHScriptFile(credentials, sshExePath, unixNodeType); | ||||||
secretValues.put("GIT_SSH", sshScript.write(credentials, unbindTempDir.getDirPath()).getRemote()); | ||||||
} | ||||||
return new MultiEnvironment(secretValues, publicValues, unbindTempDir.getUnbinder()); | ||||||
} else { | ||||||
taskListener.getLogger().println("JGit and JGitApache type Git tools are not supported by this binding"); | ||||||
return new MultiEnvironment(secretValues, publicValues); | ||||||
} | ||||||
} | ||||||
|
||||||
@Override | ||||||
public Set<String> variables(@NonNull Run<?, ?> run) { | ||||||
return Collections.emptySet(); | ||||||
} | ||||||
|
||||||
/*package*/void setGitEnvironmentVariables(@NonNull GitClient git, Map<String, String> publicValues) throws IOException, InterruptedException { | ||||||
setGitEnvironmentVariables(git, null, publicValues); | ||||||
} | ||||||
|
||||||
@Override | ||||||
public void setCredentialPairBindings(@NonNull StandardCredentials credentials, Map<String, String> secretValues, Map<String, String> publicValues) { | ||||||
//Private Key credentials not required to bind with environment variables | ||||||
} | ||||||
|
||||||
@Override | ||||||
public void setGitEnvironmentVariables(@NonNull GitClient git, Map<String, String> secretValues, Map<String, String> publicValues) throws IOException, InterruptedException { | ||||||
if (unixNodeType && isGitVersionAtLeast(git, 2, 3, 0, 0)) { | ||||||
publicValues.put("GIT_TERMINAL_PROMPT", "false"); | ||||||
} else { | ||||||
publicValues.put("GCM_INTERACTIVE", "false"); | ||||||
} | ||||||
} | ||||||
|
||||||
@Override | ||||||
public GitClient getGitClientInstance(String gitToolExe, FilePath repository, EnvVars env, TaskListener listener) throws IOException, InterruptedException { | ||||||
Git gitInstance = Git.with(listener, env).using(gitToolExe); | ||||||
return gitInstance.getClient(); | ||||||
} | ||||||
|
||||||
@Override | ||||||
protected Class<SSHUserPrivateKey> type() { | ||||||
return SSHUserPrivateKey.class; | ||||||
} | ||||||
|
||||||
private boolean isGitVersionAtLeast(GitClient git, int major, int minor, int rev, int bugfix) { | ||||||
return ((CliGitAPIImpl) git).isCliGitVerAtLeast(major, minor, rev, bugfix); | ||||||
} | ||||||
|
||||||
private String getSSHPath(GitClient git) { | ||||||
if (unixNodeType) { | ||||||
return "ssh"; | ||||||
} else { | ||||||
//Use getSSHExePathInWin(GitClient git), when support for finding ssh executable is provided for agents. | ||||||
//ssh, will work only if OpenSSH is installed on the system being used to execute the build | ||||||
return "ssh"; | ||||||
} | ||||||
} | ||||||
|
||||||
private String getSSHCmd(SSHUserPrivateKey credentials, FilePath tempDir, String sshExePath) { | ||||||
if (unixNodeType) { | ||||||
return sshExePath | ||||||
+ " -i " | ||||||
+ getPrivateKeyFile(credentials, tempDir).getRemote() | ||||||
+ " -o StrictHostKeyChecking=no"; | ||||||
} else { | ||||||
return "\"" + sshExePath + "\"" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: For better maintainability, personally I would recommend using
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice suggestion, will check this out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... This seems to go against the https://github.com/jenkinsci/git-client-plugin#ssh-host-key-verification feature. @MarkEWaite is that correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed @sboardwell . This needs to be extended to support the host key checking strategy before it is merged. |
||||||
+ " -i " | ||||||
+ "\"" | ||||||
+ getPrivateKeyFile(credentials, tempDir).getRemote() | ||||||
+ "\"" | ||||||
+ " -o StrictHostKeyChecking=no"; | ||||||
} | ||||||
} | ||||||
|
||||||
protected final class SSHScriptFile extends AbstractOnDiskBinding<SSHUserPrivateKey> { | ||||||
|
||||||
private final String sshExePath; | ||||||
private final boolean unixNodeType; | ||||||
|
||||||
protected SSHScriptFile(SSHUserPrivateKey credentials, String sshExePath, boolean unixNodeType) { | ||||||
super(SSHKeyUtils.getSinglePrivateKey(credentials) + ":" + SSHKeyUtils.getPassphraseAsString(credentials), credentials.getId()); | ||||||
this.sshExePath = sshExePath; | ||||||
this.unixNodeType = unixNodeType; | ||||||
} | ||||||
|
||||||
@Override | ||||||
protected FilePath write(SSHUserPrivateKey credentials, FilePath workspace) throws IOException, InterruptedException { | ||||||
FilePath tempFile; | ||||||
if (unixNodeType) { | ||||||
tempFile = workspace.createTempFile("gitSSHScript", ".sh"); | ||||||
tempFile.write( | ||||||
"ssh -i " | ||||||
+ getPrivateKeyFile(credentials, workspace).getRemote() | ||||||
+ " -o StrictHostKeyChecking=no $@", null); | ||||||
tempFile.chmod(0500); | ||||||
} else { | ||||||
tempFile = workspace.createTempFile("gitSSHScript", ".bat"); | ||||||
tempFile.write("@echo off\r\n" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the same reason, I think whenever a string is getting complex, it should be formatted using a String.format(). |
||||||
+ "\"" | ||||||
+ this.sshExePath | ||||||
+ "\"" | ||||||
+ " -i " | ||||||
+ "\"" | ||||||
+ getPrivateKeyFile(credentials, workspace).getRemote() | ||||||
+ "\"" | ||||||
+ " -o StrictHostKeyChecking=no", null); | ||||||
} | ||||||
return tempFile; | ||||||
} | ||||||
|
||||||
@Override | ||||||
protected Class<SSHUserPrivateKey> type() { | ||||||
return SSHUserPrivateKey.class; | ||||||
} | ||||||
} | ||||||
|
||||||
@Symbol("gitSshPrivateKey") | ||||||
@Extension | ||||||
public static final class DescriptorImpl extends BindingDescriptor<SSHUserPrivateKey> { | ||||||
|
||||||
@NonNull | ||||||
@Override | ||||||
public String getDisplayName() { | ||||||
return Messages.GitSSHPrivateKeyBinding_DisplayName(); | ||||||
} | ||||||
|
||||||
@RequirePOST | ||||||
public ListBoxModel doFillGitToolNameItems() { | ||||||
Check warningCode scanning / Jenkins Security Scan Stapler: Missing permission check
Potential missing permission check in DescriptorImpl#doFillGitToolNameItems
|
||||||
ListBoxModel items = new ListBoxModel(); | ||||||
List<GitTool> toolList = Jenkins.get().getDescriptorByType(GitSCM.DescriptorImpl.class).getGitTools(); | ||||||
for (GitTool t : toolList) { | ||||||
if (t.getClass().equals(GitTool.class)) { | ||||||
items.add(t.getName()); | ||||||
} | ||||||
} | ||||||
return items; | ||||||
} | ||||||
|
||||||
@Override | ||||||
protected Class<SSHUserPrivateKey> type() { | ||||||
return SSHUserPrivateKey.class; | ||||||
} | ||||||
|
||||||
@Override | ||||||
public boolean requiresWorkspace() { | ||||||
return true; | ||||||
} | ||||||
} | ||||||
} |
Uh oh!
There was an error while loading. Please reload this page.