Skip to content

Commit f7160bf

Browse files
authored
Fixes #1741: add ordinal attributes to sslProfile (#1744)
Include mechanism for propagating updates to affected connectors. Advertise the connection's group ordinal to the peer router via the properties map in the Open performative.
1 parent ee52aef commit f7160bf

File tree

18 files changed

+407
-142
lines changed

18 files changed

+407
-142
lines changed

include/qpid/dispatch/amqp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ extern const char * const QD_CONNECTION_PROPERTY_VERSION_KEY;
155155
extern const char * const QD_CONNECTION_PROPERTY_COST_KEY;
156156
extern const char * const QD_CONNECTION_PROPERTY_ROLE_KEY;
157157
extern const char * const QD_CONNECTION_PROPERTY_GROUP_CORRELATOR_KEY;
158+
extern const char * const QD_CONNECTION_PROPERTY_GROUP_ORDINAL_KEY;
158159
extern const char * const QD_CONNECTION_PROPERTY_CONN_ID;
159160
extern const char * const QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY;
160161
extern const char * const QD_CONNECTION_PROPERTY_FAILOVER_NETHOST_KEY;

include/qpid/dispatch/protocol_adaptor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,13 +928,14 @@ 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,
934935
bool streaming_links,
935936
bool connection_trunking);
936937

937-
void qdr_connection_info_set_group_correlator(qdr_connection_info_t *info, const char *correlator);
938+
void qdr_connection_info_set_group(qdr_connection_info_t *info, const char *correlator, uint64_t ordinal);
938939
void qdr_connection_info_set_tls(qdr_connection_info_t *info, bool enabled, char *version, char *ciphers, int ssf);
939940

940941
void qd_adaptor_listener_init(void);

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_unregister_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_ssl_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: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,53 +1376,39 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
13761376
char rversion[128];
13771377
uint64_t connection_id = qd_connection_connection_id(conn);
13781378
pn_connection_t *pn_conn = qd_connection_pn(conn);
1379-
pn_transport_t *tport = 0;
1380-
pn_sasl_t *sasl = 0;
1381-
const char *mech = 0;
1382-
const char *user = 0;
1383-
const char *container = conn->pn_conn ? pn_connection_remote_container(conn->pn_conn) : 0;
1379+
const char *host = 0;
1380+
uint64_t group_ordinal = 0;
1381+
const char *container = conn->pn_conn ? pn_connection_remote_container(conn->pn_conn) : 0;
1382+
char group_correlator[QD_DISCRIMINATOR_SIZE];
1383+
char host_local[255];
1384+
1385+
rversion[0] = 0;
1386+
group_correlator[0] = 0;
1387+
host_local[0] = 0;
13841388

1385-
rversion[0] = 0;
13861389
conn->strip_annotations_in = false;
13871390
conn->strip_annotations_out = false;
1388-
if (conn->pn_conn) {
1389-
tport = pn_connection_transport(conn->pn_conn);
1390-
}
1391-
if (tport) {
1392-
sasl = pn_sasl(tport);
1393-
if(conn->user_id)
1394-
user = conn->user_id;
1395-
else
1396-
user = pn_transport_get_user(tport);
1397-
}
1398-
1399-
if (sasl)
1400-
mech = pn_sasl_get_mech(sasl);
1391+
qd_router_connection_get_config(conn, &role, &cost, &name,
1392+
&conn->strip_annotations_in, &conn->strip_annotations_out, &link_capacity);
14011393

1402-
const char *host = 0;
1403-
char host_local[255];
1404-
const qd_server_config_t *config;
14051394
qd_connector_t *connector = qd_connection_connector(conn);
1406-
14071395
if (connector) {
1408-
config = qd_connector_get_config(connector);
1396+
const qd_server_config_t *config = qd_connector_get_server_config(connector);
14091397
snprintf(host_local, 254, "%s", config->host_port);
14101398
host = &host_local[0];
1411-
}
1412-
else
1413-
host = qd_connection_name(conn);
14141399

1415-
1416-
qd_router_connection_get_config(conn, &role, &cost, &name,
1417-
&conn->strip_annotations_in, &conn->strip_annotations_out, &link_capacity);
1418-
1419-
if (connector && !!connector->ctor_config->data_connection_count) {
1420-
memcpy(conn->group_correlator, connector->ctor_config->group_correlator, QD_DISCRIMINATOR_SIZE);
1400+
// Use the connectors tls_ordinal value as the group ordinal because the connection with the highest tls_ordinal
1401+
// value has the most up-to-date security credentials and should take precedence over connections with a lower
1402+
// ordinal value.
1403+
(void) qd_connector_get_tls_ordinal(connector, &group_ordinal);
1404+
memcpy(group_correlator, connector->ctor_config->group_correlator, QD_DISCRIMINATOR_SIZE);
14211405
if (connector->is_data_connector) {
14221406
// override the configured role to identify this as a data connection
14231407
assert(role == QDR_ROLE_INTER_ROUTER);
14241408
role = QDR_ROLE_INTER_ROUTER_DATA;
14251409
}
1410+
} else {
1411+
host = qd_connection_name(conn);
14261412
}
14271413

14281414
// check offered capabilities for streaming link support and connection trunking support
@@ -1457,10 +1443,13 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
14571443
const bool is_router = (role == QDR_ROLE_INTER_ROUTER || role == QDR_ROLE_EDGE_CONNECTION);
14581444
pn_data_rewind(props);
14591445
if (pn_data_next(props) && pn_data_type(props) == PN_MAP) {
1460-
const size_t num_items = pn_data_get_map(props);
1461-
int props_found = 0; // once all props found exit loop
1446+
1447+
const size_t num_items = pn_data_get_map(props);
1448+
const int max_props = 8; // total possible props
1449+
int props_found = 0; // once all props found exit loop
1450+
14621451
pn_data_enter(props);
1463-
for (int i = 0; i < num_items / 2 && props_found < 7; ++i) {
1452+
for (int i = 0; i < num_items / 2 && props_found < max_props; ++i) {
14641453
if (!pn_data_next(props)) break;
14651454
if (pn_data_type(props) != PN_SYMBOL) break; // invalid properties map
14661455
pn_bytes_t key = pn_data_get_symbol(props);
@@ -1493,11 +1482,26 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
14931482
} else if (key.size == strlen(QD_CONNECTION_PROPERTY_GROUP_CORRELATOR_KEY) &&
14941483
strncmp(key.start, QD_CONNECTION_PROPERTY_GROUP_CORRELATOR_KEY, key.size) == 0) {
14951484
props_found += 1;
1485+
assert(!connector); // expect: connector sets correlator, listener consumes it
14961486
if (!pn_data_next(props)) break;
14971487
if (role == QDR_ROLE_INTER_ROUTER || role == QDR_ROLE_INTER_ROUTER_DATA) {
14981488
if (pn_data_type(props) == PN_STRING) {
1489+
// pn_bytes is not null terminated
14991490
pn_bytes_t gc = pn_data_get_string(props);
1500-
strncpy(conn->group_correlator, gc.start, MIN(gc.size, QD_DISCRIMINATOR_SIZE));
1491+
size_t len = MIN(gc.size, QD_DISCRIMINATOR_BYTES);
1492+
memcpy(group_correlator, gc.start, len);
1493+
group_correlator[len] = '\0';
1494+
}
1495+
}
1496+
1497+
} else if (key.size == strlen(QD_CONNECTION_PROPERTY_GROUP_ORDINAL_KEY) &&
1498+
strncmp(key.start, QD_CONNECTION_PROPERTY_GROUP_ORDINAL_KEY, key.size) == 0) {
1499+
props_found += 1;
1500+
assert(!connector); // expect: connector sets ordinal, listener consumes it
1501+
if (!pn_data_next(props)) break;
1502+
if (role == QDR_ROLE_INTER_ROUTER || role == QDR_ROLE_INTER_ROUTER_DATA) {
1503+
if (pn_data_type(props) == PN_ULONG) {
1504+
group_ordinal = pn_data_get_ulong(props);
15011505
}
15021506
}
15031507

@@ -1530,6 +1534,7 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
15301534

15311535
} else if ((key.size == strlen(QD_CONNECTION_PROPERTY_ACCESS_ID)
15321536
&& strncmp(key.start, QD_CONNECTION_PROPERTY_ACCESS_ID, key.size) == 0)) {
1537+
props_found += 1;
15331538
if (!pn_data_next(props)) break;
15341539
if (!!connector && !!connector->vflow_record && pn_data_type(props) == PN_STRING) {
15351540
vflow_set_ref_from_pn(connector->vflow_record, VFLOW_ATTRIBUTE_PEER, props);
@@ -1539,13 +1544,35 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
15391544
// skip this key
15401545
if (!pn_data_next(props)) break;
15411546
}
1547+
1548+
// NOTE: if adding more keys update max_props value above!
15421549
}
15431550
}
15441551
}
15451552

1546-
char *proto = 0;
1547-
char *cipher = 0;
1548-
int ssl_ssf = 0;
1553+
// Gather transport-level information
1554+
1555+
pn_transport_t *tport = 0;
1556+
pn_sasl_t *sasl = 0;
1557+
const char *mech = 0;
1558+
const char *user = 0;
1559+
char *proto = 0;
1560+
char *cipher = 0;
1561+
int ssl_ssf = 0;
1562+
1563+
if (conn->pn_conn) {
1564+
tport = pn_connection_transport(conn->pn_conn);
1565+
}
1566+
if (tport) {
1567+
sasl = pn_sasl(tport);
1568+
if(conn->user_id)
1569+
user = conn->user_id;
1570+
else
1571+
user = pn_transport_get_user(tport);
1572+
}
1573+
1574+
if (sasl)
1575+
mech = pn_sasl_get_mech(sasl);
15491576

15501577
if (conn->ssl) {
15511578
proto = qd_tls_session_get_protocol_version(conn->ssl);
@@ -1567,13 +1594,14 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool
15671594
(char*) user,
15681595
container,
15691596
props,
1597+
qd_tls_session_get_ssl_profile_ordinal(conn->ssl),
15701598
ssl_ssf,
15711599
!!conn->ssl,
15721600
rversion,
15731601
streaming_links,
15741602
connection_trunking);
15751603

1576-
qdr_connection_info_set_group_correlator(connection_info, conn->group_correlator);
1604+
qdr_connection_info_set_group(connection_info, group_correlator, group_ordinal);
15771605

15781606
qdr_connection_opened(router->router_core,
15791607
amqp_adaptor.adaptor,

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
}

0 commit comments

Comments
 (0)