-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GH-445: strict KEX interoperability tests
Run an Apache MINA sshd client against OpenSSH servers that do have or do not have strict KEX.
- Loading branch information
Showing
6 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
192 changes: 192 additions & 0 deletions
192
...ore/src/test/java/org/apache/sshd/common/kex/extension/StrictKexInteroperabilityTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package org.apache.sshd.common.kex.extension; | ||
|
||
import java.io.PipedInputStream; | ||
import java.io.PipedOutputStream; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import org.apache.sshd.client.ClientFactoryManager; | ||
import org.apache.sshd.client.SshClient; | ||
import org.apache.sshd.client.channel.ChannelShell; | ||
import org.apache.sshd.client.session.ClientSession; | ||
import org.apache.sshd.client.session.ClientSessionImpl; | ||
import org.apache.sshd.client.session.SessionFactory; | ||
import org.apache.sshd.common.channel.StreamingChannel; | ||
import org.apache.sshd.common.io.IoSession; | ||
import org.apache.sshd.common.keyprovider.FileKeyPairProvider; | ||
import org.apache.sshd.util.test.BaseTestSupport; | ||
import org.apache.sshd.util.test.CommonTestSupportUtils; | ||
import org.apache.sshd.util.test.ContainerTestCase; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.experimental.categories.Category; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.testcontainers.containers.GenericContainer; | ||
import org.testcontainers.containers.output.Slf4jLogConsumer; | ||
import org.testcontainers.containers.wait.strategy.Wait; | ||
import org.testcontainers.images.builder.ImageFromDockerfile; | ||
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; | ||
import org.testcontainers.utility.MountableFile; | ||
|
||
/** | ||
* Tests to ensure that an Apache MINA sshd client can talk to OpenSSH servers with or without "strict KEX". This | ||
* implicitly tests the message sequence number handling; if sequence numbers get out of sync or are reset wrongly, | ||
* subsequent messages cannot be decrypted correctly and there will be exceptions. | ||
* | ||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a> | ||
* @see <A HREF="https://github.com/apache/mina-sshd/issues/445">Terrapin Mitigation: "strict-kex"</A> | ||
*/ | ||
@Category(ContainerTestCase.class) | ||
public class StrictKexInteroperabilityTest extends BaseTestSupport { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(StrictKexInteroperabilityTest.class); | ||
|
||
private static final String TEST_RESOURCES = "org/apache/sshd/common/kex/extensions/client"; | ||
|
||
private SshClient client; | ||
|
||
public StrictKexInteroperabilityTest() { | ||
super(); | ||
} | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
client = setupTestClient(); | ||
SessionFactory factory = new TestSessionFactory(client); | ||
client.setSessionFactory(factory); | ||
} | ||
|
||
@After | ||
public void tearDown() throws Exception { | ||
if (client != null) { | ||
client.stop(); | ||
} | ||
} | ||
|
||
private DockerfileBuilder strictKexImage(DockerfileBuilder builder, boolean withStrictKex) { | ||
if (!withStrictKex) { | ||
return builder | ||
// CentOS 7 is EOL and thus unlikely to get the security update for strict KEX. | ||
.from("centos:7.9.2009") // | ||
.run("yum install -y openssh-server") // Installs OpenSSH 7.4 | ||
.run("/usr/sbin/sshd-keygen") // Generate multiple host keys | ||
.run("adduser bob"); // Add a user | ||
} else { | ||
return builder | ||
.from("alpine:20231219") // | ||
.run("apk --update add openssh-server") // Installs OpenSSH 9.6 | ||
.run("ssh-keygen -A") // Generate multiple host keys | ||
.run("adduser -D bob") // Add a user | ||
.run("echo 'bob:passwordBob' | chpasswd"); // Give it a password to unlock the user | ||
} | ||
} | ||
|
||
@Test | ||
public void testStrictKexOff() throws Exception { | ||
testStrictKex(false); | ||
} | ||
|
||
@Test | ||
public void testStrictKexOn() throws Exception { | ||
testStrictKex(true); | ||
} | ||
|
||
private void testStrictKex(boolean withStrictKex) throws Exception { | ||
// This tests that the message sequence numbers are handled correctly. Strict KEX resets them to zero on any | ||
// KEX, without strict KEX, they're not reset. If sequence numbers get out of sync, received messages are | ||
// decrypted wrongly and there will be exceptions. | ||
@SuppressWarnings("resource") | ||
GenericContainer<?> sshdContainer = new GenericContainer<>(new ImageFromDockerfile() | ||
.withDockerfileFromBuilder(builder -> strictKexImage(builder, withStrictKex) // | ||
.run("mkdir -p /home/bob/.ssh") // Create the SSH config directory | ||
.entryPoint("/entrypoint.sh") // | ||
.build())) // | ||
.withCopyFileToContainer(MountableFile.forClasspathResource(TEST_RESOURCES + "/bob_key.pub"), | ||
"/home/bob/.ssh/authorized_keys") | ||
// entrypoint must be executable. Spotbugs doesn't like 0777, so use hex | ||
.withCopyFileToContainer( | ||
MountableFile.forClasspathResource(TEST_RESOURCES + "/entrypoint.sh", 0x1ff), | ||
"/entrypoint.sh") | ||
.waitingFor(Wait.forLogMessage(".*Server listening on :: port 22.*\\n", 1)) // | ||
.withExposedPorts(22) // | ||
.withLogConsumer(new Slf4jLogConsumer(LOG)); | ||
sshdContainer.start(); | ||
try { | ||
FileKeyPairProvider keyPairProvider = CommonTestSupportUtils.createTestKeyPairProvider(TEST_RESOURCES + "/bob_key"); | ||
client.setKeyIdentityProvider(keyPairProvider); | ||
client.start(); | ||
try (ClientSession session = client.connect("bob", sshdContainer.getHost(), sshdContainer.getMappedPort(22)) | ||
.verify(CONNECT_TIMEOUT).getSession()) { | ||
session.auth().verify(AUTH_TIMEOUT); | ||
assertTrue("Should authenticate", session.isAuthenticated()); | ||
assertTrue("Unexpected session type " + session.getClass().getName(), session instanceof TestSession); | ||
assertEquals("Unexpected strict KEX usage", withStrictKex, ((TestSession) session).usesStrictKex()); | ||
try (ChannelShell channel = session.createShellChannel()) { | ||
channel.setOut(System.out); | ||
channel.setErr(System.err); | ||
channel.setStreaming(StreamingChannel.Streaming.Sync); | ||
PipedOutputStream pos = new PipedOutputStream(); | ||
PipedInputStream pis = new PipedInputStream(pos); | ||
channel.setIn(pis); | ||
assertTrue("Could not open session", channel.open().await(DEFAULT_TIMEOUT)); | ||
LOG.info("writing some data..."); | ||
pos.write("\n\n".getBytes(StandardCharsets.UTF_8)); | ||
assertTrue("Channel should be open", channel.isOpen()); | ||
assertTrue(session.reExchangeKeys().verify(CONNECT_TIMEOUT).isDone()); | ||
assertTrue("Channel should be open", channel.isOpen()); | ||
LOG.info("writing some data..."); | ||
pos.write("\n\n".getBytes(StandardCharsets.UTF_8)); | ||
assertTrue("Channel should be open", channel.isOpen()); | ||
channel.close(true); | ||
} | ||
} | ||
} finally { | ||
sshdContainer.stop(); | ||
} | ||
} | ||
|
||
// Subclass ClientSessionImpl to get access to the strictKex flag. | ||
|
||
private static class TestSessionFactory extends SessionFactory { | ||
|
||
TestSessionFactory(ClientFactoryManager client) { | ||
super(client); | ||
} | ||
|
||
@Override | ||
protected ClientSessionImpl doCreateSession(IoSession ioSession) throws Exception { | ||
return new TestSession(getClient(), ioSession); | ||
} | ||
} | ||
|
||
private static class TestSession extends ClientSessionImpl { | ||
|
||
TestSession(ClientFactoryManager client, IoSession ioSession) throws Exception { | ||
super(client, ioSession); | ||
} | ||
|
||
boolean usesStrictKex() { | ||
return strictKex; | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
sshd-core/src/test/resources/org/apache/sshd/common/kex/extensions/client/bob_key
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
MIIEpAIBAAKCAQEAxY3Hr1SqpJIQ9SbFfGMGweVy8jg2TEH3GC1K0LudQHJwogRi | ||
+debdCqUtuSITbpPhjkeZSk9rq198d6RhT6TQmY9J8wLL2/+VXZk/rMVEEjeXQS3 | ||
ImRnL2vVmkAunv6LwfDGHIovkhwj3/lqGWphDAKnHyXusPDwQ3N4LFGgxwXvRGqc | ||
lzmP8H+KDWaaPapk1AZCBIoD4JbL8faBtLNU01r+pB3sIKvfsPJ5DxPErThfrPuD | ||
qIbA3axEqFlgX4aVl3yMnSWjfhLhO7xD3YwrtUhannHt8pZQo5FkwCGWDpkG3xs+ | ||
qK3ZACrhMFMTvPuDS83jDtEzNd5KYb4KnkOPMQIDAQABAoIBAQCE5GktgrD/39pU | ||
b25tzFehW25FjpbIGZ/UvbMUUwDnd5RZCMZj9yv1qyc7GOSwFOKmEgpmVqXNuZt9 | ||
dxFBJuT8x7Xf7Zygnp/icbBivakvuTUMMb3X/t6CwfGAwCgcgHMXVZaPYE275f4k | ||
Dq3Wxv7di3NMusGkeY/GcAipF4gmGKKe7Ck1ifRypF2cDJsgTtsoFUHNNKfnT3gf | ||
OcJsVLRl0osbsxdqU+Tep46+jHrNt8J9n2VeRNRIqGHj0CkNdpLQOs+MjvIO3Hgq | ||
9NUxwIExwaPnBpTLlWwfemCz3JQnlAineMbYBGa1tpAA3Iw56NWcNbiOPyUyffbI | ||
wBC4r1uZAoGBAPESsergFD+ontChEI+h38oM/D9DKCObZR2kz6WArZ54i1dJWOgh | ||
HCsuxgPjxmaddPKghfNhUORdZBynuS5G7n6BfItNilDiFm2KBk12d38OVovUFo1Q | ||
r5akclKf0kFxHt5TzHIrNAv7B4OF0Uk3kuDHM7ITX3qDpTSBLlzPAUUHAoGBANHJ | ||
QIPmuF2q+PXnnSgdEyiETfl/IqUTXQyxda8kRIPJKKHZKPHZePhgJKUq9VP32PrP | ||
AxIBNrS3Netsp+EAApj09hmWUcgJRIU1/wjpVGqUmguYgh8nVFOPDudOJD5ltQ/A | ||
enzQ19IkGroaQB8CBGZsPaBAvqRZ5PLbm+BZEPQHAoGAblaMMGCXY/udlQfjOJpy | ||
f1wqKBpoyMNbKJJCqBGZZaruu+jKVJSy++DQqP8b0+PFnzdxl8+24o8MP0FVNKUq | ||
i6RgiLHY2ORiN4ixEctjLjg1zJIqMEv50g06di7IYUORSVk5fhfgHourCLu66rQQ | ||
+eiy9JKBZOXUO4/U1I26mwkCgYAhfuCuLsiBLCtUGAcfwISuk3FfxMzjTpQs0qjX | ||
rhLCd/vk26eN9gs6nR88v/8ryQb8BNGYrljtwdL6I/8qDbZcdcBVlYq5RcGLA3QV | ||
GCxCWDfAYjlkgAMW1GCsze07iUG/ohvskevjwaAC1u4mBUxujhnI3I2T8EZ+AFKD | ||
H7V1QQKBgQDNt+zjSdLtA9AczxDwWmi5SbS+k+nGbi6AQO9i73wky/wxx7FonfWS | ||
2skkOUIst3HBc0Oz+CJTfNFQK6GVqtzTdlZFhMYS0ua1Djd6q6S648+K0cieY4r5 | ||
5irivHYVN8t7lBcvbA7E7yD6dHXSHsn6yOLTrV382qRfJTbxG7ZVWA== | ||
-----END RSA PRIVATE KEY----- |
1 change: 1 addition & 0 deletions
1
sshd-core/src/test/resources/org/apache/sshd/common/kex/extensions/client/bob_key.pub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFjcevVKqkkhD1JsV8YwbB5XLyODZMQfcYLUrQu51AcnCiBGL515t0KpS25IhNuk+GOR5lKT2urX3x3pGFPpNCZj0nzAsvb/5VdmT+sxUQSN5dBLciZGcva9WaQC6e/ovB8MYcii+SHCPf+WoZamEMAqcfJe6w8PBDc3gsUaDHBe9EapyXOY/wf4oNZpo9qmTUBkIEigPglsvx9oG0s1TTWv6kHewgq9+w8nkPE8StOF+s+4OohsDdrESoWWBfhpWXfIydJaN+EuE7vEPdjCu1SFqece3yllCjkWTAIZYOmQbfGz6ordkAKuEwUxO8+4NLzeMO0TM13kphvgqeQ48x user01 |
6 changes: 6 additions & 0 deletions
6
sshd-core/src/test/resources/org/apache/sshd/common/kex/extensions/client/entrypoint.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/sh | ||
|
||
chown -R bob /home/bob | ||
chmod 0600 /home/bob/.ssh/* | ||
|
||
/usr/sbin/sshd -D -ddd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters