mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Add documentation to the example master and minion configuration files.
Move minion event signing to a saner place. Enable dropping messages when signature does not verify or when minion is not adding the signature to its payloads.
This commit is contained in:
parent
e44673cdae
commit
dadf4b851c
7 changed files with 83 additions and 14 deletions
14
conf/master
14
conf/master
|
@ -366,6 +366,20 @@
|
|||
# will cause minion to throw an exception and drop the message.
|
||||
# sign_pub_messages: False
|
||||
|
||||
# Signature verification on messages published from minions
|
||||
# This requires that minions cryptographically sign the messages they
|
||||
# publish to the master. If minions are not signing, then log this information
|
||||
# at loglevel 'INFO' and drop the message without acting on it.
|
||||
# require_minion_sign_messages: False
|
||||
|
||||
# The below will drop messages when their signatures do not validate.
|
||||
# Note that when this option is False but `require_minion_sign_messages` is True
|
||||
# minions MUST sign their messages but the validity of their signatures
|
||||
# is ignored.
|
||||
# These two config options exist so a Salt infrastructure can be moved
|
||||
# to signing minion messages gradually.
|
||||
# drop_messages_signature_fail: False
|
||||
|
||||
##### Salt-SSH Configuration #####
|
||||
##########################################
|
||||
|
||||
|
|
|
@ -9,3 +9,18 @@ controls whether a minion can request that the master revoke its key. When True
|
|||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
||||
|
|
|
@ -874,6 +874,19 @@ VALID_OPTS = {
|
|||
|
||||
# File chunk size for salt-cp
|
||||
'salt_cp_chunk_size': int,
|
||||
|
||||
# Require that the minion sign messages it posts to the master on the event
|
||||
# bus
|
||||
'minion_sign_messages': bool,
|
||||
|
||||
# Have master drop messages from minions for which their signatures do
|
||||
# not verify
|
||||
'drop_messages_signature_fail': bool,
|
||||
|
||||
# Require that payloads from minions have a 'sig' entry
|
||||
# (in other words, require that minions have 'minion_sign_messages'
|
||||
# turned on)
|
||||
'require_minion_sign_messages': bool,
|
||||
}
|
||||
|
||||
# default configurations
|
||||
|
@ -1105,6 +1118,7 @@ DEFAULT_MINION_OPTS = {
|
|||
'ssl': None,
|
||||
'cache': 'localfs',
|
||||
'salt_cp_chunk_size': 65536,
|
||||
'minion_sign_messages': False,
|
||||
}
|
||||
|
||||
DEFAULT_MASTER_OPTS = {
|
||||
|
@ -1364,6 +1378,8 @@ DEFAULT_MASTER_OPTS = {
|
|||
'thin_extra_mods': '',
|
||||
'allow_minion_key_revoke': True,
|
||||
'salt_cp_chunk_size': 98304,
|
||||
'require_minion_sign_messages': False,
|
||||
'drop_messages_signature_fail': False,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -780,6 +780,7 @@ class RemoteFuncs(object):
|
|||
# If the return data is invalid, just ignore it
|
||||
if any(key not in load for key in ('return', 'jid', 'id')):
|
||||
return False
|
||||
|
||||
if load['jid'] == 'req':
|
||||
# The minion is returning a standalone job, request a jobid
|
||||
prep_fstr = '{0}.prep_jid'.format(self.opts['master_job_cache'])
|
||||
|
|
|
@ -1085,14 +1085,6 @@ class AESFuncs(object):
|
|||
)
|
||||
return False
|
||||
|
||||
if 'sig' in load:
|
||||
log.trace('Verifying signed event publish from minion')
|
||||
sig = load.pop('sig')
|
||||
this_minion_pubkey = os.path.join(self.opts['pki_dir'], 'minions/{0}'.format(load['id']))
|
||||
serialized_load = salt.serializers.msgpack.serialize(load)
|
||||
if not salt.crypt.verify_signature(this_minion_pubkey, serialized_load, sig):
|
||||
log.info('Signature for signed event from minion published on bus did not verify')
|
||||
|
||||
if 'tok' in load:
|
||||
load.pop('tok')
|
||||
|
||||
|
@ -1385,6 +1377,24 @@ class AESFuncs(object):
|
|||
|
||||
:param dict load: The minion payload
|
||||
'''
|
||||
if self.opts['require_minion_sign_messages'] and 'sig' not in load:
|
||||
log.critical('_return: Master is requiring minions to sign their messages, but there is no signature in this payload from {0}.'.format(load['id']))
|
||||
return False
|
||||
|
||||
if 'sig' in load:
|
||||
log.trace('Verifying signed event publish from minion')
|
||||
sig = load.pop('sig')
|
||||
this_minion_pubkey = os.path.join(self.opts['pki_dir'], 'minions/{0}'.format(load['id']))
|
||||
serialized_load = salt.serializers.msgpack.serialize(load)
|
||||
if not salt.crypt.verify_signature(this_minion_pubkey, serialized_load, sig):
|
||||
log.info('Failed to verify event signature from minion {0}.'.format(load['id']))
|
||||
if self.opts['drop_messages_signature_fail']:
|
||||
log.critical('Drop_messages_signature_fail is enabled, dropping message from {0}'.format(load['id']))
|
||||
return False
|
||||
else:
|
||||
log.info('But \'drop_message_signature_fail\' is disabled, so message is still accepted.')
|
||||
load['sig'] = sig
|
||||
|
||||
try:
|
||||
salt.utils.job.store_job(
|
||||
self.opts, load, event=self.event, mminion=self.mminion)
|
||||
|
@ -1428,6 +1438,9 @@ class AESFuncs(object):
|
|||
ret['fun_args'] = load['arg']
|
||||
if 'out' in load:
|
||||
ret['out'] = load['out']
|
||||
if 'sig' in load:
|
||||
ret['sig'] = load['sig']
|
||||
|
||||
self._return(ret)
|
||||
|
||||
def minion_runner(self, clear_load):
|
||||
|
|
|
@ -1158,11 +1158,25 @@ class Minion(MinionBase):
|
|||
return functions, returners, errors, executors
|
||||
|
||||
def _send_req_sync(self, load, timeout):
|
||||
|
||||
if self.opts['minion_sign_messages']:
|
||||
log.trace('Signing event to be published onto the bus.')
|
||||
minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load))
|
||||
load['sig'] = sig
|
||||
|
||||
channel = salt.transport.Channel.factory(self.opts)
|
||||
return channel.send(load, timeout=timeout)
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def _send_req_async(self, load, timeout):
|
||||
|
||||
if self.opts['minion_sign_messages']:
|
||||
log.trace('Signing event to be published onto the bus.')
|
||||
minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load))
|
||||
load['sig'] = sig
|
||||
|
||||
channel = salt.transport.client.AsyncReqChannel.factory(self.opts)
|
||||
ret = yield channel.send(load, timeout=timeout)
|
||||
raise tornado.gen.Return(ret)
|
||||
|
@ -1186,12 +1200,6 @@ class Minion(MinionBase):
|
|||
else:
|
||||
return
|
||||
|
||||
if self.opts['sign_minion_messages']:
|
||||
log.trace('Signing event being published onto the bus.')
|
||||
minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem')
|
||||
sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load))
|
||||
load['sig'] = sig
|
||||
|
||||
def timeout_handler(*_):
|
||||
log.info('fire_master failed: master could not be contacted. Request timed out.')
|
||||
return True
|
||||
|
@ -1636,6 +1644,7 @@ class Minion(MinionBase):
|
|||
log.warning(msg)
|
||||
return True
|
||||
|
||||
|
||||
if sync:
|
||||
try:
|
||||
ret_val = self._send_req_sync(load, timeout=timeout)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Import Salt libs
|
||||
import salt.minion
|
||||
|
|
Loading…
Add table
Reference in a new issue