diff --git a/README.md b/README.md index 797d01ab..4e1ad940 100755 --- a/README.md +++ b/README.md @@ -748,6 +748,12 @@ public class ApplicationTest {} ## Release notes +### 1.5.4 + +* `authorizedPublicKeysFile` becomes `authorizedPublicKeys` and is now a spring resource ; you can now use : + * `ssh.shell.authorized-public-keys=` (`file:`, `classpath:`, etc) + * `ssh.shell.authorized-public-keys-file=` + ### 1.5.3 * Rewrite script command to be usable in background with result file (options added to default command) diff --git a/lombok.config b/lombok.config index b4f81252..cfdfa030 100644 --- a/lombok.config +++ b/lombok.config @@ -1 +1,2 @@ -lombok.log.fieldName=LOGGER \ No newline at end of file +lombok.log.fieldName=LOGGER +lombok.equalsandhashcode.callsuper=CALL \ No newline at end of file diff --git a/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicApplication.java b/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicApplication.java index b56fd267..028f5765 100644 --- a/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicApplication.java +++ b/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicApplication.java @@ -26,6 +26,11 @@ @SpringBootApplication public class BasicApplication { + /** + * Start basic application + * + * @param args main args + */ public static void main(String[] args) { new SpringApplicationBuilder(BasicApplication.class).bannerMode(Banner.Mode.OFF).run(args); } diff --git a/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/DemoCommand.java b/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/DemoCommand.java index b1711b79..5be8345b 100755 --- a/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/DemoCommand.java +++ b/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/DemoCommand.java @@ -44,6 +44,11 @@ public class DemoCommand { private final SshShellHelper helper; + /** + * Default constructor + * + * @param helper ssh shell helper + */ public DemoCommand(SshShellHelper helper) { this.helper = helper; } diff --git a/samples/basic/src/main/resources/application.yml b/samples/basic/src/main/resources/application.yml index b11fd67a..9fc1356e 100755 --- a/samples/basic/src/main/resources/application.yml +++ b/samples/basic/src/main/resources/application.yml @@ -4,7 +4,7 @@ ssh: color: cyan text: 'basic::>' password: password - authorized-public-keys-file: samples/public-keys-sample + authorized-public-keys-file: samples/complete/src/main/resources/.ssh/authorized.keys commands: jmx: create: false diff --git a/samples/complete/pom.xml b/samples/complete/pom.xml index a62aab22..7c6e6929 100755 --- a/samples/complete/pom.xml +++ b/samples/complete/pom.xml @@ -79,6 +79,14 @@ true + + ${basedir}/src/main/resources + + *.yml + *.txt + + false + diff --git a/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteApplication.java b/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteApplication.java index 53be8581..8093e1cd 100644 --- a/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteApplication.java +++ b/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteApplication.java @@ -27,6 +27,11 @@ @EnableScheduling public class CompleteApplication { + /** + * Start complete application + * + * @param args main args + */ public static void main(String[] args) { SpringApplication.run(CompleteApplication.class, args); } diff --git a/samples/complete/src/main/resources/.ssh/authorized.keys b/samples/complete/src/main/resources/.ssh/authorized.keys new file mode 100644 index 00000000..d0a35160 --- /dev/null +++ b/samples/complete/src/main/resources/.ssh/authorized.keys @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDlkW3hZVrrOPjcswznq5WDbF8IV3iEZo4EP2A9Ofl2dqqfKhgObDQuMUQN1OebBpUCo9W13CUle1ca7AdXJY4ZU62+YJCDKy9+hSz+KWfl7kJx+StL57sv3ProC3n7gAH+uedY6pSlHr6zTjsiEyGZbaKaSShFBX0ta3j+vZ11T+bxyK3j7BFJ2YcUa4ys+gphm3by/LV6xEhr3tohnFhfO5COKrEYqYC97EjsgDhcaFtpzjOnFoYC+HDCAFCwKWJj+Puu9qNj+NlT0gX4DuVFbWEEwH2ZB+qhk5/b75pAGLikRTK9lPLrTX0MX283lAWrD84d6xKWZaqrcxoP1HBnTCaqs5XGrXN81UP2EK+3KYNcNoIwN9x5UABOc1vDfK91IyYNgheVl8NT+T7TYNNj27piXFlvSgDx5dmuqBopYri+IqS17KJXaVgRjbdAwkWHNCqqSW9RoGDxUWlYgOh7KhNlN2m4AcW9YMEd1z2WWaYQpevYG3p3GGEZ8Av5Hbk= francois@MacBook-Pro-de-Francois.local diff --git a/samples/complete/src/main/resources/application.yml b/samples/complete/src/main/resources/application.yml index 638aac1b..c9065c11 100755 --- a/samples/complete/src/main/resources/application.yml +++ b/samples/complete/src/main/resources/application.yml @@ -10,7 +10,7 @@ ssh: shell: authentication: security auth-provider-bean-name: customAuthManager - authorized-public-keys-file: samples/public-keys-sample + authorized-public-keys: classpath:.ssh/authorized.keys extended-file-provider: false commands: actuator: diff --git a/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellConfiguration.java b/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellConfiguration.java index ea873a54..3a54ee1e 100644 --- a/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellConfiguration.java +++ b/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellConfiguration.java @@ -78,18 +78,20 @@ public void stopServer() throws IOException { * @return ssh server */ @Bean - public SshServer sshServer() { + public SshServer sshServer() throws IOException { SshServer server = SshServer.setUpDefaultServer(); server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(properties.getHostKeyFile().toPath())); server.setHost(properties.getHost()); server.setPasswordAuthenticator(passwordAuthenticator); server.setPublickeyAuthenticator(RejectAllPublickeyAuthenticator.INSTANCE); - if (properties.getAuthorizedPublicKeysFile() != null) { - if (properties.getAuthorizedPublicKeysFile().exists() && properties.getAuthorizedPublicKeysFile().canRead()) { - server.setPublickeyAuthenticator(new SshShellPublicKeyAuthenticationProvider(properties.getAuthorizedPublicKeysFile())); + if (properties.getAuthorizedPublicKeys() != null) { + if (properties.getAuthorizedPublicKeys().exists()) { + server.setPublickeyAuthenticator(new SshShellPublicKeyAuthenticationProvider(properties.getAuthorizedPublicKeys().getFile())); + LOGGER.info("Using authorized public keys from : {}", + properties.getAuthorizedPublicKeys().getDescription()); } else { - LOGGER.warn("Could not read authorized public keys file [{}], public key authentication is disabled.", - properties.getAuthorizedPublicKeysFile().getAbsolutePath()); + LOGGER.warn("Could not read authorized public keys from : {}, public key authentication is disabled.", + properties.getAuthorizedPublicKeys().getDescription()); } } server.setPort(properties.getPort()); diff --git a/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellProperties.java b/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellProperties.java index 2ca29e27..9c497bad 100644 --- a/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellProperties.java +++ b/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellProperties.java @@ -20,6 +20,8 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; import org.springframework.validation.annotation.Validated; import java.io.File; @@ -78,7 +80,7 @@ public class SshShellProperties { private File hostKeyFile = new File(System.getProperty("java.io.tmpdir"), "hostKey.ser"); - private File authorizedPublicKeysFile; + private Resource authorizedPublicKeys; private File historyFile = new File(System.getProperty("java.io.tmpdir"), "sshShellHistory.log"); @@ -99,6 +101,10 @@ public enum AuthenticationType { private Commands commands = new Commands(); + public void setAuthorizedPublicKeysFile(File file) { + this.authorizedPublicKeys = new FileSystemResource(file); + } + /** * Prompt configuration */ diff --git a/starter/src/test/java/com/github/fonimus/ssh/shell/auth/SshShellPublicKeyAuthenticationProviderTest.java b/starter/src/test/java/com/github/fonimus/ssh/shell/auth/SshShellPublicKeyAuthenticationProviderTest.java new file mode 100644 index 00000000..14447cec --- /dev/null +++ b/starter/src/test/java/com/github/fonimus/ssh/shell/auth/SshShellPublicKeyAuthenticationProviderTest.java @@ -0,0 +1,73 @@ +package com.github.fonimus.ssh.shell.auth; + +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.server.session.ServerSession; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class SshShellPublicKeyAuthenticationProviderTest { + + private static PublicKey pub; + private static PublicKey wrongPub; + + private SshShellPublicKeyAuthenticationProvider pubKeyAuthProv; + + @BeforeAll + public static void init() throws Exception { + KeyFactory kf = KeyFactory.getInstance("RSA"); + pub = kf.generatePublic(new X509EncodedKeySpec(Files.readAllBytes(Paths.get("src/test/resources/.ssh/pub.der")))); + wrongPub = kf.generatePublic(new X509EncodedKeySpec(Files.readAllBytes(Paths.get("src/test/resources/.ssh/wrong_pub.der")))); + } + + @Test + public void testFile() throws Exception { + File file = new File("src/test/resources/.ssh/authorized.keys"); + assertTrue(file.exists()); + internalTest(file); + } + + @Test + public void testSpringFileResource() throws Exception { + FileSystemResource resource = new FileSystemResource("src/test/resources/.ssh/authorized.keys"); + assertTrue(resource.exists()); + internalTest(resource.getFile()); + } + + @Test + public void testSpringClasspathResource() throws Exception { + ClassPathResource resource = new ClassPathResource(".ssh/authorized.keys"); + assertTrue(resource.exists()); + internalTest(resource.getFile()); + } + + @Test + public void testNotExisting() throws Exception { + pubKeyAuthProv = new SshShellPublicKeyAuthenticationProvider(new File("not-existing")); + assertFalse(pubKeyAuthProv.exists()); + assertEquals(-1, pubKeyAuthProv.size()); + } + + private void internalTest(File file) throws Exception { + pubKeyAuthProv = new SshShellPublicKeyAuthenticationProvider(file); + assertTrue(pubKeyAuthProv.exists()); + ServerSession session = mock(ServerSession.class); + IoSession io = mock(IoSession.class); + when(session.getIoSession()).thenReturn(io); + assertTrue(pubKeyAuthProv.authenticate("user", pub, session)); + assertFalse(pubKeyAuthProv.authenticate("user", wrongPub, session)); + } + +} \ No newline at end of file diff --git a/starter/src/test/resources/.ssh/authorized.keys b/starter/src/test/resources/.ssh/authorized.keys new file mode 100644 index 00000000..d0a35160 --- /dev/null +++ b/starter/src/test/resources/.ssh/authorized.keys @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDlkW3hZVrrOPjcswznq5WDbF8IV3iEZo4EP2A9Ofl2dqqfKhgObDQuMUQN1OebBpUCo9W13CUle1ca7AdXJY4ZU62+YJCDKy9+hSz+KWfl7kJx+StL57sv3ProC3n7gAH+uedY6pSlHr6zTjsiEyGZbaKaSShFBX0ta3j+vZ11T+bxyK3j7BFJ2YcUa4ys+gphm3by/LV6xEhr3tohnFhfO5COKrEYqYC97EjsgDhcaFtpzjOnFoYC+HDCAFCwKWJj+Puu9qNj+NlT0gX4DuVFbWEEwH2ZB+qhk5/b75pAGLikRTK9lPLrTX0MX283lAWrD84d6xKWZaqrcxoP1HBnTCaqs5XGrXN81UP2EK+3KYNcNoIwN9x5UABOc1vDfK91IyYNgheVl8NT+T7TYNNj27piXFlvSgDx5dmuqBopYri+IqS17KJXaVgRjbdAwkWHNCqqSW9RoGDxUWlYgOh7KhNlN2m4AcW9YMEd1z2WWaYQpevYG3p3GGEZ8Av5Hbk= francois@MacBook-Pro-de-Francois.local diff --git a/starter/src/test/resources/.ssh/pub.der b/starter/src/test/resources/.ssh/pub.der new file mode 100644 index 00000000..63bccb0e Binary files /dev/null and b/starter/src/test/resources/.ssh/pub.der differ diff --git a/starter/src/test/resources/.ssh/wrong_pub.der b/starter/src/test/resources/.ssh/wrong_pub.der new file mode 100644 index 00000000..e057187b Binary files /dev/null and b/starter/src/test/resources/.ssh/wrong_pub.der differ