Skip to content

Commit 39de6fe

Browse files
committed
WL#15524 Patch #15 Visbility
Add functions and struct to MGM API: int ndb_mgm_has_tls() struct ndb_mgm_cert_table int ndb_mgm_list_certs() void ndb_mgm_cert_table_free() Add a "list certs" request and reply to the management protocol. In the management server's reply to the "get session" and "list sessions" commands, add a row indicating whether a session has TLS. In the ndb_mgm client, add the "TLS INFO" command. Test this command in a new MTR test, tls_info. Change-Id: If91c730a743b55f2b6ed805caec45a2c19de23b5
1 parent 035f136 commit 39de6fe

File tree

15 files changed

+418
-29
lines changed

15 files changed

+418
-29
lines changed

mysql-test/suite/ndb_tls/tls_info.cnf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
!include suite/ndb_tls/my.cnf
2+
3+
[mysql_cluster]
4+
ndb-tls-search-path=$MYSQLTEST_VARDIR/std_data/ndb-tls/active
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SELECT 1;
2+
1
3+
1
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--source include/have_ndb.inc
2+
--source suite/ndb_tls/include/check_openssl.inc
3+
4+
# The server is up
5+
#
6+
SELECT 1;
7+
8+
# Run "TLS INFO" from the mgm client, but discard the output
9+
# (because the number of connections reported will vary with timing)
10+
--exec $NDB_MGM -e "TLS INFO" >> $NDB_TOOLS_OUTPUT
11+
12+
# Also test the --test-tls option
13+
--exec $NDB_MGM --test-tls >> $NDB_TOOLS_OUTPUT
14+
15+
# And test "TLS INFO" from a plaintext connection
16+
--exec $NDB_MGM --ndb-mgm-tls=deferred -e "TLS INFO" >> $NDB_TOOLS_OUTPUT
17+
18+
# cleanup
19+
--remove_file $NDB_TOOLS_OUTPUT
20+

mysql-test/suite/ndb_tls/tls_off_certs.result

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
SELECT * FROM ndbinfo.certificates order by Node_id;
22
Node_id Name Expires Serial
3-
1 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2
4-
2 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2
5-
3 NDB Management Node Mar 2023 19-Apr-2024 20:70:03:B8:BE:5F:C7:FB:A8
3+
1 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2:4A
4+
2 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2:4A
5+
3 NDB Management Node Mar 2023 19-Apr-2024 20:70:03:B8:BE:5F:C7:FB:A8:3E
66
SELECT node_id, remote_node_id, encrypted from ndbinfo.transporters
77
WHERE status = 'CONNECTED' ORDER BY node_id, remote_node_id;
88
node_id remote_node_id encrypted

mysql-test/suite/ndb_tls/tls_required.result

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
SELECT * FROM ndbinfo.certificates order by Node_id;
22
Node_id Name Expires Serial
3-
1 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2
4-
2 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2
5-
3 NDB Management Node Mar 2023 19-Apr-2024 20:70:03:B8:BE:5F:C7:FB:A8
6-
51 NDB Node Mar 2023 19-Apr-2024 7B:A8:13:FB:D8:42:8E:A3:C9
7-
52 NDB Node Mar 2023 19-Apr-2024 7B:A8:13:FB:D8:42:8E:A3:C9
3+
1 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2:4A
4+
2 NDB Data Node Mar 2023 19-Apr-2024 B7:15:32:D5:AE:FF:C7:E1:E2:4A
5+
3 NDB Management Node Mar 2023 19-Apr-2024 20:70:03:B8:BE:5F:C7:FB:A8:3E
6+
51 NDB Node Mar 2023 19-Apr-2024 7B:A8:13:FB:D8:42:8E:A3:C9:06
7+
52 NDB Node Mar 2023 19-Apr-2024 7B:A8:13:FB:D8:42:8E:A3:C9:06
88
SELECT node_id, remote_node_id, encrypted from ndbinfo.transporters
99
WHERE status = 'CONNECTED' ORDER BY node_id, remote_node_id;
1010
node_id remote_node_id encrypted

storage/ndb/include/mgmapi/mgmapi.h

+64
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,70 @@ extern "C" {
16271627
int retry_delay_in_seconds, int verbose,
16281628
int tls_req_level);
16291629

1630+
/**
1631+
* Check whether a connected handle is using TLS
1632+
*
1633+
* @param handle Management handle.
1634+
*
1635+
* @return 0 if handle is not using TLS.
1636+
* 12 if handle is using TLS version 1.2
1637+
* 13 if handle is using TLS version 1.3
1638+
*/
1639+
int ndb_mgm_has_tls(NdbMgmHandle);
1640+
1641+
/**
1642+
* Struct ndb\_mgm\_cert\_table is a linked structure describing a
1643+
* TLS client session.
1644+
*/
1645+
struct ndb_mgm_cert_table {
1646+
Uint64 session_id;
1647+
char * peer_address;
1648+
char * cert_serial;
1649+
char * cert_name;
1650+
char * cert_expires;
1651+
struct ndb_mgm_cert_table * next;
1652+
};
1653+
1654+
/**
1655+
* Query TLS certificates of connected MGM clients
1656+
*
1657+
* @param handle Management handle.
1658+
* @param list Address of pointer to ndb_mgm_cert_table (out)
1659+
*
1660+
* @return The total number of linked decriptions, if positive.
1661+
* If zero, success, but no TLS connections to report.
1662+
* -1 on error.
1663+
*
1664+
* On return, list will be populated with a pointer to a table. The table
1665+
* should be freed after use by calling ndb\_mgm\_cert\_table\_free().
1666+
*/
1667+
int ndb_mgm_list_certs(NdbMgmHandle, ndb_mgm_cert_table ** list);
1668+
1669+
/**
1670+
* Free a linked list of certificate descriptions.
1671+
*/
1672+
void ndb_mgm_cert_table_free(ndb_mgm_cert_table ** list);
1673+
1674+
/**
1675+
* Struct ndb\_mgm\_tls\_stats stores TLS-related statistics from the server.
1676+
*/
1677+
struct ndb_mgm_tls_stats {
1678+
Uint32 accepted; // total client connections accepted
1679+
Uint32 upgraded; // client connections upgraded to TLS
1680+
Uint32 current; // total current open client sessions
1681+
Uint32 tls; // current open client sessions using TLS
1682+
Uint32 authfail; // total authorization failures
1683+
};
1684+
1685+
/**
1686+
* Query server TLS statistics
1687+
* @param handle Management handle
1688+
* @param result Pointer to structure that will hold result data
1689+
*
1690+
* @return 0 on success, -1 on error
1691+
*/
1692+
int ndb_mgm_get_tls_stats(NdbMgmHandle handle, ndb_mgm_tls_stats * result);
1693+
16301694
#ifdef __cplusplus
16311695
}
16321696
#endif

storage/ndb/include/mgmapi/mgmapi_debug.h

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ extern "C" {
141141
Uint32 nodeid;
142142
Uint32 parser_buffer_len;
143143
Uint32 parser_status;
144+
Uint32 tls;
144145
};
145146

146147
int ndb_mgm_get_session(NdbMgmHandle handle, Uint64 id,

storage/ndb/include/util/TlsKeyManager.hpp

+14-10
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ class ClientAuthorization;
4141

4242
class TlsKeyManager {
4343
public:
44-
enum TlsVersion { Tls12, Tls13 };
44+
struct cert_record {
45+
static constexpr size_t SN_buf_len = 65;
46+
static constexpr size_t CN_buf_len = 65;
47+
48+
char serial[SN_buf_len];
49+
char name[CN_buf_len];
50+
struct tm exp_tm;
51+
time_t expires {0};
52+
bool active {false};
53+
};
4554

4655
TlsKeyManager();
4756
~TlsKeyManager();
@@ -64,9 +73,11 @@ class TlsKeyManager {
6473
/* Get SSL_CTX */
6574
struct ssl_ctx_st * ctx() const { return m_ctx; }
6675

76+
/* Certificate table routines */
6777
void cert_table_set(int node_id, struct x509_st *);
6878
void cert_table_clear(int node_id);
6979
bool iterate_cert_table(int & node_id, cert_table_entry *);
80+
static void describe_cert(cert_record &, struct x509_st *);
7081

7182
// Check replacement date of our own node certificate
7283
// pct should be a number between 0.0 and 1.0, where 0 represents the
@@ -141,14 +152,7 @@ class TlsKeyManager {
141152
PkiFile::PathName m_key_file, m_cert_file;
142153
char * m_path_string {nullptr};
143154
TlsSearchPath * m_search_path {nullptr};
144-
static constexpr size_t SN_buf_len = 30;
145-
static constexpr size_t CN_buf_len = 65;
146-
struct private_cert_table_entry {
147-
char serial[SN_buf_len];
148-
char name[CN_buf_len];
149-
time_t expires {0};
150-
bool active {false};
151-
} m_cert_table[MAX_NODES];
155+
cert_record m_cert_table[MAX_NODES];
152156
NodeCertificate m_node_cert;
153157
NdbMutex m_cert_table_mutex;
154158
int m_error {0};
@@ -158,7 +162,7 @@ class TlsKeyManager {
158162

159163
void init(const char *, int, Node::Type, UserType);
160164

161-
bool cert_table_get(const private_cert_table_entry &,
165+
bool cert_table_get(const cert_record &,
162166
cert_table_entry *) const;
163167
void free_path_strings();
164168
};

storage/ndb/src/common/util/TlsKeyManager.cpp

+15-10
Original file line numberDiff line numberDiff line change
@@ -446,20 +446,25 @@ int TlsKeyManager::perform_client_host_auth(ClientAuthorization * auth) {
446446
/*
447447
* Certificate table routines
448448
*/
449+
void TlsKeyManager::describe_cert(cert_record & entry,
450+
struct x509_st * cert) {
451+
SerialNumber::print(entry.serial, cert_record::SN_buf_len,
452+
X509_get0_serialNumber(cert));
453+
Certificate::get_common_name(cert, entry.name, cert_record::CN_buf_len);
454+
entry.expires = 0;
455+
if(ASN1_TIME_to_tm(X509_get0_notAfter(cert), & entry.exp_tm))
456+
entry.expires = mktime(& entry.exp_tm);
457+
}
458+
449459
void TlsKeyManager::cert_table_set(int node_id, X509 * cert) {
450460
Guard mutex_guard(& m_cert_table_mutex);
451-
struct tm expires;
452461
assert(node_id < MAX_NODES);
453462
if(node_id == 0) return; // Client certs do not go into table
454463

455464
/* In the case of a multi-transporter, the entry may already be active */
456-
struct private_cert_table_entry & entry = m_cert_table[node_id];
465+
struct cert_record & entry = m_cert_table[node_id];
457466
if(! entry.active) {
458-
SerialNumber::print(entry.serial, SN_buf_len, X509_get0_serialNumber(cert));
459-
Certificate::get_common_name(cert, entry.name, CN_buf_len);
460-
entry.expires = 0;
461-
if(ASN1_TIME_to_tm(X509_get0_notAfter(cert), & expires))
462-
entry.expires = mktime(& expires);
467+
describe_cert(entry, cert);
463468
entry.active = true;
464469
}
465470
}
@@ -468,14 +473,14 @@ void TlsKeyManager::cert_table_clear(int node_id) {
468473
Guard mutex_guard(& m_cert_table_mutex);
469474
assert(node_id < MAX_NODES);
470475

471-
struct private_cert_table_entry & entry = m_cert_table[node_id];
476+
struct cert_record & entry = m_cert_table[node_id];
472477
entry.serial[0] = '\0';
473478
entry.name[0] = '\0';
474479
entry.expires = 0;
475480
entry.active = false;
476481
}
477482

478-
bool TlsKeyManager::cert_table_get(const private_cert_table_entry & row,
483+
bool TlsKeyManager::cert_table_get(const cert_record & row,
479484
cert_table_entry * client_row) const
480485
{
481486
assert(row.active);
@@ -492,7 +497,7 @@ bool TlsKeyManager::iterate_cert_table(int & node, cert_table_entry * client) {
492497
if(m_ctx) {
493498
while(node < MAX_NODES_ID) {
494499
node += 1;
495-
const private_cert_table_entry & row = m_cert_table[node];
500+
const cert_record & row = m_cert_table[node];
496501
if(row.active)
497502
return cert_table_get(row, client);
498503
}

storage/ndb/src/mgmapi/mgmapi.cpp

+121
Original file line numberDiff line numberDiff line change
@@ -4014,6 +4014,7 @@ ndb_mgm_get_session(NdbMgmHandle handle, Uint64 id,
40144014
MGM_ARG("id", Int, Mandatory, "Node ID"),
40154015
MGM_ARG("m_stopSelf", Int, Optional, "m_stopSelf"),
40164016
MGM_ARG("m_stop", Int, Optional, "stop session"),
4017+
MGM_ARG("tls", Int, Optional, "session is using TLS"),
40174018
MGM_ARG("nodeid", Int, Optional, "allocated node id"),
40184019
MGM_ARG("parser_buffer_len", Int, Optional, "waiting in buffer"),
40194020
MGM_ARG("parser_status", Int, Optional, "parser status"),
@@ -4057,6 +4058,10 @@ ndb_mgm_get_session(NdbMgmHandle handle, Uint64 id,
40574058
rlen+=sizeof(s->parser_status);
40584059
}
40594060

4061+
/* tls is a late addition to the struct, so check length */
4062+
if(*len > rlen && prop->get("tls",&(s->tls)))
4063+
rlen+=sizeof(s->tls);
4064+
40604065
*len= rlen;
40614066
retval= 1;
40624067

@@ -4230,6 +4235,122 @@ ndb_socket_t _ndb_mgm_get_socket(NdbMgmHandle h)
42304235
return h->socket.ndb_socket();
42314236
}
42324237

4238+
int ndb_mgm_has_tls(NdbMgmHandle h)
4239+
{
4240+
return h->socket.has_tls() ? 1 : 0;
4241+
}
4242+
4243+
static ndb_mgm_cert_table * new_cert_table()
4244+
{
4245+
ndb_mgm_cert_table * table = new ndb_mgm_cert_table;
4246+
table->session_id = 0;
4247+
table->peer_address = nullptr;
4248+
table->cert_serial = nullptr;
4249+
table->cert_name = nullptr;
4250+
table->cert_expires = nullptr;
4251+
table->next = nullptr;
4252+
return table;
4253+
}
4254+
4255+
int ndb_mgm_list_certs(NdbMgmHandle handle, ndb_mgm_cert_table ** data)
4256+
{
4257+
DBUG_ENTER("ndb_mgm_list_certs");
4258+
CHECK_HANDLE(handle, -1);
4259+
CHECK_CONNECTED(handle, -1);
4260+
4261+
SecureSocketOutputStream out(handle->socket, handle->timeout);
4262+
SecureSocketInputStream in(handle->socket, handle->timeout);
4263+
4264+
out.println("list certs");
4265+
out.println("%s", "");
4266+
4267+
/* See listCerts() and show_cert() in mgmsrv/Services.cpp for reply format */
4268+
int ok = false;
4269+
char buf[1024];
4270+
4271+
in.gets(buf, sizeof(buf));
4272+
if(strcmp("list certs reply\n", buf))
4273+
DBUG_RETURN(-1);
4274+
4275+
int ncerts = 0;
4276+
struct ndb_mgm_cert_table * current = *data = nullptr;
4277+
while(in.gets(buf, sizeof(buf))) {
4278+
if(strcmp("\n", buf) == 0) { /* Blank line at end of input */
4279+
ok = true;
4280+
break;
4281+
}
4282+
Vector<BaseString> parts;
4283+
BaseString line(buf);
4284+
if(line.split(parts, ":", 2) != 2)
4285+
break;
4286+
if(parts[0] == "session") {
4287+
ncerts++;
4288+
current = new_cert_table();
4289+
current->next = *data;
4290+
*data = current;
4291+
current->session_id = strtoull(parts[1].c_str(), nullptr, 10);
4292+
}
4293+
else {
4294+
char * value = strdup(parts[1].substr(1).trim("\n").c_str());
4295+
if(parts[0] == "address")
4296+
current->peer_address = value;
4297+
else if(parts[0] == "serial")
4298+
current->cert_serial = value;
4299+
else if(parts[0] == "name")
4300+
current->cert_name = value;
4301+
else if(parts[0] == "expires")
4302+
current->cert_expires = value;
4303+
else
4304+
free(value); // unexpected input
4305+
}
4306+
}
4307+
4308+
if(ok) DBUG_RETURN(ncerts);
4309+
DBUG_RETURN(-1);
4310+
}
4311+
4312+
void ndb_mgm_cert_table_free(ndb_mgm_cert_table ** list) {
4313+
while(*list) {
4314+
ndb_mgm_cert_table * t = *list;
4315+
free((void *) t->cert_expires);
4316+
free((void *) t->cert_name);
4317+
free((void *) t->cert_serial);
4318+
free((void *) t->peer_address);
4319+
*list = t->next;
4320+
delete t;
4321+
}
4322+
}
4323+
4324+
int ndb_mgm_get_tls_stats(NdbMgmHandle handle, ndb_mgm_tls_stats * result) {
4325+
DBUG_ENTER("ndb_mgm_get_tls_stats");
4326+
CHECK_HANDLE(handle, -1);
4327+
CHECK_CONNECTED(handle, -1);
4328+
4329+
const ParserRow<ParserDummy> reply[]= {
4330+
MGM_CMD("get tls stats reply", nullptr, ""),
4331+
MGM_ARG("accepted", Int, Mandatory, "Total accepted connections"),
4332+
MGM_ARG("upgraded", Int, Mandatory, "Total connections upgraded to TLS"),
4333+
MGM_ARG("current", Int, Mandatory, "Current open connections"),
4334+
MGM_ARG("tls", Int, Mandatory, "Current connections using TLS"),
4335+
MGM_ARG("authfail", Int, Mandatory, "Total authorization errors"),
4336+
MGM_END()
4337+
};
4338+
4339+
const Properties * prop =
4340+
ndb_mgm_call(handle, reply, "get tls stats", nullptr);
4341+
4342+
CHECK_REPLY(handle, prop, 0);
4343+
4344+
prop->get("accepted", & (result->accepted));
4345+
prop->get("upgraded", & (result->upgraded));
4346+
prop->get("current", & (result->current));
4347+
prop->get("tls", & (result->tls));
4348+
prop->get("authfail", & (result->authfail));
4349+
4350+
delete prop;
4351+
DBUG_RETURN(0);
4352+
}
4353+
42334354

42344355
/*
42354356
Compare function for qsort() to sort events in

0 commit comments

Comments
 (0)