Commit b5c3c86aa0 for freeswitch.com
commit b5c3c86aa078b0e63cdee4361f4b73f5991de0b9
Author: Andrey Volk <andywolk@gmail.com>
Date: Tue May 26 00:25:56 2026 +0300
[mod_commands, mod_verto] Add new reloadcert API and let mod_verto reload certificates on the fly without disconnects. (#3033)
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index 294c64ee3f..a68d5bbf70 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -2075,6 +2075,7 @@ typedef uint32_t switch_io_flag_t;
SWITCH_EVENT_CALL_DETAIL
SWITCH_EVENT_DEVICE_STATE
SWITCH_EVENT_SHUTDOWN_REQUESTED - Shutdown of the system has been requested
+ SWITCH_EVENT_CERT_RELOAD - SSL/TLS certificates reload has been requested
SWITCH_EVENT_ALL - All events at once
</pre>
@@ -2172,6 +2173,7 @@ typedef enum {
SWITCH_EVENT_DEVICE_STATE,
SWITCH_EVENT_TEXT,
SWITCH_EVENT_SHUTDOWN_REQUESTED,
+ SWITCH_EVENT_CERT_RELOAD,
SWITCH_EVENT_ALL
} switch_event_types_t;
diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c
index 22e25ea8a6..b44035044a 100644
--- a/src/mod/applications/mod_commands/mod_commands.c
+++ b/src/mod/applications/mod_commands/mod_commands.c
@@ -2860,6 +2860,22 @@ SWITCH_STANDARD_API(reload_xml_function)
return SWITCH_STATUS_SUCCESS;
}
+SWITCH_STANDARD_API(reload_cert_function)
+{
+ switch_event_t *event;
+
+ if (switch_event_create(&event, SWITCH_EVENT_CERT_RELOAD) == SWITCH_STATUS_SUCCESS) {
+ switch_event_fire(&event);
+ stream->write_function(stream, "+OK cert reload event sent\n");
+
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ stream->write_function(stream, "-ERR failed to create event\n");
+
+ return SWITCH_STATUS_FALSE;
+}
+
#define KILL_SYNTAX "<uuid> [cause]"
SWITCH_STANDARD_API(kill_function)
{
@@ -7656,6 +7672,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
SWITCH_ADD_API(commands_api_interface, "reloadacl", "Reload ACL", reload_acl_function, "");
SWITCH_ADD_API(commands_api_interface, "reload", "Reload module", reload_function, UNLOAD_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "reloadxml", "Reload XML", reload_xml_function, "");
+ SWITCH_ADD_API(commands_api_interface, "reloadcert", "Reload SSL/TLS certificates", reload_cert_function, "");
SWITCH_ADD_API(commands_api_interface, "replace", "Replace a string", replace_function, "<data>|<string1>|<string2>");
SWITCH_ADD_API(commands_api_interface, "say_string", "", say_string_function, SAY_STRING_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "sched_api", "Schedule an api command", sched_api_function, SCHED_SYNTAX);
@@ -7831,6 +7848,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
switch_console_set_complete("add nat_map status");
switch_console_set_complete("add reload ::console::list_loaded_modules");
switch_console_set_complete("add reloadacl reloadxml");
+ switch_console_set_complete("add reloadcert");
switch_console_set_complete("add show aliases");
switch_console_set_complete("add show api");
switch_console_set_complete("add show application");
diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c
index 1363416139..113a21c579 100644
--- a/src/mod/endpoints/mod_verto/mod_verto.c
+++ b/src/mod/endpoints/mod_verto/mod_verto.c
@@ -150,108 +150,162 @@ static void verto_deinit_ssl(verto_profile_t *profile)
}
}
-static void close_file(ks_socket_t *sock)
+static SSL_CTX *verto_create_ssl_ctx(verto_profile_t *profile, const char **errp)
{
- if (*sock != KS_SOCK_INVALID) {
-#ifndef WIN32
- close(*sock);
-#else
- closesocket(*sock);
-#endif
- *sock = KS_SOCK_INVALID;
- }
-}
+ SSL_CTX *ctx = SSL_CTX_new(profile->ssl_method);
-static void close_socket(ks_socket_t *sock)
-{
- if (*sock != KS_SOCK_INVALID) {
- shutdown(*sock, 2);
- close_file(sock);
- }
-}
-
-void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id, void *user_data);
+ if (!ctx) {
+ *errp = "Failed to create SSL context";
-static int verto_init_ssl(verto_profile_t *profile)
-{
- const char *err;
- int i = 0;
-
- profile->ssl_method = SSLv23_server_method(); /* create server instance */
- profile->ssl_ctx = SSL_CTX_new(profile->ssl_method); /* create context */
- profile->ssl_ready = 1;
- assert(profile->ssl_ctx);
+ return NULL;
+ }
/* Disable SSLv2 */
- SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
/* Disable SSLv3 */
- SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv3);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
/* Disable TLSv1 */
- SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_TLSv1);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
/* Disable Compression CRIME (Compression Ratio Info-leak Made Easy) */
- SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_COMPRESSION);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
- /* set the local certificate from CertFile */
if (!zstr(profile->chain)) {
if (switch_file_exists(profile->chain, NULL) != SWITCH_STATUS_SUCCESS) {
- err = "SUPPLIED CHAIN FILE NOT FOUND\n";
+ *errp = "SUPPLIED CHAIN FILE NOT FOUND";
goto fail;
}
- if (!SSL_CTX_use_certificate_chain_file(profile->ssl_ctx, profile->chain)) {
- err = "CERT CHAIN FILE ERROR";
+ if (!SSL_CTX_use_certificate_chain_file(ctx, profile->chain)) {
+ *errp = "CERT CHAIN FILE ERROR";
goto fail;
}
}
if (switch_file_exists(profile->cert, NULL) != SWITCH_STATUS_SUCCESS) {
- err = "SUPPLIED CERT FILE NOT FOUND\n";
+ *errp = "SUPPLIED CERT FILE NOT FOUND";
goto fail;
}
- if (!SSL_CTX_use_certificate_file(profile->ssl_ctx, profile->cert, SSL_FILETYPE_PEM)) {
- err = "CERT FILE ERROR";
+ if (!SSL_CTX_use_certificate_file(ctx, profile->cert, SSL_FILETYPE_PEM)) {
+ *errp = "CERT FILE ERROR";
goto fail;
}
- /* set the private key from KeyFile */
-
if (switch_file_exists(profile->key, NULL) != SWITCH_STATUS_SUCCESS) {
- err = "SUPPLIED KEY FILE NOT FOUND\n";
+ *errp = "SUPPLIED KEY FILE NOT FOUND";
goto fail;
}
- if (!SSL_CTX_use_PrivateKey_file(profile->ssl_ctx, profile->key, SSL_FILETYPE_PEM)) {
- err = "PRIVATE KEY FILE ERROR";
+ if (!SSL_CTX_use_PrivateKey_file(ctx, profile->key, SSL_FILETYPE_PEM)) {
+ *errp = "PRIVATE KEY FILE ERROR";
goto fail;
}
- /* verify private key */
- if ( !SSL_CTX_check_private_key(profile->ssl_ctx) ) {
- err = "PRIVATE KEY FILE ERROR";
+ if (!SSL_CTX_check_private_key(ctx)) {
+ *errp = "PRIVATE KEY FILE ERROR";
goto fail;
}
- SSL_CTX_set_cipher_list(profile->ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH");
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!DSS:!aNULL@STRENGTH");
- return 1;
+ return ctx;
fail:
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err);
+ SSL_CTX_free(ctx);
- profile->ssl_ready = 0;
- verto_deinit_ssl(profile);
+ return NULL;
+}
- for (i = 0; i < profile->i; i++) {
- if (profile->ip[i].secure) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL NOT ENABLED FOR LISTENER %s:%d. REVERTING TO WS\n",
- profile->ip[i].local_ip, profile->ip[i].local_port);
- profile->ip[i].secure = 0;
+static int verto_reload_ssl(verto_profile_t *profile)
+{
+ const char *err = NULL;
+ SSL_CTX *new_ctx = verto_create_ssl_ctx(profile, &err);
+
+ if (!new_ctx) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL reload failed for profile %s: %s\n", profile->name, err);
+
+ return 0;
+ }
+
+ SSL_CTX_free(profile->ssl_ctx);
+
+ profile->ssl_ctx = new_ctx;
+ profile->ssl_ready = 1;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SSL certificates reloaded for profile %s\n", profile->name);
+
+ return 1;
+}
+
+static void cert_reload_handler(switch_event_t *event)
+{
+ verto_profile_t *p;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event received, processing\n");
+
+ switch_mutex_lock(verto_globals.mutex);
+
+ for (p = verto_globals.profile_head; p; p = p->next) {
+ if (p->running) {
+ switch_thread_rwlock_wrlock(p->rwlock);
+ verto_reload_ssl(p);
+ switch_thread_rwlock_unlock(p->rwlock);
}
}
- return 0;
+ switch_mutex_unlock(verto_globals.mutex);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event processed\n");
+}
+
+static void close_file(ks_socket_t *sock)
+{
+ if (*sock != KS_SOCK_INVALID) {
+#ifndef WIN32
+ close(*sock);
+#else
+ closesocket(*sock);
+#endif
+ *sock = KS_SOCK_INVALID;
+ }
+}
+
+static void close_socket(ks_socket_t *sock)
+{
+ if (*sock != KS_SOCK_INVALID) {
+ shutdown(*sock, 2);
+ close_file(sock);
+ }
+}
+
+void verto_broadcast(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id, void *user_data);
+
+static int verto_init_ssl(verto_profile_t *profile)
+{
+ const char *err = NULL;
+ int i = 0;
+
+ profile->ssl_method = SSLv23_server_method();
+ profile->ssl_ctx = verto_create_ssl_ctx(profile, &err);
+
+ if (!profile->ssl_ctx) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err);
+
+ profile->ssl_ready = 0;
+
+ for (i = 0; i < profile->i; i++) {
+ if (profile->ip[i].secure) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SSL NOT READY FOR LISTENER %s:%d. USE reloadcert AFTER FIXING CERTIFICATES\n",
+ profile->ip[i].local_ip, profile->ip[i].local_port);
+ }
+ }
+
+ return 0;
+ }
+
+ profile->ssl_ready = 1;
+ return 1;
}
@@ -1972,6 +2026,7 @@ static void client_run(jsock_t *jsock)
ks_pool_open(&jsock->kpool);
+ switch_thread_rwlock_rdlock(jsock->profile->rwlock);
#if defined(KS_VERSION_NUM) && KS_VERSION_NUM >= 20000
params = ks_json_create_object();
ks_json_add_number_to_object(params, "payload_size_max", 1000000);
@@ -1979,8 +2034,10 @@ static void client_run(jsock_t *jsock)
#else
if (kws_init(&jsock->ws, jsock->client_socket, (jsock->ptype & PTYPE_CLIENT_SSL) ? jsock->profile->ssl_ctx : NULL, 0, flags, jsock->kpool) != KS_STATUS_SUCCESS) {
#endif
+ switch_thread_rwlock_unlock(jsock->profile->rwlock);
log_and_exit(SWITCH_LOG_NOTICE, "%s WS SETUP FAILED\n", jsock->name);
}
+ switch_thread_rwlock_unlock(jsock->profile->rwlock);
if (kws_test_flag(jsock->ws, KWS_HTTP)) {
http_run(jsock);
@@ -4690,7 +4747,7 @@ static int start_jsock(verto_profile_t *profile, ks_socket_t sock, int family)
for (i = 0; i < profile->i; i++) {
if ( profile->server_socket[i] == sock ) {
- if (profile->ip[i].secure) {
+ if (profile->ip[i].secure && profile->ssl_ready) {
ptype = PTYPE_CLIENT_SSL;
}
break;
@@ -6887,6 +6944,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load)
switch_core_register_secondary_recover_callback(modname, verto_recover_callback);
+ switch_event_bind(modname, SWITCH_EVENT_CERT_RELOAD, SWITCH_EVENT_SUBCLASS_ANY, cert_reload_handler, NULL);
+
if (verto_globals.enable_presence) {
switch_event_bind(modname, SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, presence_event_handler, NULL);
}
@@ -6922,6 +6981,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown)
switch_core_hash_destroy(&json_GLOBALS.store_hash);
switch_event_channel_unbind(NULL, verto_broadcast, NULL);
+ switch_event_unbind_callback(cert_reload_handler);
switch_event_unbind_callback(presence_event_handler);
switch_event_unbind_callback(event_handler);
diff --git a/src/switch_event.c b/src/switch_event.c
index 8a8c8d6c35..25bf961512 100644
--- a/src/switch_event.c
+++ b/src/switch_event.c
@@ -227,6 +227,7 @@ static char *EVENT_NAMES[] = {
"DEVICE_STATE",
"TEXT",
"SHUTDOWN_REQUESTED",
+ "CERT_RELOAD",
"ALL"
};