Skip to content

Instantly share code, notes, and snippets.

@aKamrani
Created July 29, 2025 08:12
Show Gist options
  • Select an option

  • Save aKamrani/ac4b1503f7674fd916fa3c8cfe906b7c to your computer and use it in GitHub Desktop.

Select an option

Save aKamrani/ac4b1503f7674fd916fa3c8cfe906b7c to your computer and use it in GitHub Desktop.
OpenResty Dynamic SSL Certificate Selection Example
#------------------------------------------------------------------------------
# OpenResty Dynamic SSL Certificate Selection Example
#
# This conf snippet demonstrates how to dynamically serve different SSL
# certificates based on the SNI (Server Name Indication) hostname using Lua.
#
# Use this when you want to serve multiple domains with different SSL certs
# from a single OpenResty (nginx) instance, e.g. for XMPP, WebSocket, or HTTPS.
#
# This config is intended for use with the official OpenResty Docker image:
# Docker image: bitnami/openresty:latest
#
# Replace "domain-1.com" and "domain-2.com" and their certificate paths as needed.
#------------------------------------------------------------------------------
stream {
upstream ejabberd_pp_stream_upstream {
server bmp-ejabberd:5225;
}
lua_shared_dict certificates_cache 10m; # Shared memory for certificates
# Load certificates into shared dict at startup
init_by_lua_block {
local ssl = require "ngx.ssl"
-- Map of domains to their certificate and key paths
local certificates = {
["domain-1.com"] = {
cert = "/etc/nginx/ssl/domain-1.com/fullchain.pem",
key = "/etc/nginx/ssl/domain-1.com/privkey.pem"
},
["domain-2.com"] = {
cert = "/etc/nginx/ssl/domain-2.com/fullchain.pem",
key = "/etc/nginx/ssl/domain-2.com/privkey.pem"
}
}
local certificates_cache = ngx.shared.certificates_cache
for domain, paths in pairs(certificates) do
local cert_file = io.open(paths.cert, "r")
local key_file = io.open(paths.key, "r")
if not cert_file or not key_file then
ngx.log(ngx.ERR, "Failed to load SSL certificate files for ", domain)
else
local cert = cert_file:read("*a")
local key = key_file:read("*a")
cert_file:close()
key_file:close()
certificates_cache:set(domain .. ":cert", cert)
certificates_cache:set(domain .. ":key", key)
end
end
}
server {
listen 5225 ssl;
# Default fallback certificate (required by nginx)
ssl_certificate /etc/nginx/ssl/domain-1.com/fullchain.pem; # This is Default fallback SSL
ssl_certificate_key /etc/nginx/ssl/domain-1.com/privkey.pem; # This is Default fallback SSL
# Dynamically select certificate based on SNI
ssl_certificate_by_lua_block {
local ssl = require "ngx.ssl"
local domain = ssl.server_name()
if not domain then
ngx.log(ngx.ERR, "No SNI hostname provided by the client")
return ngx.exit(ngx.ERROR)
end
local certificates_cache = ngx.shared.certificates_cache
local cert = certificates_cache:get(domain .. ":cert")
local key = certificates_cache:get(domain .. ":key")
if not cert or not key then
ngx.log(ngx.ERR, "No SSL certificate found for SNI hostname: ", domain)
return ngx.exit(ngx.ERROR)
end
local der_cert, err = ssl.cert_pem_to_der(cert)
if not der_cert then
ngx.log(ngx.ERR, "Failed to convert certificate to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local der_key, err = ssl.priv_key_pem_to_der(key)
if not der_key then
ngx.log(ngx.ERR, "Failed to convert private key to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ok, err = ssl.clear_certs()
if not ok then
ngx.log(ngx.ERR, "Failed to clear existing certificates: ", err)
return ngx.exit(ngx.ERROR)
end
ok, err = ssl.set_der_cert(der_cert)
if not ok then
ngx.log(ngx.ERR, "Failed to set dynamic certificate: ", err)
return ngx.exit(ngx.ERROR)
end
ok, err = ssl.set_der_priv_key(der_key)
if not ok then
ngx.log(ngx.ERR, "Failed to set dynamic private key: ", err)
return ngx.exit(ngx.ERROR)
end
}
proxy_pass ejabberd_pp_stream_upstream;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment