Commit b3e20c4
Yury Kirsanov
clusterer_controller: add zero-config multicast HA controller for clusterer
Introduces the clusterer_controller module: a zero-configuration high
availability coordinator for the clusterer module. Nodes discover each other
over authenticated, encrypted UDP multicast, elect a master deterministically
and drive clusterer sharing-tag failover automatically - no per-node node_id
lists or static topology configuration required.
Module (modules/clusterer_controller):
- UDP multicast discovery and keepalive protocol. Every packet's payload is
sealed with an AEAD behind a 2-byte cleartext magic (key-tier selector) and a
2-byte cluster_id, both bound into the tag as AAD (so a captured packet can't
be re-stamped onto another cluster_id on a shared multicast+password group).
The cluster_id is also filtered before decryption, so several clusters can
share one multicast group. Two key tiers: a bootstrap key used for the
admission handshake and the split-brain beacon, derived from the shared
password with a memory-hard KDF; and a session key (HKDF over an X25519
ECDH-agreed master salt) for all normal traffic. A startup warning fires if
the password is the default or has low estimated entropy.
- Crypto suite selected at build time: AES-256-GCM + scrypt (N=2^16) via
WolfSSL by default, or XChaCha20-Poly1305 + Argon2id when built against
libsodium (192-bit nonce; detected via pkg-config, linked dynamically). The
two wire formats are not interoperable, so all nodes must be built alike; the
active suite is logged at startup.
- Per-worker X25519 ECDH key agreement. The session key is generated once, when
the first node bootstraps the cluster, and preserved across every master
change - a new master reuses the key every member already holds, so
transitions need no re-keying and no re-JOIN cycle.
- Master / backup / member roles with a master_stickiness modparam (default 1):
a live master is not preempted by a higher-IP node that joins (it becomes the
backup instead); on master failure the backup - the highest-IP survivor - is
promoted immediately. With master_stickiness=0 the highest-IP node always
becomes master.
- Split-brain is prevented and healed by three mechanisms: (1) prevention at
join time - simultaneously-starting nodes see each other's JOIN_REQs, so a
node that has seen a higher-IP starter defers self-promotion and joins it
instead of forming an independent-key lone master; (2) same-key yield - two
masters sharing a session key see each other's MASTER_ALIVE and the lower-IP
one yields; (3) divergent-key merge - masters with different session keys
emit a bootstrap-key MASTER_BEACON, and a node hearing a superior beacon
(larger partition, ties by higher IP) re-joins that master and adopts its key.
- Fast failure detection (MASTER_ALIVE at 1s, 3s timeout) with immediate backup
promotion; graceful KEY_HANDOFF + GOODBYE on clean shutdown.
- Single event-driven worker on the OpenSIPS reactor (epoll). Per-source rate
limiting and 32-bit sequence-number replay protection before/after decrypt;
peer-table exhaustion defence.
- Join authentication: the master sends an encrypted, unforgeable JOIN_REJECT
after repeated bootstrap-decrypt failures from an IP. A wrong-password node
cannot read that reject, so it also self-detects: while joining it counts
undecryptable packets from other peers and, at the join deadline, shuts down
("cannot authenticate - wrong password?") instead of self-promoting into a
lone split-brain master. Undecryptable session packets from anything other
than the current master are ignored, so a wrong-password or malicious node on
the group cannot force the cluster into a re-JOIN churn.
- Sharing-tag control: normally the master is the sole active holder. An
operator can override this with cc_shtag_force (pin the active tag to a chosen
node) and revert with cc_shtag_auto; the override is carried in MEMBER_LIST,
survives master fail-over and auto-clears if the forced node departs. Each
node logs why its tags were (de)activated.
- Per-cluster configuration via the "cluster" modparam, with global defaults
for password, query_time, manage_shtags and master_stickiness that each
cluster can override. MI commands: cc_list_members, cc_node_info,
cc_list_config (resolved per-cluster settings, including shtag_mode),
cc_shtag_force and cc_shtag_auto.
clusterer integration (modules/clusterer):
- New controller API (clusterer_ctrl.[ch]): dynamic node add/remove, identity
update and sharing-tag control driven by the controller.
- Controller-managed sharing tags: forced to backup on startup with MI/script
tag changes blocked while managed; the active tag follows the elected master.
- Fix a NULL current_node dereference in bin_rcv_cl_packets: with a dynamically
built topology a cluster can receive BIN packets before this node's identity
is established; such packets are now dropped until it is, instead of crashing.
core / event_route:
- ipc: expose ipc_is_async_dispatch() to detect being inside an IPC RPC job.
- event_route: raise events inline in that case, avoiding a redundant async
dispatch.
test/cc_join_reject_test.py: a standalone rogue-joiner security test. From a
non-member host on the multicast segment it sends unauthenticated JOIN_REQs
(expecting a JOIN_REJECT from the master) and a fake-MASTER_ALIVE flood
(expecting the cluster to ignore it), reporting PASS/FAIL. Requires no node
config changes.
Cryptography uses WolfSSL (linked from the tls_wolfssl module) and, optionally,
libsodium for the XChaCha20-Poly1305 + Argon2id build.1 parent 98f5fb4 commit b3e20c4
21 files changed
Lines changed: 7951 additions & 76 deletions
File tree
- modules
- clusterer_controller
- doc
- test
- clusterer
- event_route
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
274 | 274 | | |
275 | 275 | | |
276 | 276 | | |
| 277 | + | |
277 | 278 | | |
278 | 279 | | |
279 | 280 | | |
| |||
301 | 302 | | |
302 | 303 | | |
303 | 304 | | |
| 305 | + | |
304 | 306 | | |
| 307 | + | |
305 | 308 | | |
306 | 309 | | |
307 | 310 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
123 | 132 | | |
124 | 133 | | |
125 | 134 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
327 | 327 | | |
328 | 328 | | |
329 | 329 | | |
330 | | - | |
331 | 330 | | |
332 | 331 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| 87 | + | |
87 | 88 | | |
88 | 89 | | |
89 | 90 | | |
| |||
106 | 107 | | |
107 | 108 | | |
108 | 109 | | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
114 | 125 | | |
115 | 126 | | |
116 | 127 | | |
| |||
194 | 205 | | |
195 | 206 | | |
196 | 207 | | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
197 | 211 | | |
198 | 212 | | |
199 | 213 | | |
| |||
528 | 542 | | |
529 | 543 | | |
530 | 544 | | |
531 | | - | |
| 545 | + | |
532 | 546 | | |
533 | 547 | | |
534 | 548 | | |
| |||
810 | 824 | | |
811 | 825 | | |
812 | 826 | | |
813 | | - | |
| 827 | + | |
814 | 828 | | |
815 | 829 | | |
816 | 830 | | |
| |||
991 | 1005 | | |
992 | 1006 | | |
993 | 1007 | | |
994 | | - | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
995 | 1012 | | |
996 | 1013 | | |
997 | 1014 | | |
| |||
1038 | 1055 | | |
1039 | 1056 | | |
1040 | 1057 | | |
1041 | | - | |
| 1058 | + | |
1042 | 1059 | | |
1043 | 1060 | | |
1044 | 1061 | | |
| |||
1094 | 1111 | | |
1095 | 1112 | | |
1096 | 1113 | | |
1097 | | - | |
| 1114 | + | |
1098 | 1115 | | |
1099 | 1116 | | |
1100 | 1117 | | |
| |||
1172 | 1189 | | |
1173 | 1190 | | |
1174 | 1191 | | |
1175 | | - | |
| 1192 | + | |
1176 | 1193 | | |
1177 | 1194 | | |
1178 | 1195 | | |
| |||
1189 | 1206 | | |
1190 | 1207 | | |
1191 | 1208 | | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
1192 | 1220 | | |
1193 | 1221 | | |
1194 | 1222 | | |
| |||
1317 | 1345 | | |
1318 | 1346 | | |
1319 | 1347 | | |
1320 | | - | |
| 1348 | + | |
1321 | 1349 | | |
1322 | 1350 | | |
1323 | 1351 | | |
| |||
1385 | 1413 | | |
1386 | 1414 | | |
1387 | 1415 | | |
1388 | | - | |
| 1416 | + | |
1389 | 1417 | | |
1390 | 1418 | | |
1391 | 1419 | | |
| |||
1460 | 1488 | | |
1461 | 1489 | | |
1462 | 1490 | | |
| 1491 | + | |
1463 | 1492 | | |
1464 | 1493 | | |
1465 | 1494 | | |
| |||
1478 | 1507 | | |
1479 | 1508 | | |
1480 | 1509 | | |
1481 | | - | |
| 1510 | + | |
1482 | 1511 | | |
1483 | 1512 | | |
1484 | 1513 | | |
| |||
1487 | 1516 | | |
1488 | 1517 | | |
1489 | 1518 | | |
1490 | | - | |
| 1519 | + | |
1491 | 1520 | | |
1492 | 1521 | | |
1493 | 1522 | | |
| |||
1497 | 1526 | | |
1498 | 1527 | | |
1499 | 1528 | | |
1500 | | - | |
| 1529 | + | |
1501 | 1530 | | |
1502 | 1531 | | |
1503 | 1532 | | |
| |||
1545 | 1574 | | |
1546 | 1575 | | |
1547 | 1576 | | |
1548 | | - | |
| 1577 | + | |
1549 | 1578 | | |
1550 | 1579 | | |
1551 | 1580 | | |
| |||
1560 | 1589 | | |
1561 | 1590 | | |
1562 | 1591 | | |
1563 | | - | |
| 1592 | + | |
1564 | 1593 | | |
1565 | 1594 | | |
1566 | 1595 | | |
| |||
1591 | 1620 | | |
1592 | 1621 | | |
1593 | 1622 | | |
1594 | | - | |
| 1623 | + | |
1595 | 1624 | | |
1596 | 1625 | | |
1597 | 1626 | | |
| |||
1744 | 1773 | | |
1745 | 1774 | | |
1746 | 1775 | | |
1747 | | - | |
1748 | | - | |
1749 | | - | |
| 1776 | + | |
| 1777 | + | |
| 1778 | + | |
| 1779 | + | |
| 1780 | + | |
| 1781 | + | |
| 1782 | + | |
| 1783 | + | |
| 1784 | + | |
| 1785 | + | |
| 1786 | + | |
| 1787 | + | |
| 1788 | + | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
| 1793 | + | |
1750 | 1794 | | |
1751 | 1795 | | |
1752 | 1796 | | |
| |||
0 commit comments