From 645812e4f88e8ac2d4163d0372694ec0696e20a5 Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Wed, 30 Aug 2023 17:36:08 +0200 Subject: [PATCH 1/3] feature: add host key information for better host key verification --- .../gitserver/GitServerContainer.java | 16 +++++ .../testcontainers/gitserver/SshHostKey.java | 62 +++++++++++++++++++ .../gitserver/GitServerContainerTest.java | 53 ++++++++++++++-- 3 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java diff --git a/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java b/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java index 3668a2d..5ed1cc7 100644 --- a/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java +++ b/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.net.URI; +import java.util.Base64; /** * Container for a plain Git Server based on the Docker image "rockstorm/git-server". @@ -18,6 +19,7 @@ public class GitServerContainer extends GenericContainer { private static DockerImageName DEFAULT_DOCKER_IMAGE_NAME = DockerImageName.parse("rockstorm/git-server"); private String gitRepoName = "testRepo"; private SshIdentity sshClientIdentity; + private SshHostKey hostKey; /** * @@ -100,6 +102,11 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { execInContainer("mkdir", "-p", gitRepoPath); execInContainer("git", "init", "--bare", gitRepoPath); execInContainer("chown", "-R", "git:git", "/srv"); + + ExecResult result = execInContainer("cat", "/etc/ssh/ssh_host_ecdsa_key.pub"); + String[] catResult = result.getStdout().split(" "); + hostKey = new SshHostKey("localhost", Base64.getDecoder().decode(catResult[1])); + } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } @@ -127,4 +134,13 @@ public String getGitPassword() { public SshIdentity getSshClientIdentity() { return sshClientIdentity; } + + /** + * Return the public host key information. + * + * @return public host key + */ + public SshHostKey getHostKey() { + return hostKey; + } } diff --git a/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java b/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java new file mode 100644 index 0000000..1fe3156 --- /dev/null +++ b/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java @@ -0,0 +1,62 @@ +package com.github.sparsick.testcontainers.gitserver; + +import java.util.Objects; + + +/** + * Value object for SSH Host key information. + */ +public class SshHostKey { + + private String hostname; + private byte[] key; + + /** + * SSH Host Key information + * @param hostname host + * @param key keystring + */ + public SshHostKey(String hostname, byte[] key) { + this.key = key; + this.hostname = hostname; + } + + /** + * Public key of the host key. + * + * @return key string + */ + public byte[] getKey() { + return key; + } + + /** + * Name of the host + * + * @return name of the host + */ + public String getHostname() { + return hostname; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SshHostKey)) return false; + SshHostKey hostKey = (SshHostKey) o; + return Objects.equals(key, hostKey.key) && Objects.equals(hostname, hostKey.hostname); + } + + @Override + public int hashCode() { + return Objects.hash(key, hostname); + } + + @Override + public String toString() { + return "HostKey{" + + "key='" + key + '\'' + + ", hostname='" + hostname + '\'' + + '}'; + } +} diff --git a/src/test/java/com/github/sparsick/testcontainers/gitserver/GitServerContainerTest.java b/src/test/java/com/github/sparsick/testcontainers/gitserver/GitServerContainerTest.java index d939767..6b0847c 100644 --- a/src/test/java/com/github/sparsick/testcontainers/gitserver/GitServerContainerTest.java +++ b/src/test/java/com/github/sparsick/testcontainers/gitserver/GitServerContainerTest.java @@ -1,5 +1,6 @@ package com.github.sparsick.testcontainers.gitserver; +import com.jcraft.jsch.HostKey; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; @@ -138,11 +139,7 @@ void pubKeyAuth(GitServerVersions gitServer) { @Override protected JSch createDefaultJSch(FS fs ) throws JSchException { JSch defaultJSch = super.createDefaultJSch( fs ); - SshIdentity sshIdentity = containerUnderTest.getSshClientIdentity(); - byte[] privateKey = sshIdentity.getPrivateKey(); - byte[] publicKey = sshIdentity.getPublicKey(); - byte[] passphrase = sshIdentity.getPassphrase(); - defaultJSch.addIdentity("git-server", privateKey, publicKey, passphrase); + configureSshIdentity(defaultJSch, containerUnderTest); return defaultJSch; } @Override @@ -154,4 +151,50 @@ protected void configure(OpenSshConfig.Host hc, Session session) { .call() ); } + + @ParameterizedTest + @EnumSource(GitServerVersions.class) + void strictHostKeyVerifivation(GitServerVersions gitServer) { + var containerUnderTest = new GitServerContainer(gitServer.getDockerImageName()).withSshKeyAuth(); + + containerUnderTest.start(); + + URI gitRepoURI = containerUnderTest.getGitRepoURIAsSSH(); + + assertThatNoException().isThrownBy(() -> + Git.cloneRepository() + .setURI(gitRepoURI.toString()) + .setDirectory(tempDir) + .setBranch("main") + .setTransportConfigCallback(transport -> { + var sshTransport = (SshTransport) transport; + sshTransport.setSshSessionFactory(new JschConfigSessionFactory() { + + @Override + protected JSch createDefaultJSch(FS fs ) throws JSchException { + JSch defaultJSch = super.createDefaultJSch( fs ); + configureSshIdentity(defaultJSch, containerUnderTest); + configureHostKeyRepository(defaultJSch, containerUnderTest); + return defaultJSch; + } + }); + }) + .call() + ); + } + + private void configureSshIdentity(JSch defaultJSch, GitServerContainer containerUnderTest) throws JSchException { + SshIdentity sshIdentity = containerUnderTest.getSshClientIdentity(); + byte[] privateKey = sshIdentity.getPrivateKey(); + byte[] publicKey = sshIdentity.getPublicKey(); + byte[] passphrase = sshIdentity.getPassphrase(); + defaultJSch.addIdentity("git-server", privateKey, publicKey, passphrase); + } + + private void configureHostKeyRepository(JSch defaultJSch, GitServerContainer containerUnderTest) throws JSchException { + SshHostKey hostKey = containerUnderTest.getHostKey(); + String host = hostKey.getHostname(); + byte[] key = hostKey.getKey(); + defaultJSch.getHostKeyRepository().add(new HostKey(host, key), null); + } } From 6909825211887b20945d8ee1e0cbbc283a63ebcf Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Wed, 30 Aug 2023 17:42:51 +0200 Subject: [PATCH 2/3] fix: ci build - replace localhost by getHost() --- .../sparsick/testcontainers/gitserver/GitServerContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java b/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java index 5ed1cc7..daffb12 100644 --- a/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java +++ b/src/main/java/com/github/sparsick/testcontainers/gitserver/GitServerContainer.java @@ -105,7 +105,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { ExecResult result = execInContainer("cat", "/etc/ssh/ssh_host_ecdsa_key.pub"); String[] catResult = result.getStdout().split(" "); - hostKey = new SshHostKey("localhost", Base64.getDecoder().decode(catResult[1])); + hostKey = new SshHostKey(getHost(), Base64.getDecoder().decode(catResult[1])); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); From 47d00549814dfc66a83813ff0d8258a9c82aec0f Mon Sep 17 00:00:00 2001 From: Sandra Parsick Date: Wed, 30 Aug 2023 17:49:19 +0200 Subject: [PATCH 3/3] chore: remove unused code --- .../testcontainers/gitserver/SshHostKey.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java b/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java index 1fe3156..a2c1812 100644 --- a/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java +++ b/src/main/java/com/github/sparsick/testcontainers/gitserver/SshHostKey.java @@ -38,25 +38,4 @@ public byte[] getKey() { public String getHostname() { return hostname; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SshHostKey)) return false; - SshHostKey hostKey = (SshHostKey) o; - return Objects.equals(key, hostKey.key) && Objects.equals(hostname, hostKey.hostname); - } - - @Override - public int hashCode() { - return Objects.hash(key, hostname); - } - - @Override - public String toString() { - return "HostKey{" + - "key='" + key + '\'' + - ", hostname='" + hostname + '\'' + - '}'; - } }