This guide covers TLS/SSL configuration for both HTTPS clients (making requests) and HTTPS servers in ICICLE Insights.
Current implementation: The
github/tasks.cppHTTP client usesClient->configure_system_ca_certificates()which readsSSL_CERT_FILEfrom the environment automatically. See http-client-guide.md for the recommended approach. This guide covers the underlying ASIO mechanisms for reference.
- Client-Side HTTPS (Making Requests)
- Server-Side HTTPS (Serving Content)
- Certificate Management
- Troubleshooting
macOS (Homebrew):
brew install ca-certificates openssl@3Linux (Ubuntu/Debian):
sudo apt-get install ca-certificates opensslThe ca-certificates package provides the trusted root certificate bundle needed to verify SSL/TLS connections.
When using glz::http_client to make HTTPS requests, you need to configure the SSL context.
Using Config (Recommended):
#include <glaze/net/http_client.hpp>
#include <asio/ssl.hpp>
#include <spdlog/spdlog.h>
#include <system_error>
#include "core/config.hpp"
// Load config with SSL_CERT_FILE from environment
auto Config = insights::core::Config::load();
glz::http_client Client;
// Configure SSL to use system certificates
Client.configure_ssl_context([&Config](auto& Ctx) {
std::error_code Ec;
// Use SSL_CERT_FILE from config if set
if (Config->SslCertFile) {
Ctx.load_verify_file(*Config->SslCertFile, Ec);
if (Ec) {
spdlog::warn("Failed to load CA certificates from SSL_CERT_FILE ({}): {}",
*Config->SslCertFile, Ec.message());
}
}
// Fall back to default paths if not set or failed
if (!Config->SslCertFile || Ec) {
Ctx.set_default_verify_paths(Ec);
if (Ec) {
spdlog::warn("Failed to set default verify paths: {}", Ec.message());
}
}
// Enable certificate verification (recommended for production)
Ctx.set_verify_mode(asio::ssl::verify_peer);
});
// Make HTTPS request
auto Response = Client.get("https://api.github.com/repos/foo/bar", Headers);Direct Hardcoded Path (Not Recommended):
glz::http_client Client;
Client.configure_ssl_context([](auto& Ctx) {
std::error_code Ec;
Ctx.load_verify_file("/opt/homebrew/etc/ca-certificates/cert.pem", Ec);
if (Ec) {
Ctx.set_default_verify_paths(Ec);
}
Ctx.set_verify_mode(asio::ssl::verify_peer);
});The recommended way to configure certificate paths is via the SSL_CERT_FILE environment variable:
# In .env file
SSL_CERT_FILE=/opt/homebrew/etc/ca-certificates/cert.pemOr export it before running:
export SSL_CERT_FILE=/opt/homebrew/etc/ca-certificates/cert.pem
just runThe application will automatically load this from Config.SslCertFile and use it for HTTPS requests.
| Platform | Certificate Path |
|---|---|
| macOS (Homebrew) | /opt/homebrew/etc/ca-certificates/cert.pem |
| macOS (Intel) | /usr/local/etc/ca-certificates/cert.pem |
| Ubuntu/Debian | /etc/ssl/certs/ca-certificates.crt |
| RHEL/CentOS | /etc/pki/tls/certs/ca-bundle.crt |
| Alpine Linux | /etc/ssl/certs/ca-certificates.crt |
For code that runs on multiple platforms:
Client.configure_ssl_context([](auto& Ctx) {
std::error_code Ec;
// Try platform-specific paths
const std::vector<std::string> CertPaths = {
"/opt/homebrew/etc/ca-certificates/cert.pem", // macOS (Apple Silicon)
"/usr/local/etc/ca-certificates/cert.pem", // macOS (Intel)
"/etc/ssl/certs/ca-certificates.crt", // Ubuntu/Debian/Alpine
"/etc/pki/tls/certs/ca-bundle.crt", // RHEL/CentOS
};
bool Loaded = false;
for (const auto& Path : CertPaths) {
Ctx.load_verify_file(Path, Ec);
if (!Ec) {
Loaded = true;
break;
}
}
if (!Loaded) {
spdlog::warn("No certificate bundle found, trying default paths");
Ctx.set_default_verify_paths(Ec);
}
Ctx.set_verify_mode(asio::ssl::verify_peer);
});Production (Recommended):
Ctx.set_verify_mode(asio::ssl::verify_peer); // Verify certificatesDevelopment Only (NOT SECURE):
// WARNING: Only for testing with self-signed certificates
// This makes you vulnerable to man-in-the-middle attacks
Ctx.set_verify_mode(asio::ssl::verify_none);Once configured, HTTPS works automatically:
// HTTP request
auto Response = Client.get("http://example.com/api", Headers);
// HTTPS request (uses SSL/TLS automatically)
auto Response = Client.get("https://example.com/api", Headers);The client detects the https:// protocol and handles:
- SSL socket creation
- TLS handshake with SNI (Server Name Indication)
- Certificate verification
- TLS 1.2/1.3 negotiation
You need an SSL certificate and private key. For development:
# Generate self-signed certificate (development only)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=localhost"For production, use certificates from:
- Let's Encrypt (free, automated)
- Commercial CA (DigiCert, GlobalSign, etc.)
- Internal CA for private networks
#include <glaze/net/http_router.hpp>
#include <asio/ssl.hpp>
// Create SSL context for server
asio::ssl::context SslContext(asio::ssl::context::tlsv12_server);
// Load certificate and private key
SslContext.use_certificate_chain_file("path/to/cert.pem");
SslContext.use_private_key_file("path/to/key.pem", asio::ssl::context::pem);
// Optional: Configure cipher suites (for security)
SslContext.set_options(
asio::ssl::context::default_workarounds |
asio::ssl::context::no_sslv2 |
asio::ssl::context::no_sslv3 |
asio::ssl::context::single_dh_use
);
// Create HTTPS server with glaze
// TODO: Add glaze HTTPS server example when implementedTwo files are required:
- Certificate Chain (
cert.pem): Your certificate + intermediate certificates - Private Key (
key.pem): Your private key (keep secure!)
Important: Never commit private keys to version control. Use environment variables or secure key management:
auto CertPath = std::getenv("SSL_CERT_FILE");
auto KeyPath = std::getenv("SSL_KEY_FILE");
if (!CertPath || !KeyPath) {
spdlog::error("SSL_CERT_FILE and SSL_KEY_FILE must be set");
return 1;
}
SslContext.use_certificate_chain_file(CertPath);
SslContext.use_private_key_file(KeyPath, asio::ssl::context::pem);Add to .env:
# HTTPS Server Configuration
SSL_CERT_FILE=/path/to/cert.pem
SSL_KEY_FILE=/path/to/key.pem
# Client Configuration (if needed)
SSL_CERT_DIR=/etc/ssl/certsLet's Encrypt (Recommended for Production):
# Install certbot
brew install certbot # macOS
sudo apt-get install certbot # Ubuntu
# Obtain certificate
sudo certbot certonly --standalone -d yourdomain.com
# Certificates will be in:
# /etc/letsencrypt/live/yourdomain.com/fullchain.pem (certificate)
# /etc/letsencrypt/live/yourdomain.com/privkey.pem (private key)Let's Encrypt certificates expire every 90 days. Set up auto-renewal:
# Test renewal
sudo certbot renew --dry-run
# Add to crontab for automatic renewal
0 0 * * * certbot renew --quiet && systemctl reload your-serviceVerify your certificate setup:
# Check certificate expiration
openssl x509 -in cert.pem -noout -dates
# Verify certificate chain
openssl verify -CAfile /opt/homebrew/etc/ca-certificates/cert.pem cert.pem
# Test HTTPS connection
curl -v https://localhost:3000Cause: OpenSSL can't find or verify the CA certificate bundle.
Solutions:
-
Install CA certificates:
brew install ca-certificates # macOS sudo apt-get install ca-certificates # Linux
-
Explicitly load certificate file:
Ctx.load_verify_file("/opt/homebrew/etc/ca-certificates/cert.pem"); -
Set environment variable:
export SSL_CERT_FILE=/opt/homebrew/etc/ca-certificates/cert.pem -
Check file exists:
ls -la /opt/homebrew/etc/ca-certificates/cert.pem
Cause: Client is trying SSL/TLS on a non-HTTPS port, or server is not configured for HTTPS.
Solution:
- Ensure URL uses
https://nothttp:// - Verify server is listening on HTTPS port
Cause: TLS version mismatch or cipher suite incompatibility.
Solutions:
-
Use modern TLS versions:
asio::ssl::context SslContext(asio::ssl::context::tlsv12_server); // TLS 1.2+
-
Check supported ciphers:
openssl ciphers -v
Cause: Certificate validity period has passed.
Solution:
- Renew certificate with Let's Encrypt:
certbot renew - Replace with new certificate from CA
Enable verbose logging:
// In client code
spdlog::set_level(spdlog::level::debug);
Client.configure_ssl_context([](auto& Ctx) {
std::error_code Ec;
Ctx.load_verify_file(CertPath, Ec);
if (Ec) {
spdlog::error("Failed to load certs: {} ({})", Ec.message(), Ec.value());
} else {
spdlog::debug("Successfully loaded certificates from {}", CertPath);
}
});Test with OpenSSL command line:
# Test connection to server
openssl s_client -connect api.github.com:443 -servername api.github.com
# Check what certificates are sent
openssl s_client -connect api.github.com:443 -showcerts✅ Do:
- Always use
verify_peerin production - Keep CA certificate bundle updated
- Use TLS 1.2 or higher
- Validate hostnames (SNI)
❌ Don't:
- Use
verify_nonein production - Disable certificate verification
- Use outdated TLS versions (SSLv3, TLS 1.0, TLS 1.1)
✅ Do:
- Use certificates from trusted CAs in production
- Rotate certificates before expiration
- Use strong cipher suites
- Enable HSTS (HTTP Strict Transport Security)
- Keep private keys secure (never commit to git)
❌ Don't:
- Use self-signed certificates in production
- Share private keys
- Use weak ciphers (RC4, DES, MD5)
- Expose private keys in logs or error messages
// Client (production)
Client.configure_ssl_context([](auto& Ctx) {
Ctx.load_verify_file("/opt/homebrew/etc/ca-certificates/cert.pem");
Ctx.set_verify_mode(asio::ssl::verify_peer);
// Use modern TLS versions only
Ctx.set_options(
asio::ssl::context::no_sslv2 |
asio::ssl::context::no_sslv3 |
asio::ssl::context::no_tlsv1 |
asio::ssl::context::no_tlsv1_1
);
});
// Server (production)
asio::ssl::context ServerCtx(asio::ssl::context::tlsv12_server);
ServerCtx.use_certificate_chain_file(std::getenv("SSL_CERT_FILE"));
ServerCtx.use_private_key_file(std::getenv("SSL_KEY_FILE"), asio::ssl::context::pem);
ServerCtx.set_options(
asio::ssl::context::default_workarounds |
asio::ssl::context::no_sslv2 |
asio::ssl::context::no_sslv3 |
asio::ssl::context::single_dh_use
);