Move rootwrap filters definition to config files

Move rootwrap filters definition from being defined within Nova
code to being defined in configuration files to facilitate pluging-in
new rootwrap commands.

Transition notes:
* nova-rootwrap now requires an additional (first) parameter pointing
  to the root-owned rootwrap.conf file, sudoers needs to be updated
  to specify that ("nova-rootwrap /etc/nova/rootwrap.conf *")
* Packagers should ship {compute,network,volume}.filters inside a
  directory listed in rootwrap.conf rather than shipping
  nova/rootwrap/{compute,network,volume}.py
* Filter definitions now only support strings. The KillFilter (which was
  using arrays as parameters) was modified and the tests updated.

Implements bp nova-rootwrap-pluggable-filters

Corresponding devstack change needs to land first, so that tests pass:
https://review.openstack.org/8842

Change-Id: I2350154cd8057bd57926ed542de035626f7de37d
This commit is contained in:
Thierry Carrez 2012-06-06 14:23:24 +02:00
parent 24da7c6458
commit f25f87168b
7 changed files with 79 additions and 407 deletions

View File

@ -18,21 +18,21 @@
"""Root wrapper for Nova
Uses modules in nova.rootwrap containing filters for commands
that nova is allowed to run as another user.
Filters which commands nova is allowed to run as another user.
To switch to using this, you should:
* Set "--root_helper=sudo nova-rootwrap" in nova.conf
* Allow nova to run nova-rootwrap as root in nova_sudoers:
nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap
(all other commands can be removed from this file)
To use this, you should set the following in nova.conf:
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
You also need to let the nova user run nova-rootwrap as root in sudoers:
nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf *
To make allowed commands node-specific, your packaging should only
install nova/rootwrap/{compute,network,volume}.py respectively on
compute, network and volume nodes (i.e. nova-api nodes should not
have any of those files installed).
install {compute,network,volume}.filters respectively on compute, network
and volume nodes (i.e. nova-api nodes should not have any of those files
installed).
"""
import ConfigParser
import os
import subprocess
import sys
@ -40,16 +40,27 @@ import sys
RC_UNAUTHORIZED = 99
RC_NOCOMMAND = 98
RC_BADCONFIG = 97
if __name__ == '__main__':
# Split arguments, require at least a command
execname = sys.argv.pop(0)
if len(sys.argv) == 0:
if len(sys.argv) < 2:
print "%s: %s" % (execname, "No command specified")
sys.exit(RC_NOCOMMAND)
configfile = sys.argv.pop(0)
userargs = sys.argv[:]
# Load configuration
config = ConfigParser.RawConfigParser()
config.read(configfile)
try:
filters_path = config.get("DEFAULT", "filters_path").split(",")
except ConfigParser.Error:
print "%s: Incorrect configuration file: %s" % (execname, configfile)
sys.exit(RC_BADCONFIG)
# Add ../ to sys.path to allow running from branch
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(execname),
os.pardir, os.pardir))
@ -59,7 +70,7 @@ if __name__ == '__main__':
from nova.rootwrap import wrapper
# Execute command if it matches any of the loaded filters
filters = wrapper.load_filters()
filters = wrapper.load_filters(filters_path)
filtermatch = wrapper.match_filter(filters, userargs)
if filtermatch:
obj = subprocess.Popen(filtermatch.get_command(userargs),

View File

@ -1,207 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack, LLC.
# 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.
from nova.rootwrap import filters
filterlist = [
# nova/virt/disk/mount.py: 'kpartx', '-a', device
# nova/virt/disk/mount.py: 'kpartx', '-d', device
filters.CommandFilter("/sbin/kpartx", "root"),
# nova/virt/disk/mount.py: 'tune2fs', '-c', 0, '-i', 0, mapped_device
# nova/virt/xenapi/vm_utils.py: "tune2fs", "-O ^has_journal", part_path
# nova/virt/xenapi/vm_utils.py: "tune2fs", "-j", partition_path
filters.CommandFilter("/sbin/tune2fs", "root"),
# nova/virt/disk/mount.py: 'mount', mapped_device, mount_dir
# nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'..
filters.CommandFilter("/bin/mount", "root"),
# nova/virt/disk/mount.py: 'umount', mapped_device
# nova/virt/xenapi/vm_utils.py: 'umount', dev_path
filters.CommandFilter("/bin/umount", "root"),
# nova/virt/disk/nbd.py: 'qemu-nbd', '-c', device, image
# nova/virt/disk/nbd.py: 'qemu-nbd', '-d', device
filters.CommandFilter("/usr/bin/qemu-nbd", "root"),
# nova/virt/disk/loop.py: 'losetup', '--find', '--show', image
# nova/virt/disk/loop.py: 'losetup', '--detach', device
filters.CommandFilter("/sbin/losetup", "root"),
# nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-i'
# nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-m' dev
filters.CommandFilter("/usr/bin/guestmount", "root"),
# nova/virt/disk/guestfs.py: 'fusermount', 'u', mount_dir
filters.CommandFilter("/bin/fusermount", "root"),
filters.CommandFilter("/usr/bin/fusermount", "root"),
# nova/virt/disk/api.py: 'tee', metadata_path
# nova/virt/disk/api.py: 'tee', '-a', keyfile
# nova/virt/disk/api.py: 'tee', netfile
filters.CommandFilter("/usr/bin/tee", "root"),
# nova/virt/disk/api.py: 'mkdir', '-p', sshdir
# nova/virt/disk/api.py: 'mkdir', '-p', netdir
filters.CommandFilter("/bin/mkdir", "root"),
# nova/virt/disk/api.py: 'chown', 'root', sshdir
# nova/virt/disk/api.py: 'chown', 'root:root', netdir
# nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
# nova/virt/libvirt/connection.py: 'chown', os.getuid(), console_log
# nova/virt/libvirt/connection.py: 'chown', 'root', basepath('disk')
# nova/utils.py: 'chown', owner_uid, path
filters.CommandFilter("/bin/chown", "root"),
# nova/virt/disk/api.py: 'chmod', '700', sshdir
# nova/virt/disk/api.py: 'chmod', 755, netdir
filters.CommandFilter("/bin/chmod", "root"),
# nova/virt/disk/api.py: 'cp', os.path.join(fs...
filters.CommandFilter("/bin/cp", "root"),
# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, "address",..
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, "address", ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
filters.CommandFilter("/sbin/ip", "root"),
# nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev
# nova/network/linux_net.py: 'tunctl', '-b', '-t', dev
filters.CommandFilter("/usr/sbin/tunctl", "root"),
filters.CommandFilter("/bin/tunctl", "root"),
# nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
# nova/network/linux_net.py: 'ovs-vsctl', ....
filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),
# nova/network/linux_net.py: 'ovs-ofctl', ....
filters.CommandFilter("/usr/bin/ovs-ofctl", "root"),
# nova/virt/libvirt/connection.py: 'dd', "if=%s" % virsh_output, ...
filters.CommandFilter("/bin/dd", "root"),
# nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ...
filters.CommandFilter("/sbin/iscsiadm", "root"),
# nova/virt/xenapi/vm_utils.py: "parted", "--script", ...
# nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*.
filters.CommandFilter("/sbin/parted", "root"),
filters.CommandFilter("/usr/sbin/parted", "root"),
# nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s
filters.CommandFilter("/sbin/fdisk", "root"),
# nova/virt/xenapi/vm_utils.py: "e2fsck", "-f", "-p", partition_path
filters.CommandFilter("/sbin/e2fsck", "root"),
# nova/virt/xenapi/vm_utils.py: "resize2fs", partition_path
filters.CommandFilter("/sbin/resize2fs", "root"),
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd,), '-t', ...
filters.CommandFilter("/sbin/iptables-save", "root"),
filters.CommandFilter("/usr/sbin/iptables-save", "root"),
filters.CommandFilter("/sbin/ip6tables-save", "root"),
filters.CommandFilter("/usr/sbin/ip6tables-save", "root"),
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
filters.CommandFilter("/sbin/iptables-restore", "root"),
filters.CommandFilter("/usr/sbin/iptables-restore", "root"),
filters.CommandFilter("/sbin/ip6tables-restore", "root"),
filters.CommandFilter("/usr/sbin/ip6tables-restore", "root"),
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
filters.CommandFilter("/usr/bin/arping", "root"),
filters.CommandFilter("/sbin/arping", "root"),
# nova/network/linux_net.py: 'route', '-n'
# nova/network/linux_net.py: 'route', 'del', 'default', 'gw'
# nova/network/linux_net.py: 'route', 'add', 'default', 'gw'
# nova/network/linux_net.py: 'route', '-n'
# nova/network/linux_net.py: 'route', 'del', 'default', 'gw', old_gw, ..
# nova/network/linux_net.py: 'route', 'add', 'default', 'gw', old_gateway
filters.CommandFilter("/sbin/route", "root"),
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
filters.CommandFilter("/usr/bin/dhcp_release", "root"),
# nova/network/linux_net.py: 'kill', '-9', pid
# nova/network/linux_net.py: 'kill', '-HUP', pid
filters.KillFilter("/bin/kill", "root",
['-9', '-HUP'], ['/usr/sbin/dnsmasq']),
# nova/network/linux_net.py: 'kill', pid
filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),
# nova/network/linux_net.py: dnsmasq call
filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),
# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'),..
filters.CommandFilter("/usr/sbin/radvd", "root"),
# nova/network/linux_net.py: 'brctl', 'addbr', bridge
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
filters.CommandFilter("/sbin/brctl", "root"),
filters.CommandFilter("/usr/sbin/brctl", "root"),
# nova/virt/libvirt/utils.py: 'mkswap'
# nova/virt/xenapi/vm_utils.py: 'mkswap'
filters.CommandFilter("/sbin/mkswap", "root"),
# nova/virt/xenapi/vm_utils.py: 'mkfs'
filters.CommandFilter("/sbin/mkfs", "root"),
# nova/virt/libvirt/utils.py: 'qemu-img'
filters.CommandFilter("/usr/bin/qemu-img", "root"),
# nova/virt/disk/api.py: 'touch', target
filters.CommandFilter("/usr/bin/touch", "root"),
# nova/virt/libvirt/connection.py:
filters.ReadFileFilter("/etc/iscsi/initiatorname.iscsi"),
# nova/virt/libvirt/connection.py:
filters.CommandFilter("/sbin/lvremove", "root"),
# nova/virt/libvirt/utils.py:
filters.CommandFilter("/sbin/lvcreate", "root"),
# nova/virt/libvirt/utils.py:
filters.CommandFilter("/sbin/vgs", "root")
]

View File

@ -91,28 +91,33 @@ class DnsmasqFilter(CommandFilter):
class KillFilter(CommandFilter):
"""Specific filter for the kill calls.
1st argument is a list of accepted signals (emptystring means no signal)
2nd argument is a list of accepted affected executables.
1st argument is the user to run /bin/kill under
2nd argument is the location of the affected executable
Subsequent arguments list the accepted signals (if any)
This filter relies on /proc to accurately determine affected
executable, so it will only work on procfs-capable systems (not OSX).
"""
def __init__(self, *args):
super(KillFilter, self).__init__("/bin/kill", *args)
def match(self, userargs):
if userargs[0] != "kill":
return False
args = list(userargs)
if len(args) == 3:
# A specific signal is requested
signal = args.pop(1)
if signal not in self.args[0]:
if signal not in self.args[1:]:
# Requested signal not in accepted list
return False
else:
if len(args) != 2:
# Incorrect number of arguments
return False
if '' not in self.args[0]:
# No signal, but list doesn't include empty string
if len(self.args) > 1:
# No signal requested, but filter requires specific signal
return False
try:
command = os.readlink("/proc/%d/exe" % int(args[1]))
@ -120,8 +125,8 @@ class KillFilter(CommandFilter):
# the end if an executable is updated or deleted
if command.endswith(" (deleted)"):
command = command[:command.rindex(" ")]
if command not in self.args[1]:
# Affected executable not in accepted list
if command != self.args[0]:
# Affected executable does not match
return False
except (ValueError, OSError):
# Incorrect PID

View File

@ -1,98 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack, LLC.
# 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.
from nova.rootwrap import filters
filterlist = [
# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, "address",..
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, "address", ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
# nova/network/linux_net.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
filters.CommandFilter("/sbin/ip", "root"),
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd,), '-t', ...
filters.CommandFilter("/sbin/iptables-save", "root"),
filters.CommandFilter("/usr/sbin/iptables-save", "root"),
filters.CommandFilter("/sbin/ip6tables-save", "root"),
filters.CommandFilter("/usr/sbin/ip6tables-save", "root"),
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
filters.CommandFilter("/sbin/iptables-restore", "root"),
filters.CommandFilter("/usr/sbin/iptables-restore", "root"),
filters.CommandFilter("/sbin/ip6tables-restore", "root"),
filters.CommandFilter("/usr/sbin/ip6tables-restore", "root"),
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
filters.CommandFilter("/usr/bin/arping", "root"),
filters.CommandFilter("/sbin/arping", "root"),
# nova/network/linux_net.py: 'route', '-n'
# nova/network/linux_net.py: 'route', 'del', 'default', 'gw'
# nova/network/linux_net.py: 'route', 'add', 'default', 'gw'
# nova/network/linux_net.py: 'route', '-n'
# nova/network/linux_net.py: 'route', 'del', 'default', 'gw', old_gw, ..
# nova/network/linux_net.py: 'route', 'add', 'default', 'gw', old_gateway
filters.CommandFilter("/sbin/route", "root"),
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
filters.CommandFilter("/usr/bin/dhcp_release", "root"),
# nova/network/linux_net.py: 'kill', '-9', pid
# nova/network/linux_net.py: 'kill', '-HUP', pid
filters.KillFilter("/bin/kill", "root",
['-9', '-HUP'], ['/usr/sbin/dnsmasq']),
# nova/network/linux_net.py: 'kill', pid
filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),
# nova/network/linux_net.py: dnsmasq call
filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),
# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'),..
filters.CommandFilter("/usr/sbin/radvd", "root"),
# nova/network/linux_net.py: 'brctl', 'addbr', bridge
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
filters.CommandFilter("/sbin/brctl", "root"),
filters.CommandFilter("/usr/sbin/brctl", "root"),
# nova/network/linux_net.py: 'ovs-vsctl', ....
filters.CommandFilter("/usr/bin/ovs-vsctl", "root"),
# nova/network/linux_net.py: 'ovs-ofctl', ....
filters.CommandFilter("/usr/bin/ovs-ofctl", "root"),
# nova/network/linux_net.py: 'sysctl', ....
filters.CommandFilter("/sbin/sysctl", "root"),
]

View File

@ -1,45 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack, LLC.
# 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.
from nova.rootwrap import filters
filterlist = [
# nova/volume/iscsi.py: iscsi_helper '--op' ...
filters.CommandFilter("/usr/sbin/ietadm", "root"),
filters.CommandFilter("/usr/sbin/tgtadm", "root"),
# nova/volume/driver.py: 'vgs', '--noheadings', '-o', 'name'
filters.CommandFilter("/sbin/vgs", "root"),
# nova/volume/driver.py: 'lvcreate', '-L', sizestr, '-n', volume_name,..
# nova/volume/driver.py: 'lvcreate', '-L', ...
filters.CommandFilter("/sbin/lvcreate", "root"),
# nova/volume/driver.py: 'dd', 'if=%s' % srcstr, 'of=%s' % deststr,...
filters.CommandFilter("/bin/dd", "root"),
# nova/volume/driver.py: 'lvremove', '-f', "%s/%s" % ...
filters.CommandFilter("/sbin/lvremove", "root"),
# nova/volume/driver.py: 'lvdisplay', '--noheading', '-C', '-o', 'Attr',..
filters.CommandFilter("/sbin/lvdisplay", "root"),
# nova/volume/driver.py: 'iscsiadm', '-m', 'discovery', '-t',...
# nova/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ...
filters.CommandFilter("/sbin/iscsiadm", "root"),
]

View File

@ -15,29 +15,39 @@
# License for the specific language governing permissions and limitations
# under the License.
import ConfigParser
import os
import sys
import string
from nova.rootwrap import filters
FILTERS_MODULES = ['nova.rootwrap.compute',
'nova.rootwrap.network',
'nova.rootwrap.volume',
]
def build_filter(class_name, *args):
"""Returns a filter object of class class_name"""
if not hasattr(filters, class_name):
# TODO(ttx): Log the error (whenever nova-rootwrap has a log file)
return None
filterclass = getattr(filters, class_name)
return filterclass(*args)
def load_filters():
"""Load filters from modules present in nova.rootwrap."""
filters = []
for modulename in FILTERS_MODULES:
try:
__import__(modulename)
module = sys.modules[modulename]
filters = filters + module.filterlist
except ImportError:
# It's OK to have missing filters, since filter modules are
# shipped with specific nodes rather than with python-nova
pass
return filters
def load_filters(filters_path):
"""Load filters from a list of directories"""
filterlist = []
for filterdir in filters_path:
if not os.path.isdir(filterdir):
continue
for filterfile in os.listdir(filterdir):
filterconfig = ConfigParser.RawConfigParser()
filterconfig.read(os.path.join(filterdir, filterfile))
for (name, value) in filterconfig.items("Filters"):
filterdefinition = [string.strip(s) for s in value.split(',')]
newfilter = build_filter(*filterdefinition)
if newfilter is None:
continue
filterlist.append(newfilter)
return filterlist
def match_filter(filters, userargs):

View File

@ -67,35 +67,33 @@ class RootwrapTestCase(test.TestCase):
"Test requires /proc filesystem (procfs)")
def test_KillFilter(self):
p = subprocess.Popen(["/bin/sleep", "5"])
f = filters.KillFilter("/bin/kill", "root",
["-ALRM"],
["/bin/sleep", "/usr/bin/sleep"])
usercmd = ['kill', '-9', p.pid]
f = filters.KillFilter("root", "/bin/sleep", "-9", "-HUP")
f2 = filters.KillFilter("root", "/usr/bin/sleep", "-9", "-HUP")
usercmd = ['kill', '-ALRM', p.pid]
# Incorrect signal should fail
self.assertFalse(f.match(usercmd))
self.assertFalse(f.match(usercmd) or f2.match(usercmd))
usercmd = ['kill', p.pid]
# Providing no signal should fail
self.assertFalse(f.match(usercmd))
self.assertFalse(f.match(usercmd) or f2.match(usercmd))
# Providing matching signal should be allowed
usercmd = ['kill', '-9', p.pid]
self.assertTrue(f.match(usercmd) or f2.match(usercmd))
f = filters.KillFilter("/bin/kill", "root",
["-9", ""],
["/bin/sleep", "/usr/bin/sleep"])
usercmd = ['kill', '-9', os.getpid()]
f = filters.KillFilter("root", "/bin/sleep")
f2 = filters.KillFilter("root", "/usr/bin/sleep")
usercmd = ['kill', os.getpid()]
# Our own PID does not match /bin/sleep, so it should fail
self.assertFalse(f.match(usercmd))
usercmd = ['kill', '-9', 999999]
self.assertFalse(f.match(usercmd) or f2.match(usercmd))
usercmd = ['kill', 999999]
# Nonexistant PID should fail
self.assertFalse(f.match(usercmd))
self.assertFalse(f.match(usercmd) or f2.match(usercmd))
usercmd = ['kill', p.pid]
# Providing no signal should work
self.assertTrue(f.match(usercmd))
usercmd = ['kill', '-9', p.pid]
# Providing -9 signal should work
self.assertTrue(f.match(usercmd))
def test_KillFilter_no_raise(self):
"""Makes sure ValueError from bug 926412 is gone"""
f = filters.KillFilter("/bin/kill", "root", [""])
f = filters.KillFilter("root", "")
# Providing anything other than kill should be False
usercmd = ['notkill', 999999]
self.assertFalse(f.match(usercmd))
@ -109,9 +107,7 @@ class RootwrapTestCase(test.TestCase):
def fake_readlink(blah):
return '/bin/commandddddd (deleted)'
f = filters.KillFilter("/bin/kill", "root",
[""],
["/bin/commandddddd"])
f = filters.KillFilter("root", "/bin/commandddddd")
usercmd = ['kill', 1234]
# Providing no signal should work
self.stubs.Set(os, 'readlink', fake_readlink)