mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
This patch integrates the new access control management tools into 'xm'
and 'xend' and supports label/ssid translation support for migration/life-migration/resume. Signed-off by: Reiner Sailer <sailer@us.ibm.com>
This commit is contained in:
1 parent
e7f4f3a1de
commit
5ab002b998
4 files changed
+178
-32
No files matched your search
@@ -38,6 +38,7 @@ from xen.xend.XendError import XendError, XendInvalidDomain
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
from xen.xend.xenstore.xswatch import xswatch
|
||||
from xen.util import security
|
||||
|
||||
|
||||
xc = xen.lowlevel.xc.xc()
|
||||
@@ -265,7 +266,7 @@ class XendDomain:
|
||||
# handling in the relocation-socket handling code (relocate.py) is
|
||||
# poor, so we need to log this for debugging.
|
||||
log.exception("Restore failed")
|
||||
raise
|
||||
raise XendError("Restore failed")
|
||||
|
||||
|
||||
def restore_(self, config):
|
||||
@@ -283,6 +284,7 @@ class XendDomain:
|
||||
"""
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
security.refresh_ssidref(config)
|
||||
dominfo = XendDomainInfo.restore(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
|
||||
@@ -33,7 +33,7 @@ import threading
|
||||
import xen.lowlevel.xc
|
||||
from xen.util import asserts
|
||||
from xen.util.blkif import blkdev_uname_to_file
|
||||
|
||||
from xen.util import security
|
||||
import balloon
|
||||
import image
|
||||
import sxp
|
||||
@@ -126,7 +126,6 @@ VM_CONFIG_PARAMS = [
|
||||
# file, so those are handled separately.
|
||||
ROUNDTRIPPING_CONFIG_ENTRIES = [
|
||||
('uuid', str),
|
||||
('ssidref', int),
|
||||
('vcpus', int),
|
||||
('vcpu_avail', int),
|
||||
('cpu_weight', float),
|
||||
@@ -144,7 +143,6 @@ ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS
|
||||
#
|
||||
VM_STORE_ENTRIES = [
|
||||
('uuid', str),
|
||||
('ssidref', int),
|
||||
('vcpus', int),
|
||||
('vcpu_avail', int),
|
||||
('memory', int),
|
||||
@@ -297,6 +295,9 @@ def parseConfig(config):
|
||||
result['cpu'] = get_cfg('cpu', int)
|
||||
result['cpus'] = get_cfg('cpus', str)
|
||||
result['image'] = get_cfg('image')
|
||||
tmp_security = get_cfg('security')
|
||||
if tmp_security:
|
||||
result['security'] = tmp_security
|
||||
|
||||
try:
|
||||
if result['image']:
|
||||
@@ -443,7 +444,7 @@ class XendDomainInfo:
|
||||
self.validateInfo()
|
||||
|
||||
self.image = None
|
||||
|
||||
self.security = None
|
||||
self.store_port = None
|
||||
self.store_mfn = None
|
||||
self.console_port = None
|
||||
@@ -521,6 +522,7 @@ class XendDomainInfo:
|
||||
else:
|
||||
entries = VM_STORE_ENTRIES
|
||||
entries.append(('image', str))
|
||||
entries.append(('security', str))
|
||||
|
||||
map(lambda x, y: useIfNeeded(x[0], y), entries,
|
||||
self.readVMDetails(entries))
|
||||
@@ -544,7 +546,6 @@ class XendDomainInfo:
|
||||
|
||||
try:
|
||||
defaultInfo('name', lambda: "Domain-%d" % self.domid)
|
||||
defaultInfo('ssidref', lambda: 0)
|
||||
defaultInfo('on_poweroff', lambda: "destroy")
|
||||
defaultInfo('on_reboot', lambda: "restart")
|
||||
defaultInfo('on_crash', lambda: "restart")
|
||||
@@ -571,12 +572,16 @@ class XendDomainInfo:
|
||||
defaultInfo('backend', lambda: [])
|
||||
defaultInfo('device', lambda: [])
|
||||
defaultInfo('image', lambda: None)
|
||||
defaultInfo('security', lambda: None)
|
||||
|
||||
self.check_name(self.info['name'])
|
||||
|
||||
if isinstance(self.info['image'], str):
|
||||
self.info['image'] = sxp.from_string(self.info['image'])
|
||||
|
||||
if isinstance(self.info['security'], str):
|
||||
self.info['security'] = sxp.from_string(self.info['security'])
|
||||
|
||||
if self.info['memory'] == 0:
|
||||
if self.infoIsSet('mem_kb'):
|
||||
self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
|
||||
@@ -674,6 +679,20 @@ class XendDomainInfo:
|
||||
if self.infoIsSet('image'):
|
||||
to_store['image'] = sxp.to_string(self.info['image'])
|
||||
|
||||
if self.infoIsSet('security'):
|
||||
security = self.info['security']
|
||||
to_store['security'] = sxp.to_string(security)
|
||||
for idx in range(0, len(security)):
|
||||
if security[idx][0] == 'access_control':
|
||||
to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
|
||||
for aidx in range(1, len(security[idx])):
|
||||
if security[idx][aidx][0] == 'label':
|
||||
to_store['security/access_control/label'] = security[idx][aidx][1]
|
||||
if security[idx][aidx][0] == 'policy':
|
||||
to_store['security/access_control/policy'] = security[idx][aidx][1]
|
||||
if security[idx][0] == 'ssidref':
|
||||
to_store['security/ssidref'] = str(security[idx][1])
|
||||
|
||||
log.debug("Storing VM details: %s", to_store)
|
||||
|
||||
self.writeVm(to_store)
|
||||
@@ -766,9 +785,8 @@ class XendDomainInfo:
|
||||
self.storeVm('vcpu_avail', self.info['vcpu_avail'])
|
||||
self.writeDom(self.vcpuDomDetails())
|
||||
|
||||
|
||||
def getSsidref(self):
|
||||
return self.info['ssidref']
|
||||
def getLabel(self):
|
||||
return security.get_security_info(self.info, 'label')
|
||||
|
||||
def getMemoryTarget(self):
|
||||
"""Get this domain's target memory size, in KB."""
|
||||
@@ -960,12 +978,21 @@ class XendDomainInfo:
|
||||
"""
|
||||
|
||||
log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
|
||||
|
||||
if not info:
|
||||
info = dom_get(self.domid)
|
||||
if not info:
|
||||
return
|
||||
|
||||
#manually update ssidref / security fields
|
||||
if security.on() and info.has_key('ssidref'):
|
||||
if (info['ssidref'] != 0) and self.info.has_key('security'):
|
||||
security_field = self.info['security']
|
||||
if not security_field:
|
||||
#create new security element
|
||||
self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
|
||||
#ssidref field not used any longer
|
||||
info.pop('ssidref')
|
||||
|
||||
self.info.update(info)
|
||||
self.validateInfo()
|
||||
self.refreshShutdown(info)
|
||||
@@ -1002,7 +1029,6 @@ class XendDomainInfo:
|
||||
s += " id=" + str(self.domid)
|
||||
s += " name=" + self.info['name']
|
||||
s += " memory=" + str(self.info['memory'])
|
||||
s += " ssidref=" + str(self.info['ssidref'])
|
||||
s += ">"
|
||||
return s
|
||||
|
||||
@@ -1064,6 +1090,9 @@ class XendDomainInfo:
|
||||
if self.infoIsSet('image'):
|
||||
sxpr.append(['image', self.info['image']])
|
||||
|
||||
if self.infoIsSet('security'):
|
||||
sxpr.append(['security', self.info['security']])
|
||||
|
||||
for cls in controllerClasses:
|
||||
for config in self.getDeviceConfigurations(cls):
|
||||
sxpr.append(['device', config])
|
||||
@@ -1165,12 +1194,11 @@ class XendDomainInfo:
|
||||
@raise: VmError on error
|
||||
"""
|
||||
|
||||
log.debug('XendDomainInfo.construct: %s %s',
|
||||
self.domid,
|
||||
self.info['ssidref'])
|
||||
log.debug('XendDomainInfo.construct: %s',
|
||||
self.domid)
|
||||
|
||||
self.domid = xc.domain_create(
|
||||
dom = 0, ssidref = self.info['ssidref'],
|
||||
dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
|
||||
handle = uuid.fromString(self.info['uuid']))
|
||||
|
||||
if self.domid < 0:
|
||||
|
||||
@@ -35,6 +35,7 @@ import xen.xend.XendClient
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xend.XendBootloader import bootloader
|
||||
from xen.util import blkif
|
||||
from xen.util import security
|
||||
|
||||
from xen.xm.opts import *
|
||||
|
||||
@@ -145,10 +146,6 @@ gopts.var('memory', val='MEMORY',
|
||||
fn=set_int, default=128,
|
||||
use="Domain memory in MB.")
|
||||
|
||||
gopts.var('ssidref', val='SSIDREF',
|
||||
fn=set_u32, default=0,
|
||||
use="Security Identifier.")
|
||||
|
||||
gopts.var('maxmem', val='MEMORY',
|
||||
fn=set_int, default=None,
|
||||
use="Maximum domain memory in MB.")
|
||||
@@ -293,6 +290,14 @@ gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
|
||||
number can be found in /etc/xen/vtpm.db. Use the backend in the
|
||||
given domain.""")
|
||||
|
||||
gopts.var('access_control', val="policy=POLICY,label=LABEL",
|
||||
fn=append_value, default=[],
|
||||
use="""Add a security label and the security policy reference that defines it.
|
||||
The local ssid reference is calculated when starting/resuming the domain. At
|
||||
this time, the policy is checked against the active policy as well. This way,
|
||||
migrating through save/restore is covered and local labels are automatically
|
||||
created correctly on the system where a domain is started / resumed.""")
|
||||
|
||||
gopts.var('nics', val="NUM",
|
||||
fn=set_int, default=-1,
|
||||
use="""DEPRECATED. Use empty vif entries instead.
|
||||
@@ -502,6 +507,43 @@ def configure_usb(config_devs, vals):
|
||||
config_usb = ['usb', ['path', path]]
|
||||
config_devs.append(['device', config_usb])
|
||||
|
||||
|
||||
def configure_security(config, vals):
|
||||
"""Create the config for ACM security labels.
|
||||
"""
|
||||
access_control = vals.access_control
|
||||
num = len(access_control)
|
||||
if num == 1:
|
||||
d = access_control[0]
|
||||
policy = d.get('policy')
|
||||
label = d.get('label')
|
||||
if policy != security.active_policy:
|
||||
err("Security policy (" + policy + ") incompatible with enforced policy ("
|
||||
+ security.active_policy + ")." )
|
||||
config_access_control = ['access_control',
|
||||
['policy', policy],
|
||||
['label', label] ]
|
||||
|
||||
#ssidref cannot be specified together with access_control
|
||||
if sxp.child_value(config, 'ssidref'):
|
||||
err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
|
||||
#else calculate ssidre from label
|
||||
ssidref = security.label2ssidref(label, policy)
|
||||
if not ssidref :
|
||||
err("ERROR calculating ssidref from access_control.")
|
||||
security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
|
||||
config.append(security_label)
|
||||
elif num == 0:
|
||||
if hasattr(vals, 'ssidref'):
|
||||
if not security.on():
|
||||
err("ERROR: Security ssidref specified but no policy active.")
|
||||
ssidref = getattr(vals, 'ssidref')
|
||||
security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
|
||||
config.append(security_label)
|
||||
elif num > 1:
|
||||
err("VM config error: Multiple access_control definitions!")
|
||||
|
||||
|
||||
def configure_vtpm(config_devs, vals):
|
||||
"""Create the config for virtual TPM interfaces.
|
||||
"""
|
||||
@@ -595,9 +637,9 @@ def make_config(vals):
|
||||
if v:
|
||||
config.append([n, v])
|
||||
|
||||
map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
|
||||
'on_poweroff', 'on_reboot', 'on_crash', 'vcpus'])
|
||||
|
||||
map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
|
||||
'on_reboot', 'on_crash', 'vcpus'])
|
||||
|
||||
if vals.uuid is not None:
|
||||
config.append(['uuid', vals.uuid])
|
||||
if vals.cpu is not None:
|
||||
@@ -628,6 +670,7 @@ def make_config(vals):
|
||||
configure_vifs(config_devs, vals)
|
||||
configure_usb(config_devs, vals)
|
||||
configure_vtpm(config_devs, vals)
|
||||
configure_security(config, vals)
|
||||
config += config_devs
|
||||
|
||||
return config
|
||||
@@ -696,6 +739,29 @@ def preprocess_vtpm(vals):
|
||||
vtpms.append(d)
|
||||
vals.vtpm = vtpms
|
||||
|
||||
def preprocess_access_control(vals):
|
||||
if not vals.access_control:
|
||||
return
|
||||
access_controls = []
|
||||
num = len(vals.access_control)
|
||||
if num == 1:
|
||||
access_control = (vals.access_control)[0]
|
||||
d = {}
|
||||
a = access_control.split(',')
|
||||
if len(a) > 2:
|
||||
err('Too many elements in access_control specifier: ' + access_control)
|
||||
for b in a:
|
||||
(k, v) = b.strip().split('=', 1)
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
if k not in ['policy','label']:
|
||||
err('Invalid access_control specifier: ' + access_control)
|
||||
d[k] = v
|
||||
access_controls.append(d)
|
||||
vals.access_control = access_controls
|
||||
elif num > 1:
|
||||
err('Multiple access_control definitions.')
|
||||
|
||||
def preprocess_ip(vals):
|
||||
if vals.ip or vals.dhcp != 'off':
|
||||
dummy_nfs_server = '1.2.3.4'
|
||||
@@ -785,6 +851,7 @@ def preprocess(vals):
|
||||
preprocess_nfs(vals)
|
||||
preprocess_vnc(vals)
|
||||
preprocess_vtpm(vals)
|
||||
preprocess_access_control(vals)
|
||||
|
||||
|
||||
def comma_sep_kv_to_dict(c):
|
||||
|
||||
+59
-10
@@ -40,6 +40,7 @@ from xen.xm.opts import *
|
||||
import console
|
||||
import xen.xend.XendClient
|
||||
from xen.xend.XendClient import server
|
||||
from xen.util import security
|
||||
|
||||
# getopt.gnu_getopt is better, but only exists in Python 2.3+. Use
|
||||
# getopt.getopt if gnu_getopt is not available. This will mean that options
|
||||
@@ -55,6 +56,8 @@ create_help = """create [-c] <ConfigFile>
|
||||
destroy_help = "destroy <DomId> Terminate a domain immediately"
|
||||
help_help = "help Display this message"
|
||||
list_help = "list [--long] [DomId, ...] List information about domains"
|
||||
list_label_help = "list [--label] [DomId, ...] List information about domains including their labels"
|
||||
|
||||
mem_max_help = "mem-max <DomId> <Mem> Set maximum memory reservation for a domain"
|
||||
mem_set_help = "mem-set <DomId> <Mem> Adjust the current memory usage for a domain"
|
||||
migrate_help = "migrate <DomId> <Host> Migrate a domain to another machine"
|
||||
@@ -114,6 +117,12 @@ vnet_list_help = "vnet-list [-l|--long] list vnets"
|
||||
vnet_create_help = "vnet-create <config> create a vnet from a config file"
|
||||
vnet_delete_help = "vnet-delete <vnetid> delete a vnet"
|
||||
vtpm_list_help = "vtpm-list <DomId> [--long] list virtual TPM devices"
|
||||
addlabel_help = "addlabel <ConfigFile> <label> Add security label to ConfigFile"
|
||||
cfgbootpolicy_help = "cfgbootpolicy <policy> Add policy to boot configuration "
|
||||
dumppolicy_help = "dumppolicy Print hypervisor ACM state information"
|
||||
loadpolicy_help = "loadpolicy <policy> Load binary policy into hypervisor"
|
||||
makepolicy_help = "makepolicy <policy> Build policy and create .bin/.map files"
|
||||
labels_help = "labels [policy] [type=DOM|..] List <type> labels for (active) policy."
|
||||
|
||||
short_command_list = [
|
||||
"console",
|
||||
@@ -140,6 +149,7 @@ domain_commands = [
|
||||
"domid",
|
||||
"domname",
|
||||
"list",
|
||||
"list_label",
|
||||
"mem-max",
|
||||
"mem-set",
|
||||
"migrate",
|
||||
@@ -185,8 +195,17 @@ vnet_commands = [
|
||||
"vnet-delete",
|
||||
]
|
||||
|
||||
acm_commands = [
|
||||
"labels",
|
||||
"addlabel",
|
||||
"makepolicy",
|
||||
"loadpolicy",
|
||||
"cfgbootpolicy",
|
||||
"dumppolicy"
|
||||
]
|
||||
|
||||
all_commands = (domain_commands + host_commands + scheduler_commands +
|
||||
device_commands + vnet_commands)
|
||||
device_commands + vnet_commands + acm_commands)
|
||||
|
||||
|
||||
def commandToHelp(cmd):
|
||||
@@ -225,6 +244,9 @@ xm full list of subcommands:
|
||||
Vnet commands:
|
||||
""" + help_spacer.join(map(commandToHelp, vnet_commands)) + """
|
||||
|
||||
Access Control commands:
|
||||
""" + help_spacer.join(map(commandToHelp, acm_commands)) + """
|
||||
|
||||
<DomName> can be substituted for <DomId> in xm subcommands.
|
||||
|
||||
For a short list of subcommands run 'xm help'
|
||||
@@ -332,8 +354,9 @@ def getDomains(domain_names):
|
||||
def xm_list(args):
|
||||
use_long = 0
|
||||
show_vcpus = 0
|
||||
show_labels = 0
|
||||
try:
|
||||
(options, params) = getopt.gnu_getopt(args, 'lv', ['long','vcpus'])
|
||||
(options, params) = getopt.gnu_getopt(args, 'lv', ['long','vcpus','label'])
|
||||
except getopt.GetoptError, opterr:
|
||||
err(opterr)
|
||||
sys.exit(1)
|
||||
@@ -343,6 +366,8 @@ def xm_list(args):
|
||||
use_long = 1
|
||||
if k in ['-v', '--vcpus']:
|
||||
show_vcpus = 1
|
||||
if k in ['--label']:
|
||||
show_labels = 1
|
||||
|
||||
if show_vcpus:
|
||||
print >>sys.stderr, (
|
||||
@@ -354,6 +379,8 @@ def xm_list(args):
|
||||
|
||||
if use_long:
|
||||
map(PrettyPrint.prettyprint, doms)
|
||||
elif show_labels:
|
||||
xm_label_list(doms)
|
||||
else:
|
||||
xm_brief_list(doms)
|
||||
|
||||
@@ -369,7 +396,7 @@ def parse_doms_info(info):
|
||||
'vcpus' : get_info('online_vcpus', int, 0),
|
||||
'state' : get_info('state', str, '??'),
|
||||
'cpu_time' : get_info('cpu_time', float, 0),
|
||||
'ssidref' : get_info('ssidref', int, 0),
|
||||
'seclabel' : security.get_security_printlabel(info),
|
||||
}
|
||||
|
||||
|
||||
@@ -391,13 +418,29 @@ def xm_brief_list(doms):
|
||||
print 'Name ID Mem(MiB) VCPUs State Time(s)'
|
||||
for dom in doms:
|
||||
d = parse_doms_info(dom)
|
||||
if (d['ssidref'] != 0):
|
||||
d['ssidstr'] = (" s:%04x/p:%04x" %
|
||||
((d['ssidref'] >> 16) & 0xffff,
|
||||
d['ssidref'] & 0xffff))
|
||||
print ("%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s %(cpu_time)7.1f" % d)
|
||||
|
||||
|
||||
def xm_label_list(doms):
|
||||
output = []
|
||||
print 'Name ID Mem(MiB) VCPUs State Time(s) Label'
|
||||
for dom in doms:
|
||||
d = parse_doms_info(dom)
|
||||
l = "%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s %(cpu_time)7.1f " % d
|
||||
if security.active_policy not in ['INACTIVE', 'NULL', 'DEFAULT']:
|
||||
if d['seclabel']:
|
||||
line = (l, d['seclabel'])
|
||||
else:
|
||||
line = (l, "ERROR")
|
||||
elif security.active_policy in ['DEFAULT']:
|
||||
line = (l, "DEFAULT")
|
||||
else:
|
||||
d['ssidstr'] = ""
|
||||
print ("%(name)-32s %(dom)3d %(mem)8d %(vcpus)5d %(state)5s %(cpu_time)7.1f%(ssidstr)s" % d)
|
||||
line = (l, "INACTIVE")
|
||||
output.append(line)
|
||||
#sort by labels
|
||||
output.sort(lambda x,y: cmp( x[1].lower(), y[1].lower()))
|
||||
for l in output:
|
||||
print l[0] + l[1]
|
||||
|
||||
|
||||
def xm_vcpu_list(args):
|
||||
@@ -1010,7 +1053,13 @@ subcommands = [
|
||||
'create',
|
||||
'migrate',
|
||||
'sysrq',
|
||||
'shutdown'
|
||||
'shutdown',
|
||||
'labels',
|
||||
'addlabel',
|
||||
'cfgbootpolicy',
|
||||
'makepolicy',
|
||||
'loadpolicy',
|
||||
'dumppolicy'
|
||||
]
|
||||
|
||||
for c in subcommands:
|
||||
|
||||