Configurable exec_dirs to find rootwrap commands
Adds support for a configurable set of trusted directories to search executables in (exec_dirs), which defaults to system PATH. If your filter specifies an exec_path that doesn't start with '/', then it will be searched in exec_dirs. Avoids having to write multiple filters to care for distro differences. Fixes bug 1079723. Also returns a specific error rather than try to run absent executables. Change-Id: Idab03bb0be6832a75ffeed4e78d25d0543f5caf9
This commit is contained in:
@@ -42,6 +42,7 @@ import sys
|
|||||||
RC_UNAUTHORIZED = 99
|
RC_UNAUTHORIZED = 99
|
||||||
RC_NOCOMMAND = 98
|
RC_NOCOMMAND = 98
|
||||||
RC_BADCONFIG = 97
|
RC_BADCONFIG = 97
|
||||||
|
RC_NOEXECFOUND = 96
|
||||||
|
|
||||||
|
|
||||||
def _subprocess_setup():
|
def _subprocess_setup():
|
||||||
@@ -65,6 +66,11 @@ if __name__ == '__main__':
|
|||||||
config.read(configfile)
|
config.read(configfile)
|
||||||
try:
|
try:
|
||||||
filters_path = config.get("DEFAULT", "filters_path").split(",")
|
filters_path = config.get("DEFAULT", "filters_path").split(",")
|
||||||
|
if config.has_option("DEFAULT", "exec_dirs"):
|
||||||
|
exec_dirs = config.get("DEFAULT", "exec_dirs").split(",")
|
||||||
|
else:
|
||||||
|
# Use system PATH if exec_dirs is not specified
|
||||||
|
exec_dirs = os.environ["PATH"].split(':')
|
||||||
except ConfigParser.Error:
|
except ConfigParser.Error:
|
||||||
print "%s: Incorrect configuration file: %s" % (execname, configfile)
|
print "%s: Incorrect configuration file: %s" % (execname, configfile)
|
||||||
sys.exit(RC_BADCONFIG)
|
sys.exit(RC_BADCONFIG)
|
||||||
@@ -79,16 +85,24 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Execute command if it matches any of the loaded filters
|
# Execute command if it matches any of the loaded filters
|
||||||
filters = wrapper.load_filters(filters_path)
|
filters = wrapper.load_filters(filters_path)
|
||||||
filtermatch = wrapper.match_filter(filters, userargs)
|
try:
|
||||||
if filtermatch:
|
filtermatch = wrapper.match_filter(filters, userargs,
|
||||||
obj = subprocess.Popen(filtermatch.get_command(userargs),
|
exec_dirs=exec_dirs)
|
||||||
stdin=sys.stdin,
|
if filtermatch:
|
||||||
stdout=sys.stdout,
|
obj = subprocess.Popen(filtermatch.get_command(userargs,
|
||||||
stderr=sys.stderr,
|
exec_dirs=exec_dirs),
|
||||||
preexec_fn=_subprocess_setup,
|
stdin=sys.stdin,
|
||||||
env=filtermatch.get_environment(userargs))
|
stdout=sys.stdout,
|
||||||
obj.wait()
|
stderr=sys.stderr,
|
||||||
sys.exit(obj.returncode)
|
preexec_fn=_subprocess_setup,
|
||||||
|
env=filtermatch.get_environment(userargs))
|
||||||
|
obj.wait()
|
||||||
|
sys.exit(obj.returncode)
|
||||||
|
|
||||||
print "Unauthorized command: %s" % ' '.join(userargs)
|
except wrapper.FilterMatchNotExecutable as exc:
|
||||||
sys.exit(RC_UNAUTHORIZED)
|
print "Executable not found: %s" % exc.match.exec_path
|
||||||
|
sys.exit(RC_NOEXECFOUND)
|
||||||
|
|
||||||
|
except wrapper.NoFilterMatched:
|
||||||
|
print "Unauthorized command: %s" % ' '.join(userargs)
|
||||||
|
sys.exit(RC_UNAUTHORIZED)
|
||||||
|
@@ -5,3 +5,9 @@
|
|||||||
# List of directories to load filter definitions from (separated by ',').
|
# List of directories to load filter definitions from (separated by ',').
|
||||||
# These directories MUST all be only writeable by root !
|
# These directories MUST all be only writeable by root !
|
||||||
filters_path=/etc/nova/rootwrap.d,/usr/share/nova/rootwrap
|
filters_path=/etc/nova/rootwrap.d,/usr/share/nova/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
|
||||||
|
@@ -5,13 +5,9 @@
|
|||||||
|
|
||||||
[Filters]
|
[Filters]
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
||||||
iptables-save: CommandFilter, /sbin/iptables-save, root
|
iptables-save: CommandFilter, iptables-save, root
|
||||||
iptables-save_usr: CommandFilter, /usr/sbin/iptables-save, root
|
ip6tables-save: CommandFilter, ip6tables-save, root
|
||||||
ip6tables-save: CommandFilter, /sbin/ip6tables-save, root
|
|
||||||
ip6tables-save_usr: CommandFilter, /usr/sbin/ip6tables-save, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
||||||
iptables-restore: CommandFilter, /sbin/iptables-restore, root
|
iptables-restore: CommandFilter, iptables-restore, root
|
||||||
iptables-restore_usr: CommandFilter, /usr/sbin/iptables-restore, root
|
ip6tables-restore: CommandFilter, ip6tables-restore, root
|
||||||
ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root
|
|
||||||
ip6tables-restore_usr: CommandFilter, /usr/sbin/ip6tables-restore, root
|
|
||||||
|
@@ -72,8 +72,7 @@ ip: CommandFilter, /sbin/ip, root
|
|||||||
|
|
||||||
# nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev
|
# nova/virt/libvirt/vif.py: 'tunctl', '-b', '-t', dev
|
||||||
# nova/network/linux_net.py: 'tunctl', '-b', '-t', dev
|
# nova/network/linux_net.py: 'tunctl', '-b', '-t', dev
|
||||||
tunctl: CommandFilter, /bin/tunctl, root
|
tunctl: CommandFilter, tunctl, root
|
||||||
tunctl_usr: CommandFilter, /usr/sbin/tunctl, root
|
|
||||||
|
|
||||||
# nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
|
# nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
|
||||||
# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
|
# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
|
||||||
@@ -87,13 +86,11 @@ ovs-ofctl: CommandFilter, /usr/bin/ovs-ofctl, root
|
|||||||
dd: CommandFilter, /bin/dd, root
|
dd: CommandFilter, /bin/dd, root
|
||||||
|
|
||||||
# nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ...
|
# nova/virt/xenapi/volume_utils.py: 'iscsiadm', '-m', ...
|
||||||
iscsiadm: CommandFilter, /sbin/iscsiadm, root
|
iscsiadm: CommandFilter, iscsiadm, root
|
||||||
iscsiadm_usr: CommandFilter, /usr/bin/iscsiadm, root
|
|
||||||
|
|
||||||
# nova/virt/xenapi/vm_utils.py: parted, --script, ...
|
# nova/virt/xenapi/vm_utils.py: parted, --script, ...
|
||||||
# nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*.
|
# nova/virt/xenapi/vm_utils.py: 'parted', '--script', dev_path, ..*.
|
||||||
parted: CommandFilter, /sbin/parted, root
|
parted: CommandFilter, parted, root
|
||||||
parted_usr: CommandFilter, /usr/sbin/parted, root
|
|
||||||
|
|
||||||
# nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s
|
# nova/virt/xenapi/vm_utils.py: fdisk %(dev_path)s
|
||||||
fdisk: CommandFilter, /sbin/fdisk, root
|
fdisk: CommandFilter, /sbin/fdisk, root
|
||||||
@@ -105,21 +102,16 @@ e2fsck: CommandFilter, /sbin/e2fsck, root
|
|||||||
resize2fs: CommandFilter, /sbin/resize2fs, root
|
resize2fs: CommandFilter, /sbin/resize2fs, root
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
||||||
iptables-save: CommandFilter, /sbin/iptables-save, root
|
iptables-save: CommandFilter, iptables-save, root
|
||||||
iptables-save_usr: CommandFilter, /usr/sbin/iptables-save, root
|
ip6tables-save: CommandFilter, ip6tables-save, root
|
||||||
ip6tables-save: CommandFilter, /sbin/ip6tables-save, root
|
|
||||||
ip6tables-save_usr: CommandFilter, /usr/sbin/ip6tables-save, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
||||||
iptables-restore: CommandFilter, /sbin/iptables-restore, root
|
iptables-restore: CommandFilter, iptables-restore, root
|
||||||
iptables-restore_usr: CommandFilter, /usr/sbin/iptables-restore, root
|
ip6tables-restore: CommandFilter, ip6tables-restore, root
|
||||||
ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root
|
|
||||||
ip6tables-restore_usr: CommandFilter, /usr/sbin/ip6tables-restore, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
|
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
|
||||||
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
|
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
|
||||||
arping: CommandFilter, /usr/bin/arping, root
|
arping: CommandFilter, arping, root
|
||||||
arping_sbin: CommandFilter, /sbin/arping, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
|
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
|
||||||
dhcp_release: CommandFilter, /usr/bin/dhcp_release, root
|
dhcp_release: CommandFilter, /usr/bin/dhcp_release, root
|
||||||
@@ -142,8 +134,7 @@ radvd: CommandFilter, /usr/sbin/radvd, root
|
|||||||
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
|
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
|
||||||
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
|
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
|
||||||
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
|
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
|
||||||
brctl: CommandFilter, /sbin/brctl, root
|
brctl: CommandFilter, brctl, root
|
||||||
brctl_usr: CommandFilter, /usr/sbin/brctl, root
|
|
||||||
|
|
||||||
# nova/virt/libvirt/utils.py: 'mkswap'
|
# nova/virt/libvirt/utils.py: 'mkswap'
|
||||||
# nova/virt/xenapi/vm_utils.py: 'mkswap'
|
# nova/virt/xenapi/vm_utils.py: 'mkswap'
|
||||||
@@ -156,8 +147,7 @@ mkfs: CommandFilter, /sbin/mkfs, root
|
|||||||
qemu-img: CommandFilter, /usr/bin/qemu-img, root
|
qemu-img: CommandFilter, /usr/bin/qemu-img, root
|
||||||
|
|
||||||
# nova/virt/disk/vfs/localfs.py: 'readlink', '-e'
|
# nova/virt/disk/vfs/localfs.py: 'readlink', '-e'
|
||||||
readlink: CommandFilter, /bin/readlink, root
|
readlink: CommandFilter, readlink, root
|
||||||
readlink_usr: CommandFilter, /usr/bin/readlink, root
|
|
||||||
|
|
||||||
# nova/virt/disk/api.py: 'touch', target
|
# nova/virt/disk/api.py: 'touch', target
|
||||||
touch: CommandFilter, /usr/bin/touch, root
|
touch: CommandFilter, /usr/bin/touch, root
|
||||||
|
@@ -40,21 +40,16 @@ ebtables: CommandFilter, /sbin/ebtables, root
|
|||||||
ebtables_usr: CommandFilter, /usr/sbin/ebtables, root
|
ebtables_usr: CommandFilter, /usr/sbin/ebtables, root
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
|
||||||
iptables-save: CommandFilter, /sbin/iptables-save, root
|
iptables-save: CommandFilter, iptables-save, root
|
||||||
iptables-save_usr: CommandFilter, /usr/sbin/iptables-save, root
|
ip6tables-save: CommandFilter, ip6tables-save, root
|
||||||
ip6tables-save: CommandFilter, /sbin/ip6tables-save, root
|
|
||||||
ip6tables-save_usr: CommandFilter, /usr/sbin/ip6tables-save, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
|
||||||
iptables-restore: CommandFilter, /sbin/iptables-restore, root
|
iptables-restore: CommandFilter, iptables-restore, root
|
||||||
iptables-restore_usr: CommandFilter, /usr/sbin/iptables-restore, root
|
ip6tables-restore: CommandFilter, ip6tables-restore, root
|
||||||
ip6tables-restore: CommandFilter, /sbin/ip6tables-restore, root
|
|
||||||
ip6tables-restore_usr: CommandFilter, /usr/sbin/ip6tables-restore, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
|
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
|
||||||
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
|
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
|
||||||
arping: CommandFilter, /usr/bin/arping, root
|
arping: CommandFilter, arping, root
|
||||||
arping_sbin: CommandFilter, /sbin/arping, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
|
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
|
||||||
dhcp_release: CommandFilter, /usr/bin/dhcp_release, root
|
dhcp_release: CommandFilter, /usr/bin/dhcp_release, root
|
||||||
@@ -77,8 +72,7 @@ radvd: CommandFilter, /usr/sbin/radvd, root
|
|||||||
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
|
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
|
||||||
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
|
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
|
||||||
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
|
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
|
||||||
brctl: CommandFilter, /sbin/brctl, root
|
brctl: CommandFilter, brctl, root
|
||||||
brctl_usr: CommandFilter, /usr/sbin/brctl, root
|
|
||||||
|
|
||||||
# nova/network/linux_net.py: 'sysctl', ....
|
# nova/network/linux_net.py: 'sysctl', ....
|
||||||
sysctl: CommandFilter, /sbin/sysctl, root
|
sysctl: CommandFilter, /sbin/sysctl, root
|
||||||
|
@@ -26,6 +26,23 @@ class CommandFilter(object):
|
|||||||
self.exec_path = exec_path
|
self.exec_path = exec_path
|
||||||
self.run_as = run_as
|
self.run_as = run_as
|
||||||
self.args = args
|
self.args = args
|
||||||
|
self.real_exec = None
|
||||||
|
|
||||||
|
def get_exec(self, exec_dirs=[]):
|
||||||
|
"""Returns existing executable, or empty string if none found"""
|
||||||
|
if self.real_exec is not None:
|
||||||
|
return self.real_exec
|
||||||
|
self.real_exec = ""
|
||||||
|
if self.exec_path.startswith('/'):
|
||||||
|
if os.access(self.exec_path, os.X_OK):
|
||||||
|
self.real_exec = self.exec_path
|
||||||
|
else:
|
||||||
|
for binary_path in exec_dirs:
|
||||||
|
expanded_path = os.path.join(binary_path, self.exec_path)
|
||||||
|
if os.access(expanded_path, os.X_OK):
|
||||||
|
self.real_exec = expanded_path
|
||||||
|
break
|
||||||
|
return self.real_exec
|
||||||
|
|
||||||
def match(self, userargs):
|
def match(self, userargs):
|
||||||
"""Only check that the first argument (command) matches exec_path"""
|
"""Only check that the first argument (command) matches exec_path"""
|
||||||
@@ -33,12 +50,13 @@ class CommandFilter(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_command(self, userargs):
|
def get_command(self, userargs, exec_dirs=[]):
|
||||||
"""Returns command to execute (with sudo -u if run_as != root)."""
|
"""Returns command to execute (with sudo -u if run_as != root)."""
|
||||||
|
to_exec = self.get_exec(exec_dirs=exec_dirs) or self.exec_path
|
||||||
if (self.run_as != 'root'):
|
if (self.run_as != 'root'):
|
||||||
# Used to run commands at lesser privileges
|
# Used to run commands at lesser privileges
|
||||||
return ['sudo', '-u', self.run_as, self.exec_path] + userargs[1:]
|
return ['sudo', '-u', self.run_as, to_exec] + userargs[1:]
|
||||||
return [self.exec_path] + userargs[1:]
|
return [to_exec] + userargs[1:]
|
||||||
|
|
||||||
def get_environment(self, userargs):
|
def get_environment(self, userargs):
|
||||||
"""Returns specific environment to set, None if none"""
|
"""Returns specific environment to set, None if none"""
|
||||||
@@ -82,9 +100,10 @@ class DnsmasqFilter(CommandFilter):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_command(self, userargs):
|
def get_command(self, userargs, exec_dirs=[]):
|
||||||
|
to_exec = self.get_exec(exec_dirs=exec_dirs) or self.exec_path
|
||||||
dnsmasq_pos = userargs.index('dnsmasq')
|
dnsmasq_pos = userargs.index('dnsmasq')
|
||||||
return [self.exec_path] + userargs[dnsmasq_pos + 1:]
|
return [to_exec] + userargs[dnsmasq_pos + 1:]
|
||||||
|
|
||||||
def get_environment(self, userargs):
|
def get_environment(self, userargs):
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
@@ -23,6 +23,20 @@ import string
|
|||||||
from nova.rootwrap import filters
|
from nova.rootwrap import filters
|
||||||
|
|
||||||
|
|
||||||
|
class NoFilterMatched(Exception):
|
||||||
|
"""This exception is raised when no filter matched."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FilterMatchNotExecutable(Exception):
|
||||||
|
"""
|
||||||
|
This exception is raised when a filter matched but no executable was
|
||||||
|
found.
|
||||||
|
"""
|
||||||
|
def __init__(self, match=None, **kwargs):
|
||||||
|
self.match = match
|
||||||
|
|
||||||
|
|
||||||
def build_filter(class_name, *args):
|
def build_filter(class_name, *args):
|
||||||
"""Returns a filter object of class class_name"""
|
"""Returns a filter object of class class_name"""
|
||||||
if not hasattr(filters, class_name):
|
if not hasattr(filters, class_name):
|
||||||
@@ -50,23 +64,29 @@ def load_filters(filters_path):
|
|||||||
return filterlist
|
return filterlist
|
||||||
|
|
||||||
|
|
||||||
def match_filter(filters, userargs):
|
def match_filter(filters, userargs, exec_dirs=[]):
|
||||||
"""
|
"""
|
||||||
Checks user command and arguments through command filters and
|
Checks user command and arguments through command filters and
|
||||||
returns the first matching filter, or None is none matched.
|
returns the first matching filter.
|
||||||
|
Raises NoFilterMatched if no filter matched.
|
||||||
|
Raises FilterMatchNotExecutable if no executable was found for the
|
||||||
|
best filter match.
|
||||||
"""
|
"""
|
||||||
|
first_not_executable_filter = None
|
||||||
found_filter = None
|
|
||||||
|
|
||||||
for f in filters:
|
for f in filters:
|
||||||
if f.match(userargs):
|
if f.match(userargs):
|
||||||
# Try other filters if executable is absent
|
# Try other filters if executable is absent
|
||||||
if not os.access(f.exec_path, os.X_OK):
|
if not f.get_exec(exec_dirs=exec_dirs):
|
||||||
if not found_filter:
|
if not first_not_executable_filter:
|
||||||
found_filter = f
|
first_not_executable_filter = f
|
||||||
continue
|
continue
|
||||||
# Otherwise return matching filter for execution
|
# Otherwise return matching filter for execution
|
||||||
return f
|
return f
|
||||||
|
|
||||||
# No filter matched or first missing executable
|
if first_not_executable_filter:
|
||||||
return found_filter
|
# A filter matched, but no executable was found for it
|
||||||
|
raise FilterMatchNotExecutable(match=first_not_executable_filter)
|
||||||
|
|
||||||
|
# No filter matched
|
||||||
|
raise NoFilterMatched()
|
||||||
|
@@ -43,16 +43,16 @@ class RootwrapTestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_RegExpFilter_reject(self):
|
def test_RegExpFilter_reject(self):
|
||||||
usercmd = ["ls", "root"]
|
usercmd = ["ls", "root"]
|
||||||
filtermatch = wrapper.match_filter(self.filters, usercmd)
|
self.assertRaises(wrapper.NoFilterMatched,
|
||||||
self.assertTrue(filtermatch is None)
|
wrapper.match_filter, self.filters, usercmd)
|
||||||
|
|
||||||
def test_missing_command(self):
|
def test_missing_command(self):
|
||||||
valid_but_missing = ["foo_bar_not_exist"]
|
valid_but_missing = ["foo_bar_not_exist"]
|
||||||
invalid = ["foo_bar_not_exist_and_not_matched"]
|
invalid = ["foo_bar_not_exist_and_not_matched"]
|
||||||
filtermatch = wrapper.match_filter(self.filters, valid_but_missing)
|
self.assertRaises(wrapper.FilterMatchNotExecutable,
|
||||||
self.assertTrue(filtermatch is not None)
|
wrapper.match_filter, self.filters, valid_but_missing)
|
||||||
filtermatch = wrapper.match_filter(self.filters, invalid)
|
self.assertRaises(wrapper.NoFilterMatched,
|
||||||
self.assertTrue(filtermatch is None)
|
wrapper.match_filter, self.filters, invalid)
|
||||||
|
|
||||||
def _test_DnsmasqFilter(self, filter_class, config_file_arg):
|
def _test_DnsmasqFilter(self, filter_class, config_file_arg):
|
||||||
usercmd = ['env', config_file_arg + '=A', 'NETWORK_ID=foobar',
|
usercmd = ['env', config_file_arg + '=A', 'NETWORK_ID=foobar',
|
||||||
@@ -136,6 +136,14 @@ class RootwrapTestCase(test.TestCase):
|
|||||||
self.assertEqual(f.get_command(usercmd), ['/bin/cat', goodfn])
|
self.assertEqual(f.get_command(usercmd), ['/bin/cat', goodfn])
|
||||||
self.assertTrue(f.match(usercmd))
|
self.assertTrue(f.match(usercmd))
|
||||||
|
|
||||||
|
def test_exec_dirs_search(self):
|
||||||
|
# This test supposes you have /bin/cat or /usr/bin/cat locally
|
||||||
|
f = filters.CommandFilter("cat", "root")
|
||||||
|
usercmd = ['cat', '/f']
|
||||||
|
self.assertTrue(f.match(usercmd))
|
||||||
|
self.assertTrue(f.get_command(usercmd, exec_dirs=['/bin',
|
||||||
|
'/usr/bin']) in (['/bin/cat', '/f'], ['/usr/bin/cat', '/f']))
|
||||||
|
|
||||||
def test_skips(self):
|
def test_skips(self):
|
||||||
# Check that all filters are skipped and that the last matches
|
# Check that all filters are skipped and that the last matches
|
||||||
usercmd = ["cat", "/"]
|
usercmd = ["cat", "/"]
|
||||||
|
Reference in New Issue
Block a user