Merge pull request #13680 from pass-by-value/file_perms_no_exit

File perms no exit
This commit is contained in:
Thomas S Hatch 2014-06-30 17:43:54 -06:00
commit 50ee9dd5af
7 changed files with 127 additions and 7 deletions

View File

@ -57,7 +57,14 @@ class SaltCMD(parsers.SaltCMDOptionParser):
self.setup_logfile_logger()
try:
local = salt.client.get_local_client(self.get_config_file_path())
# We don't need to bail on config file permission errors
# if the CLI
# process is run with the -a flag
skip_perm_errors = self.options.eauth != ''
local = salt.client.get_local_client(
self.get_config_file_path(),
skip_perm_errors=skip_perm_errors)
except SaltClientError as exc:
self.exit(2, '{0}\n'.format(exc))
return

View File

@ -55,7 +55,8 @@ log = logging.getLogger(__name__)
def get_local_client(
c_path=os.path.join(syspaths.CONFIG_DIR, 'master'),
mopts=None):
mopts=None,
skip_perm_errors=False):
'''
.. versionadded:: Helium
@ -71,7 +72,7 @@ def get_local_client(
import salt.client.raet
return salt.client.raet.LocalClient(mopts=opts)
elif opts['transport'] == 'zeromq':
return LocalClient(mopts=opts)
return LocalClient(mopts=opts, skip_perm_errors=skip_perm_errors)
class LocalClient(object):
@ -96,7 +97,7 @@ class LocalClient(object):
'''
def __init__(self,
c_path=os.path.join(syspaths.CONFIG_DIR, 'master'),
mopts=None):
mopts=None, skip_perm_errors=False):
if mopts:
self.opts = mopts
else:
@ -110,6 +111,7 @@ class LocalClient(object):
self.opts = salt.config.client_config(c_path)
self.serial = salt.payload.Serial(self.opts)
self.salt_user = self.__get_user()
self.skip_perm_errors = skip_perm_errors
self.key = self.__read_master_key()
self.event = salt.utils.event.get_event(
'master',
@ -132,7 +134,9 @@ class LocalClient(object):
keyfile = os.path.join(self.opts['cachedir'],
'.{0}_key'.format(key_user))
# Make sure all key parent directories are accessible
salt.utils.verify.check_path_traversal(self.opts['cachedir'], key_user)
salt.utils.verify.check_path_traversal(self.opts['cachedir'],
key_user,
self.skip_perm_errors)
try:
with salt.utils.fopen(keyfile, 'r') as key:
@ -1350,6 +1354,11 @@ class LocalClient(object):
# Make sure the publisher is running by checking the unix socket
if not os.path.exists(os.path.join(self.opts['sock_dir'],
'publish_pull.ipc')):
log.error(
'Unable to connect to the publisher! '
'You do not have permissions to access '
'{0}'.format(self.opts['sock_dir'])
)
return {'jid': '0', 'minions': []}
payload_kwargs = self._prep_pub(

View File

@ -241,6 +241,8 @@ VALID_OPTS = {
'ping_interval': int,
'cli_summary': bool,
'max_minions': int,
'username': str,
'password': str,
}
# default configurations
@ -360,6 +362,8 @@ DEFAULT_MINION_OPTS = {
'raet_port': 4510,
'restart_on_error': False,
'ping_interval': 0,
'username': None,
'password': None,
}
DEFAULT_MASTER_OPTS = {

View File

@ -1653,6 +1653,18 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
action='store_true',
help=('Display summary information about a salt command')
)
self.add_option(
'--username',
dest='username',
nargs=1,
help=('Username for external authentication')
)
self.add_option(
'--password',
dest='password',
nargs=1,
help=('Password for external authentication')
)
def _mixin_after_parsed(self):
if len(self.args) <= 1 and not self.options.doc:

View File

@ -341,7 +341,7 @@ def list_path_traversal(path):
return out
def check_path_traversal(path, user='root'):
def check_path_traversal(path, user='root', skip_perm_errors=False):
'''
Walk from the root up to a directory and verify that the current
user has access to read each directory. This is used for making
@ -362,6 +362,12 @@ def check_path_traversal(path, user='root'):
else:
msg += ' Please give {0} read permissions.'.format(user,
tpath)
# We don't need to bail on config file permission errors
# if the CLI
# process is run with the -a flag
if skip_perm_errors:
return
# Propagate this exception up so there isn't a sys.exit()
# in the middle of code that could be imported elsewhere.
raise SaltClientError(msg)

View File

@ -28,7 +28,7 @@ config_opt:
yaml_utf8: True
external_auth:
auto:
pam:
saltdev:
- '@wheel'
- '@runner'

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
'''
tests.integration.shell.auth
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''
# Import python libs
import os
import yaml
import pipes
import shutil
# Import Salt Testing libs
from salttesting.helpers import (
ensure_in_syspath,
destructiveTest,
with_system_user)
ensure_in_syspath('../../')
# Import salt libs
import integration
import salt.utils
from salttesting import skipIf
import random
class AuthTest(integration.ShellCase):
'''
Test auth mechanisms
'''
_call_binary_ = 'salt'
is_root = os.geteuid() != 0
@destructiveTest
@skipIf(is_root, 'You must be logged in as root to run this test')
# @with_system_user('saltdev') - doesn't work with ShellCase
def test_pam_auth_valid_user(self):
'''
test pam auth mechanism is working with a valid user
'''
alphabet = ('abcdefghijklmnopqrstuvwxyz'
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
self.password = ''
# generate password
for _ in range(20):
next_index = random.randrange(len(alphabet))
self.password = self.password + alphabet[next_index]
# hash the password
from salt.utils.pycrypto import gen_hash
pwd = gen_hash('salt', self.password, 'sha512')
self.run_call("shadow.set_password saltdev '{0}'".format(pwd))
cmd = ('-a pam "*"'
' test.ping --username {0}'
' --password {1}'.format('saltdev', self.password))
resp = self.run_salt(cmd)
self.assertTrue(
'minion:' in resp
)
@skipIf(is_root, 'You must be logged in as root to run this test')
def test_pam_auth_invalid_user(self):
'''
test pam auth mechanism errors for an invalid user
'''
cmd = ('-a pam'
' * test.ping --username nouser'
' --password {0}'.format('abcd1234'))
resp = self.run_salt(cmd)
self.assertTrue(
'Failed to authenticate' in ''.join(resp)
)
if __name__ == '__main__':
from integration import run_tests
run_tests(AuthTest)