Add support for dropping privileges
Drop privileges to a regular user prior to executing any command defined by the snap. If the user and group don't already exist, they will be created. Additionally, file permissios and ownership of setup files are adjusted to limit access from other users. Change-Id: I8563abce55b2b20936eb4e1d55a9016b97e8f6e0
This commit is contained in:
parent
110b773d98
commit
e51cf60780
|
@ -60,6 +60,7 @@ class OpenStackSnap(object):
|
|||
utils = SnapUtils()
|
||||
LOG.debug(setup)
|
||||
|
||||
utils.ensure_key('install', setup.keys())
|
||||
install = setup['install']
|
||||
if install == 'classic':
|
||||
root_dir = '/'
|
||||
|
@ -70,11 +71,28 @@ class OpenStackSnap(object):
|
|||
LOG.error(_msg)
|
||||
raise ValueError(_msg)
|
||||
|
||||
utils.ensure_key('users', setup.keys())
|
||||
for user, groups in setup['users'].items():
|
||||
home = os.path.join(root_dir, "/var/lib/", user)
|
||||
utils.add_user(user, groups, home)
|
||||
|
||||
utils.ensure_key('default_owner', setup.keys())
|
||||
default_user = setup['default_owner'].split(':')[0]
|
||||
default_group = setup['default_owner'].split(':')[1]
|
||||
|
||||
utils.ensure_key('default_dir_mode', setup.keys())
|
||||
default_dir_mode = setup['default_dir_mode']
|
||||
|
||||
utils.ensure_key('default_file_mode', setup.keys())
|
||||
default_file_mode = setup['default_file_mode']
|
||||
|
||||
if 'dirs' in setup.keys():
|
||||
for directory in setup['dirs']:
|
||||
directory = os.path.join(root_dir, directory)
|
||||
dir_name = directory.format(**utils.snap_env)
|
||||
utils.ensure_dir(dir_name)
|
||||
utils.rchmod(dir_name, default_dir_mode, default_file_mode)
|
||||
utils.rchown(dir_name, default_user, default_group)
|
||||
|
||||
if 'templates' in setup.keys():
|
||||
for template in setup['templates']:
|
||||
|
@ -84,8 +102,9 @@ class OpenStackSnap(object):
|
|||
utils.ensure_dir(target_file, is_file=True)
|
||||
LOG.debug('Rendering {} to {}'.format(template, target_file))
|
||||
with open(target_file, 'w') as tf:
|
||||
os.fchmod(tf.fileno(), 0o640)
|
||||
tf.write(renderer.render(template, utils.snap_env))
|
||||
utils.chmod(target_file, default_file_mode)
|
||||
utils.chown(target_file, default_user, default_group)
|
||||
|
||||
if 'copyfiles' in setup.keys():
|
||||
for source, target in setup['copyfiles'].items():
|
||||
|
@ -99,6 +118,28 @@ class OpenStackSnap(object):
|
|||
continue
|
||||
LOG.debug('Copying file {} to {}'.format(s_file, d_file))
|
||||
shutil.copy2(s_file, d_file)
|
||||
utils.chmod(d_file, default_file_mode)
|
||||
utils.chown(d_file, default_user, default_group)
|
||||
|
||||
if 'chmod' in setup.keys():
|
||||
for target in setup['chmod']:
|
||||
target_path = target.format(**utils.snap_env)
|
||||
mode = setup['chmod'][target]
|
||||
utils.chmod(target_path, mode)
|
||||
|
||||
if 'chown' in setup.keys():
|
||||
for target in setup['chown']:
|
||||
target_path = target.format(**utils.snap_env)
|
||||
user = setup['chown'][target].split(':')[0]
|
||||
group = setup['chown'][target].split(':')[1]
|
||||
utils.chown(target_path, user, group)
|
||||
|
||||
if 'rchown' in setup.keys():
|
||||
for target in setup['rchown']:
|
||||
target_path = target.format(**utils.snap_env)
|
||||
user = setup['rchown'][target].split(':')[0]
|
||||
group = setup['rchown'][target].split(':')[1]
|
||||
utils.rchown(target_path, user, group)
|
||||
|
||||
def execute(self, argv):
|
||||
'''Execute snap command building out configuration and log options'''
|
||||
|
@ -110,6 +151,9 @@ class OpenStackSnap(object):
|
|||
LOG.error(_msg)
|
||||
raise ValueError(_msg)
|
||||
|
||||
utils.ensure_key('run_as', entry_point)
|
||||
user, groups = list(entry_point['run_as'].items())[0]
|
||||
|
||||
other_args = argv[2:]
|
||||
LOG.debug(entry_point)
|
||||
|
||||
|
@ -174,5 +218,7 @@ class OpenStackSnap(object):
|
|||
LOG.debug('Configuration file {} not found'
|
||||
', skipping'.format(cfile))
|
||||
|
||||
utils.drop_privileges(user, groups)
|
||||
|
||||
LOG.debug('Executing command {}'.format(' '.join(cmd)))
|
||||
os.execvp(cmd[0], cmd)
|
||||
|
|
|
@ -13,6 +13,8 @@ entry_points:
|
|||
- "/etc/nova/nova.conf"
|
||||
config-dirs:
|
||||
- "/etc/nova/conf.d"
|
||||
run_as:
|
||||
nova: [nova]
|
||||
nova-scheduler:
|
||||
type: simple
|
||||
binary: nova-scheduler
|
||||
|
@ -21,10 +23,16 @@ entry_points:
|
|||
config-dirs:
|
||||
- "/etc/nova/conf.d"
|
||||
log-file: "/var/log/nova/scheduler.log"
|
||||
run_as:
|
||||
nova: [nova]
|
||||
keystone-api:
|
||||
type: uwsgi
|
||||
uwsgi-dir: "/etc/uwsgi"
|
||||
log-file: "/var/log/uwsgi/keystone.log"
|
||||
run_as:
|
||||
keystone: [keystone]
|
||||
nova-broken:
|
||||
type: unknown
|
||||
binary: nova-broken
|
||||
run_as:
|
||||
nova: [nova]
|
||||
|
|
|
@ -49,6 +49,7 @@ class TestOpenStackSnapExecute(test_base.TestCase):
|
|||
def mock_snap_utils(self, mock_utils):
|
||||
snap_utils = mock_utils.return_value
|
||||
snap_utils.snap_env.return_value = MOCK_SNAP_ENV
|
||||
snap_utils.drop_privileges.return_value = None
|
||||
|
||||
@patch('snap_openstack.base.SnapUtils')
|
||||
@patch.object(base, 'os')
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import grp
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -72,3 +75,69 @@ class SnapUtils(object):
|
|||
if not os.path.exists(dir_name):
|
||||
LOG.info('Creating directory {}'.format(dir_name))
|
||||
os.makedirs(dir_name, 0o750)
|
||||
|
||||
def ensure_key(self, key, keys):
|
||||
'''Ensure key exists and raise ValueError if it doesn't'''
|
||||
if key not in keys:
|
||||
_msg = '{} key is required'.format(key)
|
||||
LOG.error(_msg)
|
||||
raise ValueError(_msg)
|
||||
|
||||
def add_user(self, user, groups, home):
|
||||
'''Add user to the system as a member of one ore more groups'''
|
||||
for group in groups:
|
||||
LOG.debug('Adding group {} to system'.format(group))
|
||||
cmd = ['addgroup', '--system', group]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
self.ensure_dir(home)
|
||||
LOG.debug('Adding user {} to system'.format(user))
|
||||
cmd = ['adduser', '--quiet', '--system', '--home', home,
|
||||
'--no-create-home', '--shell', '/bin/false', user]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
for group in groups:
|
||||
LOG.debug('Adding user {} to group {}'.format(user, group))
|
||||
cmd = ['adduser', user, group]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
def chown(self, path, user, group):
|
||||
'''Change the owner of the specified file'''
|
||||
LOG.debug('Changing owner of {} to {}:{}'.format(path, user, group))
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
os.chown(path, uid, gid)
|
||||
|
||||
def chmod(self, path, mode):
|
||||
'''Change the file mode bits of the specified file'''
|
||||
LOG.debug('Changing file mode of {} to {}'.format(path, oct(mode)))
|
||||
os.chmod(path, mode)
|
||||
|
||||
def rchown(self, root_dir, user, group):
|
||||
'''Recursively change owner starting at the specified directory'''
|
||||
self.chown(root_dir, user, group)
|
||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||
for d in dirnames:
|
||||
self.chown(os.path.join(dirpath, d), user, group)
|
||||
for f in filenames:
|
||||
self.chown(os.path.join(dirpath, f), user, group)
|
||||
|
||||
def rchmod(self, root_dir, dir_mode, file_mode):
|
||||
'''Recursively change mode bits starting at the specified directory'''
|
||||
self.chmod(root_dir, dir_mode)
|
||||
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||
for d in dirnames:
|
||||
self.chmod(os.path.join(dirpath, d), dir_mode)
|
||||
for f in filenames:
|
||||
self.chmod(os.path.join(dirpath, f), file_mode)
|
||||
|
||||
def drop_privileges(self, user, groups):
|
||||
'''Drop privileges to the specified user and group(s)'''
|
||||
LOG.debug('Dropping privileges to {}:{}'.format(user, groups))
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
gid = grp.getgrnam(groups[0]).gr_gid
|
||||
gids = [grp.getgrnam(g).gr_gid for g in groups]
|
||||
os.setgroups([])
|
||||
os.setgroups(gids)
|
||||
os.setgid(gid)
|
||||
os.setuid(uid)
|
||||
|
|
Loading…
Reference in New Issue