Add a specific filter for kill commands
Use a specific KillFilter to restrict kill commands run as root. This implementation checks the signals and the executables actually affected, using procfs. Fixes bug 918226. Change-Id: I6f220d741423c4b8e0e792b647760b3ef521b9b2
This commit is contained in:
parent
bfdb9b1f5e
commit
c48fbe9843
@ -146,8 +146,11 @@ filterlist = [
|
||||
|
||||
# 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.CommandFilter("/bin/kill", "root"),
|
||||
filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),
|
||||
|
||||
# nova/network/linux_net.py: dnsmasq call
|
||||
filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),
|
||||
|
@ -88,3 +88,37 @@ class DnsmasqFilter(CommandFilter):
|
||||
env['FLAGFILE'] = userargs[0].split('=')[-1]
|
||||
env['NETWORK_ID'] = userargs[1].split('=')[-1]
|
||||
return env
|
||||
|
||||
|
||||
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.
|
||||
|
||||
This filter relies on /proc to accurately determine affected
|
||||
executable, so it will only work on procfs-capable systems (not OSX).
|
||||
"""
|
||||
|
||||
def match(self, userargs):
|
||||
if len(userargs) == 3:
|
||||
signal = userargs.pop(1)
|
||||
if signal not in self.args[0]:
|
||||
# Requested signal not in accepted list
|
||||
return False
|
||||
else:
|
||||
if len(userargs) != 2:
|
||||
# Incorrect number of arguments
|
||||
return False
|
||||
if '' not in self.args[0]:
|
||||
# No signal, but list doesn't include empty string
|
||||
return False
|
||||
pid = userargs[1]
|
||||
try:
|
||||
command = os.readlink("/proc/%d/exe" % pid)
|
||||
if command not in self.args[1]:
|
||||
# Affected executable not in accepted list
|
||||
return False
|
||||
except:
|
||||
# Incorrect PID
|
||||
return False
|
||||
return True
|
||||
|
@ -62,8 +62,11 @@ filterlist = [
|
||||
|
||||
# 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.CommandFilter("/bin/kill", "root"),
|
||||
filters.KillFilter("/bin/kill", "root", [''], ['/usr/sbin/radvd']),
|
||||
|
||||
# nova/network/linux_net.py: dnsmasq call
|
||||
filters.DnsmasqFilter("/usr/sbin/dnsmasq", "root"),
|
||||
|
@ -14,6 +14,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from nova.rootwrap import filters
|
||||
from nova.rootwrap import wrapper
|
||||
from nova import test
|
||||
@ -60,6 +63,36 @@ class RootwrapTestCase(test.TestCase):
|
||||
self.assertEqual(env.get('FLAGFILE'), 'A')
|
||||
self.assertEqual(env.get('NETWORK_ID'), 'foobar')
|
||||
|
||||
@test.skip_if(not os.path.exists("/proc/%d" % os.getpid()),
|
||||
"Test requires /proc filesystem (procfs)")
|
||||
def test_KillFilter(self):
|
||||
p = subprocess.Popen(["/bin/sleep", "5"])
|
||||
f = filters.KillFilter("/bin/kill", "root",
|
||||
["-ALRM"],
|
||||
["/bin/sleep"])
|
||||
usercmd = ['kill', '-9', p.pid]
|
||||
# Incorrect signal should fail
|
||||
self.assertFalse(f.match(usercmd))
|
||||
usercmd = ['kill', p.pid]
|
||||
# Providing no signal should fail
|
||||
self.assertFalse(f.match(usercmd))
|
||||
|
||||
f = filters.KillFilter("/bin/kill", "root",
|
||||
["-9", ""],
|
||||
["/bin/sleep"])
|
||||
usercmd = ['kill', '-9', os.getpid()]
|
||||
# Our own PID does not match /bin/sleep, so it should fail
|
||||
self.assertFalse(f.match(usercmd))
|
||||
usercmd = ['kill', '-9', 999999]
|
||||
# Nonexistant PID should fail
|
||||
self.assertFalse(f.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_skips(self):
|
||||
# Check that all filters are skipped and that the last matches
|
||||
usercmd = ["cat", "/"]
|
||||
|
Loading…
Reference in New Issue
Block a user