-
Notifications
You must be signed in to change notification settings - Fork 0
TLS Configuration
Elio provides TLS/SSL support via OpenSSL. This guide covers TLS configuration for secure connections.
TLS (Transport Layer Security) provides:
- Encryption: Data is encrypted in transit
- Authentication: Verify server (and optionally client) identity
- Integrity: Detect tampering with data
Elio uses OpenSSL as its TLS backend. OpenSSL is mature, widely available on Linux distributions, and supports modern TLS 1.3 with ALPN for HTTP/2 negotiation. It provides industry-standard certificate management and is the most broadly deployed TLS library, ensuring compatibility with existing infrastructure and tooling.
TLS support requires:
- OpenSSL library (1.1.1+ recommended)
- CMake option:
-DELIO_ENABLE_TLS=ON
The tls_context manages TLS configuration:
#include <elio/tls/tls_context.hpp>
#include <elio/tls/tls_stream.hpp>
using namespace elio::tls;
// Create client TLS context
tls_context ctx(tls_mode::client);
// Use system CA certificates for verification
ctx.use_default_verify_paths();
// Enable peer verification (recommended)
ctx.set_verify_mode(verify_mode::peer);// Create server TLS context
tls_context ctx(tls_mode::server);
// Load server certificate and private key
ctx.load_certificate("/path/to/server.crt");
ctx.load_private_key("/path/to/server.key");
// Optionally require client certificates
ctx.set_verify_mode(verify_mode::fail_if_no_cert);
ctx.load_verify_locations("/path/to/ca.crt");enum class verify_mode {
none, // No verification (insecure!)
peer, // Verify peer certificate
fail_if_no_cert // Fail if peer has no certificate (server mode)
};
ctx.set_verify_mode(verify_mode::fail_if_no_cert);// Load specific CA certificate file
ctx.load_verify_locations("/path/to/ca-bundle.crt");
// Load directory of CA certificates
ctx.load_verify_locations("", "/etc/ssl/certs/");
// Load both file and directory
ctx.load_verify_locations("/path/to/ca-bundle.crt", "/etc/ssl/certs/");
// Use system default CA store
ctx.use_default_verify_paths();For mutual TLS authentication:
tls_context ctx(tls_mode::client);
// Load client certificate
ctx.load_certificate("/path/to/client.crt");
// Load client private key (with optional password for encrypted keys)
ctx.load_private_key("/path/to/client.key", "my_secret_password");ALPN is required for HTTP/2:
// For HTTP/2 client (comma-separated protocol list)
ctx.set_alpn_protocols("h2,http/1.1");
// After connection, check negotiated protocol
tls_stream stream = /* ... */;
std::string_view proto = stream.alpn_protocol();
if (proto == "h2") {
// Use HTTP/2
} else {
// Fall back to HTTP/1.1
}coro::task<void> connect_example() {
tls_context ctx(tls_mode::client);
ctx.use_default_verify_paths();
ctx.set_verify_mode(verify_mode::peer);
// Connect with TLS
auto stream = co_await tls_connect(ctx, "example.com", 443);
if (!stream) {
std::cerr << "TLS connection failed: " << strerror(errno) << std::endl;
co_return;
}
// Use the stream
co_await stream->write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
char buffer[4096];
auto result = co_await stream->read(buffer, sizeof(buffer));
if (result.success()) {
std::cout.write(buffer, result.bytes_transferred());
}
co_await stream->shutdown();
}coro::task<void> server_example() {
tls_context ctx(tls_mode::server);
ctx.load_certificate("/path/to/server.crt");
ctx.load_private_key("/path/to/server.key");
auto listener = tls_listener::bind(
net::ipv4_address("0.0.0.0", 8443),
ctx
);
if (!listener) {
std::cerr << "Failed to bind" << std::endl;
co_return;
}
while (true) {
auto client = co_await listener->accept();
if (client) {
handle_client(std::move(*client)).go();
}
}
}The TLS version is configured via the tls_version enum passed to the tls_context constructor:
// Require TLS 1.2 or higher (default)
tls_context ctx(tls_mode::client, tls_version::tls_1_2_or_higher);
// Require TLS 1.3 only
tls_context ctx(tls_mode::client, tls_version::tls_1_3_only);
// Exactly TLS 1.2
tls_context ctx(tls_mode::client, tls_version::tls_1_2);// Set preferred cipher suites (TLS 1.2)
ctx.set_ciphers("ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20");
// Set cipher suites for TLS 1.3
ctx.set_ciphersuites("TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256");coro::task<void> handle_tls_errors() {
tls_context ctx(tls_mode::client);
ctx.use_default_verify_paths();
ctx.set_verify_mode(verify_mode::peer);
auto stream = co_await tls_connect(ctx, "expired.badssl.com", 443);
if (!stream) {
// Check errno for specific error
// Common TLS-related errors:
// - Certificate verification failed
// - Connection refused
// - Handshake timeout
std::cerr << "TLS error: " << strerror(errno) << std::endl;
co_return;
}
// Check certificate verification result
long verify_result = stream->verify_result();
if (verify_result != X509_V_OK) {
std::cerr << "Certificate error: "
<< X509_verify_cert_error_string(verify_result)
<< std::endl;
}
}coro::task<void> inspect_connection() {
auto stream = co_await tls_connect(ctx, "example.com", 443);
if (!stream) co_return;
// TLS version
std::cout << "TLS Version: " << stream->version() << std::endl;
// Cipher suite
std::cout << "Cipher: " << stream->cipher() << std::endl;
// ALPN protocol
std::cout << "ALPN: " << stream->alpn_protocol() << std::endl;
// Peer certificate
X509* cert = stream->peer_certificate();
if (cert) {
// Extract certificate info
X509_NAME* subject = X509_get_subject_name(cert);
char buf[256];
X509_NAME_oneline(subject, buf, sizeof(buf));
std::cout << "Subject: " << buf << std::endl;
X509_free(cert);
}
}http::client_config config;
config.verify_certificate = true; // Enable verification
http::client client(ctx, config);
// Access and customize TLS context
auto& tls_ctx = client.tls_context();
tls_ctx.load_verify_locations("/path/to/custom-ca.crt");
auto resp = co_await client.get("https://example.com/");h2_client client(ctx);
// Configure TLS for HTTP/2
auto& tls_ctx = client.tls_context();
// HTTP/2 requires ALPN
// (automatically configured by h2_client)
// Get negotiated protocol after connection
// h2_client uses HTTP/2 if server supports it-
Always verify certificates in production
- Only disable for testing/development
- Use
verify_mode::peerat minimum
-
Use modern TLS versions
- Require TLS 1.2+ for security
- Consider TLS 1.3 for best security and performance
-
Configure strong cipher suites
- Prefer ECDHE for forward secrecy
- Use AESGCM or ChaCha20 for encryption
-
Handle errors gracefully
- Check return values
- Log certificate verification failures
-
Keep certificates updated
- Monitor certificate expiration
- Use automated renewal (Let's Encrypt, etc.)
-
Protect private keys
- Use file permissions (chmod 600)
- Consider hardware security modules for high-security applications
Use badssl.com for testing various TLS scenarios:
// Test certificate verification
co_await tls_connect(ctx, "expired.badssl.com", 443); // Should fail
co_await tls_connect(ctx, "wrong.host.badssl.com", 443); // Should fail
co_await tls_connect(ctx, "self-signed.badssl.com", 443); // Should fail
co_await tls_connect(ctx, "sha256.badssl.com", 443); // Should succeed