Cameleer

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.* (with camel.ssl.enabled=true) sets the certificates and trust store for every TLS client endpoint, sslContextParameters sets them per endpoint, and clientAuthentication=REQUIRE turns 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 wantset
HTTPS for REST and platform-http routescamel.server.sslEnabled=true, camel.server.ssl.certFile, camel.server.ssl.keyFile
a client endpoint to trust a private CAcamel.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 endpointan sslContextParameters bean, or the component's own SSL options
public CAs on WindowsSSL_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:

properties
camel.server.sslEnabled = true
camel.server.ssl.certFile = server.crt
camel.server.ssl.keyFile = server.key

The 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:

bash
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:

properties
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.key

camel.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
keyStorea combined PEM file (certificate chain and key), a PKCS#12 file or a JKS keystore
keystorePassword, keyPassword, keyStoreType, certAliashow to open the keystore and which entry to use
certFile, keyFilethe certificate chain and private key as separate PEM files
trustStoreCA certificates to trust: PEM files (comma separated), inline PEM, or a PKCS#12 or JKS file with trustStorePassword and trustStoreType
useSystemTrustStorealso trust the system store when a trustStore is given
secureSocketProtocol, secureSocketProtocols, minVersion, maxVersionthe TLS versions: TLSv1.2, TLSv1.3 (both by default; older versions are not implemented)
clientAuthenticationfor a server: NONE (default), WANT or REQUIRE a client certificate
crlFilea certificate revocation list for client certificates
serverName, hostnameVerificationthe SNI name to send, and whether to check the host name (on by default)
alpn, sessionTimeout, sessionResumptionALPN protocols and session resumption
trustAllCertificatesturn 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.

properties
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.crt

As 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:

exceptionwhen
SSLPeerUnverifiedExceptionthe server's certificate is not trusted, expired or for another host (unable to find trusted issuer 'CN=localhost' for 'CN=localhost')
SSLHandshakeExceptionthe handshake failed, for example the peer sent an alert (certificate_required, handshake_failure)
SSLExceptionother 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.

yaml example: tls-hello-server
- route:
    id: hello-tls
    from:
      uri: platform-http:/hello
      steps:
        - setBody:
            constant: hello over TLS
yaml example: tls-hello-client
- route:
    id: call-hello
    from:
      uri: direct:call
      steps:
        - to: "https://localhost:{{server.port}}/hello"
bash
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 TLS

The client side, as a cameleer test file: with the trust store the call succeeds, without it the certificate is refused.

yaml
- 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:

sourceplaceholderconfigured 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

surfacebuilt inwhat to do
REST and platform-http routesTLS and mutual TLS; no user authenticationcheck 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 serverAPI keys, JWT (JWKS or HMAC), OAuth token introspection, protected resource metadata, audience checks, Origin checkscamel.server.mcp-* options: see MCP server authentication
AI toolsauthorization policies, approvals, rate limits, redaction, an audit logai-tool options
management endpoints (/q/*)nonekeep 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.

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.

Edit this page on GitHub