This is an automated email from the ASF dual-hosted git repository.
davidzollo pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new 32f4ed3808 [Fix][Connector-V2] Support SFTP keyfile option (#11078)
32f4ed3808 is described below
commit 32f4ed380898e009b6725035894a9c596ebc850e
Author: Jast <[email protected]>
AuthorDate: Sat Jun 13 19:38:01 2026 +0800
[Fix][Connector-V2] Support SFTP keyfile option (#11078)
---
docs/en/connectors/sink/SftpFile.md | 9 ++++--
docs/en/connectors/source/SftpFile.md | 3 +-
docs/zh/connectors/sink/SftpFile.md | 11 ++++++--
docs/zh/connectors/source/SftpFile.md | 3 +-
.../seatunnel/file/sftp/config/SftpConf.java | 15 +++++++---
.../file/sftp/config/SftpFileBaseOptions.java | 5 ++++
.../file/sftp/sink/SftpFileSinkFactory.java | 3 +-
.../file/sftp/source/SftpFileSourceFactory.java | 1 +
.../seatunnel/file/sftp/SftpFileFactoryTest.java | 32 +++++++++++++++++++++-
9 files changed, 69 insertions(+), 13 deletions(-)
diff --git a/docs/en/connectors/sink/SftpFile.md
b/docs/en/connectors/sink/SftpFile.md
index 683918c299..4d6a0592a1 100644
--- a/docs/en/connectors/sink/SftpFile.md
+++ b/docs/en/connectors/sink/SftpFile.md
@@ -46,7 +46,8 @@ If you use SeaTunnel Engine, It automatically integrated the
hadoop jar when you
| host | string | yes | -
|
|
| port | int | yes | -
|
|
| user | string | yes | -
|
|
-| password | string | yes | -
|
|
+| password | string | no | -
| Required when `keyfile` is not set.
|
+| keyfile | string | no | -
| Private key file path used for SFTP public key
authentication.
|
| path | string | yes | -
|
|
| tmp_path | string | yes | /tmp/seatunnel
| The result file will write to a tmp path first and
then use `mv` to submit tmp dir to target dir. Need a FTP dir.
|
| custom_filename | boolean | no | false
| Whether you need custom the filename
|
@@ -97,7 +98,11 @@ The target sftp user is required
### password [string]
-The target sftp password is required
+The target sftp password. Required when `keyfile` is not set.
+
+### keyfile [string]
+
+The private key file path used for SFTP public key authentication.
### path [string]
diff --git a/docs/en/connectors/source/SftpFile.md
b/docs/en/connectors/source/SftpFile.md
index f32adda510..3e95521f07 100644
--- a/docs/en/connectors/source/SftpFile.md
+++ b/docs/en/connectors/source/SftpFile.md
@@ -83,7 +83,8 @@ The File does not have a specific type list, and we can
indicate which SeaTunnel
| host | String | Yes | -
| The target sftp host is required
|
| port | Int | Yes | -
| The target sftp port is required
|
| user | String | Yes | -
| The target sftp username is required
|
-| password | String | Yes | -
| The target sftp password is required
|
+| password | String | No | -
| The target sftp password. Required when `keyfile` is not set.
|
+| keyfile | String | No | -
| The private key file path used for SFTP public key authentication.
|
| path | String | Yes | -
| The source file path.
|
| file_format_type | String | Yes | -
| Please check #file_format_type below
|
| file_filter_pattern | String | No | -
| Filter pattern, which used for filtering files.
|
diff --git a/docs/zh/connectors/sink/SftpFile.md
b/docs/zh/connectors/sink/SftpFile.md
index 7a4c791fdf..884e5356fe 100644
--- a/docs/zh/connectors/sink/SftpFile.md
+++ b/docs/zh/connectors/sink/SftpFile.md
@@ -45,7 +45,8 @@ import ChangeLog from '../changelog/connector-file-sftp.md';
| host | string | 是 | -
|
|
| port | int | 是 | -
|
|
| user | string | 是 | -
|
|
-| password | string | 是 | -
|
|
+| password | string | 否 | -
| 未配置 `keyfile` 时需要配置。
|
+| keyfile | string | 否 | -
| 用于 SFTP 公钥认证的私钥文件路径。
|
| path | string | 是 | -
|
|
| tmp_path | string | 是 | /tmp/seatunnel
| 结果文件将首先写入临时路径,然后使用`mv`将临时目录剪切到目标目录。需要一个FTP目录。
|
| custom_filename | boolean | 否 | false
| 是否需要自定义文件名
|
@@ -94,7 +95,11 @@ import ChangeLog from '../changelog/connector-file-sftp.md';
### password [string]
-目标sftp密码,必填。
+目标sftp密码。未配置 `keyfile` 时需要配置。
+
+### keyfile [string]
+
+用于 SFTP 公钥认证的私钥文件路径。
### path [string]
@@ -359,4 +364,4 @@ LocalFile {
## 变更日志
-<ChangeLog />
\ No newline at end of file
+<ChangeLog />
diff --git a/docs/zh/connectors/source/SftpFile.md
b/docs/zh/connectors/source/SftpFile.md
index c225af5a58..ce71dd1cb5 100644
--- a/docs/zh/connectors/source/SftpFile.md
+++ b/docs/zh/connectors/source/SftpFile.md
@@ -83,7 +83,8 @@ import ChangeLog from '../changelog/connector-file-sftp.md';
| host | String | 是 | - |
目标sftp主机是必需的
|
| port | Int | 是 | - |
目标sftp端口是必需的
|
| user | String | 是 | - |
目标sftp用户名是必需的
|
-| password | String | 是 | - |
目标sftp密码是必需的
|
+| password | String | 否 | - |
目标sftp密码。未配置 `keyfile` 时需要配置。
|
+| keyfile | String | 否 | - | 用于 SFTP
公钥认证的私钥文件路径。
|
| path | String | 是 | - | 源文件路径。
|
| file_format_type | String | 是 | - |
请查看下面的#file_format_type
|
| file_filter_pattern | String | 否 | - |
过滤模式,用于过滤文件。
|
diff --git
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpConf.java
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpConf.java
index 2849b5b7fa..75d0e3e3f0 100644
---
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpConf.java
+++
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpConf.java
@@ -19,6 +19,7 @@ package
org.apache.seatunnel.connectors.seatunnel.file.sftp.config;
import org.apache.seatunnel.api.configuration.ReadonlyConfig;
import org.apache.seatunnel.connectors.seatunnel.file.config.HadoopConf;
+import
org.apache.seatunnel.connectors.seatunnel.file.sftp.system.SFTPFileSystem;
import java.util.HashMap;
@@ -47,10 +48,16 @@ public class SftpConf extends HadoopConf {
String defaultFS = String.format("sftp://%s:%s", host, port);
HadoopConf hadoopConf = new SftpConf(defaultFS);
HashMap<String, String> sftpOptions = new HashMap<>();
- sftpOptions.put("fs.sftp.user." + host,
config.get(SftpFileBaseOptions.SFTP_USER));
- sftpOptions.put(
- "fs.sftp.password." + host + "." +
config.get(SftpFileBaseOptions.SFTP_USER),
- config.get(SftpFileBaseOptions.SFTP_PASSWORD));
+ String user = config.get(SftpFileBaseOptions.SFTP_USER);
+ sftpOptions.put(SFTPFileSystem.FS_SFTP_USER_PREFIX + host, user);
+ config.getOptional(SftpFileBaseOptions.SFTP_PASSWORD)
+ .ifPresent(
+ password ->
+ sftpOptions.put(
+ SFTPFileSystem.FS_SFTP_PASSWORD_PREFIX
+ host + "." + user,
+ password));
+ config.getOptional(SftpFileBaseOptions.SFTP_KEYFILE)
+ .ifPresent(keyfile ->
sftpOptions.put(SFTPFileSystem.FS_SFTP_KEYFILE, keyfile));
hadoopConf.setExtraOptions(sftpOptions);
return hadoopConf;
}
diff --git
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpFileBaseOptions.java
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpFileBaseOptions.java
index 5a614fbec7..75e435c3f7 100644
---
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpFileBaseOptions.java
+++
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/config/SftpFileBaseOptions.java
@@ -27,6 +27,11 @@ public class SftpFileBaseOptions extends FileBaseOptions {
.stringType()
.noDefaultValue()
.withDescription("SFTP server password");
+ public static final Option<String> SFTP_KEYFILE =
+ Options.key("keyfile")
+ .stringType()
+ .noDefaultValue()
+ .withDescription("SFTP private key file path");
public static final Option<String> SFTP_USER =
Options.key("user")
.stringType()
diff --git
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/sink/SftpFileSinkFactory.java
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/sink/SftpFileSinkFactory.java
index eb68f31158..0bfe1196dd 100644
---
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/sink/SftpFileSinkFactory.java
+++
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/sink/SftpFileSinkFactory.java
@@ -52,7 +52,8 @@ public class SftpFileSinkFactory extends
BaseMultipleTableFileSinkFactory {
.required(SftpFileSinkOptions.SFTP_HOST)
.required(SftpFileSinkOptions.SFTP_PORT)
.required(SftpFileSinkOptions.SFTP_USER)
- .required(SftpFileSinkOptions.SFTP_PASSWORD)
+ .optional(SftpFileSinkOptions.SFTP_PASSWORD)
+ .optional(SftpFileSinkOptions.SFTP_KEYFILE)
.optional(FileBaseSinkOptions.FILE_FORMAT_TYPE)
.optional(FileBaseSinkOptions.SCHEMA_SAVE_MODE)
.optional(FileBaseSinkOptions.DATA_SAVE_MODE)
diff --git
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/source/SftpFileSourceFactory.java
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/source/SftpFileSourceFactory.java
index 16e22ae664..4ce72d9eb4 100644
---
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/source/SftpFileSourceFactory.java
+++
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/source/SftpFileSourceFactory.java
@@ -51,6 +51,7 @@ public class SftpFileSourceFactory implements
TableSourceFactory {
.optional(SftpFileSourceOptions.SFTP_PORT)
.optional(SftpFileSourceOptions.SFTP_USER)
.optional(SftpFileSourceOptions.SFTP_PASSWORD)
+ .optional(SftpFileSourceOptions.SFTP_KEYFILE)
.optional(FileBaseSourceOptions.FILE_FORMAT_TYPE)
.conditional(
FileBaseSourceOptions.FILE_FORMAT_TYPE,
diff --git
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/test/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/SftpFileFactoryTest.java
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/test/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/SftpFileFactoryTest.java
index 9ed5942d18..b38f8c8794 100644
---
a/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/test/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/SftpFileFactoryTest.java
+++
b/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/test/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/SftpFileFactoryTest.java
@@ -17,17 +17,24 @@
package org.apache.seatunnel.connectors.seatunnel.file.sftp;
+import org.apache.seatunnel.api.configuration.ReadonlyConfig;
import org.apache.seatunnel.api.configuration.util.Expression;
import org.apache.seatunnel.api.configuration.util.OptionRule;
import org.apache.seatunnel.api.configuration.util.RequiredOption;
import
org.apache.seatunnel.connectors.seatunnel.file.config.FileBaseSourceOptions;
import org.apache.seatunnel.connectors.seatunnel.file.config.FileSyncMode;
+import org.apache.seatunnel.connectors.seatunnel.file.config.HadoopConf;
+import org.apache.seatunnel.connectors.seatunnel.file.sftp.config.SftpConf;
import
org.apache.seatunnel.connectors.seatunnel.file.sftp.sink.SftpFileSinkFactory;
import
org.apache.seatunnel.connectors.seatunnel.file.sftp.source.SftpFileSourceFactory;
+import
org.apache.seatunnel.connectors.seatunnel.file.sftp.system.SFTPFileSystem;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import java.util.HashMap;
+import java.util.Map;
+
class SftpFileFactoryTest {
@Test
@@ -48,6 +55,9 @@ class SftpFileFactoryTest {
optionRule.getOptionalOptions().contains(FileBaseSourceOptions.SCAN_INTERVAL));
Assertions.assertTrue(
optionRule.getOptionalOptions().contains(FileBaseSourceOptions.START_MODE));
+ Assertions.assertTrue(
+ optionRule.getOptionalOptions().stream()
+ .anyMatch(option -> "keyfile".equals(option.key())));
Expression expectExpression =
Expression.of(FileBaseSourceOptions.SYNC_MODE,
FileSyncMode.UPDATE);
@@ -60,6 +70,26 @@ class SftpFileFactoryTest {
required.getOptions()
.contains(FileBaseSourceOptions.TARGET_PATH))
.anyMatch(required ->
expectExpression.equals(required.getExpression())));
- Assertions.assertNotNull((new SftpFileSinkFactory()).optionRule());
+ OptionRule sinkOptionRule = (new SftpFileSinkFactory()).optionRule();
+ Assertions.assertNotNull(sinkOptionRule);
+ Assertions.assertTrue(
+ sinkOptionRule.getOptionalOptions().stream()
+ .anyMatch(option -> "keyfile".equals(option.key())));
+ }
+
+ @Test
+ void buildHadoopConfWithKeyfile() {
+ Map<String, Object> configMap = new HashMap<>();
+ configMap.put("host", "sftp.example.com");
+ configMap.put("port", 22);
+ configMap.put("user", "seatunnel");
+ configMap.put("password", "secret");
+ configMap.put("keyfile", "/home/seatunnel/.ssh/id_rsa");
+
+ HadoopConf hadoopConf =
SftpConf.buildWithConfig(ReadonlyConfig.fromMap(configMap));
+
+ Assertions.assertEquals(
+ "/home/seatunnel/.ssh/id_rsa",
+
hadoopConf.getExtraOptions().get(SFTPFileSystem.FS_SFTP_KEYFILE));
}
}