66import collections
77import copy
88import ctypes
9- import functools
109import logging
1110import multiprocessing
1211import os
@@ -142,7 +141,6 @@ def get_serial(cls, opts=None, event=None):
142141 def rotate_secrets (
143142 cls , opts = None , event = None , use_lock = True , owner = False , publisher = None
144143 ):
145- log .info ("Rotating master AES key" )
146144 if opts is None :
147145 opts = {}
148146
@@ -173,6 +171,41 @@ def rotate_secrets(
173171 log .debug ("Pinging all connected minions due to key rotation" )
174172 salt .utils .master .ping_all_connected_minions (opts )
175173
174+ @classmethod
175+ def rotate_cluster_secret (
176+ cls , opts = None , event = None , use_lock = True , owner = False , publisher = None
177+ ):
178+ log .debug ("Rotating cluster AES key" )
179+ if opts is None :
180+ opts = {}
181+
182+ if use_lock :
183+ with cls .secrets ["cluster_aes" ]["secret" ].get_lock ():
184+ cls .secrets ["cluster_aes" ][
185+ "secret"
186+ ].value = salt .utils .stringutils .to_bytes (
187+ cls .secrets ["cluster_aes" ]["reload" ](remove = owner )
188+ )
189+ else :
190+ cls .secrets ["cluster_aes" ][
191+ "secret"
192+ ].value = salt .utils .stringutils .to_bytes (
193+ cls .secrets ["cluster_aes" ]["reload" ](remove = owner )
194+ )
195+
196+ if event :
197+ event .fire_event (
198+ {f"rotate_cluster_aes_key" : True }, tag = "rotate_cluster_aes_key"
199+ )
200+
201+ if publisher :
202+ publisher .send_aes_key_event ()
203+
204+ if opts .get ("ping_on_rotate" ):
205+ # Ping all minions to get them to pick up the new key
206+ log .debug ("Pinging all connected minions due to key rotation" )
207+ salt .utils .master .ping_all_connected_minions (opts )
208+
176209
177210class Maintenance (salt .utils .process .SignalHandlingProcess ):
178211 """
@@ -358,7 +391,7 @@ def handle_key_rotate(self, now, drop_file_wait=5):
358391
359392 if to_rotate :
360393 if self .opts .get ("cluster_id" , None ):
361- SMaster .rotate_secrets (
394+ SMaster .rotate_cluster_secret (
362395 self .opts , self .event , owner = True , publisher = self .ipc_publisher
363396 )
364397 else :
@@ -714,6 +747,20 @@ def _pre_flight(self):
714747 log .critical ("Master failed pre flight checks, exiting\n " )
715748 sys .exit (salt .defaults .exitcodes .EX_GENERIC )
716749
750+ def read_or_generate_key (self , remove = False , fs_wait = 0.1 ):
751+ """
752+ Used to manage a cluster aes session key file.
753+ """
754+ path = os .path .join (self .opts ["cluster_pki_dir" ], ".aes" )
755+ if remove :
756+ os .remove (path )
757+ key = salt .crypt .Crypticle .read_key (path )
758+ if key :
759+ return key
760+ salt .crypt .Crypticle .write_key (path )
761+ time .sleep (fs_wait )
762+ return salt .crypt .Crypticle .read_key (path )
763+
717764 def start (self ):
718765 """
719766 Turn on the master server components
@@ -731,22 +778,18 @@ def start(self):
731778 # signal handlers
732779 with salt .utils .process .default_signals (signal .SIGINT , signal .SIGTERM ):
733780 if self .opts ["cluster_id" ]:
734- keypath = os .path .join (self .opts ["cluster_pki_dir" ], ".aes" )
735- cluster_keygen = functools .partial (
736- salt .crypt .Crypticle .read_or_generate_key ,
737- keypath ,
738- )
739781 # Setup the secrets here because the PubServerChannel may need
740782 # them as well.
741783 SMaster .secrets ["cluster_aes" ] = {
742784 "secret" : multiprocessing .Array (
743- ctypes .c_char , salt .utils .stringutils .to_bytes (cluster_keygen ())
785+ ctypes .c_char ,
786+ salt .utils .stringutils .to_bytes (self .read_or_generate_key ()),
744787 ),
745788 "serial" : multiprocessing .Value (
746789 ctypes .c_longlong ,
747790 lock = False , # We'll use the lock from 'secret'
748791 ),
749- "reload" : cluster_keygen ,
792+ "reload" : self . read_or_generate_key ,
750793 }
751794
752795 SMaster .secrets ["aes" ] = {
@@ -779,7 +822,7 @@ def start(self):
779822 ipc_publisher .pre_fork (self .process_manager )
780823 self .process_manager .add_process (
781824 EventMonitor ,
782- args = [self .opts ],
825+ args = [self .opts , ipc_publisher ],
783826 name = "EventMonitor" ,
784827 )
785828
@@ -908,19 +951,19 @@ class EventMonitor(salt.utils.process.SignalHandlingProcess):
908951 - Handle key rotate events.
909952 """
910953
911- def __init__ (self , opts , channels = None , name = "EventMonitor" ):
954+ def __init__ (self , opts , ipc_publisher , channels = None , name = "EventMonitor" ):
912955 super ().__init__ (name = name )
913956 self .opts = opts
914957 if channels is None :
915958 channels = []
916959 self .channels = channels
960+ self .ipc_publisher = ipc_publisher
917961
918962 async def handle_event (self , package ):
919963 """
920964 Event handler for publish forwarder
921965 """
922966 tag , data = salt .utils .event .SaltEvent .unpack (package )
923- log .debug ("Event monitor got event %s %r" , tag , data )
924967 if tag .startswith ("salt/job" ) and tag .endswith ("/publish" ):
925968 peer_id = data .pop ("__peer_id" , None )
926969 if peer_id :
@@ -937,9 +980,15 @@ async def handle_event(self, package):
937980 for chan in self .channels :
938981 tasks .append (asyncio .create_task (chan .publish (data )))
939982 await asyncio .gather (* tasks )
940- elif tag == "rotate_aes_key" :
941- log .debug ("Event monitor recieved rotate aes key event, rotating key." )
942- SMaster .rotate_secrets (self .opts , owner = False )
983+ elif tag == "rotate_cluster_aes_key" :
984+ peer_id = data .pop ("__peer_id" , None )
985+ if peer_id :
986+ log .debug ("Rotating AES session key" )
987+ SMaster .rotate_cluster_secret (
988+ self .opts , owner = False , publisher = self .ipc_publisher
989+ )
990+ else :
991+ log .trace ("Ignore tag %s" , tag )
943992
944993 def run (self ):
945994 io_loop = tornado .ioloop .IOLoop ()
0 commit comments