TLS and security: HTTPS, mutual TLS, trust stores and secrets
Cameleer has its own TLS 1.3 and 1.2 implementation and is configured with Camel's names:
camel.server.ssl.*gives the HTTP server a certificate,camel.ssl.*(withcamel.ssl.enabled=true) sets the certificates and trust store for every TLS client endpoint,sslContextParameterssets them per endpoint, andclientAuthentication=REQUIREturns on mutual TLS. Certificates are always verified unless you explicitly turn verification off for a test. Secrets come from environment variables or AWS, Azure and Google Cloud secret stores through placeholders.
Applies to: every TLS endpoint (HTTP, Kafka, AMQP, MQTT, SMTP/IMAP, FTPS, databases, cloud services) and the embedded HTTP server ยท Apache Camel: Camel Configuration Utilities (JSSE)
| you want | set |
|---|---|
HTTPS for REST and platform-http routes | camel.server.sslEnabled=true, camel.server.ssl.certFile, camel.server.ssl.keyFile |
| a client endpoint to trust a private CA | camel.ssl.enabled=true, camel.ssl.trustStore=ca.crt |
| a client certificate (mutual TLS as the client) | camel.ssl.certFile and camel.ssl.keyFile (or keyStore) |
| clients to present a certificate (mutual TLS as the server) | camel.server.ssl.clientAuthentication=REQUIRE, camel.server.ssl.trustStore |
| different settings for one endpoint | an sslContextParameters bean, or the component's own SSL options |
| public CAs on Windows | SSL_CERT_FILE pointing at a PEM bundle |
| secrets out of route files | {{env:NAME}}, {{aws:name}}, {{azure:name}}, {{gcp:name}} |
HTTPS for your routes
REST DSL and platform-http routes are served by the embedded HTTP server on
camel.server.port. Give it a certificate and turn TLS on:
camel.server.sslEnabled = true
camel.server.ssl.certFile = server.crt
camel.server.ssl.keyFile = server.keyThe certificate and key are PEM files; camel.server.ssl.keyStore takes a combined PEM file, a
PKCS#12 file or a JKS keystore instead (with keystorePassword, and keyStoreType when the file
name does not say). camel.server.useGlobalSslContextParameters=true uses the camel.ssl.*
parameters for the server too. One endpoint can serve TLS on its own with ssl=true or
sslContextParameters=#bean on its URI.
For a development certificate:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
-keyout server.key -out server.crt -days 30 \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"Everything on camel.server.port speaks what the port speaks: with camel.server.sslEnabled=true,
the management endpoints (/q/health, /q/metrics, /q/dev), /q/attestation and the MCP
server are HTTPS too, next to the HTTPS routes. To keep health probes and metrics on plain HTTP
(for example for a Kubernetes probe or a Prometheus scraper inside the cluster), move them to
Camel's separate management port with camel.management.enabled=true (port 9876 by default,
see Observability).
TLS for client endpoints
A TLS endpoint (https:, kafka: with securityProtocol=SSL, ftps:, smtps:, PostgreSQL with
sslmode in its JDBC URL, cloud services, ...) verifies the server's certificate chain
and host name against the system trust store by default. For a private CA or a client
certificate, set the global parameters, which every TLS endpoint then uses:
camel.ssl.enabled = true
camel.ssl.trustStore = ca.crt
# mutual TLS: the client's certificate and key
camel.ssl.certFile = client.crt
camel.ssl.keyFile = client.keycamel.ssl.* has no effect without camel.ssl.enabled=true, as in Camel. An endpoint with
useGlobalSslContextParameters=true uses them even when the flag is off.
key (under camel.ssl. or camel.server.ssl.) | what it sets |
|---|---|
keyStore | a combined PEM file (certificate chain and key), a PKCS#12 file or a JKS keystore |
keystorePassword, keyPassword, keyStoreType, certAlias | how to open the keystore and which entry to use |
certFile, keyFile | the certificate chain and private key as separate PEM files |
trustStore | CA certificates to trust: PEM files (comma separated), inline PEM, or a PKCS#12 or JKS file with trustStorePassword and trustStoreType |
useSystemTrustStore | also trust the system store when a trustStore is given |
secureSocketProtocol, secureSocketProtocols, minVersion, maxVersion | the TLS versions: TLSv1.2, TLSv1.3 (both by default; older versions are not implemented) |
clientAuthentication | for a server: NONE (default), WANT or REQUIRE a client certificate |
crlFile | a certificate revocation list for client certificates |
serverName, hostnameVerification | the SNI name to send, and whether to check the host name (on by default) |
alpn, sessionTimeout, sessionResumption | ALPN protocols and session resumption |
trustAllCertificates | turn off certificate verification: for tests only, never in production |
Per endpoint. An sslContextParameters=#name option on the URI, or
camel.component.<scheme>.sslContextParameters=#name, uses a bean registered in Rust
(cameleer_tls::SslContextParameters). Several components also take their own SSL options, with
Camel's names: Kafka's sslTruststoreLocation and sslKeystoreLocation, the mail components'
sslContextParameters, PostgreSQL's sslmode and sslrootcert in the JDBC URL. Each component page lists them.
The system trust store
Without a trustStore, Cameleer trusts the CA bundle named by SSL_CERT_FILE, or else the first
of /etc/ssl/certs/ca-certificates.crt, /etc/pki/tls/certs/ca-bundle.crt,
/etc/ssl/ca-bundle.pem, /etc/ssl/cert.pem and
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem. The Docker image, most Linux distributions
and macOS have one. Windows has none of these paths and Cameleer does not read the Windows
certificate store, so set SSL_CERT_FILE there (for example to the cacert.pem bundle from
curl's website).
Mutual TLS
As the server: require a client certificate signed by a CA you trust.
camel.server.sslEnabled = true
camel.server.ssl.certFile = server.crt
camel.server.ssl.keyFile = server.key
camel.server.ssl.clientAuthentication = REQUIRE
camel.server.ssl.trustStore = clients-ca.crtAs the client: present a certificate with camel.ssl.certFile and camel.ssl.keyFile (above), or
the component's own options (Kafka: sslKeystoreLocation). A client without a certificate gets
SSLHandshakeException: TLS alert received from peer: certificate_required (116).
What a TLS failure looks like
TLS errors use Camel's exception kinds, so onException and doCatch can handle them:
| exception | when |
|---|---|
SSLPeerUnverifiedException | the server's certificate is not trusted, expired or for another host (unable to find trusted issuer 'CN=localhost' for 'CN=localhost') |
SSLHandshakeException | the handshake failed, for example the peer sent an alert (certificate_required, handshake_failure) |
SSLException | other TLS errors; the parent of the two above |
A worked example
Cameleer on both sides: a server route over HTTPS, and a client route that calls it and trusts
its certificate. It runs in the test suite (tls_guide_worked_example in
crates/cameleer-main/tests/docs_reference.rs), with a certificate made like the one above.
- route:
id: hello-tls
from:
uri: platform-http:/hello
steps:
- setBody:
constant: hello over TLS- route:
id: call-hello
from:
uri: direct:call
steps:
- to: "https://localhost:{{server.port}}/hello"cameleer run server.camel.yaml --port 8443 \
--prop camel.server.sslEnabled=true \
--prop camel.server.ssl.certFile=server.crt --prop camel.server.ssl.keyFile=server.key
curl --cacert server.crt https://localhost:8443/hello # hello over TLSThe client side, as a cameleer test file: with the trust store the call
succeeds, without it the certificate is refused.
- test:
name: the client trusts the server's certificate
routes: [client.camel.yaml]
properties:
server.port: 8443
camel.ssl.enabled: true
camel.ssl.trustStore: server.crt
steps:
- send: {to: direct:call}
expect: {body: hello over TLS}
- test:
name: without the trust store the certificate is refused
routes: [client.camel.yaml]
properties:
server.port: 8443
steps:
- send: {to: direct:call}
expect: {error: SSLPeerUnverifiedException}Secrets
Keep keys and passwords out of route files and images:
| source | placeholder | configured with |
|---|---|---|
| an environment variable | {{env:DB_PASSWORD}}, {{env:DB_PASSWORD:default}} | the process environment (Kubernetes secrets, Docker -e, --env-file) |
| AWS Secrets Manager | {{aws:orders/db-password}} | camel.vault.aws.* |
| Azure Key Vault | {{azure:db-password}} | camel.vault.azure.* |
| Google Secret Manager | {{gcp:db-password}} | camel.vault.gcp.* |
CAMEL_* environment variables also set configuration keys directly
(CAMEL_SSL_KEYSTORE_PASSWORD sets camel.ssl.keystorePassword). The vault placeholders and their options are in
cloud credentials; the AWS and Azure vaults can
restart routes when a secret changes. Cameleer does not decrypt Jasypt ENC(...) values.
Securing what Cameleer serves
| surface | built in | what to do |
|---|---|---|
REST and platform-http routes | TLS and mutual TLS; no user authentication | check a header or token in the route (choice on ${header.Authorization}, or a call to your identity provider), or put an API gateway or proxy in front |
| the MCP server | API keys, JWT (JWKS or HMAC), OAuth token introspection, protected resource metadata, audience checks, Origin checks | camel.server.mcp-* options: see MCP server authentication |
| AI tools | authorization policies, approvals, rate limits, redaction, an audit log | ai-tool options |
management endpoints (/q/*) | none | keep them on a private network, or move them to the management port bound to loopback (camel.management.enabled=true, camel.management.host=127.0.0.1); /q/dev can stop routes |
Messages. The crypto and PGP data formats encrypt and sign payloads, and XML Security signs and verifies XML documents.
The container. The image runs as a non-root user on a distroless base with no shell, and works
with a read-only root filesystem (--read-only, Kubernetes readOnlyRootFilesystem: true).
Evidence of what runs (confidential VMs)
In an Intel TDX or AMD SEV-SNP confidential VM, or a dstack CVM, Cameleer can prove to a client
what it is running: a hardware report bound to the client's nonce, to the server's TLS key and to a
manifest of the routes, configuration and MCP tools. It is on main for the next release; see
Attestation in confidential VMs, including what it does not prove yet.
Trust in the implementation
Cameleer's TLS, X.509, SSH and cryptography are written from scratch in Rust with no third-party
libraries. They are tested against OpenSSL (s_client and s_server), the JDK's JSSE, Go's
crypto/tls, and real servers such as PostgreSQL, RabbitMQ, ActiveMQ, Mosquitto, vsftpd,
Postfix and Dovecot (INTEROP.md). The code has not had an independent security
audit yet; it is on the roadmap. Report a
vulnerability privately as described in SECURITY.md.
Common questions
How do I enable HTTPS for a REST API?
Set camel.server.sslEnabled=true, camel.server.ssl.certFile and camel.server.ssl.keyFile
(or camel.server.ssl.keyStore). Every REST and platform-http route on that port is then
served over TLS.
How do I call a service that uses a private or self-signed CA?
Set camel.ssl.enabled=true and camel.ssl.trustStore to the CA's PEM file. Do not turn off
verification: trustAllCertificates exists for tests only.
Why does a camel.ssl.* setting have no effect?
The global parameters apply only with camel.ssl.enabled=true (or on an endpoint with
useGlobalSslContextParameters=true).
Why does HTTPS fail on Windows with "unable to find trusted issuer"?
Cameleer does not read the Windows certificate store. Set SSL_CERT_FILE to a PEM bundle of CA
certificates.
Does Cameleer support TLS 1.0 or 1.1?
No, only TLS 1.2 and 1.3.
How do I keep a password out of my route file?
Use {{env:NAME}} or a vault placeholder ({{aws:...}}, {{azure:...}}, {{gcp:...}}), and
pass the value through the environment or the secret store.
Related
- Configuration, cloud credentials, MCP server
- kafka (SSL and SASL), http, platform-http
- FAQ: which CA certificates does Cameleer trust?
- Attestation in confidential VMs: hardware evidence bound to the server's TLS key
Found a problem?
If something on this page doesn't match what Cameleer does, or you hit a bug while building
with it, please help fix it, whether you are a person or a coding agent:
open an issue that describes what you saw,
then fork the repository, fix it, add or update a test, run cargo test --workspace, and open
a pull request. CONTRIBUTING.md
explains the ground rules.