diff --git a/lib/constants.py b/lib/constants.py index 4fa186e9f62be332f0136f1e656e0163686f6fee..92c3523b72eefe507d6da3e2676129f5a061de16 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -670,6 +670,7 @@ HV_VNC_X509 = "vnc_x509_path" HV_VNC_X509_VERIFY = "vnc_x509_verify" HV_KVM_SPICE_BIND = "spice_bind" HV_KVM_SPICE_IP_VERSION = "spice_ip_version" +HV_KVM_SPICE_PASSWORD_FILE = "spice_password_file" HV_ACPI = "acpi" HV_PAE = "pae" HV_USE_BOOTLOADER = "use_bootloader" @@ -715,6 +716,7 @@ HVS_PARAMETER_TYPES = { HV_VNC_X509_VERIFY: VTYPE_BOOL, HV_KVM_SPICE_BIND: VTYPE_STRING, HV_KVM_SPICE_IP_VERSION: VTYPE_INT, + HV_KVM_SPICE_PASSWORD_FILE: VTYPE_STRING, HV_ACPI: VTYPE_BOOL, HV_PAE: VTYPE_BOOL, HV_USE_BOOTLOADER: VTYPE_BOOL, @@ -1295,6 +1297,7 @@ HVC_DEFAULTS = { HV_VNC_PASSWORD_FILE: "", HV_KVM_SPICE_BIND: "", HV_KVM_SPICE_IP_VERSION: IFACE_NO_IP_VERSION_SPECIFIED, + HV_KVM_SPICE_PASSWORD_FILE: "", HV_KVM_FLOPPY_IMAGE_PATH: "", HV_CDROM_IMAGE_PATH: "", HV_KVM_CDROM2_IMAGE_PATH: "", diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 6aa2374a42a9d5037f37df549125ab5b77f613f2..fe9b9c877e5284dbbe5bdc1ad0599ed815ae9c55 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -418,6 +418,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): x in constants.VALID_IP_VERSIONS), "the SPICE IP version should be 4 or 6", None, None), + constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK, constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK, @@ -1015,8 +1016,10 @@ class KVMHypervisor(hv_base.BaseHypervisor): # ValidateParameters checked it. spice_address = spice_bind - spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address, - instance.network_port) + spice_arg = "addr=%s,port=%s" % (spice_address, instance.network_port) + if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]: + spice_arg = "%s,disable-ticketing" % spice_arg + if spice_ip_version: spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version) @@ -1236,6 +1239,26 @@ class KVMHypervisor(hv_base.BaseHypervisor): change_cmd = "change vnc password %s" % vnc_pwd self._CallMonitorCommand(instance.name, change_cmd) + # Setting SPICE password. We are not vulnerable to malicious passwordless + # connection attempts because SPICE by default does not allow connections + # if neither a password nor the "disable_ticketing" options are specified. + # As soon as we send the password via QMP, that password is a valid ticket + # for connection. + spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE] + if spice_password_file: + try: + spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True) + qmp = QmpConnection(self._InstanceQmpMonitor(instance.name)) + qmp.connect() + arguments = { + "protocol": "spice", + "password": spice_pwd, + } + qmp.Execute("set_password", arguments) + except EnvironmentError, err: + raise errors.HypervisorError("Failed to open SPICE password file %s: %s" + % (spice_password_file, err)) + for filename in temp_files: utils.RemoveFile(filename) @@ -1558,8 +1581,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): " security model is 'none' or 'pool'") spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] + spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION] + spice_password_file = hvparams[constants.HV_KVM_SPICE_PASSWORD_FILE] if spice_bind: - spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION] if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: # if an IP version is specified, the spice_bind parameter must be an # IP of that family @@ -1574,6 +1598,17 @@ class KVMHypervisor(hv_base.BaseHypervisor): raise errors.HypervisorError("spice: got an IPv6 address (%s), but" " the specified IP version is %s" % (spice_bind, spice_ip_version)) + else: + if spice_ip_version: + raise errors.HypervisorError("spice: the %s option is useless" + " without %s" % + (constants.HV_KVM_SPICE_IP_VERSION, + constants.HV_KVM_SPICE_BIND)) + if spice_password_file: + raise errors.HypervisorError("spice: the %s option is useless" + " without %s" % + (constants.HV_KVM_SPICE_PASSWORD_FILE, + constants.HV_KVM_SPICE_BIND)) @classmethod def ValidateParameters(cls, hvparams): diff --git a/man/gnt-instance.rst b/man/gnt-instance.rst index 5d906a40399a1bec58936ba33251b34faa492bd3..c3a0c920a050674a009d423c66b902c0540f9558 100644 --- a/man/gnt-instance.rst +++ b/man/gnt-instance.rst @@ -300,6 +300,13 @@ spice\_ip\_version this case, if the ``spice_ip_version`` parameter is not used, the default IP version of the cluster will be used. +spice\_password\_file + Valid for the KVM hypervisor. + + Specifies a file containing the password that must be used when + connecting via the SPICE protocol. If the option is not specified, + passwordless connections are allowed. + acpi Valid for the Xen HVM and KVM hypervisors.