Created
July 29, 2025 08:12
-
-
Save aKamrani/ac4b1503f7674fd916fa3c8cfe906b7c to your computer and use it in GitHub Desktop.
OpenResty Dynamic SSL Certificate Selection Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #------------------------------------------------------------------------------ | |
| # 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