Separates key_dir from cache_dir, The key files (i.e. '.root_key', '.sudo_...') must not be shared with other masters.

This commit is contained in:
kstreee 2017-10-20 11:06:31 +09:00
parent 19a5383c59
commit 20bf4eed1d
No known key found for this signature in database
GPG Key ID: 43F5F108E2B7859E
9 changed files with 43 additions and 28 deletions

View File

@ -147,6 +147,7 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di
os.path.join(self.config['cachedir'], 'jobs'),
os.path.join(self.config['cachedir'], 'proc'),
self.config['sock_dir'],
self.config['key_dir'],
self.config['token_dir'],
self.config['syndic_dir'],
self.config['sqlite_queue_dir'],
@ -160,7 +161,7 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di
v_dirs,
self.config['user'],
permissive=self.config['permissive_pki_access'],
pki_dir=self.config['pki_dir'],
sensitive_dirs=[self.config['pki_dir'], self.config['key_dir']],
)
# Clear out syndics from cachedir
for syndic_file in os.listdir(self.config['syndic_dir']):
@ -280,7 +281,7 @@ class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: di
v_dirs,
self.config['user'],
permissive=self.config['permissive_pki_access'],
pki_dir=self.config['pki_dir'],
sensitive_dirs=[self.config['pki_dir']],
)
except OSError as error:
self.environment_failure(error)
@ -467,7 +468,7 @@ class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): #
v_dirs,
self.config['user'],
permissive=self.config['permissive_pki_access'],
pki_dir=self.config['pki_dir'],
sensitive_dirs=[self.config['pki_dir']],
)
except OSError as error:
self.environment_failure(error)
@ -575,7 +576,7 @@ class Syndic(salt.utils.parsers.SyndicOptionParser, DaemonsMixin): # pylint: di
],
self.config['user'],
permissive=self.config['permissive_pki_access'],
pki_dir=self.config['pki_dir'],
sensitive_dirs=[self.config['pki_dir']],
)
except OSError as error:
self.environment_failure(error)

View File

@ -78,7 +78,7 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
if 'token' in self.config:
import salt.utils.files
try:
with salt.utils.files.fopen(os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_:
with salt.utils.files.fopen(os.path.join(self.config['key_dir'], '.root_key'), 'r') as fp_:
kwargs['key'] = fp_.readline()
except IOError:
kwargs['token'] = self.config['token']

View File

@ -188,11 +188,11 @@ class LocalClient(object):
# The username may contain '\' if it is in Windows
# 'DOMAIN\username' format. Fix this for the keyfile path.
key_user = key_user.replace(u'\\', u'_')
keyfile = os.path.join(self.opts[u'cachedir'],
keyfile = os.path.join(self.opts[u'key_dir'],
u'.{0}_key'.format(key_user))
try:
# Make sure all key parent directories are accessible
salt.utils.verify.check_path_traversal(self.opts[u'cachedir'],
salt.utils.verify.check_path_traversal(self.opts[u'key_dir'],
key_user,
self.skip_perm_errors)
with salt.utils.files.fopen(keyfile, u'r') as key:

View File

@ -186,6 +186,9 @@ VALID_OPTS = {
# The directory used to store public key data
'pki_dir': str,
# The directory to store authentication keys of a master's local environment.
'key_dir': str,
# A unique identifier for this daemon
'id': str,
@ -1420,6 +1423,7 @@ DEFAULT_MASTER_OPTS = {
'archive_jobs': False,
'root_dir': salt.syspaths.ROOT_DIR,
'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'master'),
'key_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'key'),
'key_cache': '',
'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'master'),
'file_roots': {
@ -2399,7 +2403,7 @@ def syndic_config(master_config_path,
opts.update(syndic_opts)
# Prepend root_dir to other paths
prepend_root_dirs = [
'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
'pki_dir', 'key_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
'autosign_file', 'autoreject_file', 'token_dir'
]
for config_key in ('log_file', 'key_logfile', 'syndic_log_file'):
@ -3770,7 +3774,7 @@ def apply_master_config(overrides=None, defaults=None):
# Prepend root_dir to other paths
prepend_root_dirs = [
'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
'pki_dir', 'key_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
'autosign_file', 'autoreject_file', 'token_dir', 'syndic_dir',
'sqlite_queue_dir'
]

View File

@ -183,11 +183,11 @@ def mk_key(opts, user):
# The username may contain '\' if it is in Windows
# 'DOMAIN\username' format. Fix this for the keyfile path.
keyfile = os.path.join(
opts['cachedir'], '.{0}_key'.format(user.replace('\\', '_'))
opts['key_dir'], '.{0}_key'.format(user.replace('\\', '_'))
)
else:
keyfile = os.path.join(
opts['cachedir'], '.{0}_key'.format(user)
opts['key_dir'], '.{0}_key'.format(user)
)
if os.path.exists(keyfile):

View File

@ -124,7 +124,7 @@ class KeyCLI(object):
if self.opts[u'eauth']:
if u'token' in self.opts:
try:
with salt.utils.files.fopen(os.path.join(self.opts[u'cachedir'], u'.root_key'), u'r') as fp_:
with salt.utils.files.fopen(os.path.join(self.opts[u'key_dir'], u'.root_key'), u'r') as fp_:
low[u'key'] = fp_.readline()
except IOError:
low[u'token'] = self.opts[u'token']

View File

@ -205,7 +205,7 @@ class Runner(RunnerClient):
if self.opts.get(u'eauth'):
if u'token' in self.opts:
try:
with salt.utils.files.fopen(os.path.join(self.opts[u'cachedir'], u'.root_key'), u'r') as fp_:
with salt.utils.files.fopen(os.path.join(self.opts[u'key_dir'], u'.root_key'), u'r') as fp_:
low[u'key'] = fp_.readline()
except IOError:
low[u'token'] = self.opts[u'token']

View File

@ -194,13 +194,13 @@ def verify_files(files, user):
return True
def verify_env(dirs, user, permissive=False, pki_dir='', skip_extra=False):
def verify_env(dirs, user, permissive=False, sensitive_dirs=None, skip_extra=False):
'''
Verify that the named directories are in place and that the environment
can shake the salt
'''
if salt.utils.platform.is_windows():
return win_verify_env(dirs, permissive, pki_dir, skip_extra)
return win_verify_env(dirs, permissive, sensitive_dirs, skip_extra)
import pwd # after confirming not running Windows
try:
pwnam = pwd.getpwnam(user)
@ -275,10 +275,11 @@ def verify_env(dirs, user, permissive=False, pki_dir='', skip_extra=False):
# to read in what it needs to integrate.
#
# If the permissions aren't correct, default to the more secure 700.
# If acls are enabled, the pki_dir needs to remain readable, this
# is still secure because the private keys are still only readable
# by the user running the master
if dir_ == pki_dir:
# If acls are enabled, the sensitive_dirs (i.e. pki_dir, key_dir) needs to
# remain readable, this is still secure because the private keys are still
# only readable by the user running the master
sensitive_dirs = sensitive_dirs or []
if dir_ in sensitive_dirs:
smode = stat.S_IMODE(mode.st_mode)
if smode != 448 and smode != 488:
if os.access(dir_, os.W_OK):
@ -525,7 +526,7 @@ def verify_log(opts):
log.warning('Insecure logging configuration detected! Sensitive data may be logged.')
def win_verify_env(dirs, permissive=False, pki_dir='', skip_extra=False):
def win_verify_env(dirs, permissive=False, sensitive_dirs=None, skip_extra=False):
'''
Verify that the named directories are in place and that the environment
can shake the salt
@ -596,8 +597,9 @@ def win_verify_env(dirs, permissive=False, pki_dir='', skip_extra=False):
sys.stderr.write(msg.format(dir_, err))
sys.exit(err.errno)
# The PKI dir gets its own permissions
if dir_ == pki_dir:
# The senitive_dirs (i.e. pki_dir, key_dir) gets its own permissions
sensitive_dirs = sensitive_dirs or []
if dir_ in sensitive_dirs:
try:
# Make Administrators group the owner
salt.utils.win_dacl.set_owner(path, 'S-1-5-32-544')

View File

@ -111,13 +111,21 @@ class TestVerify(TestCase):
def test_verify_env(self):
root_dir = tempfile.mkdtemp(dir=TMP)
var_dir = os.path.join(root_dir, 'var', 'log', 'salt')
verify_env([var_dir], getpass.getuser())
key_dir = os.path.join(root_dir, 'key_dir')
verify_env([var_dir, key_dir], getpass.getuser(), sensitive_dirs=[key_dir])
self.assertTrue(os.path.exists(var_dir))
dir_stat = os.stat(var_dir)
self.assertEqual(dir_stat.st_uid, os.getuid())
self.assertEqual(dir_stat.st_mode & stat.S_IRWXU, stat.S_IRWXU)
self.assertEqual(dir_stat.st_mode & stat.S_IRWXG, 40)
self.assertEqual(dir_stat.st_mode & stat.S_IRWXO, 5)
self.assertTrue(os.path.exists(key_dir))
var_dir_stat = os.stat(var_dir)
self.assertEqual(var_dir_stat.st_uid, os.getuid())
self.assertEqual(var_dir_stat.st_mode & stat.S_IRWXU, stat.S_IRWXU)
self.assertEqual(var_dir_stat.st_mode & stat.S_IRWXG, 40)
self.assertEqual(var_dir_stat.st_mode & stat.S_IRWXO, 5)
key_dir_stat = os.stat(key_dir)
self.assertEqual(key_dir_stat.st_mode & stat.S_IRWXU, stat.S_IRWXU)
self.assertEqual(key_dir_stat.st_mode & stat.S_IRWXG, 0)
self.assertEqual(key_dir_stat.st_mode & stat.S_IRWXO, 0)
@requires_network(only_local_network=True)
def test_verify_socket(self):