diff --git a/README.md b/README.md index ba89551b..4318d08f 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,9 @@ It support the [Common Options](#CommonOptions) and the following options: | [bcgov.access.input.sftp.max-message-per-poll](#bcgovaccessinputsftpmaxmesssageperpoll) | String | No | | [bcgov.access.input.sftp.ssh-private-key](#bcgovaccessinputsftpsshprivatekey) | Resource | No | | [bcgov.access.input.sftp.ssh-private-passphrase](#bcgovaccessinputsftpsshprivatepassphrase) | String | No | +| [bcgov.access.input.sftp.allow-unknown-key](#bcgovaccessinputsftpallowunknownkey) | boolean | No | +| [bcgov.access.input.sftp.known-host-file](#bcgovaccessinputsftpknownhostfile) | String | Yes (if allow-unknown-key is false) | + ##### bcgov.access.input.sftp.host @@ -259,6 +262,19 @@ Sets the location of the private key. Sets the passphrase for the private key. +##### bcgov.access.input.sftp.allow-unknown-key + +* Value type is Boolean +* Default value is false +* When no UserInfo has been provided, set to true to unconditionally allow connecting to an unknown host or when a host's key has changed (see knownHosts) + +##### bcgov.access.input.sftp.known-host-file + +* Value type is String +* Specifies the filename that will be used for a host key repository. The file has the same format as OpenSSH's known_hosts file. +* If allow-unknown-key is false, this property must be set correctly, or KnownHostFileNotDefinedException or KnownHostFileNotFoundException will be thrown. +* If allow-unknown-key is true, this property will be ignored. + ## Output Plugins You can configure the document input using `bcgov.access.output` property. @@ -449,22 +465,33 @@ To view the message in a queue, login to [rabbitmq management console](http://lo ![binding](docs/document.ready.bind.png) ####if you want to run the sample app using sftp do the following: -Create a sftp server container + +step 1. Create a sftp server container (from WindowsPowerShell or GitBash) ```bash docker run -p 22:22 -d atmoz/sftp myname:pass:::upload ``` -User "myname" with password "pass" can login with sftp and upload files to a folder called "upload". We are forwarding the container's port 22 to the host's port 22. -Use a Sftp Client application ( such as Fillzilla, WinSCP, coreFTP) to connect to the server.(use sftp protocal and ip: localhost, port:22) -update the [application.yml](jrcc-access-spring-boot-sample-app/src/main/resources/application.yml) + +step 2. User "myname" with password "pass" can login with sftp and upload files to a folder called "upload". We are forwarding the container's port 22 to the host's port 22. + +step 3. Use a Sftp Client application ( such as Filezilla, WinSCP, coreFTP) to connect to the server.(use sftp protocal and ip: localhost, port:22) + +step 4. If you do not want unconditionally allow connecting to an unknown host or when a host's key has changed, you need to provide known_hosts file. +User following command to generate known_hosts file for started sftp server (from WindowsPowerShell or GitBash). +```bash +ssh-keyscan -v -p 22 localhost>>known_hosts +``` +step 5. Update the [application.yml](jrcc-access-spring-boot-sample-app/src/main/resources/application.yml) ```properties -spring: - main: - web-application-type: none +main: + web-application-type: none logging: level: ca: gov: bc: DEBUG + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %X{transaction.filename} - %msg%n" + file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %X{transaction.filename} - %msg%n" bcgov: access: input: @@ -478,6 +505,8 @@ bcgov: remote-directory: /upload max-message-per-poll: 5 cron: 0/5 * * * * * + allow-unknown-keys: false + known-host-file: C:\Users\user\.ssh\known_hosts output: document-type: test-doc plugin: console diff --git a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotDefinedException.java b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotDefinedException.java new file mode 100644 index 00000000..c71909d2 --- /dev/null +++ b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotDefinedException.java @@ -0,0 +1,16 @@ +package ca.bc.gov.open.jrccaccess.autoconfigure.config.exceptions; + +/** + * An exception which is thrown when using sftp known_hosts file is not defined in configuration while allowUnknownKey is set to false. + * + */ +public class KnownHostFileNotDefinedException extends InvalidConfigException { + /** + * Constructs a new KnownHostFileNotDefinedException with the specified detail message. + * + * @param message the detail message. The detail message is saved for later retrieval by the Throwable.getMessage() method. + */ + public KnownHostFileNotDefinedException(String message) { + super(message); + } +} diff --git a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotFoundException.java b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotFoundException.java new file mode 100644 index 00000000..5af281bd --- /dev/null +++ b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/config/exceptions/KnownHostFileNotFoundException.java @@ -0,0 +1,15 @@ +package ca.bc.gov.open.jrccaccess.autoconfigure.config.exceptions; + +/** + * An exception which is thrown when application cannot find specified known_hosts file. + */ +public class KnownHostFileNotFoundException extends InvalidConfigException{ + /** + * Constructs a new InvalidConfigException with the specified detail message. + * + * @param message the detail message. The detail message is saved for later retrieval by the Throwable.getMessage() method. + */ + public KnownHostFileNotFoundException(String message) { + super(message); + } +} diff --git a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/AutoConfiguration.java b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/AutoConfiguration.java index 81213ae7..b8522c5b 100644 --- a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/AutoConfiguration.java +++ b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/AutoConfiguration.java @@ -1,5 +1,8 @@ package ca.bc.gov.open.jrccaccess.autoconfigure.plugins.sftp; +import ca.bc.gov.open.jrccaccess.autoconfigure.config.exceptions.InvalidConfigException; +import ca.bc.gov.open.jrccaccess.autoconfigure.config.exceptions.KnownHostFileNotDefinedException; +import ca.bc.gov.open.jrccaccess.autoconfigure.config.exceptions.KnownHostFileNotFoundException; import com.jcraft.jsch.ChannelSftp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +23,7 @@ import org.springframework.integration.sftp.session.SftpRemoteFileTemplate; import javax.websocket.MessageHandler; +import java.io.File; import java.io.InputStream; @Configuration @@ -45,7 +49,7 @@ public AutoConfiguration(SftpInputProperties sftpInputProperties) { @Bean - public SessionFactory sftpSessionFactory() { + public SessionFactory sftpSessionFactory() throws InvalidConfigException { DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true); factory.setHost(properties.getHost()); factory.setPort(properties.getPort()); @@ -56,13 +60,32 @@ public SessionFactory sftpSessionFactory() { } else { factory.setPassword(properties.getPassword()); } - factory.setAllowUnknownKeys(true); + boolean isAllowUnknownKeys = properties.isAllowUnknownKeys(); + factory.setAllowUnknownKeys(isAllowUnknownKeys); + if(!isAllowUnknownKeys){ + String knownHostFileStr = properties.getKnownHostFile(); + if(knownHostFileStr == null || knownHostFileStr == "" ) + throw new KnownHostFileNotDefinedException("Must define known_hosts file when allow-unknown-keys is false. "); + + File knownHostFile = new File(knownHostFileStr); + if( ! knownHostFile.exists() ) + throw new KnownHostFileNotFoundException("Cannot find known_hosts file when allow-unknown-keys is false."); + + factory.setKnownHosts(properties.getKnownHostFile()); + } + return new CachingSessionFactory<>(factory); } @Bean public SftpRemoteFileTemplate template() { - return new SftpRemoteFileTemplate(sftpSessionFactory()); + try { + return new SftpRemoteFileTemplate(sftpSessionFactory()); + }catch(InvalidConfigException ex) + { + logger.error(ex.getMessage()); + } + return null; } @Bean diff --git a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputProperties.java b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputProperties.java index 8cce612b..6afeca4c 100644 --- a/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputProperties.java +++ b/jrcc-access-spring-boot-autoconfigure/src/main/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputProperties.java @@ -8,7 +8,7 @@ import javax.validation.constraints.Min; /** - * Represents the rabbitmq output plugin properties + * Represents the rabbitmq input plugin properties * @author alexjoybc * @since 0.6.0 * @@ -37,6 +37,10 @@ public class SftpInputProperties { private String sshPrivatePassphrase; + private boolean allowUnknownKeys; + + private String knownHostFile; + public String getRemoteDirectory() { return remoteDirectory; } @@ -119,4 +123,34 @@ public void setSshPrivatePassphrase(String sshPrivatePassphrase) { this.sshPrivatePassphrase = sshPrivatePassphrase; } + /** + * @return if allow Unknown Keys + */ + public boolean isAllowUnknownKeys() { + return this.allowUnknownKeys; + } + + /** + * Set to true to unconditionally allow connecting to an unknown host or when a host's key has changed (see knownHosts). + * Default false. Set to true if a knownHosts file is not provided. + * @param allowUnknowKeys : true or false + */ + public void setAllowUnknownKeys(boolean allowUnknowKeys) { + this.allowUnknownKeys = allowUnknowKeys; + } + + /** + * @return the filename that will be used for a host key repository. The file has the same format as OpenSSH's known_hosts file. + */ + public String getKnownHostFile() { + return this.knownHostFile; + } + + /** + * set the known_hosts file name, including path + * @param knownHostFile + */ + public void setKnownHostFile(String knownHostFile) { + this.knownHostFile = knownHostFile; + } } diff --git a/jrcc-access-spring-boot-autoconfigure/src/test/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputPropertiesTests.java b/jrcc-access-spring-boot-autoconfigure/src/test/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputPropertiesTests.java index 669c0d2c..74675caf 100644 --- a/jrcc-access-spring-boot-autoconfigure/src/test/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputPropertiesTests.java +++ b/jrcc-access-spring-boot-autoconfigure/src/test/java/ca/bc/gov/open/jrccaccess/autoconfigure/plugins/sftp/SftpInputPropertiesTests.java @@ -19,5 +19,29 @@ public void with_ssh_key_should_return_ssh_key() { } + @Test + public void default_allow_unknown_key_return_false() { + SftpInputProperties sut = new SftpInputProperties(); + Assert.assertFalse(sut.isAllowUnknownKeys()); + } + + @Test + public void set_allow_unknown_key_should_succeed() { + SftpInputProperties sut = new SftpInputProperties(); + sut.setAllowUnknownKeys(true); + Assert.assertTrue(sut.isAllowUnknownKeys()); + } + @Test + public void default_get_known_hosts_return_null() { + SftpInputProperties sut = new SftpInputProperties(); + Assert.assertNull(sut.getKnownHostFile()); + } + + @Test + public void set_known_hosts_should_succeed() { + SftpInputProperties sut = new SftpInputProperties(); + sut.setKnownHostFile("c://test//known_hosts"); + Assert.assertEquals(sut.getKnownHostFile(), "c://test//known_hosts"); + } } diff --git a/jrcc-access-spring-boot-sample-app/src/main/resources/application.yml b/jrcc-access-spring-boot-sample-app/src/main/resources/application.yml index 67122866..15f975d7 100644 --- a/jrcc-access-spring-boot-sample-app/src/main/resources/application.yml +++ b/jrcc-access-spring-boot-sample-app/src/main/resources/application.yml @@ -1,6 +1,5 @@ -spring: - main: - web-application-type: none +main: + web-application-type: none logging: level: ca: @@ -16,4 +15,4 @@ bcgov: plugin: console output: document-type: test-doc - plugin: console + plugin: console \ No newline at end of file