Skip to content

Commit 0230404

Browse files
committed
Fixes #1741: add ordinal attributes to sslProfile
Include mechanism for propagating updates to affected connectors. Advertise the connection's ordinal to the peer router via the properties map in the Open performative.
1 parent 056111b commit 0230404

File tree

19 files changed

+304
-69
lines changed

19 files changed

+304
-69
lines changed

include/qpid/dispatch/amqp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ extern const char * const QD_CONNECTION_PROPERTY_ADAPTOR_KEY;
165165
extern const char * const QD_CONNECTION_PROPERTY_TCP_ADAPTOR_VALUE;
166166
extern const char * const QD_CONNECTION_PROPERTY_ANNOTATIONS_VERSION_KEY;
167167
extern const char * const QD_CONNECTION_PROPERTY_ACCESS_ID;
168+
extern const char * const QD_CONNECTION_PROPERTY_TLS_ORDINAL;
168169
/// @}
169170

170171
/** @name Terminus Addresses */

include/qpid/dispatch/protocol_adaptor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,7 @@ qdr_connection_info_t *qdr_connection_info(bool is_encrypted,
928928
const char *user,
929929
const char *container,
930930
pn_data_t *connection_properties,
931+
uint64_t tls_ordinal,
931932
int ssl_ssf,
932933
bool ssl,
933934
const char *version,

include/qpid/dispatch/tls_common.h

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,17 @@ struct qd_ssl2_profile_t {
8484
char *uid_name_mapping_file;
8585

8686
/**
87-
* version: Version assigned to the current configuration
88-
* oldest_valid_version: Previous sslProfile updates with versions values < oldest_valid_version have expired.
87+
* Certificate Rotation
88+
* ordinal: Identifies the current configuration's revision
89+
* oldest_valid_ordinal: Previous configurations with ordinal values < oldest_valid_ordinal have expired.
8990
*/
90-
long version;
91-
long oldest_valid_version;
91+
uint64_t ordinal;
92+
uint64_t oldest_valid_ordinal;
9293
};
9394

9495
/**
95-
* Create a new TLS qd_tls_config_t instance with the given configuration
96+
* Create a new TLS qd_tls_config_t instance with the given configuration. This is called when a listener/connector
97+
* record is created.
9698
*
9799
* @param ssl_profile_name the name of the sslProfile configuration to use
98100
* @param p_type protocol type for the child connections (TCP or AMQP)
@@ -117,6 +119,45 @@ qd_tls_config_t *qd_tls_config(const char *ssl_profile_name,
117119
void qd_tls_config_decref(qd_tls_config_t *config);
118120

119121

122+
/**
123+
* Get the values of the ordinal/oldestValidOrdinal assocated with the TLS configuration.
124+
*
125+
* Note: To avoid races this function can only be called from the context of the management thread.
126+
*
127+
* @param config The qd_tls_config_t to query.
128+
* @return the value for the ordinal/lastValidOrdinal sslProfile attributes used by this config
129+
*/
130+
uint64_t qd_tls_config_get_ordinal(const qd_tls_config_t *config);
131+
uint64_t qd_tls_config_get_oldest_valid_ordinal(const qd_tls_config_t *config);
132+
133+
134+
/** Register a callback to monitor updates to the TLS configuration
135+
*
136+
* Register a callback function that will be invoked by the management thread whenever the sslProfile record associated
137+
* with the qd_tls_config_t is updated. Note that the update callback is invoked on the management thread while the
138+
* qd_tls_config is locked. This prevents new TLS sessions from being created using the updated configuration until
139+
* after the update callback returns.
140+
*
141+
* @param update_cb_context Opaque handle passed to update callback function.
142+
* @param update_cb Optional callback when the sslProfile has been updated by management.
143+
*/
144+
typedef void (*qd_tls_config_update_cb_t)(const qd_tls_config_t *config,
145+
void *update_cb_context);
146+
void qd_tls_config_register_update_callback(qd_tls_config_t *config, void *update_cb_context,
147+
qd_tls_config_update_cb_t update_cb);
148+
149+
150+
/**
151+
* Cancel the update callback.
152+
*
153+
* Deregisters the update callback provided in qd_tls_config(). No further calls to the callback will occur on return
154+
* from this call. Can only be called from the context of the management thread.
155+
*
156+
* @param config The qd_tls_config_t whose callback will be cancelled
157+
*/
158+
void qd_tls_config_cancel_update_callback(qd_tls_config_t *config);
159+
160+
120161
/**
121162
* Release a TLS session context.
122163
*
@@ -153,6 +194,13 @@ char *qd_tls_session_get_protocol_ciphers(const qd_tls_session_t *session);
153194
*/
154195
int qd_tls_session_get_ssf(const qd_tls_session_t *session);
155196

197+
/**
198+
* Get the ordinal of the sslProfile used to create this session
199+
*
200+
* @param session to be queried
201+
* @return the value of the sslProfile ordinal associated with this session.
202+
*/
203+
uint64_t qd_tls_session_get_profile_ordinal(const qd_tls_session_t *session);
156204

157205
/**
158206
* Fill out the given *profile with the configuration from the named sslProfile record.

python/skupper_router/management/skrouter.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -725,15 +725,15 @@
725725
"description": "The absolute path to the file containing the unique id to display name mapping",
726726
"create": true
727727
},
728-
"version": {
728+
"ordinal": {
729729
"type": "integer",
730-
"description": "RESERVED FOR FUTURE USE",
730+
"description": "Indicates the revision of the TLS certificates provided. Used by certificate rotation. Each time the TLS certificates associated with this profile are updated the ordinal value will be increased. The most recent version of the TLS credentials will have the highest numerical ordinal value.",
731731
"create": true,
732732
"update": true
733733
},
734-
"oldestValidVersion": {
734+
"oldestValidOrdinal": {
735735
"type": "integer",
736-
"description": "RESERVED FOR FUTURE USE",
736+
"description": "Used by certificate rotation to remove connections that were created using TLS certificates that are no longer considered valid. Any active connections based on TLS certificates an ordinal value less than oldesValidOrdinal will be immediately terminated by the router.",
737737
"create": true,
738738
"update": true
739739
}

src/adaptors/amqp/amqp_adaptor.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,7 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
15671567
(char*) user,
15681568
container,
15691569
props,
1570+
qd_tls_session_get_profile_ordinal(conn->ssl),
15701571
ssl_ssf,
15711572
!!conn->ssl,
15721573
rversion,

src/adaptors/amqp/connection_manager.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ QD_EXPORT qd_listener_t *qd_dispatch_configure_listener(qd_dispatch_t *qd, qd_en
8080
qd_listener_decref(li);
8181
return 0;
8282
}
83+
li->tls_ordinal = qd_tls_config_get_ordinal(li->tls_config);
84+
li->tls_oldest_valid_ordinal = qd_tls_config_get_oldest_valid_ordinal(li->tls_config);
8385
}
8486

8587
char *fol = qd_entity_opt_string(entity, "failoverUrls", 0);
@@ -285,8 +287,8 @@ QD_EXPORT qd_error_t qd_entity_refresh_connector(qd_entity_t* entity, void *impl
285287

286288
QD_EXPORT qd_connector_config_t *qd_dispatch_configure_connector(qd_dispatch_t *qd, qd_entity_t *entity)
287289
{
288-
qd_connection_manager_t *cm = qd->connection_manager;
289-
qd_connector_config_t *ctor_config = qd_connector_config_create(qd, entity);
290+
qd_connection_manager_t *cm = qd->connection_manager;
291+
qd_connector_config_t *ctor_config = qd_connector_config_create(qd, entity);
290292
if (!ctor_config) {
291293
return 0;
292294
}

src/adaptors/amqp/qd_connection.c

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,14 @@ static void connection_wake(qd_connection_t *ctx)
7474
}
7575

7676

77-
static void decorate_connection(qd_connection_t *ctx, const qd_server_config_t *config)
77+
/** Setup connection capabilities and properties.
78+
* These are communicated to the peer via the Open performative.
79+
*/
80+
static void decorate_connection(qd_connection_t *ctx)
7881
{
79-
qd_server_t *qd_server = ctx->server;
80-
pn_connection_t *conn = ctx->pn_conn;
81-
//
82-
// Set the container name
83-
//
84-
pn_connection_set_container(conn, qd_server_get_container_name(qd_server));
82+
pn_connection_t *conn = ctx->pn_conn;
83+
const qd_server_config_t *config = qd_connection_config(ctx);
8584

86-
//
87-
// Advertise our container capabilities.
88-
//
8985
{
9086
// offered: extension capabilities this router supports
9187
pn_data_t *ocaps = pn_connection_offered_capabilities(conn);
@@ -151,10 +147,17 @@ static void decorate_connection(qd_connection_t *ctx, const qd_server_config_t *
151147
}
152148

153149
if (ctx->connector && (ctx->connector->is_data_connector || !!ctx->connector->ctor_config->data_connection_count)) {
150+
uint64_t tls_ordinal;
154151
pn_data_put_symbol(pn_connection_properties(conn),
155152
pn_bytes(strlen(QD_CONNECTION_PROPERTY_GROUP_CORRELATOR_KEY), QD_CONNECTION_PROPERTY_GROUP_CORRELATOR_KEY));
156153
pn_data_put_string(pn_connection_properties(conn),
157154
pn_bytes(strnlen(ctx->group_correlator, QD_DISCRIMINATOR_SIZE - 1), ctx->group_correlator));
155+
156+
if (qd_connection_get_tls_ordinal(qd_conn, &tls_ordinal)) {
157+
pn_data_put_symbol(pn_connection_properties(conn),
158+
pn_bytes(strlen(QD_CONNECTION_PROPERTY_TLS_ORDINAL), QD_CONNECTION_PROPERTY_TLS_ORDINAL));
159+
pn_data_put_ulong(pn_connection_properties(conn), tls_ordinal);
160+
}
158161
}
159162

160163
if (ctx->listener && !!ctx->listener->vflow_record) {
@@ -246,6 +249,8 @@ void qd_connection_init(qd_connection_t *ctx, qd_server_t *server, const qd_serv
246249
{
247250
ctx->pn_conn = pn_connection();
248251
assert(ctx->pn_conn);
252+
253+
pn_connection_set_container(ctx->pn_conn, qd_server_get_container_name(server));
249254
sys_mutex_init(&ctx->deferred_call_lock);
250255
ctx->role = qd_strdup(config->role);
251256
ctx->server = server;
@@ -262,18 +267,13 @@ void qd_connection_init(qd_connection_t *ctx, qd_server_t *server, const qd_serv
262267
DEQ_INIT(ctx->free_link_list);
263268
DEQ_INIT(ctx->child_sessions);
264269

265-
// note: setup connector or listener before decorating the connection since
266-
// decoration involves accessing the connection's parent.
267-
268270
if (!!connector) {
269271
assert(!listener);
270272
qd_connector_add_connection(connector, ctx);
271273
} else if (!!listener) {
272274
qd_listener_add_connection(listener, ctx);
273275
}
274276

275-
decorate_connection(ctx, config);
276-
277277
sys_mutex_lock(&amqp_adaptor.lock);
278278
DEQ_INSERT_TAIL(amqp_adaptor.conn_list, ctx);
279279
sys_mutex_unlock(&amqp_adaptor.lock);
@@ -619,13 +619,15 @@ static bool setup_ssl_sasl_and_open(qd_connection_t *ctx)
619619
pn_sasl_allowed_mechs(sasl, config->sasl_mechanisms);
620620
pn_sasl_set_allow_insecure_mechs(sasl, config->allowInsecureAuthentication);
621621

622+
decorate_connection(ctx);
622623
pn_connection_open(ctx->pn_conn);
623624
return true;
624625
}
625626

626627

627628
/* Configure the transport once it is bound to the connection */
628-
static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
629+
static void on_connection_bound(qd_server_t *server, pn_event_t *e)
630+
{
629631
pn_connection_t *pn_conn = pn_event_connection(e);
630632
qd_connection_t *ctx = pn_connection_get_context(pn_conn);
631633
pn_transport_t *tport = pn_connection_transport(pn_conn);
@@ -643,9 +645,21 @@ static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
643645
pn_transport_set_tracer(tport, transport_tracer);
644646
}
645647

646-
const qd_server_config_t *config = NULL;
648+
const qd_server_config_t *config = qd_connection_config(ctx);
649+
assert(config);
650+
651+
//
652+
// Common transport configuration.
653+
//
654+
pn_transport_set_max_frame(tport, config->max_frame_size);
655+
pn_transport_set_idle_timeout(tport, config->idle_timeout_seconds * 1000);
656+
// pn_transport_set_channel_max sets the maximum session *identifier*, not the total number of sessions. Thus Proton
657+
// will allow sessions with identifiers [0..max_sessions], which is one greater than the value we pass to
658+
// pn_transport_set_channel_max. So to limit the maximum number of simultaineous sessions to config->max_sessions we
659+
// have to decrement it by one for Proton.
660+
pn_transport_set_channel_max(tport, config->max_sessions - 1);
661+
647662
if (ctx->listener) { /* Accepting an incoming connection */
648-
config = &ctx->listener->config;
649663
const char *name = config->host_port;
650664
pn_transport_set_server(tport);
651665
set_rhost_port(ctx);
@@ -676,13 +690,14 @@ static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
676690
pn_transport_require_auth(tport, config->requireAuthentication);
677691
pn_transport_require_encryption(tport, config->requireEncryption);
678692
pn_sasl_set_allow_insecure_mechs(sasl, config->allowInsecureAuthentication);
693+
decorate_connection(ctx);
679694

680695
// This log statement is kept at INFO level because this shows the inter-router
681696
// connections and that is useful when debugging router issues.
682697
qd_log(LOG_SERVER, QD_LOG_INFO, "[C%" PRIu64 "] Accepted connection to %s from %s",
683698
ctx->connection_id, name, ctx->rhost_port);
699+
684700
} else if (ctx->connector) { /* Establishing an outgoing connection */
685-
config = &ctx->connector->ctor_config->config;
686701
if (!setup_ssl_sasl_and_open(ctx)) {
687702
qd_log(LOG_SERVER, QD_LOG_ERROR, "[C%" PRIu64 "] Connection aborted due to internal setup error",
688703
ctx->connection_id);
@@ -695,17 +710,6 @@ static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
695710
connect_fail(ctx, QD_AMQP_COND_INTERNAL_ERROR, "unknown Connection");
696711
return;
697712
}
698-
699-
//
700-
// Common transport configuration.
701-
//
702-
pn_transport_set_max_frame(tport, config->max_frame_size);
703-
pn_transport_set_idle_timeout(tport, config->idle_timeout_seconds * 1000);
704-
// pn_transport_set_channel_max sets the maximum session *identifier*, not the total number of sessions. Thus Proton
705-
// will allow sessions with identifiers [0..max_sessions], which is one greater than the value we pass to
706-
// pn_transport_set_channel_max. So to limit the maximum number of simultaineous sessions to config->max_sessions we
707-
// have to decrement it by one for Proton.
708-
pn_transport_set_channel_max(tport, config->max_sessions - 1);
709713
}
710714

711715
void qd_container_handle_event(qd_container_t *container, pn_event_t *event, pn_connection_t *pn_conn, qd_connection_t *qd_conn);
@@ -848,3 +852,14 @@ void qd_amqp_connection_set_tracing(bool enable_tracing)
848852
sys_mutex_unlock(&amqp_adaptor.lock);
849853
}
850854
}
855+
856+
857+
bool qd_connection_get_tls_ordinal(const qd_connection_t *qd_conn, uint64_t *tls_ordinal)
858+
{
859+
if (qd_conn->ssl) {
860+
*tls_ordinal = qd_tls_session_get_profile_ordinal(qd_conn->ssl);
861+
return true;
862+
}
863+
*tls_ordinal = 0;
864+
return false;
865+
}

src/adaptors/amqp/qd_connection.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,13 @@ void qd_connection_transport_tracer(pn_transport_t *transport, const char *messa
297297

298298
bool qd_connection_handle_event(qd_server_t *qd_server, pn_event_t *e, void *context);
299299
bool qd_connection_strip_annotations_in(const qd_connection_t *c);
300+
301+
/**
302+
* Get the value of the TLS ordinal that is in use by this connection.
303+
*
304+
* @return True if the TLS ordinal is configured and tls_ordinal has been set, false if the connection has no TLS
305+
* ordinal.
306+
*/
307+
bool qd_connection_get_tls_ordinal(const qd_connection_t *qd_conn, uint64_t *tls_ordinal);
308+
300309
#endif

0 commit comments

Comments
 (0)