Use rootwrap to execute iptables instead of requiring root
This patch set adds support for rootwrap in order to execute iptables. Co-Authored-By: Dmitry Tantsur <dtantsur@redhat.com> Change-Id: I7c424c17222f119730b8c5ac0daafd9906282e4d Closes-bug: #1495844
This commit is contained in:
parent
7e10d5c2fa
commit
52ef561c9f
24
README.rst
24
README.rst
|
@ -161,6 +161,23 @@ for the other possible configuration options.
|
||||||
Configuration file contains a password and thus should be owned by ``root``
|
Configuration file contains a password and thus should be owned by ``root``
|
||||||
and should have access rights like ``0600``.
|
and should have access rights like ``0600``.
|
||||||
|
|
||||||
|
**ironic-inspector** requires root rights for managing iptables. It gets them
|
||||||
|
by running ``ironic-inspector-rootwrap`` utility with ``sudo``. To allow it,
|
||||||
|
copy ``rootwrap.conf`` to the configuration directory (e.g. as
|
||||||
|
``/etc/ironic-inspector/rootwrap.conf`` and create file
|
||||||
|
``/etc/sudoers.d/ironic-inspector-rootwrap`` with the following content::
|
||||||
|
|
||||||
|
stack ALL=(root) NOPASSWD: /usr/bin/ironic-inspector-rootwrap /etc/ironic-inspector/rootwrap.conf *
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``rootwrap.conf`` must be writeable only by root.
|
||||||
|
|
||||||
|
Replace ``stack`` with whatever user you'll be using to run
|
||||||
|
**ironic-inspector**.
|
||||||
|
|
||||||
|
Configuring PXE
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
As for PXE boot environment, you'll need:
|
As for PXE boot environment, you'll need:
|
||||||
|
|
||||||
* TFTP server running and accessible (see below for using *dnsmasq*).
|
* TFTP server running and accessible (see below for using *dnsmasq*).
|
||||||
|
@ -280,15 +297,10 @@ will be accessed by ramdisk on a booting machine).
|
||||||
Running
|
Running
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
Run as ``root``::
|
::
|
||||||
|
|
||||||
ironic-inspector --config-file /etc/ironic-inspector/inspector.conf
|
ironic-inspector --config-file /etc/ironic-inspector/inspector.conf
|
||||||
|
|
||||||
.. note::
|
|
||||||
Running as ``root`` is not required if **ironic-inspector** does not
|
|
||||||
manage the firewall (i.e. ``manage_firewall`` is set to ``false`` in the
|
|
||||||
configuration file).
|
|
||||||
|
|
||||||
A good starting point for writing your own *systemd* unit should be `one used
|
A good starting point for writing your own *systemd* unit should be `one used
|
||||||
in Fedora <http://pkgs.fedoraproject.org/cgit/openstack-ironic-discoverd.git/plain/openstack-ironic-discoverd.service>`_
|
in Fedora <http://pkgs.fedoraproject.org/cgit/openstack-ironic-discoverd.git/plain/openstack-ironic-discoverd.service>`_
|
||||||
(note usage of old name).
|
(note usage of old name).
|
||||||
|
|
|
@ -4,8 +4,9 @@ IRONIC_INSPECTOR_BIN_DIR=$(get_python_exec_prefix)
|
||||||
IRONIC_INSPECTOR_BIN_FILE=$IRONIC_INSPECTOR_BIN_DIR/ironic-inspector
|
IRONIC_INSPECTOR_BIN_FILE=$IRONIC_INSPECTOR_BIN_DIR/ironic-inspector
|
||||||
IRONIC_INSPECTOR_CONF_DIR=${IRONIC_INSPECTOR_CONF_DIR:-/etc/ironic-inspector}
|
IRONIC_INSPECTOR_CONF_DIR=${IRONIC_INSPECTOR_CONF_DIR:-/etc/ironic-inspector}
|
||||||
IRONIC_INSPECTOR_CONF_FILE=$IRONIC_INSPECTOR_CONF_DIR/inspector.conf
|
IRONIC_INSPECTOR_CONF_FILE=$IRONIC_INSPECTOR_CONF_DIR/inspector.conf
|
||||||
IRONIC_INSPECTOR_CMD="sudo $IRONIC_INSPECTOR_BIN_FILE --config-file $IRONIC_INSPECTOR_CONF_FILE"
|
IRONIC_INSPECTOR_CMD="$IRONIC_INSPECTOR_BIN_FILE --config-file $IRONIC_INSPECTOR_CONF_FILE"
|
||||||
IRONIC_INSPECTOR_DHCP_CONF_FILE=$IRONIC_INSPECTOR_CONF_DIR/dnsmasq.conf
|
IRONIC_INSPECTOR_DHCP_CONF_FILE=$IRONIC_INSPECTOR_CONF_DIR/dnsmasq.conf
|
||||||
|
IRONIC_INSPECTOR_ROOTWRAP_CONF_FILE=$IRONIC_INSPECTOR_CONF_DIR/rootwrap.conf
|
||||||
IRONIC_INSPECTOR_DATA_DIR=$DATA_DIR/ironic-inspector
|
IRONIC_INSPECTOR_DATA_DIR=$DATA_DIR/ironic-inspector
|
||||||
IRONIC_INSPECTOR_ADMIN_USER=${IRONIC_INSPECTOR_ADMIN_USER:-ironic-inspector}
|
IRONIC_INSPECTOR_ADMIN_USER=${IRONIC_INSPECTOR_ADMIN_USER:-ironic-inspector}
|
||||||
IRONIC_INSPECTOR_MANAGE_FIREWALL=$(trueorfalse True $IRONIC_INSPECTOR_MANAGE_FIREWALL)
|
IRONIC_INSPECTOR_MANAGE_FIREWALL=$(trueorfalse True $IRONIC_INSPECTOR_MANAGE_FIREWALL)
|
||||||
|
@ -145,6 +146,20 @@ function configure_inspector {
|
||||||
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
|
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
|
||||||
setup_colorized_logging $IRONIC_INSPECTOR_CONF_FILE DEFAULT
|
setup_colorized_logging $IRONIC_INSPECTOR_CONF_FILE DEFAULT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cp "$IRONIC_INSPECTOR_DIR/rootwrap.conf" "$IRONIC_INSPECTOR_ROOTWRAP_CONF_FILE"
|
||||||
|
cp -r "$IRONIC_INSPECTOR_DIR/rootwrap.d" "$IRONIC_INSPECTOR_CONF_DIR"
|
||||||
|
local ironic_inspector_rootwrap=$(get_rootwrap_location ironic-inspector)
|
||||||
|
local rootwrap_sudoer_cmd="$ironic_inspector_rootwrap $IRONIC_INSPECTOR_CONF_DIR/rootwrap.conf *"
|
||||||
|
|
||||||
|
# Set up the rootwrap sudoers for ironic-inspector
|
||||||
|
local tempfile=`mktemp`
|
||||||
|
echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_sudoer_cmd" >$tempfile
|
||||||
|
chmod 0640 $tempfile
|
||||||
|
sudo chown root:root $tempfile
|
||||||
|
sudo mv $tempfile /etc/sudoers.d/ironic-inspector-rootwrap
|
||||||
|
|
||||||
|
inspector_iniset DEFAULT rootwrap_config $IRONIC_INSPECTOR_ROOTWRAP_CONF_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
function configure_inspector_swift {
|
function configure_inspector_swift {
|
||||||
|
@ -188,6 +203,7 @@ function cleanup_inspector {
|
||||||
rm -rf $IRONIC_INSPECTOR_DATA_DIR
|
rm -rf $IRONIC_INSPECTOR_DATA_DIR
|
||||||
rm -f $IRONIC_TFTPBOOT_DIR/pxelinux.cfg/default
|
rm -f $IRONIC_TFTPBOOT_DIR/pxelinux.cfg/default
|
||||||
rm -f $IRONIC_TFTPBOOT_DIR/ironic-inspector.*
|
rm -f $IRONIC_TFTPBOOT_DIR/ironic-inspector.*
|
||||||
|
sudo rm -f /etc/sudoers.d/ironic-inspector-rootwrap
|
||||||
|
|
||||||
# Try to clean up firewall rules
|
# Try to clean up firewall rules
|
||||||
sudo iptables -D INPUT -i $IRONIC_INSPECTOR_INTERFACE -p udp \
|
sudo iptables -D INPUT -i $IRONIC_INSPECTOR_INTERFACE -p udp \
|
||||||
|
|
|
@ -63,6 +63,10 @@
|
||||||
# value)
|
# value)
|
||||||
#ipmi_address_fields = ilo_address,drac_host,cimc_address
|
#ipmi_address_fields = ilo_address,drac_host,cimc_address
|
||||||
|
|
||||||
|
# Path to the rootwrap configuration file to use for running commands
|
||||||
|
# as root (string value)
|
||||||
|
#rootwrap_config = /etc/ironic-inspector/rootwrap.conf
|
||||||
|
|
||||||
#
|
#
|
||||||
# From oslo.log
|
# From oslo.log
|
||||||
#
|
#
|
||||||
|
@ -403,6 +407,9 @@
|
||||||
# Verify HTTPS connections. (boolean value)
|
# Verify HTTPS connections. (boolean value)
|
||||||
#insecure = false
|
#insecure = false
|
||||||
|
|
||||||
|
# The region in which the identity server can be found. (string value)
|
||||||
|
#region_name = <None>
|
||||||
|
|
||||||
# Directory used to cache files related to PKI tokens. (string value)
|
# Directory used to cache files related to PKI tokens. (string value)
|
||||||
#signing_dir = <None>
|
#signing_dir = <None>
|
||||||
|
|
||||||
|
|
|
@ -249,6 +249,10 @@ SERVICE_OPTS = [
|
||||||
default=['ilo_address', 'drac_host', 'cimc_address'],
|
default=['ilo_address', 'drac_host', 'cimc_address'],
|
||||||
help='Ironic driver_info fields that are equivalent '
|
help='Ironic driver_info fields that are equivalent '
|
||||||
'to ipmi_address.'),
|
'to ipmi_address.'),
|
||||||
|
cfg.StrOpt('rootwrap_config',
|
||||||
|
default="/etc/ironic-inspector/rootwrap.conf",
|
||||||
|
help='Path to the rootwrap configuration file to use for '
|
||||||
|
'running commands as root'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ NEW_CHAIN = None
|
||||||
CHAIN = None
|
CHAIN = None
|
||||||
INTERFACE = None
|
INTERFACE = None
|
||||||
LOCK = semaphore.BoundedSemaphore()
|
LOCK = semaphore.BoundedSemaphore()
|
||||||
BASE_COMMAND = ('iptables',)
|
BASE_COMMAND = None
|
||||||
|
|
||||||
|
|
||||||
def _iptables(*args, **kwargs):
|
def _iptables(*args, **kwargs):
|
||||||
|
@ -61,19 +61,20 @@ def init():
|
||||||
INTERFACE = CONF.firewall.dnsmasq_interface
|
INTERFACE = CONF.firewall.dnsmasq_interface
|
||||||
CHAIN = CONF.firewall.firewall_chain
|
CHAIN = CONF.firewall.firewall_chain
|
||||||
NEW_CHAIN = CHAIN + '_temp'
|
NEW_CHAIN = CHAIN + '_temp'
|
||||||
|
BASE_COMMAND = ('sudo', 'ironic-inspector-rootwrap',
|
||||||
|
CONF.rootwrap_config, 'iptables',)
|
||||||
|
|
||||||
# -w flag makes iptables wait for xtables lock, but it's not supported
|
# -w flag makes iptables wait for xtables lock, but it's not supported
|
||||||
# everywhere yet
|
# everywhere yet
|
||||||
try:
|
try:
|
||||||
with open(os.devnull, 'wb') as null:
|
with open(os.devnull, 'wb') as null:
|
||||||
subprocess.check_call(['iptables', '-w', '-h'],
|
subprocess.check_call(BASE_COMMAND + ('-w', '-h'),
|
||||||
stderr=null, stdout=null)
|
stderr=null, stdout=null)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
LOG.warn(_LW('iptables does not support -w flag, please update '
|
LOG.warn(_LW('iptables does not support -w flag, please update '
|
||||||
'it to at least version 1.4.21'))
|
'it to at least version 1.4.21'))
|
||||||
BASE_COMMAND = ('iptables',)
|
|
||||||
else:
|
else:
|
||||||
BASE_COMMAND = ('iptables', '-w')
|
BASE_COMMAND += ('-w',)
|
||||||
|
|
||||||
_clean_up(CHAIN)
|
_clean_up(CHAIN)
|
||||||
# Not really needed, but helps to validate that we have access to iptables
|
# Not really needed, but helps to validate that we have access to iptables
|
||||||
|
|
|
@ -39,6 +39,8 @@ class TestFirewall(test_base.NodeTest):
|
||||||
self.assertEqual(0, mock_iptables.call_count)
|
self.assertEqual(0, mock_iptables.call_count)
|
||||||
|
|
||||||
def test_init_args(self, mock_call, mock_get_client, mock_iptables):
|
def test_init_args(self, mock_call, mock_get_client, mock_iptables):
|
||||||
|
rootwrap_path = '/some/fake/path'
|
||||||
|
CONF.set_override('rootwrap_config', rootwrap_path)
|
||||||
firewall.init()
|
firewall.init()
|
||||||
init_expected_args = [
|
init_expected_args = [
|
||||||
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport', '67',
|
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport', '67',
|
||||||
|
@ -52,10 +54,14 @@ class TestFirewall(test_base.NodeTest):
|
||||||
for (args, call) in zip(init_expected_args, call_args_list):
|
for (args, call) in zip(init_expected_args, call_args_list):
|
||||||
self.assertEqual(args, call[0])
|
self.assertEqual(args, call[0])
|
||||||
|
|
||||||
self.assertEqual(('iptables', '-w'), firewall.BASE_COMMAND)
|
expected = ('sudo', 'ironic-inspector-rootwrap', rootwrap_path,
|
||||||
|
'iptables', '-w')
|
||||||
|
self.assertEqual(expected, firewall.BASE_COMMAND)
|
||||||
|
|
||||||
def test_init_args_old_iptables(self, mock_call, mock_get_client,
|
def test_init_args_old_iptables(self, mock_call, mock_get_client,
|
||||||
mock_iptables):
|
mock_iptables):
|
||||||
|
rootwrap_path = '/some/fake/path'
|
||||||
|
CONF.set_override('rootwrap_config', rootwrap_path)
|
||||||
mock_call.side_effect = subprocess.CalledProcessError(2, '')
|
mock_call.side_effect = subprocess.CalledProcessError(2, '')
|
||||||
firewall.init()
|
firewall.init()
|
||||||
init_expected_args = [
|
init_expected_args = [
|
||||||
|
@ -70,7 +76,9 @@ class TestFirewall(test_base.NodeTest):
|
||||||
for (args, call) in zip(init_expected_args, call_args_list):
|
for (args, call) in zip(init_expected_args, call_args_list):
|
||||||
self.assertEqual(args, call[0])
|
self.assertEqual(args, call[0])
|
||||||
|
|
||||||
self.assertEqual(('iptables',), firewall.BASE_COMMAND)
|
expected = ('sudo', 'ironic-inspector-rootwrap', rootwrap_path,
|
||||||
|
'iptables',)
|
||||||
|
self.assertEqual(expected, firewall.BASE_COMMAND)
|
||||||
|
|
||||||
def test_init_kwargs(self, mock_call, mock_get_client, mock_iptables):
|
def test_init_kwargs(self, mock_call, mock_get_client, mock_iptables):
|
||||||
firewall.init()
|
firewall.init()
|
||||||
|
|
|
@ -16,6 +16,7 @@ oslo.config>=2.3.0 # Apache-2.0
|
||||||
oslo.db>=2.4.1 # Apache-2.0
|
oslo.db>=2.4.1 # Apache-2.0
|
||||||
oslo.i18n>=1.5.0 # Apache-2.0
|
oslo.i18n>=1.5.0 # Apache-2.0
|
||||||
oslo.log>=1.8.0 # Apache-2.0
|
oslo.log>=1.8.0 # Apache-2.0
|
||||||
|
oslo.rootwrap>=2.0.0 # Apache-2.0
|
||||||
oslo.utils>=2.0.0 # Apache-2.0
|
oslo.utils>=2.0.0 # Apache-2.0
|
||||||
six>=1.9.0
|
six>=1.9.0
|
||||||
stevedore>=1.5.0 # Apache-2.0
|
stevedore>=1.5.0 # Apache-2.0
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Configuration for ironic-inspector-rootwrap
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
# List of directories to load filter definitions from (separated by ',').
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
filters_path=/etc/ironic-inspector/rootwrap.d,/usr/share/ironic-inspector/rootwrap
|
||||||
|
|
||||||
|
# List of directories to search executables in, in case filters do not
|
||||||
|
# explicitely specify a full path (separated by ',')
|
||||||
|
# If not specified, defaults to system PATH environment variable.
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin
|
||||||
|
|
||||||
|
# Enable logging to syslog
|
||||||
|
# Default value is False
|
||||||
|
use_syslog=False
|
||||||
|
|
||||||
|
# Which syslog facility to use.
|
||||||
|
# Valid values include auth, authpriv, syslog, user0, user1...
|
||||||
|
# Default value is 'syslog'
|
||||||
|
syslog_log_facility=syslog
|
||||||
|
|
||||||
|
# Which messages to log.
|
||||||
|
# INFO means log all usage
|
||||||
|
# ERROR means only log unsuccessful attempts
|
||||||
|
syslog_log_level=ERROR
|
|
@ -0,0 +1,6 @@
|
||||||
|
# ironic-inspector-rootwrap command filters for firewall manipulation
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[Filters]
|
||||||
|
# ironic_inspector/firewall.py
|
||||||
|
iptables: CommandFilter, iptables, root
|
|
@ -21,6 +21,7 @@ packages =
|
||||||
[entry_points]
|
[entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
ironic-inspector = ironic_inspector.main:main
|
ironic-inspector = ironic_inspector.main:main
|
||||||
|
ironic-inspector-rootwrap = oslo_rootwrap.cmd:main
|
||||||
ironic_inspector.hooks.processing =
|
ironic_inspector.hooks.processing =
|
||||||
scheduler = ironic_inspector.plugins.standard:SchedulerHook
|
scheduler = ironic_inspector.plugins.standard:SchedulerHook
|
||||||
validate_interfaces = ironic_inspector.plugins.standard:ValidateInterfacesHook
|
validate_interfaces = ironic_inspector.plugins.standard:ValidateInterfacesHook
|
||||||
|
|
Loading…
Reference in New Issue