Pierre Donias
2025-05-27 14:25:39 +02:00
committed by GitHub
parent b8f9be6553
commit bfc39542aa
7 changed files with 166 additions and 8 deletions

View File

@@ -106,6 +106,7 @@ type BaseXoVm = BaseXapiXo & {
videoram?: number
viridian: boolean
virtualizationMode: DOMAIN_TYPE
vulnerabilities: { xsa468: boolean | { reason: string; driver?: string; version?: string } }
xenStoreData: Record<string, string>
/**
* @deprecated use pvDriversVersion instead

View File

@@ -7,6 +7,8 @@
> Security fixes and new features should go in this section
- [VM] Detect XSA-468 vulnerable VMs. Read our announcement on the [XCP-ng blog](https://xcp-ng.org/blog/xsa-468-windows-pv-driver-vulnerabilities) for more details.
### Enhancements
> Users must be able to say: “Nice enhancement, I'm eager to test it”
@@ -23,10 +25,11 @@
> Users must be able to say: “I had this issue, happy to know it's fixed”
- **XO 6**:
- [VM] Add auto redirection from /vm/[id] to /vm/[id]/console (PR [#8553](https://github.com/vatesfr/xen-orchestra/pull/8553))
- [Hosts] Avoid getting XO tasks logs flooded with errors on `host.isPubKeyTooShort` (PR [#8605](https://github.com/vatesfr/xen-orchestra/pull/8605))
- [VM] Fix "an error has occurred" in Advanced tab when VTPM is `null` (PR [#8601](https://github.com/vatesfr/xen-orchestra/pull/8601))
- [Hosts] Avoid getting XO tasks logs flooded with errors on `host.isPubKeyTooShort` (PR [#8605](https://github.com/vatesfr/xen-orchestra/pull/8605))
- [VM] Fix "an error has occurred" in Advanced tab when VTPM is `null` (PR [#8601](https://github.com/vatesfr/xen-orchestra/pull/8601))
### Packages to release
@@ -50,7 +53,7 @@
- @xen-orchestra/rest-api minor
- @xen-orchestra/web patch
- @xen-orchestra/web-core patch
- xo-server patch
- xo-web patch
- xo-server minor
- xo-web minor
<!--packages-end-->

View File

@@ -67,6 +67,7 @@
"blocked-at": "^1.2.0",
"bluebird": "^3.5.1",
"body-parser": "^1.20.0",
"compare-versions": "^6.1.1",
"complex-matcher": "^0.7.1",
"compression": "^1.7.3",
"connect-flash": "^0.1.1",

View File

@@ -5,6 +5,7 @@ import * as xoData from '@xen-orchestra/xapi/xoData.mjs'
import ensureArray from './_ensureArray.mjs'
import normalizeVmNetworks from './_normalizeVmNetworks.mjs'
import semver from 'semver'
import { compareVersions, validate as validateVersion } from 'compare-versions'
import { createLogger } from '@xen-orchestra/log'
import { extractIpFromVmNetworks } from './_extractIpFromVmNetworks.mjs'
import { extractProperty, forEach, isEmpty, mapFilter, parseXml } from './utils.mjs'
@@ -93,6 +94,82 @@ const getVmGuestToolsProps = vm => {
}
}
// ***** May 2025 - Xen Security Advisory XSA-468 - Windows PV drivers vulnerability *****
// Vendor → (driver → version)
const XSA468_VULNERABLE_VERSIONS = {
Xen_Project: {
xencons: '9.1.0.2',
xeniface: '9.1.0.0',
xenbus: '9.1.0.1',
},
Amazon_Inc_: {
xeniface: '8.3.0',
},
XenServer: {
xeniface: '9.1.12.93',
xenbus: '9.1.11.114',
},
Citrix: {
xeniface: '9.1.1.11',
xenbus: '9.1.2.14',
},
XCP_ng: {
xencons: '9.0.9048.9047',
xeniface: '9.0.9048.9047',
xenbus: '9.0.9048.9047',
},
}
const isVmVulnerable_XSA468 = vm => {
const guestMetrics = vm.$guest_metrics
if (vm.platform?.device_id !== '0002') {
// Not a Windows VM
return false
}
if (!guestMetrics.PV_drivers_detected) {
// No PV drivers installed: no vulnerability
return false
}
const pvDriversVersion = guestMetrics.PV_drivers_version
let versionDetected = false
for (const [key, value] of Object.entries(pvDriversVersion)) {
if (['major', 'minor', 'micro', 'build'].includes(key)) {
continue
}
const [vendor, version] = value.split(' ')
if (!validateVersion(version)) {
warn(`Invalid version number for ${vendor}: ${key} ${version}`)
continue
}
const vendorVulnerableVersion = XSA468_VULNERABLE_VERSIONS[vendor]?.[key]
if (vendorVulnerableVersion === undefined) {
continue
}
versionDetected = true
try {
if (compareVersions(version, vendorVulnerableVersion) <= 0) {
// PV drivers installed and vulnerable version detected
return { reason: 'pv-driver-version-vulnerable', driver: key, version }
}
} catch (err) {
warn(err)
}
}
// - PV drivers installed and could check safe versions: no vulnerability
// - PV drivers installed but could not check versions: potential vulnerability
return versionDetected ? false : { reason: 'no-pv-drivers-detected' }
}
// ***************************************************************************************
// ===================================================================
const TRANSFORMS = {
@@ -405,6 +482,7 @@ const TRANSFORMS = {
isNestedVirtEnabled: semver.satisfies(String(obj.$pool.$master.software_version.platform_version), '>=3.4')
? obj.platform['nested-virt'] === 'true'
: obj.platform['exp-nested-hvm'] === 'true',
vulnerabilities: { xsa468: isVmVulnerable_XSA468(obj) },
viridian: obj.platform.viridian === 'true',
mainIpAddress: extractIpFromVmNetworks(guestMetrics?.networks),
high_availability: obj.ha_restart_priority,

View File

@@ -496,6 +496,7 @@ const NoObjects = props =>
}
return acc
}, []),
vulnerable: Object.values(vm.vulnerabilities).filter(Boolean).length > 0,
},
{
container: { value: containers[vm.$container || vm.$pool] },

View File

@@ -1,4 +1,5 @@
import _ from 'intl'
import BulkIcons from 'bulk-icons'
import Component from 'base-component'
import Ellipsis, { EllipsisContainer } from 'ellipsis'
import Icon from 'icon'
@@ -65,6 +66,60 @@ export default class VmItem extends Component {
(powerState, operations) => (!isEmpty(operations) ? 'Busy' : powerState)
)
_getAlerts = createSelector(
() => this.props.item,
vm => {
const alerts = []
if (vm.vulnerabilities.xsa468) {
const { reason, driver, version } = vm.vulnerabilities.xsa468
if (reason === 'no-pv-drivers-detected') {
alerts.push({
level: 'warning',
render: (
<p>
<span>
We cannot detect the version of Windows PV drivers on this VM. You may be running an outdated version.
Check XCP-ng's{' '}
<a href='https://docs.xcp-ng.org/vms/#windows-guest-tools-security' target='_blank' rel='noreferrer'>
Windows Guest Tools Security documentation
</a>{' '}
for more details.
</span>
<br />
<br />
Still seeing this message even though you updated PV drivers? Please update your XCP-ng.
</p>
),
})
} else {
alerts.push({
level: 'danger',
render: (
<p>
<span>
This VM is running a Windows PV driver vulnerable to XSA-468 ({driver} {version}). You must upgrade
your Windows PV drivers now. See{' '}
<a
href='https://docs.xcp-ng.org/vms/#xsa-468-multiple-windows-pv-driver-vulnerabilities'
target='_blank'
rel='noreferrer'
>
XCP-ng's documentation
</a>{' '}
for more details.
</span>
</p>
),
})
}
}
return alerts
}
)
render() {
const { item: vm, container, expandAll, isAdmin, selected } = this.props
const resourceSet = this._getResourceSet()
@@ -103,6 +158,8 @@ export default class VmItem extends Component {
useLongClick
/>
</Ellipsis>
&nbsp;
<BulkIcons alerts={this._getAlerts()} />
</EllipsisContainer>
</Col>
<Col mediumSize={4} className='hidden-md-down'>

View File

@@ -55,7 +55,13 @@ import Import from './import'
import keymap, { help } from '../keymap'
import Tooltip from '../common/tooltip'
import { createCollectionWrapper, createGetObjectsOfType } from '../common/selectors'
import { bindXcpngLicense, rebindLicense, subscribeXcpngLicenses, subscribeXostorLicenses, subscribeSelfLicenses } from '../common/xo'
import {
bindXcpngLicense,
rebindLicense,
subscribeXcpngLicenses,
subscribeXostorLicenses,
subscribeSelfLicenses,
} from '../common/xo'
import { SOURCES } from '../common/xoa-plans'
import { getLicenseNearExpiration } from '../common/xoa-updater'
@@ -140,16 +146,20 @@ export const ICON_POOL_LICENSE = {
@addSubscriptions({
xcpLicenses: subscribeXcpngLicenses,
xostorLicenses: subscribeXostorLicenses,
selfLicences: subscribeSelfLicenses
selfLicences: subscribeSelfLicenses,
})
@connectStore(state => {
const getHosts = createGetObjectsOfType('host')
const getXostors = createGetObjectsOfType('SR').filter([sr => sr.SR_type === 'linstor'])
const getXsa468VulnerableVms = createGetObjectsOfType('VM').filter([
vm => vm.vulnerabilities?.xsa468?.reason === 'pv-driver-version-vulnerable',
])
return {
trial: state.xoaTrialState,
registerNeeded: state.xoaUpdaterState === 'registerNeeded',
signedUp: !!state.user,
hosts: getHosts(state),
xsa468VulnerableVms: getXsa468VulnerableVms(state),
xostors: getXostors(state),
}
})
@@ -314,7 +324,7 @@ export const ICON_POOL_LICENSE = {
})
return xostorLicenseInfoByXostorId
}
},
},
})
export default class XoApp extends Component {
@@ -439,7 +449,7 @@ export default class XoApp extends Component {
}
render() {
const { signedUp, trial, registerNeeded } = this.props
const { signedUp, trial, registerNeeded, xsa468VulnerableVms } = this.props
const { pathname } = this.context.router.location
const licenseNearExpiration = this.props.selfLicences && getLicenseNearExpiration(this.props.selfLicences, trial)
// If we are under expired or unstable trial (signed up only)
@@ -448,6 +458,7 @@ export default class XoApp extends Component {
(blockXoaAccess(trial) || licenseNearExpiration?.blocked === true) &&
!(pathname.startsWith('/xoa/') || pathname === '/backup/restore')
const plan = getXoaPlan()
return (
<IntlProvider>
<ThemeProvider theme={themes.base}>
@@ -502,6 +513,12 @@ export default class XoApp extends Component {
})}
</div>
)}
{Object.keys(xsa468VulnerableVms).length > 0 && (
<div className='alert alert-danger mb-0'>
IMPORTANT! Some of your VMs are vulnerable.{' '}
<Link to='/home?s=vulnerable%3F'>Please check immediately.</Link>
</div>
)}
<div style={CONTAINER_STYLE}>
<Shortcuts
name='XoApp'