This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 5c9d5bc1c133 CAMEL-25890: camel-ftp - FTPS: verify the host name of
the server over TLS (#28186)
5c9d5bc1c133 is described below
commit 5c9d5bc1c1333cc4b4a36cfb5d737b2944a1be02
Author: Federico Mariani <[email protected]>
AuthorDate: Wed Oct 21 12:54:03 2026 +0200
CAMEL-25890: camel-ftp - FTPS: verify the host name of the server over TLS
(#28186)
The FTPS client created by the ftps endpoint did not check that the server
certificate matches the host of the endpoint. A new hostnameVerification
option (default true, the same name as in camel-mina, camel-mllp and
camel-smpp) enables endpoint checking on the client the endpoint creates; a
custom ftpClient is left unchanged. The 4.24 upgrade guide notes the
behaviour
change and that hostnameVerification=false keeps the previous behaviour. The
test keystores are regenerated with a localhost subject alternative name.
---
.../org/apache/camel/catalog/components/ftps.json | 17 +--
.../apache/camel/catalog/docs/ftp-component.adoc | 13 +++
.../file/remote/FtpsEndpointConfigurer.java | 6 ++
.../file/remote/FtpsEndpointUriFactory.java | 3 +-
.../apache/camel/component/file/remote/ftps.json | 17 +--
.../camel-ftp/src/main/docs/ftp-component.adoc | 13 +++
.../component/file/remote/FtpsConfiguration.java | 15 +++
.../camel/component/file/remote/FtpsEndpoint.java | 2 +
.../FtpsEndpointHostnameVerificationTest.java | 74 +++++++++++++
.../FileToFtpsHostnameVerificationIT.java | 87 ++++++++++++++++
components/camel-ftp/src/test/resources/client.jks | Bin 1960 -> 2089 bytes
components/camel-ftp/src/test/resources/server.jks | Bin 1961 -> 2971 bytes
.../java/org/apache/camel/util/SecurityUtils.java | 1 +
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 12 +++
.../endpoint/dsl/FtpsEndpointBuilderFactory.java | 114 +++++++++++++++++++++
.../src/main/resources/server.jks | Bin 1961 -> 2971 bytes
16 files changed, 357 insertions(+), 17 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
index 81aaec1cd35d..075f91d010d2 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
@@ -169,13 +169,14 @@
"execProt": { "index": 114, "kind": "parameter", "displayName": "Exec
Prot", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "The exec protection
level PROT command. C - Clear S - Safe(SSL protocol only) E - Confidential(SSL
protoc [...]
"ftpClientKeyStoreParameters": { "index": 115, "kind": "parameter",
"displayName": "Ftp Client Key Store Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"ftpClient.keyStore.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "description": "Set the key store parameters. This is a
multi-value option with prefix: ftpClient.keyStore." },
"ftpClientTrustStoreParameters": { "index": 116, "kind": "parameter",
"displayName": "Ftp Client Trust Store Parameters", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"ftpClient.trustStore.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "description": "Set the trust store parameters. This is
a multi-value option with prefix: ftpClient.trustStore." },
- "implicit": { "index": 117, "kind": "parameter", "displayName":
"Implicit", "group": "security", "label": "security", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the security mode
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
- "password": { "index": 118, "kind": "parameter", "displayName":
"Password", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Password to use for
login" },
- "securityProtocol": { "index": 119, "kind": "parameter", "displayName":
"Security Protocol", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "TLSv1.3",
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the underlying
security protocol." },
- "sslContextParameters": { "index": 120, "kind": "parameter",
"displayName": "Ssl Context Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false,
"autowired": false, "secret": false, "description": "Gets the JSSE
configuration that overrides any settings in
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and
FtpsConfiguration#getSecurityPr [...]
- "username": { "index": 121, "kind": "parameter", "displayName":
"Username", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Username to use for
login" },
- "shuffle": { "index": 122, "kind": "parameter", "displayName": "Shuffle",
"group": "sort", "label": "consumer,sort", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "To shuffle the list of
files (sort in random order)" },
- "sortBy": { "index": 123, "kind": "parameter", "displayName": "Sort By",
"group": "sort", "label": "consumer,sort", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Built-in sort by using the File Language.
Supports nested sorts, so you can have a sort by file name and as a 2nd group
sort by modified date." },
- "sorter": { "index": 124, "kind": "parameter", "displayName": "Sorter",
"group": "sort", "label": "consumer,sort", "required": false, "type": "object",
"javaType":
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Pluggable sorter as a java.util.Comparator class." }
+ "hostnameVerification": { "index": 117, "kind": "parameter",
"displayName": "Hostname Verification", "group": "security", "label":
"security", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "security":
"insecure:ssl", "insecureValue": "false", "defaultValue": true,
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Whether to ch [...]
+ "implicit": { "index": 118, "kind": "parameter", "displayName":
"Implicit", "group": "security", "label": "security", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the security mode
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
+ "password": { "index": 119, "kind": "parameter", "displayName":
"Password", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Password to use for
login" },
+ "securityProtocol": { "index": 120, "kind": "parameter", "displayName":
"Security Protocol", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "TLSv1.3",
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the underlying
security protocol." },
+ "sslContextParameters": { "index": 121, "kind": "parameter",
"displayName": "Ssl Context Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false,
"autowired": false, "secret": false, "description": "Gets the JSSE
configuration that overrides any settings in
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and
FtpsConfiguration#getSecurityPr [...]
+ "username": { "index": 122, "kind": "parameter", "displayName":
"Username", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Username to use for
login" },
+ "shuffle": { "index": 123, "kind": "parameter", "displayName": "Shuffle",
"group": "sort", "label": "consumer,sort", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "To shuffle the list of
files (sort in random order)" },
+ "sortBy": { "index": 124, "kind": "parameter", "displayName": "Sort By",
"group": "sort", "label": "consumer,sort", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Built-in sort by using the File Language.
Supports nested sorts, so you can have a sort by file name and as a 2nd group
sort by modified date." },
+ "sorter": { "index": 125, "kind": "parameter", "displayName": "Sorter",
"group": "sort", "label": "consumer,sort", "required": false, "type": "object",
"javaType":
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Pluggable sorter as a java.util.Comparator class." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
index 9276d50ec3f8..1064898ead14 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
@@ -80,6 +80,19 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== FTPS host name verification
+
+The FTPS client that Camel creates checks, on the control connection, that the
certificate of the server matches the
+host of the endpoint, with the same rules as an HTTPS client. The check
happens during the TLS handshake, before login.
+Data connections (`PROT P`) use the same trust manager, but Apache Commons Net
does not check their host name. A
+connection to an IP address needs a certificate that lists that address as a
subject alternative name. Set
+`hostnameVerification=false` to turn this check off; the certificate is still
checked by the trust manager. The option
+does not apply to a custom `ftpClient`.
+
+Use the `hostnameVerification` option to control the check. Setting
`ftpClient.endpointCheckingEnabled` on the endpoint
+also turns it on or off, and takes precedence over `hostnameVerification`, but
the security policy (for example
+`camel.main.profile=prod`) only checks the `hostnameVerification` option.
+
=== FTPS component default trust store
When using the `ftpClient.` properties related to SSL with the FTPS
diff --git
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
index 0517052361d0..9b2c72c4e272 100644
---
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
+++
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
@@ -33,6 +33,8 @@ public class FtpsEndpointConfigurer extends
FtpEndpointConfigurer implements Gen
case "ftpClientKeyStoreParameters":
target.setFtpClientKeyStoreParameters(property(camelContext,
java.util.Map.class, value)); return true;
case "ftpclienttruststoreparameters":
case "ftpClientTrustStoreParameters":
target.setFtpClientTrustStoreParameters(property(camelContext,
java.util.Map.class, value)); return true;
+ case "hostnameverification":
+ case "hostnameVerification":
target.getConfiguration().setHostnameVerification(property(camelContext,
boolean.class, value)); return true;
case "implicit":
target.getConfiguration().setImplicit(property(camelContext, boolean.class,
value)); return true;
case "securityprotocol":
case "securityProtocol":
target.getConfiguration().setSecurityProtocol(property(camelContext,
java.lang.String.class, value)); return true;
@@ -55,6 +57,8 @@ public class FtpsEndpointConfigurer extends
FtpEndpointConfigurer implements Gen
case "ftpClientKeyStoreParameters": return java.util.Map.class;
case "ftpclienttruststoreparameters":
case "ftpClientTrustStoreParameters": return java.util.Map.class;
+ case "hostnameverification":
+ case "hostnameVerification": return boolean.class;
case "implicit": return boolean.class;
case "securityprotocol":
case "securityProtocol": return java.lang.String.class;
@@ -78,6 +82,8 @@ public class FtpsEndpointConfigurer extends
FtpEndpointConfigurer implements Gen
case "ftpClientKeyStoreParameters": return
target.getFtpClientKeyStoreParameters();
case "ftpclienttruststoreparameters":
case "ftpClientTrustStoreParameters": return
target.getFtpClientTrustStoreParameters();
+ case "hostnameverification":
+ case "hostnameVerification": return
target.getConfiguration().isHostnameVerification();
case "implicit": return target.getConfiguration().isImplicit();
case "securityprotocol":
case "securityProtocol": return
target.getConfiguration().getSecurityProtocol();
diff --git
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
index e1802aef6f99..b10e071881c3 100644
---
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
+++
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class FtpsEndpointUriFactory extends
org.apache.camel.support.component.E
private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(125);
+ Set<String> props = new HashSet<>(126);
props.add("account");
props.add("activePortRange");
props.add("allowNullBody");
@@ -77,6 +77,7 @@ public class FtpsEndpointUriFactory extends
org.apache.camel.support.component.E
props.add("greedy");
props.add("handleDirectoryParserAbsoluteResult");
props.add("host");
+ props.add("hostnameVerification");
props.add("idempotent");
props.add("idempotentEager");
props.add("idempotentKey");
diff --git
a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
index 81aaec1cd35d..075f91d010d2 100644
---
a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
+++
b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
@@ -169,13 +169,14 @@
"execProt": { "index": 114, "kind": "parameter", "displayName": "Exec
Prot", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "The exec protection
level PROT command. C - Clear S - Safe(SSL protocol only) E - Confidential(SSL
protoc [...]
"ftpClientKeyStoreParameters": { "index": 115, "kind": "parameter",
"displayName": "Ftp Client Key Store Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"ftpClient.keyStore.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "description": "Set the key store parameters. This is a
multi-value option with prefix: ftpClient.keyStore." },
"ftpClientTrustStoreParameters": { "index": 116, "kind": "parameter",
"displayName": "Ftp Client Trust Store Parameters", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"ftpClient.trustStore.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "description": "Set the trust store parameters. This is
a multi-value option with prefix: ftpClient.trustStore." },
- "implicit": { "index": 117, "kind": "parameter", "displayName":
"Implicit", "group": "security", "label": "security", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the security mode
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
- "password": { "index": 118, "kind": "parameter", "displayName":
"Password", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Password to use for
login" },
- "securityProtocol": { "index": 119, "kind": "parameter", "displayName":
"Security Protocol", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "TLSv1.3",
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the underlying
security protocol." },
- "sslContextParameters": { "index": 120, "kind": "parameter",
"displayName": "Ssl Context Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false,
"autowired": false, "secret": false, "description": "Gets the JSSE
configuration that overrides any settings in
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and
FtpsConfiguration#getSecurityPr [...]
- "username": { "index": 121, "kind": "parameter", "displayName":
"Username", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Username to use for
login" },
- "shuffle": { "index": 122, "kind": "parameter", "displayName": "Shuffle",
"group": "sort", "label": "consumer,sort", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "To shuffle the list of
files (sort in random order)" },
- "sortBy": { "index": 123, "kind": "parameter", "displayName": "Sort By",
"group": "sort", "label": "consumer,sort", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Built-in sort by using the File Language.
Supports nested sorts, so you can have a sort by file name and as a 2nd group
sort by modified date." },
- "sorter": { "index": 124, "kind": "parameter", "displayName": "Sorter",
"group": "sort", "label": "consumer,sort", "required": false, "type": "object",
"javaType":
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Pluggable sorter as a java.util.Comparator class." }
+ "hostnameVerification": { "index": 117, "kind": "parameter",
"displayName": "Hostname Verification", "group": "security", "label":
"security", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "security":
"insecure:ssl", "insecureValue": "false", "defaultValue": true,
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Whether to ch [...]
+ "implicit": { "index": 118, "kind": "parameter", "displayName":
"Implicit", "group": "security", "label": "security", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the security mode
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
+ "password": { "index": 119, "kind": "parameter", "displayName":
"Password", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Password to use for
login" },
+ "securityProtocol": { "index": 120, "kind": "parameter", "displayName":
"Security Protocol", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "TLSv1.3",
"configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Set the underlying
security protocol." },
+ "sslContextParameters": { "index": 121, "kind": "parameter",
"displayName": "Ssl Context Parameters", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false,
"autowired": false, "secret": false, "description": "Gets the JSSE
configuration that overrides any settings in
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and
FtpsConfiguration#getSecurityPr [...]
+ "username": { "index": 122, "kind": "parameter", "displayName":
"Username", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.file.remote.FtpsConfiguration",
"configurationField": "configuration", "description": "Username to use for
login" },
+ "shuffle": { "index": 123, "kind": "parameter", "displayName": "Shuffle",
"group": "sort", "label": "consumer,sort", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "To shuffle the list of
files (sort in random order)" },
+ "sortBy": { "index": 124, "kind": "parameter", "displayName": "Sort By",
"group": "sort", "label": "consumer,sort", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Built-in sort by using the File Language.
Supports nested sorts, so you can have a sort by file name and as a 2nd group
sort by modified date." },
+ "sorter": { "index": 125, "kind": "parameter", "displayName": "Sorter",
"group": "sort", "label": "consumer,sort", "required": false, "type": "object",
"javaType":
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Pluggable sorter as a java.util.Comparator class." }
}
}
diff --git a/components/camel-ftp/src/main/docs/ftp-component.adoc
b/components/camel-ftp/src/main/docs/ftp-component.adoc
index 9276d50ec3f8..1064898ead14 100644
--- a/components/camel-ftp/src/main/docs/ftp-component.adoc
+++ b/components/camel-ftp/src/main/docs/ftp-component.adoc
@@ -80,6 +80,19 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== FTPS host name verification
+
+The FTPS client that Camel creates checks, on the control connection, that the
certificate of the server matches the
+host of the endpoint, with the same rules as an HTTPS client. The check
happens during the TLS handshake, before login.
+Data connections (`PROT P`) use the same trust manager, but Apache Commons Net
does not check their host name. A
+connection to an IP address needs a certificate that lists that address as a
subject alternative name. Set
+`hostnameVerification=false` to turn this check off; the certificate is still
checked by the trust manager. The option
+does not apply to a custom `ftpClient`.
+
+Use the `hostnameVerification` option to control the check. Setting
`ftpClient.endpointCheckingEnabled` on the endpoint
+also turns it on or off, and takes precedence over `hostnameVerification`, but
the security policy (for example
+`camel.main.profile=prod`) only checks the `hostnameVerification` option.
+
=== FTPS component default trust store
When using the `ftpClient.` properties related to SSL with the FTPS
diff --git
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
index ea102238e8b3..c61c6232fa8a 100644
---
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
+++
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
@@ -37,6 +37,8 @@ public class FtpsConfiguration extends FtpConfiguration {
private String execProt;
@UriParam(label = "security")
private Long execPbsz;
+ @UriParam(label = "security", defaultValue = "true", security =
"insecure:ssl", insecureValue = "false")
+ private boolean hostnameVerification = true;
public FtpsConfiguration() {
setProtocol("ftps");
@@ -119,4 +121,17 @@ public class FtpsConfiguration extends FtpConfiguration {
public void setExecPbsz(Long execPbsz) {
this.execPbsz = execPbsz;
}
+
+ public boolean isHostnameVerification() {
+ return hostnameVerification;
+ }
+
+ /**
+ * Whether to check that the certificate of the server matches the host
Camel connects to, with the same rules as an
+ * HTTPS client. When disabled, any certificate accepted by the trust
manager is accepted, whatever host it was
+ * issued for. This option does not apply to a custom ftpClient.
+ */
+ public void setHostnameVerification(boolean hostnameVerification) {
+ this.hostnameVerification = hostnameVerification;
+ }
}
diff --git
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
index 7223f29c5a7b..37251328723b 100644
---
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
+++
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
@@ -164,6 +164,8 @@ public class FtpsEndpoint extends FtpEndpoint<FTPFile> {
}
}
+
client.setEndpointCheckingEnabled(getFtpsConfiguration().isHostnameVerification());
+
return client;
}
diff --git
a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
new file mode 100644
index 000000000000..2b80917be695
--- /dev/null
+++
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.camel.component.file.remote;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.commons.net.ftp.FTPSClient;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FtpsEndpointHostnameVerificationTest extends CamelTestSupport {
+
+ @BindToRegistry("sslContextParameters")
+ private final SSLContextParameters sslContextParameters = new
SSLContextParameters();
+
+ @BindToRegistry("customFtpClient")
+ private final FTPSClient customFtpClient = new FTPSClient();
+
+ @Test
+ public void createdClientChecksServerHostNameByDefault() throws Exception {
+ FtpsEndpoint endpoint = context.getEndpoint("ftps://localhost/dir",
FtpsEndpoint.class);
+
+ FTPSClient client = (FTPSClient) endpoint.createFtpClient();
+
+ assertTrue(client.isEndpointCheckingEnabled());
+ }
+
+ @Test
+ public void createdClientWithSslContextParametersChecksServerHostName()
throws Exception {
+ FtpsEndpoint endpoint =
context.getEndpoint("ftps://localhost/dir?sslContextParameters=#sslContextParameters",
+ FtpsEndpoint.class);
+
+ FTPSClient client = (FTPSClient) endpoint.createFtpClient();
+
+ assertTrue(client.isEndpointCheckingEnabled());
+ }
+
+ @Test
+ public void hostnameVerificationCanBeTurnedOff() throws Exception {
+ FtpsEndpoint endpoint =
context.getEndpoint("ftps://localhost/dir?hostnameVerification=false",
FtpsEndpoint.class);
+ FtpsEndpoint endpointWithSslContextParameters = context.getEndpoint(
+
"ftps://localhost/other?hostnameVerification=false&sslContextParameters=#sslContextParameters",
+ FtpsEndpoint.class);
+
+ assertFalse(((FTPSClient)
endpoint.createFtpClient()).isEndpointCheckingEnabled());
+ assertFalse(((FTPSClient)
endpointWithSslContextParameters.createFtpClient()).isEndpointCheckingEnabled());
+ }
+
+ @Test
+ public void customFtpClientIsNotChanged() throws Exception {
+ FtpsEndpoint endpoint =
context.getEndpoint("ftps://localhost/dir?ftpClient=#customFtpClient",
FtpsEndpoint.class);
+
+ endpoint.createRemoteFileOperations();
+
+ assertFalse(customFtpClient.isEndpointCheckingEnabled());
+ }
+}
diff --git
a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
new file mode 100644
index 000000000000..5ae945cc81be
--- /dev/null
+++
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
@@ -0,0 +1,87 @@
+/*
+ * 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.camel.component.file.remote.integration;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static org.apache.camel.test.junit6.TestSupport.assertFileExists;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test that the ftps component checks the host name of the server: the
certificate of the test server only lists
+ * localhost, not 127.0.0.1.
+ */
+@EnabledIf(value =
"org.apache.camel.test.infra.ftp.services.embedded.FtpsUtil#hasRequiredAlgorithms")
+public class FileToFtpsHostnameVerificationIT extends
FtpsServerExplicitSSLWithoutClientAuthTestSupport {
+
+ @BindToRegistry("sslContextParameters")
+ public SSLContextParameters createSslContextParams() {
+ KeyStoreParameters ksp = new KeyStoreParameters();
+ ksp.setResource("server.jks");
+ ksp.setPassword("password");
+
+ TrustManagersParameters tmp = new TrustManagersParameters();
+ tmp.setKeyStore(ksp);
+
+ SSLContextParameters sslContextParameters = new SSLContextParameters();
+ sslContextParameters.setSecureSocketProtocol("TLSv1.2");
+ sslContextParameters.setTrustManagers(tmp);
+
+ return sslContextParameters;
+ }
+
+ private String getFtpUrl(String host, String directory) {
+ return "ftps://admin@" + host + ":" + service.getPort() + "/" +
directory
+ +
"?password=admin&disableSecureDataChannelDefaults=true&implicit=false"
+ +
"&sslContextParameters=#sslContextParameters&maximumReconnectAttempts=0&fileName=hello.txt";
+ }
+
+ @Test
+ public void uploadToHostListedInCertificate() {
+ template.sendBody(getFtpUrl("localhost", "listed"), "Hello World");
+
+ assertFileExists(service.ftpFile("listed/hello.txt"));
+ }
+
+ @Test
+ public void uploadToHostNotListedInCertificateFails() {
+ String uri = getFtpUrl("127.0.0.1", "notlisted");
+
+ Exception ex = assertThrows(CamelExecutionException.class, () ->
template.sendBody(uri, "Hello World"));
+
+ assertNotNull(ObjectHelper.getException(SSLHandshakeException.class,
ex), "The TLS handshake should fail");
+ assertFalse(service.ftpFile("notlisted/hello.txt").toFile().exists());
+ }
+
+ @Test
+ public void
uploadToHostNotListedInCertificateWithHostnameVerificationTurnedOff() {
+ template.sendBody(getFtpUrl("127.0.0.1", "unchecked") +
"&hostnameVerification=false", "Hello World");
+
+ assertFileExists(service.ftpFile("unchecked/hello.txt"));
+ }
+}
diff --git a/components/camel-ftp/src/test/resources/client.jks
b/components/camel-ftp/src/test/resources/client.jks
index cf3228bd89c4..3086fb935589 100644
Binary files a/components/camel-ftp/src/test/resources/client.jks and
b/components/camel-ftp/src/test/resources/client.jks differ
diff --git a/components/camel-ftp/src/test/resources/server.jks
b/components/camel-ftp/src/test/resources/server.jks
index 28f294b3e9a9..3efde9123134 100644
Binary files a/components/camel-ftp/src/test/resources/server.jks and
b/components/camel-ftp/src/test/resources/server.jks differ
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
index 4713b7842e34..6c1e6ee5bcb3 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
@@ -159,6 +159,7 @@ public final class SecurityUtils {
"component:opa",
"component:openfga"));
owners.put("hostnameverification", Set.of(
+ "component:ftps",
"component:mina",
"component:mllp",
"component:netty",
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index d403cecd0527..d22ffd4ca0b3 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1560,6 +1560,18 @@ MINA does not pass a failed handshake on as an error:
the producer fails with a
`org.apache.mina.filter.ssl.SslHandler`. A producer that starts timing out
after the upgrade should be checked for a
certificate that does not list the host.
+=== camel-ftp - the host name of the FTPS server is verified
+
+The `ftps` endpoint now checks, on the control connection, that the
certificate of the server matches the host of the
+endpoint, with the same rules as an HTTPS client, with `sslContextParameters`
as well as with the
+`ftpClient.trustStore.*` options. Before, any certificate accepted by the
trust manager was accepted, whatever host the
+certificate was issued for. Data connections are not checked by host name.
+
+A connection to a host name or an IP address that the certificate does not
list now fails the TLS handshake, before
+login; an IP address must be listed as a subject alternative name of the
certificate. Use a certificate that matches the
+host of the endpoint, or set the new `hostnameVerification` option to `false`
to keep the previous behaviour. A custom
+`ftpClient` set on the endpoint is not changed.
+
=== camel-aws-secrets-manager - the CloudTrail refresh task reads every event
since the last check
With `camel.vault.aws.refreshEnabled=true` and without SQS notification, each
check now reads every page of the
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
index b3d72e667651..965ea58aede7 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
@@ -2244,6 +2244,44 @@ public interface FtpsEndpointBuilderFactory {
doSetMultiValueProperties("ftpClientTrustStoreParameters",
"ftpClient.trustStore.", values);
return this;
}
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointConsumerBuilder hostnameVerification(boolean
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option will be converted to a <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointConsumerBuilder hostnameVerification(String
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
/**
* Set the security mode (Implicit/Explicit). true - Implicit Mode /
* False - Explicit Mode.
@@ -4221,6 +4259,44 @@ public interface FtpsEndpointBuilderFactory {
doSetMultiValueProperties("ftpClientTrustStoreParameters",
"ftpClient.trustStore.", values);
return this;
}
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointProducerBuilder hostnameVerification(boolean
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option will be converted to a <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointProducerBuilder hostnameVerification(String
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
/**
* Set the security mode (Implicit/Explicit). true - Implicit Mode /
* False - Explicit Mode.
@@ -5701,6 +5777,44 @@ public interface FtpsEndpointBuilderFactory {
doSetMultiValueProperties("ftpClientTrustStoreParameters",
"ftpClient.trustStore.", values);
return this;
}
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointBuilder hostnameVerification(boolean
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
+ /**
+ * Whether to check that the certificate of the server matches the host
+ * Camel connects to, with the same rules as an HTTPS client. When
+ * disabled, any certificate accepted by the trust manager is accepted,
+ * whatever host it was issued for. This option does not apply to a
+ * custom ftpClient.
+ *
+ * The option will be converted to a <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: security
+ *
+ * @param hostnameVerification the value to set
+ * @return the dsl builder
+ */
+ default FtpsEndpointBuilder hostnameVerification(String
hostnameVerification) {
+ doSetProperty("hostnameVerification", hostnameVerification);
+ return this;
+ }
/**
* Set the security mode (Implicit/Explicit). true - Implicit Mode /
* False - Explicit Mode.
diff --git a/test-infra/camel-test-infra-ftp/src/main/resources/server.jks
b/test-infra/camel-test-infra-ftp/src/main/resources/server.jks
index 28f294b3e9a9..3efde9123134 100644
Binary files a/test-infra/camel-test-infra-ftp/src/main/resources/server.jks
and b/test-infra/camel-test-infra-ftp/src/main/resources/server.jks differ