mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
feat(xo-server): add content security policy (#10101)
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
- xo-server : Permissions were ignored for some pool functions ([#10093](https://github.com/vatesfr/xen-orchestra/pull/10093))
|
||||
- [Users/Create] Removed default user's password from the logs during account creation (PR [#10094](https://github.com/vatesfr/xen-orchestra/pull/10094))
|
||||
- [Security] Serve a default `Content-Security-Policy` to mitigate cross-site scripting and related attacks. It can be extended or disabled through the `http.helmet` configuration (PR [#10101](https://github.com/vatesfr/xen-orchestra/pull/10101))
|
||||
|
||||
### Enhancements
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ loadBalancerReEnableDelay = '30 minutes'
|
||||
|
||||
# https:#github.com/websockets/ws#websocket-compression
|
||||
[apiWebSocketOptions]
|
||||
perMessageDeflate = { threshold = 524288 } # 512kiB
|
||||
perMessageDeflate = {threshold = 524288}# 512kiB
|
||||
|
||||
[authentication]
|
||||
defaultTokenValidity = '30 days'
|
||||
@@ -145,9 +145,13 @@ useForwardedHeaders = false
|
||||
# Helmet handles HTTP security via headers
|
||||
#
|
||||
# https://helmetjs.github.io/docs/
|
||||
#
|
||||
# A Content-Security-Policy is applied by default (defined in xo-server). Values
|
||||
# set here are merged on top: array values (e.g. CSP directives) extend the
|
||||
# defaults, other values override them.
|
||||
#[http.helmet.hsts]
|
||||
#includeSubDomains = false
|
||||
|
||||
#
|
||||
# Each `http.listen.<name>` entry defines a specific listening configuration for
|
||||
# the HTTP server.
|
||||
#
|
||||
@@ -189,11 +193,11 @@ requestTimeout = 0
|
||||
|
||||
[http.proxies]
|
||||
# [port] is used to reuse the same port declared in [http.listen.0]
|
||||
'/openmetrics' = 'http://localhost:9004'
|
||||
'/v5/api' = '[protocol]//localhost:[port]/api'
|
||||
'/v5/api/updater' = 'ws://localhost:9001'
|
||||
'/v5/rest' = '[protocol]//localhost:[port]/rest'
|
||||
'/v5/netdata' = '[protocol]//localhost:[port]/netdata'
|
||||
'/openmetrics' = 'http://localhost:9004'
|
||||
'/v5/rest' = '[protocol]//localhost:[port]/rest'
|
||||
|
||||
[logs]
|
||||
# Display all logs matching this filter, regardless of their level
|
||||
@@ -229,15 +233,16 @@ timeout = 600e3
|
||||
ignoreVmSnapshotResources = false
|
||||
|
||||
[xapiOptions]
|
||||
|
||||
maxUncoalescedVdis = 1
|
||||
# The duration XO will wait for a host to be live before assuming it failed to
|
||||
# restart
|
||||
restartHostTimeout = '20 minutes'
|
||||
maxUncoalescedVdis = 1
|
||||
vdiDelayBeforeRemovingCloudConfigDrive = '5 min'
|
||||
vdiExportConcurrency = 12
|
||||
vmEvacuationConcurrency = 3
|
||||
vmExportConcurrency = 2
|
||||
vmSnapshotConcurrency = 2
|
||||
vdiDelayBeforeRemovingCloudConfigDrive = '5 min'
|
||||
|
||||
poolMarkingInterval = '6 hours'
|
||||
poolMarkingMaxAge = '48 hours'
|
||||
@@ -265,13 +270,12 @@ vmTag = 'XOA Proxy'
|
||||
xoaUpgradeTimeout = '5 min'
|
||||
|
||||
[rest-api]
|
||||
dashboardCacheTimeout = '1 min'
|
||||
dashboardCacheExpiresIn = '1 min'
|
||||
dashboardCacheTimeout = '1 min'
|
||||
# Allow to set a limit of memory per SSE client
|
||||
# before destroying the client. In MiB
|
||||
maxRamAllocatedPerSseClient = 20
|
||||
|
||||
|
||||
[jsonrpc-api]
|
||||
xvaImportFromUrlTimeout = '6s'
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
"dev": "cross-env NODE_ENV=development yarn run _build --watch",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "node dist/cli.mjs",
|
||||
"start:dev": "cross-env NODE_ENV=development node dist/cli.mjs",
|
||||
"test": "cd dist && node --test"
|
||||
},
|
||||
"author": {
|
||||
|
||||
@@ -116,6 +116,7 @@ html
|
||||
div
|
||||
button.btn.btn-block
|
||||
| Sign in with password
|
||||
//- Note: if this script changes, make sure to update the hash in xo-server/src/index.mjs to match the new content.
|
||||
script.
|
||||
(function () {
|
||||
var d = document
|
||||
|
||||
@@ -11,6 +11,7 @@ import httpProxy from 'http-proxy'
|
||||
import includes from 'lodash/includes.js'
|
||||
import memoryStoreFactory from 'memorystore'
|
||||
import merge from 'lodash/merge.js'
|
||||
import mergeWith from 'lodash/mergeWith.js'
|
||||
import ms from 'ms'
|
||||
import once from 'lodash/once.js'
|
||||
import proxyAddr from 'proxy-addr'
|
||||
@@ -62,7 +63,6 @@ const [APP_NAME, APP_VERSION] = (() => {
|
||||
const { name, version } = JSON.parse(fse.readFileSync(new URL('../package.json', import.meta.url)))
|
||||
return [name, version]
|
||||
})()
|
||||
|
||||
// ===================================================================
|
||||
|
||||
configure([
|
||||
@@ -146,6 +146,21 @@ async function updateLocalConfig(diff) {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const DEFAULT_HELMET_CONFIG = {
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
'default-src': ["'self'"],
|
||||
// Hashes of XO's own inline scripts. If either changes, update its hash.
|
||||
'script-src': [
|
||||
"'self'",
|
||||
"'sha256-sIj1FFjxCJxsQo5Hw5ZkhT+9Gc+Q6LmIkJzdjARCn0o='", // xo-web/src/index.pug
|
||||
"'sha256-Z5hWOtGcISU7nkyObsPm3ZZvPpAYxzoiQutkJucVkm8='", // xo-server/signin.pug
|
||||
],
|
||||
'style-src': ["'self'", "'unsafe-inline'"],
|
||||
'img-src': ["'self'", 'data:'],
|
||||
},
|
||||
},
|
||||
}
|
||||
async function createExpressApp(config) {
|
||||
const app = createExpress()
|
||||
|
||||
@@ -153,8 +168,11 @@ async function createExpressApp(config) {
|
||||
//
|
||||
// https://expressjs.com/en/api.html#app.set
|
||||
app.set('json spaces', 2)
|
||||
|
||||
app.use(helmet(config.http.helmet))
|
||||
const isDev = (process.env.NODE_ENV ?? '').trim().toLowerCase() === 'development'
|
||||
const helmetConfig = mergeWith({}, isDev ? {} : DEFAULT_HELMET_CONFIG, config.http.helmet, (dst, src) =>
|
||||
Array.isArray(dst) ? dst.concat(src) : undefined
|
||||
)
|
||||
app.use(helmet(helmetConfig))
|
||||
|
||||
app.use(compression())
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ html.no-js(
|
||||
|
||||
//- .visible-js to display content only when JavaScript is ENABLED.
|
||||
//- .hidden-js to display content only when JavaScript is DISABLED.
|
||||
//- Note: if this line changes, make sure to update the hash in xo-server/src/index.mjs to match the new content.
|
||||
script !function(d){d.className=d.className.replace(/\bno-js\b/,'js')}(document.documentElement)
|
||||
|
||||
style .no-js .visible-js,.js .hidden-js{display:none}
|
||||
|
||||
Reference in New Issue
Block a user