qubespolicy: add $adminvm keyword for specifying dom0 aka AdminVM
Fixes QubesOS/qubes-issues#2872
This commit is contained in:
parent
a937bb173a
commit
26ea836f67
@ -30,6 +30,10 @@ Each line consist of three values separated by white characters (space(s), tab(s
|
|||||||
- `$dispvm:vm-name` - _new_ Disposable VM created from AppVM `vm-name`
|
- `$dispvm:vm-name` - _new_ Disposable VM created from AppVM `vm-name`
|
||||||
- `$dispvm` - _new_ Disposable VM created from AppVM pointed by caller
|
- `$dispvm` - _new_ Disposable VM created from AppVM pointed by caller
|
||||||
property `default_dispvm`, which defaults to global property `default_dispvm`
|
property `default_dispvm`, which defaults to global property `default_dispvm`
|
||||||
|
- `$adminvm` - Admin VM aka dom0
|
||||||
|
|
||||||
|
Dom0 can only be matched explicitly - either as `dom0` or `$adminvm` keyword.
|
||||||
|
None of `$anyvm`, `$tag:some-tag`, `$type:AdminVM` will match.
|
||||||
|
|
||||||
3. Action and optional action parameters, one of:
|
3. Action and optional action parameters, one of:
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ def verify_target_value(system_info, value):
|
|||||||
'''
|
'''
|
||||||
if value == '$dispvm':
|
if value == '$dispvm':
|
||||||
return True
|
return True
|
||||||
|
elif value == '$adminvm':
|
||||||
|
return True
|
||||||
elif value.startswith('$dispvm:'):
|
elif value.startswith('$dispvm:'):
|
||||||
dispvm_base = value.split(':', 1)[1]
|
dispvm_base = value.split(':', 1)[1]
|
||||||
if dispvm_base not in system_info['domains']:
|
if dispvm_base not in system_info['domains']:
|
||||||
@ -93,6 +95,8 @@ def verify_special_value(value, for_target=True):
|
|||||||
return True
|
return True
|
||||||
elif value == '$anyvm':
|
elif value == '$anyvm':
|
||||||
return True
|
return True
|
||||||
|
elif value == '$adminvm':
|
||||||
|
return True
|
||||||
elif value.startswith('$dispvm:') and for_target:
|
elif value.startswith('$dispvm:') and for_target:
|
||||||
return True
|
return True
|
||||||
elif value == '$dispvm' and for_target:
|
elif value == '$dispvm' and for_target:
|
||||||
@ -184,8 +188,9 @@ class PolicyRule(object):
|
|||||||
'allow action for $default rule must specify target= option')
|
'allow action for $default rule must specify target= option')
|
||||||
|
|
||||||
if self.override_target is not None:
|
if self.override_target is not None:
|
||||||
if self.override_target.startswith('$') and not \
|
if self.override_target.startswith('$') and \
|
||||||
self.override_target.startswith('$dispvm'):
|
not self.override_target.startswith('$dispvm') and \
|
||||||
|
self.override_target != '$adminvm':
|
||||||
raise PolicySyntaxError(filename, lineno,
|
raise PolicySyntaxError(filename, lineno,
|
||||||
'target= option needs to name specific target')
|
'target= option needs to name specific target')
|
||||||
|
|
||||||
@ -216,11 +221,19 @@ class PolicyRule(object):
|
|||||||
if not verify_target_value(system_info, value):
|
if not verify_target_value(system_info, value):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# handle $adminvm keyword
|
||||||
|
if policy_value == 'dom0':
|
||||||
|
# TODO: log a warning in Qubes 4.1
|
||||||
|
policy_value = '$adminvm'
|
||||||
|
|
||||||
|
if value == 'dom0':
|
||||||
|
value = '$adminvm'
|
||||||
|
|
||||||
# allow any _valid_, non-dom0 target
|
# allow any _valid_, non-dom0 target
|
||||||
if policy_value == '$anyvm':
|
if policy_value == '$anyvm':
|
||||||
return value != 'dom0'
|
return value != '$adminvm'
|
||||||
|
|
||||||
# exact match, including $dispvm*
|
# exact match, including $dispvm* and $adminvm
|
||||||
if value == policy_value:
|
if value == policy_value:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -229,6 +242,11 @@ class PolicyRule(object):
|
|||||||
if value.startswith('$dispvm'):
|
if value.startswith('$dispvm'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# require $adminvm to be matched explicitly (not through $tag or $type)
|
||||||
|
# - if not matched already, reject it
|
||||||
|
if value == '$adminvm':
|
||||||
|
return False
|
||||||
|
|
||||||
# at this point, value name a specific target
|
# at this point, value name a specific target
|
||||||
domain_info = system_info['domains'][value]
|
domain_info = system_info['domains'][value]
|
||||||
|
|
||||||
@ -293,6 +311,8 @@ class PolicyRule(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# TODO log a warning?
|
# TODO log a warning?
|
||||||
pass
|
pass
|
||||||
|
elif self.target == '$adminvm':
|
||||||
|
yield self.target
|
||||||
elif self.target == '$dispvm':
|
elif self.target == '$dispvm':
|
||||||
yield self.target
|
yield self.target
|
||||||
else:
|
else:
|
||||||
@ -378,6 +398,8 @@ class PolicyAction(object):
|
|||||||
assert self.action == Action.allow
|
assert self.action == Action.allow
|
||||||
assert self.target is not None
|
assert self.target is not None
|
||||||
|
|
||||||
|
if self.target == '$adminvm':
|
||||||
|
self.target = 'dom0'
|
||||||
if self.target == 'dom0':
|
if self.target == 'dom0':
|
||||||
cmd = '{multiplexer} {service} {source} {original_target}'.format(
|
cmd = '{multiplexer} {service} {source} {original_target}'.format(
|
||||||
multiplexer=QUBES_RPC_MULTIPLEXER_PATH,
|
multiplexer=QUBES_RPC_MULTIPLEXER_PATH,
|
||||||
|
@ -31,7 +31,7 @@ tmp_policy_dir = '/tmp/policy'
|
|||||||
system_info = {
|
system_info = {
|
||||||
'domains': {
|
'domains': {
|
||||||
'dom0': {
|
'dom0': {
|
||||||
'tags': [],
|
'tags': ['dom0-tag'],
|
||||||
'type': 'AdminVM',
|
'type': 'AdminVM',
|
||||||
'default_dispvm': 'default-dvm',
|
'default_dispvm': 'default-dvm',
|
||||||
'dispvm_allowed': False,
|
'dispvm_allowed': False,
|
||||||
@ -102,6 +102,8 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
qubespolicy.verify_target_value(system_info, 'test-template'))
|
qubespolicy.verify_target_value(system_info, 'test-template'))
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
qubespolicy.verify_target_value(system_info, 'test-standalone'))
|
qubespolicy.verify_target_value(system_info, 'test-standalone'))
|
||||||
|
self.assertTrue(
|
||||||
|
qubespolicy.verify_target_value(system_info, '$adminvm'))
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
qubespolicy.verify_target_value(system_info, 'no-such-vm'))
|
qubespolicy.verify_target_value(system_info, 'no-such-vm'))
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
@ -127,6 +129,8 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
for_target=False))
|
for_target=False))
|
||||||
self.assertTrue(qubespolicy.verify_special_value('$type:AppVM',
|
self.assertTrue(qubespolicy.verify_special_value('$type:AppVM',
|
||||||
for_target=False))
|
for_target=False))
|
||||||
|
self.assertTrue(qubespolicy.verify_special_value('$adminvm',
|
||||||
|
for_target=False))
|
||||||
self.assertFalse(qubespolicy.verify_special_value('$default',
|
self.assertFalse(qubespolicy.verify_special_value('$default',
|
||||||
for_target=False))
|
for_target=False))
|
||||||
self.assertFalse(qubespolicy.verify_special_value('$dispvm',
|
self.assertFalse(qubespolicy.verify_special_value('$dispvm',
|
||||||
@ -197,6 +201,20 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
self.assertIsNone(line.override_user)
|
self.assertIsNone(line.override_user)
|
||||||
self.assertEqual(line.default_target, 'test-vm1')
|
self.assertEqual(line.default_target, 'test-vm1')
|
||||||
|
|
||||||
|
def test_024_line_simple(self):
|
||||||
|
line = qubespolicy.PolicyRule(
|
||||||
|
'$anyvm $adminvm ask,default_target=$adminvm',
|
||||||
|
'filename', 12)
|
||||||
|
self.assertEqual(line.filename, 'filename')
|
||||||
|
self.assertEqual(line.lineno, 12)
|
||||||
|
self.assertEqual(line.action, qubespolicy.Action.ask)
|
||||||
|
self.assertEqual(line.source, '$anyvm')
|
||||||
|
self.assertEqual(line.target, '$adminvm')
|
||||||
|
self.assertEqual(line.full_action, 'ask,default_target=$adminvm')
|
||||||
|
self.assertIsNone(line.override_target)
|
||||||
|
self.assertIsNone(line.override_user)
|
||||||
|
self.assertEqual(line.default_target, '$adminvm')
|
||||||
|
|
||||||
def test_030_line_invalid(self):
|
def test_030_line_invalid(self):
|
||||||
invalid_lines = [
|
invalid_lines = [
|
||||||
'$dispvm $default allow', # $dispvm can't be a source
|
'$dispvm $default allow', # $dispvm can't be a source
|
||||||
@ -236,6 +254,9 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
self.assertTrue(is_match_single(system_info,
|
self.assertTrue(is_match_single(system_info,
|
||||||
'$anyvm', '$dispvm:default-dvm'))
|
'$anyvm', '$dispvm:default-dvm'))
|
||||||
self.assertTrue(is_match_single(system_info, '$dispvm', '$dispvm'))
|
self.assertTrue(is_match_single(system_info, '$dispvm', '$dispvm'))
|
||||||
|
self.assertTrue(is_match_single(system_info, '$adminvm', '$adminvm'))
|
||||||
|
self.assertTrue(is_match_single(system_info, '$adminvm', 'dom0'))
|
||||||
|
self.assertTrue(is_match_single(system_info, 'dom0', '$adminvm'))
|
||||||
self.assertTrue(is_match_single(system_info, 'dom0', 'dom0'))
|
self.assertTrue(is_match_single(system_info, 'dom0', 'dom0'))
|
||||||
self.assertTrue(is_match_single(system_info,
|
self.assertTrue(is_match_single(system_info,
|
||||||
'$dispvm:default-dvm', '$dispvm:default-dvm'))
|
'$dispvm:default-dvm', '$dispvm:default-dvm'))
|
||||||
@ -254,6 +275,15 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
self.assertFalse(is_match_single(system_info,
|
self.assertFalse(is_match_single(system_info,
|
||||||
'$dispvm:test-vm1', '$dispvm:test-vm1'))
|
'$dispvm:test-vm1', '$dispvm:test-vm1'))
|
||||||
self.assertFalse(is_match_single(system_info, '$anyvm', 'dom0'))
|
self.assertFalse(is_match_single(system_info, '$anyvm', 'dom0'))
|
||||||
|
self.assertFalse(is_match_single(system_info, '$anyvm', '$adminvm'))
|
||||||
|
self.assertFalse(is_match_single(system_info,
|
||||||
|
'$tag:dom0-tag', '$adminvm'))
|
||||||
|
self.assertFalse(is_match_single(system_info,
|
||||||
|
'$type:AdminVM', '$adminvm'))
|
||||||
|
self.assertFalse(is_match_single(system_info,
|
||||||
|
'$tag:dom0-tag', 'dom0'))
|
||||||
|
self.assertFalse(is_match_single(system_info,
|
||||||
|
'$type:AdminVM', 'dom0'))
|
||||||
self.assertFalse(is_match_single(system_info, '$tag:tag1', 'dom0'))
|
self.assertFalse(is_match_single(system_info, '$tag:tag1', 'dom0'))
|
||||||
self.assertFalse(is_match_single(system_info, '$anyvm', '$tag:tag1'))
|
self.assertFalse(is_match_single(system_info, '$anyvm', '$tag:tag1'))
|
||||||
self.assertFalse(is_match_single(system_info, '$anyvm', '$type:AppVM'))
|
self.assertFalse(is_match_single(system_info, '$anyvm', '$type:AppVM'))
|
||||||
@ -339,6 +369,13 @@ class TC_00_PolicyRule(qubes.tests.QubesTestCase):
|
|||||||
line.expand_override_target(system_info, 'test-no-dvm'),
|
line.expand_override_target(system_info, 'test-no-dvm'),
|
||||||
'dom0')
|
'dom0')
|
||||||
|
|
||||||
|
def test_075_expand_override_target_dom0(self):
|
||||||
|
line = qubespolicy.PolicyRule(
|
||||||
|
'$anyvm $anyvm allow,target=$adminvm')
|
||||||
|
self.assertEqual(
|
||||||
|
line.expand_override_target(system_info, 'test-no-dvm'),
|
||||||
|
'$adminvm')
|
||||||
|
|
||||||
|
|
||||||
class TC_10_PolicyAction(qubes.tests.QubesTestCase):
|
class TC_10_PolicyAction(qubes.tests.QubesTestCase):
|
||||||
def test_000_init(self):
|
def test_000_init(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user