Skip to content

[JENKINS-69149] Set 'Known hosts file' strategy as default #882

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

Closed
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
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ It is best to disable Windows Credentials Manager when installing Git on Jenkins
== Ssh Host Key verification

Git Client plugin provides various options to verify the SSH keys presented by Git repository host servers.
By default, Git Client plugin uses "Accept first connection" strategy, which automatically adds keys for hosts that have not been seen before to `known_hosts`, and does not allow connections to previously-seen hosts with modified keys.
By default, Git Client plugin uses the "Known hosts file" strategy to verify all host keys using the known_hosts file.
Configure the host key verification strategy from "Manage Jenkins" >> "Configure Global Security" >> "Git Host Key Verification Configuration".

image::images/ssh-host-key-verification.png[Configure Ssh host key verification]
Expand Down
Binary file modified images/ssh-host-key-verification.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Launcher.LocalLauncher;
import hudson.Util;
import hudson.console.HyperlinkNote;
import hudson.model.TaskListener;
import hudson.plugins.git.Branch;
import hudson.plugins.git.GitException;
Expand Down Expand Up @@ -2697,6 +2698,10 @@ private String launchCommandIn(ArgumentListBuilder args, File workDir, EnvVars e

return stdout;
} catch (GitException | InterruptedException e) {
if (e.getMessage().contains("unsupported option \"accept-new\"")) {
listener.getLogger().println(HyperlinkNote.encodeTo("https://plugins.jenkins.io/git-client/#plugin-content-ssh-host-key-verification",
"If you are using OpenSSH < 7.6 please choose another strategy to verify ssh host key in 'Manage Jenkins' -> 'Configure Global Security' -> 'Git Host Key Verification Configuration'"));
}
throw e;
} catch (Throwable e) {
reportFailureClues();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Main;
import hudson.model.PersistentDescriptor;
import jenkins.model.GlobalConfiguration;
import jenkins.model.GlobalConfigurationCategory;
import org.jenkinsci.plugins.gitclient.verifier.HostKeyVerifierFactory;
import org.jenkinsci.plugins.gitclient.verifier.AcceptFirstConnectionStrategy;
import org.jenkinsci.plugins.gitclient.verifier.KnownHostsFileVerificationStrategy;
import org.jenkinsci.plugins.gitclient.verifier.NoHostKeyVerificationStrategy;
import org.jenkinsci.plugins.gitclient.verifier.SshHostKeyVerificationStrategy;

@Extension
Expand All @@ -21,7 +23,11 @@ GlobalConfigurationCategory getCategory() {
}

public SshHostKeyVerificationStrategy<? extends HostKeyVerifierFactory> getSshHostKeyVerificationStrategy() {
return sshHostKeyVerificationStrategy != null ? sshHostKeyVerificationStrategy : new AcceptFirstConnectionStrategy();
return sshHostKeyVerificationStrategy != null ? sshHostKeyVerificationStrategy : getDefaultStrategy();
}

private SshHostKeyVerificationStrategy<?> getDefaultStrategy() {
return Main.isUnitTest ? new NoHostKeyVerificationStrategy() : new KnownHostsFileVerificationStrategy();
}

public void setSshHostKeyVerificationStrategy(SshHostKeyVerificationStrategy<? extends HostKeyVerifierFactory> sshHostKeyVerificationStrategy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.trilead.ssh2.Connection;
import com.trilead.ssh2.KnownHosts;
import hudson.console.HyperlinkNote;
import hudson.model.TaskListener;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.logging.Level;
Expand All @@ -17,6 +19,9 @@ public class KnownHostsFileVerifier extends HostKeyVerifierFactory {
public AbstractCliGitHostKeyVerifier forCliGit(TaskListener listener) {
return tempKnownHosts -> {
listener.getLogger().println("Verifying host key using known hosts file");
if (Files.notExists(new File(SshHostKeyVerificationStrategy.KNOWN_HOSTS_DEFAULT).toPath())) {
logHint(listener);
}
return "-o StrictHostKeyChecking=yes";
};
}
Expand All @@ -25,7 +30,12 @@ public AbstractCliGitHostKeyVerifier forCliGit(TaskListener listener) {
public AbstractJGitHostKeyVerifier forJGit(TaskListener listener) {
KnownHosts knownHosts;
try {
knownHosts = Files.exists(getKnownHostsFile().toPath()) ? new KnownHosts(getKnownHostsFile()) : new KnownHosts();
if (Files.exists(getKnownHostsFile().toPath())) {
knownHosts = new KnownHosts(getKnownHostsFile());
} else {
logHint(listener);
knownHosts = new KnownHosts();
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, e, () -> "Could not load known hosts.");
knownHosts = new KnownHosts();
Expand Down Expand Up @@ -54,4 +64,11 @@ public boolean verifyServerHostKey(String hostname, int port, String serverHostK
}
}

private void logHint(TaskListener listener) {
listener.getLogger().println(HyperlinkNote.encodeTo("https://plugins.jenkins.io/git-client/#plugin-content-ssh-host-key-verification","You're using 'Known hosts file' strategy to verify ssh host keys," +
" but your known_hosts file does not exist, please go to " +
"'Manage Jenkins' -> 'Configure Global Security' -> 'Git Host Key Verification Configuration' " +
"and configure host key verification."));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public abstract class SshHostKeyVerificationStrategy<T extends HostKeyVerifierFactory> extends AbstractDescribableImpl<SshHostKeyVerificationStrategy<T>> implements ExtensionPoint {

private static final String KNOWN_HOSTS_DEFAULT = Paths.get(System.getProperty("user.home"), ".ssh", "known_hosts").toString();
public static final String KNOWN_HOSTS_DEFAULT = Paths.get(System.getProperty("user.home"), ".ssh", "known_hosts").toString();
private static final String JGIT_KNOWN_HOSTS_PROPERTY = SshHostKeyVerificationStrategy.class.getName() + ".jgit_known_hosts_file";
private static final String JGIT_KNOWN_HOSTS_FILE_PATH = StringUtils.defaultIfBlank(System.getProperty(JGIT_KNOWN_HOSTS_PROPERTY), KNOWN_HOSTS_DEFAULT);
public static final File JGIT_KNOWN_HOSTS_FILE = new File(JGIT_KNOWN_HOSTS_FILE_PATH);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<p>Controls how Git plugin verifies the keys presented by the host during SSH connecting.
<dl>
<dt>Accept first connection (default)</dt>
<dt>Known hosts file (default)</dt>
<dd>Verifies all host keys using the <code>known_hosts</code> file.</dd>
<dt>Accept first connection</dt>
<dd>Automatically adds host keys to the <code>known_hosts</code> file if the host has not been seen before, and does not allow connections to previously-seen hosts with modified keys.</dd>
<dd> - Note that when using ephemeral agents (ex. cloud agents), this strategy is essentially equivalent to <strong>No verification</strong> because it uses the <code>known_hosts</code> file on the agent. To avoid this, you can pre-configure <code>known_hosts</code> with all relevant hosts when creating the images or templates used to define your agents, or use the <strong>Manually provided keys</strong> or <strong>Known hosts file</strong> strategies.</dd>
<dd> - OpenSSH version 7.6 or higher is required to use this option with command line Git.</dd>
<dt>Known hosts file</dt>
<dd>Verifies all host keys using the <code>known_hosts</code> file.</dd>
<dt>Manually provided keys</dt>
<dd>Verifies all host keys using a set of keys manually configured here.</dd>
<dt>No verification (not recommended)</dt>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,22 @@ public class GitHostKeyVerificationConfigurationTest {
@Rule
public RestartableJenkinsRule r = new RestartableJenkinsRule();

private final GitHostKeyVerificationConfiguration gitHostKeyVerificationConfiguration;

public GitHostKeyVerificationConfigurationTest() {
gitHostKeyVerificationConfiguration = new GitHostKeyVerificationConfiguration();
}

@Test
public void testGetSshHostKeyVerificationStrategyInitiallyAcceptFirst() {
assertThat(gitHostKeyVerificationConfiguration.getSshHostKeyVerificationStrategy(), instanceOf(AcceptFirstConnectionStrategy.class));
}

@Test
public void testGitHostKeyVerificationConfigurationSavedBetweenSessions() throws Exception {
public void testGitHostKeyVerificationConfigurationSavedBetweenSessions() {
String hostKey = "github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
ManuallyProvidedKeyVerificationStrategy manuallyProvidedKeyVerificationStrategy = new ManuallyProvidedKeyVerificationStrategy(hostKey);
r.then(step -> {
assertThat(GitHostKeyVerificationConfiguration.get().getSshHostKeyVerificationStrategy(), instanceOf(AcceptFirstConnectionStrategy.class));
assertThat(GitHostKeyVerificationConfiguration.get().getSshHostKeyVerificationStrategy(), instanceOf(NoHostKeyVerificationStrategy.class));
GitHostKeyVerificationConfiguration.get().setSshHostKeyVerificationStrategy(manuallyProvidedKeyVerificationStrategy);
});

r.then(step -> {
assertThat(GitHostKeyVerificationConfiguration.get().getSshHostKeyVerificationStrategy(), is(manuallyProvidedKeyVerificationStrategy));
GitHostKeyVerificationConfiguration.get().setSshHostKeyVerificationStrategy(new NoHostKeyVerificationStrategy());
GitHostKeyVerificationConfiguration.get().setSshHostKeyVerificationStrategy(new AcceptFirstConnectionStrategy());
});

r.then(step -> {
assertThat(GitHostKeyVerificationConfiguration.get().getSshHostKeyVerificationStrategy(), instanceOf(NoHostKeyVerificationStrategy.class));
assertThat(GitHostKeyVerificationConfiguration.get().getSshHostKeyVerificationStrategy(), instanceOf(AcceptFirstConnectionStrategy.class));
GitHostKeyVerificationConfiguration.get().setSshHostKeyVerificationStrategy(new KnownHostsFileVerificationStrategy());
});

Expand Down