oslo.rootwrap/oslo_rootwrap/cmd.py
Hervé Beraud 9caec06faf Stop to use the __future__ module.
The __future__ module [1] was used in this context to ensure compatibility
between python 2 and python 3.

We previously dropped the support of python 2.7 [2] and now we only support
python 3 so we don't need to continue to use this module and the imports
listed below.

Imports commonly used and their related PEPs:
- `division` is related to PEP 238 [3]
- `print_function` is related to PEP 3105 [4]
- `unicode_literals` is related to PEP 3112 [5]
- `with_statement` is related to PEP 343 [6]
- `absolute_import` is related to PEP 328 [7]

[1] https://docs.python.org/3/library/__future__.html
[2] https://governance.openstack.org/tc/goals/selected/ussuri/drop-py27.html
[3] https://www.python.org/dev/peps/pep-0238
[4] https://www.python.org/dev/peps/pep-3105
[5] https://www.python.org/dev/peps/pep-3112
[6] https://www.python.org/dev/peps/pep-0343
[7] https://www.python.org/dev/peps/pep-0328

Change-Id: I79f231823939e421a04377ca19e2e65b12d9b26a
2020-06-02 20:40:20 +02:00

164 lines
5.8 KiB
Python

# Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Root wrapper for OpenStack services
Filters which commands a service is allowed to run as another user.
To use this with oslo, you should set the following in
oslo.conf:
rootwrap_config=/etc/oslo/rootwrap.conf
You also need to let the oslo user run oslo-rootwrap
as root in sudoers:
oslo ALL = (root) NOPASSWD: /usr/bin/oslo-rootwrap
/etc/oslo/rootwrap.conf *
Service packaging should deploy .filters files only on nodes where
they are needed, to avoid allowing more than is necessary.
"""
import logging
import os
import sys
from oslo_rootwrap import subprocess
from oslo_rootwrap import wrapper
from six import moves
try:
# This isn't available on all platforms (e.g. Windows).
import resource
except ImportError:
resource = None
RC_UNAUTHORIZED = 99
RC_NOCOMMAND = 98
RC_BADCONFIG = 97
RC_NOEXECFOUND = 96
SIGNAL_BASE = 128
def _exit_error(execname, message, errorcode, log=True):
print("%s: %s" % (execname, message), file=sys.stderr)
if log:
logging.error(message)
sys.exit(errorcode)
def daemon():
return main(run_daemon=True)
def main(run_daemon=False):
# Split arguments, require at least a command
execname = sys.argv.pop(0)
if run_daemon:
if len(sys.argv) != 1:
_exit_error(execname, "Extra arguments to daemon", RC_NOCOMMAND,
log=False)
else:
if len(sys.argv) < 2:
_exit_error(execname, "No command specified", RC_NOCOMMAND,
log=False)
configfile = sys.argv.pop(0)
# Load configuration
try:
rawconfig = moves.configparser.RawConfigParser()
rawconfig.read(configfile)
config = wrapper.RootwrapConfig(rawconfig)
except ValueError as exc:
msg = "Incorrect value in %s: %s" % (configfile, exc.args[0])
_exit_error(execname, msg, RC_BADCONFIG, log=False)
except moves.configparser.Error:
_exit_error(execname, "Incorrect configuration file: %s" % configfile,
RC_BADCONFIG, log=False)
if resource:
# When use close_fds=True on Python 2.x, calling subprocess with
# close_fds=True (which we do by default) can be inefficient when
# the current fd ulimits are large, because it blindly closes
# all fds in the range(1, $verylargenumber)
# Lower our ulimit to a reasonable value to regain performance.
fd_limits = resource.getrlimit(resource.RLIMIT_NOFILE)
sensible_fd_limit = min(config.rlimit_nofile, fd_limits[0])
if (fd_limits[0] > sensible_fd_limit):
# Close any fd beyond sensible_fd_limit prior adjusting our
# rlimit to ensure all fds are closed
for fd_entry in os.listdir('/proc/self/fd'):
# NOTE(dmllr): In a previous patch revision non-numeric
# dir entries were silently ignored which reviewers
# didn't like. Readd exception handling when it occurs.
fd = int(fd_entry)
if fd >= sensible_fd_limit:
os.close(fd)
# Unfortunately this inherits to our children, so allow them to
# re-raise by passing through the hard limit unmodified
resource.setrlimit(
resource.RLIMIT_NOFILE, (sensible_fd_limit, fd_limits[1]))
# This is set on import to the hard ulimit. if its defined we
# already have imported it, so we need to update it to the new
# limit.
if (hasattr(subprocess, 'MAXFD') and
subprocess.MAXFD > sensible_fd_limit):
subprocess.MAXFD = sensible_fd_limit
if config.use_syslog:
wrapper.setup_syslog(execname,
config.syslog_log_facility,
config.syslog_log_level)
filters = wrapper.load_filters(config.filters_path)
if run_daemon:
# NOTE(dims): When not running as daemon, this import
# slows us down just a bit. So moving it here so we have
# it only when we need it.
from oslo_rootwrap import daemon as daemon_mod
daemon_mod.daemon_start(config, filters)
else:
run_one_command(execname, config, filters, sys.argv)
def run_one_command(execname, config, filters, userargs):
# Execute command if it matches any of the loaded filters
try:
obj = wrapper.start_subprocess(
filters, userargs,
exec_dirs=config.exec_dirs,
log=config.use_syslog,
stdin=sys.stdin,
stdout=sys.stdout,
stderr=sys.stderr)
returncode = obj.wait()
# Fix returncode of Popen
if returncode < 0:
returncode = SIGNAL_BASE - returncode
sys.exit(returncode)
except wrapper.FilterMatchNotExecutable as exc:
msg = ("Executable not found: %s (filter match = %s)"
% (exc.match.exec_path, exc.match.name))
_exit_error(execname, msg, RC_NOEXECFOUND, log=config.use_syslog)
except wrapper.NoFilterMatched:
msg = ("Unauthorized command: %s (no filter matched)"
% ' '.join(userargs))
_exit_error(execname, msg, RC_UNAUTHORIZED, log=config.use_syslog)