Skip to content

Commit c7a0234

Browse files
committed
Enh: CLI add cli to generate/import encryption key. If need, reload the key in the daemon.
Enh: TEST now UDP key is set with CLI call.
1 parent c75f095 commit c7a0234

File tree

66 files changed

+187
-85
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+187
-85
lines changed

bin/opsbro

+1-2
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,16 @@ if __name__ == '__main__':
5757
parser.error = old_error
5858

5959
# We need to import our libs, but can be an install or directly in the tarball (if executed directly from the install tar ball)
60-
install_less = False
6160
# In windows exe, __file__ is not set
6261
if not hasattr(sys, 'frozen'):
6362
my_file = os.path.abspath(os.path.dirname(__file__))
6463
else:
6564
my_file = os.path.dirname(os.path.abspath(sys.argv[0]))
6665
my_root_dir = os.path.dirname(my_file)
66+
6767
# Both files must be present
6868
tar_ball_detection_files = (os.path.join(my_root_dir, 'README.md'), os.path.join(my_root_dir, 'setup.py'))
6969
if os.path.exists(tar_ball_detection_files[0]) and os.path.exists(tar_ball_detection_files[1]):
70-
install_less = True
7170
# Be sure that the opsbro lib is the install root one
7271
sys.path.insert(0, my_root_dir)
7372
from opsbro.defaultpaths import remap_from_install_dir

data/core-configuration/packs/core-cli-gossip/cli/cli.py

+91-10
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
# Gabes Jean, [email protected]
66

77
from __future__ import print_function
8+
import os
89
import sys
910
import time
1011
import itertools
12+
import uuid
13+
import base64
1114

1215
from opsbro.characters import CHARACTERS
1316
from opsbro.log import cprint, logger, sprintf
@@ -234,6 +237,65 @@ def do_zone_list():
234237
cprint(sub_zname, color='cyan')
235238

236239

240+
def _save_key(key_string, zone_name, key_path):
241+
with open(key_path, 'wb') as f:
242+
f.write(key_string)
243+
244+
cprint('%s OK the key is saved as file %s' % (CHARACTERS.check, key_path))
245+
246+
# Try to send the information to the agent, so it can reload the key
247+
try:
248+
get_opsbro_json('/agent/zones-keys/reload/%s' % zone_name)
249+
except get_request_errors():
250+
cprint(' | The agent seems to not be started. Skipping hot key reload.', color='grey')
251+
return
252+
253+
254+
def do_zone_key_generate(zone, erase=False):
255+
from opsbro.defaultpaths import DEFAULT_CFG_DIR
256+
from opsbro.configurationmanager import ZONE_KEYS_DIRECTORY_NAME
257+
print_h1('Generate a new key for the zone %s' % zone)
258+
259+
key_path = os.path.join(DEFAULT_CFG_DIR, ZONE_KEYS_DIRECTORY_NAME, '%s.key' % zone)
260+
261+
if os.path.exists(key_path) and not erase:
262+
cprint('ERROR: the key %s is already existing', color='red')
263+
cprint(' %s Note: You can use the --erase parameter to erase over an existing key' % (CHARACTERS.corner_bottom_left), color='grey')
264+
sys.exit(2)
265+
266+
k = uuid.uuid1().hex[:16]
267+
b64_k = base64.b64encode(k)
268+
cprint('Encryption key for the zone ', end='')
269+
cprint(zone, color='magenta', end='')
270+
cprint(' :', end='')
271+
cprint(b64_k, color='green')
272+
_save_key(b64_k, zone, key_path)
273+
274+
275+
def do_zone_key_import(zone, key, erase=False):
276+
from opsbro.defaultpaths import DEFAULT_CFG_DIR
277+
from opsbro.configurationmanager import ZONE_KEYS_DIRECTORY_NAME
278+
279+
key_path = os.path.join(DEFAULT_CFG_DIR, ZONE_KEYS_DIRECTORY_NAME, '%s.key' % zone)
280+
281+
if os.path.exists(key_path) and not erase:
282+
cprint('ERROR: the key %s is already existing', color='red')
283+
cprint(' %s Note: You can use the --erase parameter to erase over an existing key' % (CHARACTERS.corner_bottom_left), color='grey')
284+
sys.exit(2)
285+
# check key is base64(len16)
286+
try:
287+
raw_key = base64.b64decode(key)
288+
except TypeError: # bad key
289+
cprint('ERROR: the key is not valid. (not base4 encoded)', color='red')
290+
sys.exit(2)
291+
if len(raw_key) != 16:
292+
cprint('ERROR: the key is not valid. (not 128bits)', color='red')
293+
sys.exit(2)
294+
295+
# Note: key is the original b64 encoded one, we did check it
296+
_save_key(key, zone, key_path)
297+
298+
237299
def __print_detection_spinner(timeout):
238300
spinners = itertools.cycle(CHARACTERS.spinners)
239301
start = time.time()
@@ -430,30 +492,30 @@ def do_wait_members(name='', display_name='', group='', count=1, timeout=30):
430492

431493

432494
exports = {
433-
do_members : {
495+
do_members : {
434496
'keywords' : ['gossip', 'members'],
435497
'args' : [
436498
{'name': '--detail', 'type': 'bool', 'default': False, 'description': 'Show detail mode for the cluster members'},
437499
],
438500
'description': 'List the cluster members'
439501
},
440502

441-
do_members_history : {
503+
do_members_history : {
442504
'keywords' : ['gossip', 'history'],
443505
'args' : [
444506
],
445507
'description': 'Show the history of the gossip nodes'
446508
},
447509

448-
do_join : {
510+
do_join : {
449511
'keywords' : ['gossip', 'join'],
450512
'description': 'Join another node cluster',
451513
'args' : [
452514
{'name': 'seed', 'default': '', 'description': 'Other node to join. For example 192.168.0.1:6768'},
453515
],
454516
},
455517

456-
do_leave : {
518+
do_leave : {
457519
'keywords' : ['gossip', 'leave'],
458520
'description': 'Put in leave a cluster node',
459521
'args' : [
@@ -462,7 +524,7 @@ def do_wait_members(name='', display_name='', group='', count=1, timeout=30):
462524
],
463525
},
464526

465-
do_zone_change : {
527+
do_zone_change : {
466528
'keywords' : ['gossip', 'zone', 'change'],
467529
'args' : [
468530
{'name': 'name', 'default': '', 'description': 'Change to the zone'},
@@ -471,15 +533,34 @@ def do_wait_members(name='', display_name='', group='', count=1, timeout=30):
471533
'description' : 'Change the zone of the node'
472534
},
473535

474-
do_zone_list : {
536+
do_zone_list : {
475537
'keywords' : ['gossip', 'zone', 'list'],
476538
'args' : [
477539
],
478540
'allow_temporary_agent': {'enabled': True, },
479541
'description' : 'List all known zones of the node'
480542
},
481543

482-
do_detect_nodes : {
544+
do_zone_key_generate: {
545+
'keywords' : ['gossip', 'zone', 'key', 'generate'],
546+
'args' : [
547+
{'name': '--zone', 'description': 'Name of zone to generate a key for'},
548+
{'name': '--erase', 'type': 'bool', 'default': False, 'description': 'Erase the key if already exiting.'},
549+
],
550+
'description': 'Generate a gossip encryption key for the zone'
551+
},
552+
553+
do_zone_key_import : {
554+
'keywords' : ['gossip', 'zone', 'key', 'import'],
555+
'args' : [
556+
{'name': '--zone', 'description': 'Name of zone to import the key for'},
557+
{'name': '--key', 'description': 'Key to import.'},
558+
{'name': '--erase', 'type': 'bool', 'default': False, 'description': 'Erase the key if already exiting.'},
559+
],
560+
'description': 'Import a gossip encryption key for the zone'
561+
},
562+
563+
do_detect_nodes : {
483564
'keywords' : ['gossip', 'detect'],
484565
'args' : [
485566
{'name': '--auto-join', 'default': False, 'description': 'Try to join the first detected proxy node. If no proxy is founded, join the first one.', 'type': 'bool'},
@@ -488,7 +569,7 @@ def do_wait_members(name='', display_name='', group='', count=1, timeout=30):
488569
'description': 'Try to detect (broadcast) others nodes in the network'
489570
},
490571

491-
do_wait_event : {
572+
do_wait_event : {
492573
'keywords' : ['gossip', 'events', 'wait'],
493574
'args' : [
494575
{'name': 'event-type', 'description': 'Name of the event to wait for'},
@@ -497,15 +578,15 @@ def do_wait_members(name='', display_name='', group='', count=1, timeout=30):
497578
'description': 'Wait until the event is detected'
498579
},
499580

500-
do_gossip_add_event: {
581+
do_gossip_add_event : {
501582
'keywords' : ['gossip', 'events', 'add'],
502583
'args' : [
503584
{'name': 'event-type', 'description': 'Name of the event to add'},
504585
],
505586
'description': 'Add a event to the gossip members'
506587
},
507588

508-
do_wait_members : {
589+
do_wait_members : {
509590
'keywords' : ['gossip', 'wait-members'],
510591
'args' : [
511592
{'name': '--name', 'description': 'Name of the members to wait for be alive'},

opsbro/configurationmanager.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
logger = LoggerFactory.create_logger('configuration')
1717

1818

19+
ZONE_KEYS_DIRECTORY_NAME = 'zone_keys'
20+
1921
class ConfigurationManager(object):
2022
# The parameter for the main cluster class is list here, and we will give back to it what we did read in the
2123
# local.yaml file (one set ones)
@@ -120,6 +122,8 @@ def get_parameters_for_cluster_from_configuration(self):
120122

121123
def load_main_cfg_dir(self, cfg_dir):
122124
self.main_cfg_directory = cfg_dir
125+
# also compute other directories from this
126+
self.zone_keys_directory = os.path.join(self.main_cfg_directory, ZONE_KEYS_DIRECTORY_NAME)
123127
self.load_cfg_dir(self.main_cfg_directory, load_focus='agent')
124128

125129

@@ -184,8 +188,7 @@ def load_agent_parameters(self, o):
184188
if 'zone' in o:
185189
zone = o['zone']
186190
zonemgr = self.get_zonemgr()
187-
zone_keys_directory = os.path.join(self.main_cfg_directory, 'zone_keys')
188-
zonemgr.add_zone(zone, zone_keys_directory)
191+
zonemgr.add_zone(zone)
189192

190193
# grok all others data so we can use them in our checks
191194
cluster_parameters = self.__class__.cluster_parameters

opsbro/encrypter.py

+17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .log import LoggerFactory
99
from .util import bytes_to_unicode, unicode_to_bytes
10+
from .library import libstore
1011

1112
# Global logger for this part
1213
logger = LoggerFactory.create_logger('gossip')
@@ -87,6 +88,22 @@ def load_zone_encryption_key(self, zone_encryption_key, zone_name):
8788
logger.debug('Loading the encryption key %s for the zone %s' % (key_fingerprint, zone_name))
8889

8990

91+
# We did load a zone, or maybe we need to check that the zone did not change it's key
92+
# if so, reload it
93+
def load_or_reload_key_for_zone_if_need(self, zone_name):
94+
from .configurationmanager import configmgr
95+
# If the zone have a key, load it into the encrypter so we will be
96+
# able to use it to exchange with this zone
97+
# The key can be a file in the zone key directory, with the name of the zone.key
98+
zone_keys_directory = configmgr.zone_keys_directory
99+
key_file = os.path.join(zone_keys_directory, '%s.key' % zone_name)
100+
if os.path.exists(key_file):
101+
logger.debug('The zone %s have a key file (%s)' % (zone_name, key_file))
102+
with open(key_file, 'rb') as f:
103+
encryption_key = f.read().strip()
104+
self.load_zone_encryption_key(encryption_key, zone_name)
105+
106+
90107
def _get_key_from_zone(self, zone_name):
91108
if zone_name is None or zone_name not in self.fingerprints_from_zone:
92109
from .gossip import gossiper

opsbro/gossip.py

+8
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,14 @@ def get_zones():
17731773
return jsoner.dumps(zonemgr.get_zones())
17741774

17751775

1776+
# Reload the key for the zone: zone_name
1777+
@http_export('/agent/zones-keys/reload/:zone_name', method='GET', protected=True)
1778+
def get_reload_zone_key(zone_name):
1779+
response.content_type = 'application/json'
1780+
encrypter = libstore.get_encrypter()
1781+
return jsoner.dumps(encrypter.load_or_reload_key_for_zone_if_need(zone_name))
1782+
1783+
17761784
@http_export('/agent/event/:event_type', method='GET', protected=True)
17771785
def get_event(event_type):
17781786
response.content_type = 'application/json'

opsbro/zonemanager.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# Global logger for this part
88
logger = LoggerFactory.create_logger('configuration')
99

10+
1011
class Zone(object):
1112
def __init__(self):
1213
pass
@@ -69,26 +70,19 @@ def __get_sub_zones_rec(self, zname, sub_zones_set, level):
6970
self.__get_sub_zones_rec(sub_zone_name, sub_zones_set, level + 1)
7071

7172

72-
def add_zone(self, zone, zone_keys_directory):
73+
def add_zone(self, zone):
7374
name = zone.get('name', '')
7475
if not name:
7576
return
7677
self.zones[name] = zone
7778
# We did change, dirty the trees
7879
self._dirty_tree = True
79-
80+
8081
# If the zone have a key, load it into the encrypter so we will be
8182
# able to use it to exchange with this zone
8283
# The key can be a file in the zone key directory, with the name of the zone.key
83-
key_file = os.path.join(zone_keys_directory, '%s.key' % name)
84-
if os.path.exists(key_file):
85-
logger.debug('The zone %s have a key file (%s)' % (name, key_file))
86-
with open(key_file, 'rb') as f:
87-
encryption_key = f.read().strip()
88-
encrypter = libstore.get_encrypter()
89-
encrypter.load_zone_encryption_key(encryption_key, name)
90-
else:
91-
logger.debug('The zone %s do not have a key file (%s)' % (name, key_file))
84+
encrypter = libstore.get_encrypter()
85+
encrypter.load_or_reload_key_for_zone_if_need(name)
9286

9387

9488
def get_top_zones_from(self, zname):

test/docker-files/docker-file-DEMO-HTTP-PYTHON3-http-node-client.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ WORKDIR /root/opsbro-oss
1515
RUN python setup.py install
1616

1717
# Ask for an encrypted test
18-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
18+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
1919

2020
# The node1 will try to connect to node2 and auto join it
2121
ENTRYPOINT test/test_demo1_http.sh "NODE-CLIENT"

test/docker-files/docker-file-DEMO-HTTP-PYTHON3-http-node-haproxy.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ WORKDIR /root/opsbro-oss
2020
RUN python setup.py install
2121

2222
# Ask for an encrypted test
23-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
23+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2424

2525

2626
RUN opsbro packs overload global.haproxy

test/docker-files/docker-file-DEMO-HTTP-PYTHON3-http-node-http-1.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ WORKDIR /root/opsbro-oss
1717
RUN python setup.py install
1818

1919
# Ask for an encrypted test
20-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
20+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2121

2222
# The node1 will try to connect to node2 and auto join it
2323
ENTRYPOINT /etc/init.d/apache2 start; test/test_demo1_http.sh "NODE-HTTP-1"

test/docker-files/docker-file-DEMO-HTTP-PYTHON3-http-node-http-2.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ WORKDIR /root/opsbro-oss
1616
RUN python setup.py install
1717

1818
# Ask for an encrypted test
19-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
19+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2020

2121
# The node1 will try to connect to node2 and auto join it
2222
ENTRYPOINT /etc/init.d/apache2 start; test/test_demo1_http.sh "NODE-HTTP-2"

test/docker-files/docker-file-DEMO-HTTP-http-node-client.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ WORKDIR /root/opsbro-oss
1515
RUN python setup.py install
1616

1717
# Ask for an encrypted test
18-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
18+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
1919

2020
# The node1 will try to connect to node2 and auto join it
2121
ENTRYPOINT test/test_demo1_http.sh "NODE-CLIENT"

test/docker-files/docker-file-DEMO-HTTP-http-node-haproxy.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ WORKDIR /root/opsbro-oss
2020
RUN python setup.py install
2121

2222
# Ask for an encrypted test
23-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
23+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2424

2525

2626
RUN opsbro packs overload global.haproxy

test/docker-files/docker-file-DEMO-HTTP-http-node-http-1.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ WORKDIR /root/opsbro-oss
1717
RUN python setup.py install
1818

1919
# Ask for an encrypted test
20-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
20+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2121

2222
# The node1 will try to connect to node2 and auto join it
2323
ENTRYPOINT /etc/init.d/apache2 start; test/test_demo1_http.sh "NODE-HTTP-1"

test/docker-files/docker-file-DEMO-HTTP-http-node-http-2.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ WORKDIR /root/opsbro-oss
1616
RUN python setup.py install
1717

1818
# Ask for an encrypted test
19-
RUN echo -n "NGNjZWI2ZmEyMzEyMTFlOA==" > /etc/opsbro/zone_keys/internet.key
19+
RUN opsbro gossip zone key import --zone internet --key "NGNjZWI2ZmEyMzEyMTFlOA=="
2020

2121
# The node1 will try to connect to node2 and auto join it
2222
ENTRYPOINT /etc/init.d/apache2 start; test/test_demo1_http.sh "NODE-HTTP-2"

0 commit comments

Comments
 (0)