Prosody: new settings to provide a custom certificates dir
This commit is contained in:
parent
a87a622cba
commit
801798852c
@ -205,6 +205,7 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
|
||||
return options.formValues['prosody-c2s'] !== true
|
||||
case 'prosody-s2s-port':
|
||||
case 'prosody-s2s-interfaces':
|
||||
case 'prosody-certificates-dir':
|
||||
return options.formValues['prosody-room-allow-s2s'] !== true
|
||||
case 'prosody-components-port':
|
||||
case 'prosody-components-list':
|
||||
|
@ -170,6 +170,12 @@ prosody_s2s_interfaces_description: |
|
||||
<li>172.18.0.42</li>
|
||||
</ul>
|
||||
|
||||
prosody_certificates_dir_label: "Certificates directory"
|
||||
prosody_certificates_dir_description: |
|
||||
If this field is empty, the plugin will generate and use self-signed certificates.<br>
|
||||
If you want to use other certificates, just specify here the folder where
|
||||
Prosody can find them. Note: the `peertube` user must have read access to this directory.
|
||||
|
||||
prosody_c2s_label: "Enable client to server connections"
|
||||
prosody_c2s_description: |
|
||||
Enable XMPP clients to connect to the built-in Prosody server.<br>
|
||||
|
@ -180,6 +180,12 @@ prosody_s2s_interfaces_description: |
|
||||
<li>172.18.0.42</li>
|
||||
</ul>
|
||||
|
||||
prosody_certificates_dir_label: "Dossiers des certificats"
|
||||
prosody_certificates_dir_description: |
|
||||
Si ce champ est vide, le plugin va générer et utiliser des certificats auto-signés.<br>
|
||||
Si vous voulez utiliser d'autres certificats, vous avez juste à spécifier ici le dossier où
|
||||
Prosody peut les trouver. Note: l'utilisateur `peertube` doit avoir un accès en lecture à ce dossier.
|
||||
|
||||
prosody_c2s_label: "Activer les connexions client vers serveur"
|
||||
prosody_c2s_description: |
|
||||
Autoriser les clients XMPP à se connecter au serveur Prosody.<br>
|
||||
|
@ -80,6 +80,14 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
||||
}
|
||||
result.messages.push(`Room content will be saved for '${wantedConfig.logExpiration.value}'`)
|
||||
|
||||
if (wantedConfig.paths.certs === undefined) {
|
||||
result.messages.push({
|
||||
level: 'error',
|
||||
message: 'Error: The certificates path is misconfigured.'
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
|
||||
result.messages.push(`The prosody configuration file (${filePath}) exists`)
|
||||
const actualContent = await fs.promises.readFile(filePath, {
|
||||
|
@ -7,71 +7,10 @@ import * as fs from 'fs'
|
||||
|
||||
interface Renew {
|
||||
timer: NodeJS.Timer
|
||||
lastFromDirMtime?: number
|
||||
}
|
||||
let renew: Renew | undefined
|
||||
|
||||
function _filePathToTest (options: RegisterServerOptions, config: ProsodyConfig): string {
|
||||
return path.resolve(config.paths.certs, config.host + '.crt')
|
||||
}
|
||||
|
||||
async function ensureProsodyCertificates (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
if (config.certificates !== 'generate-self-signed') { return }
|
||||
const logger = options.peertubeHelpers.logger
|
||||
logger.info('Prosody needs certicicates, checking if certificates are okay...')
|
||||
|
||||
const prosodyDomain = config.host
|
||||
const filepath = _filePathToTest(options, config)
|
||||
if (fs.existsSync(filepath)) {
|
||||
logger.info(`The certificate ${filepath} exists, no need to generate it`)
|
||||
return
|
||||
}
|
||||
|
||||
// Using: prososyctl --config /.../prosody.cfg.lua cert generate prosodyDomain.tld
|
||||
await prosodyCtl(options, 'cert', {
|
||||
additionalArgs: ['generate', prosodyDomain],
|
||||
yesMode: true,
|
||||
stdErrFilter: (data) => {
|
||||
// For an unknow reason, `prosodyctl cert generate` outputs openssl data on stderr...
|
||||
// So we filter these logs.
|
||||
if (data.match(/Generating \w+ private key/)) { return false }
|
||||
if (data.match(/^[.+o*\n]*$/m)) { return false }
|
||||
if (data.match(/e is \d+/)) { return false }
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function renewCheckSelfSigned (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
// We have to check if the self signed certificate is still valid.
|
||||
// Prosodyctl generated certificates are valid 365 days.
|
||||
// We will renew it every 10 months (and every X minutes in debug mode)
|
||||
|
||||
const renewEvery = isDebugMode(options) ? 5 * 60000 : 3600000 * 24 * 30 * 10
|
||||
// getting the file date...
|
||||
const filepath = _filePathToTest(options, config)
|
||||
if (!fs.existsSync(filepath)) {
|
||||
logger.error('Missing certificate file: ' + filepath)
|
||||
return
|
||||
}
|
||||
const stat = fs.statSync(filepath)
|
||||
const age = (new Date()).getTime() - stat.mtimeMs
|
||||
if (age <= renewEvery) {
|
||||
logger.debug(`The age of the certificate ${filepath} is ${age}ms, which is less than the period ${renewEvery}ms`)
|
||||
return
|
||||
}
|
||||
logger.info(`The age of the certificate ${filepath} is ${age}ms, which is more than the period ${renewEvery}ms`)
|
||||
await ensureProsodyCertificates(options, config)
|
||||
await reloadProsody(options)
|
||||
}
|
||||
|
||||
async function renewCheck (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
if (config.certificates === 'generate-self-signed') {
|
||||
return renewCheckSelfSigned(options, config)
|
||||
}
|
||||
throw new Error('Unknown value for config.certificates')
|
||||
}
|
||||
|
||||
function startProsodyCertificatesRenewCheck (options: RegisterServerOptions, config: ProsodyConfig): void {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
|
||||
@ -109,6 +48,105 @@ function stopProsodyCertificatesRenewCheck (options: RegisterServerOptions): voi
|
||||
clearInterval(renew.timer)
|
||||
}
|
||||
|
||||
async function ensureProsodyCertificates (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
if (config.certificates !== 'generate-self-signed') { return }
|
||||
const logger = options.peertubeHelpers.logger
|
||||
logger.info('Prosody needs certificates, checking if certificates are okay...')
|
||||
|
||||
const prosodyDomain = config.host
|
||||
const filepath = _filePathToTest(options, config)
|
||||
if (!filepath) { return }
|
||||
if (fs.existsSync(filepath)) {
|
||||
logger.info(`The certificate ${filepath} exists, no need to generate it`)
|
||||
return
|
||||
}
|
||||
|
||||
// Using: prososyctl --config /.../prosody.cfg.lua cert generate prosodyDomain.tld
|
||||
await prosodyCtl(options, 'cert', {
|
||||
additionalArgs: ['generate', prosodyDomain],
|
||||
yesMode: true,
|
||||
stdErrFilter: (data) => {
|
||||
// For an unknow reason, `prosodyctl cert generate` outputs openssl data on stderr...
|
||||
// So we filter these logs.
|
||||
if (data.match(/Generating \w+ private key/)) { return false }
|
||||
if (data.match(/^[.+o*\n]*$/m)) { return false }
|
||||
if (data.match(/e is \d+/)) { return false }
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function renewCheck (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
if (config.certificates === 'generate-self-signed') {
|
||||
return renewCheckSelfSigned(options, config)
|
||||
}
|
||||
if (config.certificates === 'use-from-dir') {
|
||||
return renewCheckFromDir(options, config)
|
||||
}
|
||||
throw new Error('Unknown value for config.certificates')
|
||||
}
|
||||
|
||||
async function renewCheckSelfSigned (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
// We have to check if the self signed certificate is still valid.
|
||||
// Prosodyctl generated certificates are valid 365 days.
|
||||
// We will renew it every 10 months (and every X minutes in debug mode)
|
||||
|
||||
const renewEvery = isDebugMode(options) ? 5 * 60000 : 3600000 * 24 * 30 * 10
|
||||
// getting the file date...
|
||||
const filepath = _filePathToTest(options, config)
|
||||
if (!filepath) { return }
|
||||
if (!fs.existsSync(filepath)) {
|
||||
logger.error('Missing certificate file: ' + filepath)
|
||||
return
|
||||
}
|
||||
const stat = fs.statSync(filepath)
|
||||
const age = (new Date()).getTime() - stat.mtimeMs
|
||||
if (age <= renewEvery) {
|
||||
logger.debug(`The age of the certificate ${filepath} is ${age}ms, which is less than the period ${renewEvery}ms`)
|
||||
return
|
||||
}
|
||||
logger.info(`The age of the certificate ${filepath} is ${age}ms, which is more than the period ${renewEvery}ms`)
|
||||
await ensureProsodyCertificates(options, config)
|
||||
await reloadProsody(options)
|
||||
}
|
||||
|
||||
async function renewCheckFromDir (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
|
||||
// We will browse all dir files, get the more recent file update time, and compare it to the previous call.
|
||||
const logger = options.peertubeHelpers.logger
|
||||
if (!renew) { return }
|
||||
let mtimeMs: number | undefined
|
||||
const dir = config.paths.certs
|
||||
if (!dir) { return }
|
||||
const files = fs.readdirSync(dir, { withFileTypes: true })
|
||||
files.forEach(file => {
|
||||
if (!file.isFile()) { return }
|
||||
const stat = fs.statSync(path.resolve(dir, file.name))
|
||||
if (stat.mtimeMs > (mtimeMs ?? 0)) {
|
||||
mtimeMs = stat.mtimeMs
|
||||
}
|
||||
})
|
||||
logger.debug('renewCheckFromDir: the most recent file in the certs dir has mtimeMs=' + (mtimeMs ?? '').toString())
|
||||
if (!mtimeMs) {
|
||||
return
|
||||
}
|
||||
if (!renew.lastFromDirMtime) {
|
||||
renew.lastFromDirMtime = mtimeMs
|
||||
return
|
||||
}
|
||||
if (renew.lastFromDirMtime === mtimeMs) {
|
||||
logger.debug('No change in certs modification dates.')
|
||||
return
|
||||
}
|
||||
logger.info('There is a file that was modified in the certs dir, reloading prosody...')
|
||||
await reloadProsody(options)
|
||||
}
|
||||
|
||||
function _filePathToTest (options: RegisterServerOptions, config: ProsodyConfig): string | null {
|
||||
if (!config.paths.certs) { return null }
|
||||
return path.resolve(config.paths.certs, config.host + '.crt')
|
||||
}
|
||||
|
||||
export {
|
||||
ensureProsodyCertificates,
|
||||
startProsodyCertificatesRenewCheck,
|
||||
|
@ -27,7 +27,9 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
||||
logger.debug('Calling getProsodyFilePaths')
|
||||
|
||||
const dir = await getWorkingDir(options)
|
||||
const settings = await options.settingsManager.getSettings(['use-system-prosody', 'prosody-room-allow-s2s'])
|
||||
const settings = await options.settingsManager.getSettings([
|
||||
'use-system-prosody', 'prosody-room-allow-s2s', 'prosody-certificates-dir'
|
||||
])
|
||||
let exec
|
||||
let execArgs: string[] = []
|
||||
let execCtl
|
||||
@ -60,8 +62,20 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
||||
}
|
||||
}
|
||||
|
||||
let certsDir = path.resolve(dir, 'certs')
|
||||
if (settings['prosody-room-allow-s2s']) {
|
||||
let certsDir: string | undefined = path.resolve(dir, 'certs')
|
||||
let certsDirIsCustom = false
|
||||
if ((settings['prosody-certificates-dir'] as string ?? '') !== '') {
|
||||
if (!fs.statSync(settings['prosody-certificates-dir'] as string).isDirectory()) {
|
||||
// We can throw an exception here...
|
||||
// Because if the user input a wrong directory, the plugin will not register,
|
||||
// and he will never be able to fix the conf
|
||||
logger.error('Certificate directory does not exist or is not a directory')
|
||||
certsDir = undefined
|
||||
} else {
|
||||
certsDir = settings['prosody-certificates-dir'] as string
|
||||
}
|
||||
certsDirIsCustom = true
|
||||
} else if (settings['prosody-room-allow-s2s']) {
|
||||
// Note: when using prosodyctl to generate self-signed certificates,
|
||||
// there are wrongly generated in the data dir.
|
||||
// So we will use this dir as the certs dir.
|
||||
@ -76,6 +90,7 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
||||
config: path.resolve(dir, 'prosody.cfg.lua'),
|
||||
data: path.resolve(dir, 'data'),
|
||||
certs: certsDir,
|
||||
certsDirIsCustom,
|
||||
modules: path.resolve(__dirname, '../../prosody-modules'),
|
||||
avatars: path.resolve(__dirname, '../../avatars'),
|
||||
exec,
|
||||
@ -87,7 +102,7 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
||||
}
|
||||
}
|
||||
|
||||
type ProsodyConfigCertificates = false | 'generate-self-signed'
|
||||
type ProsodyConfigCertificates = false | 'generate-self-signed' | 'use-from-dir'
|
||||
|
||||
interface ProsodyConfig {
|
||||
content: string
|
||||
@ -114,6 +129,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
'prosody-room-allow-s2s',
|
||||
'prosody-s2s-port',
|
||||
'prosody-s2s-interfaces',
|
||||
'prosody-certificates-dir',
|
||||
'prosody-room-type',
|
||||
'prosody-peertube-uri',
|
||||
'prosody-components',
|
||||
@ -185,6 +201,9 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
|
||||
if (enableRoomS2S) {
|
||||
certificates = 'generate-self-signed'
|
||||
if (config.paths.certsDirIsCustom) {
|
||||
certificates = 'use-from-dir'
|
||||
}
|
||||
const s2sPort = (settings['prosody-s2s-port'] as string) || '5269'
|
||||
if (!/^\d+$/.test(s2sPort)) {
|
||||
throw new Error('Invalid s2s port')
|
||||
|
@ -173,7 +173,9 @@ class ProsodyConfigContent {
|
||||
this.global.set('consider_bosh_secure', false)
|
||||
// this.global.set('cross_domain_websocket', false) No more needed with Prosody 0.12
|
||||
this.global.set('consider_websocket_secure', false)
|
||||
if (this.paths.certs) {
|
||||
this.global.set('certificates', this.paths.certs)
|
||||
}
|
||||
|
||||
this.muc.set('muc_room_locking', false)
|
||||
this.muc.set('muc_tombstones', false)
|
||||
|
@ -5,7 +5,8 @@ interface ProsodyFilePaths {
|
||||
log: string
|
||||
config: string
|
||||
data: string
|
||||
certs: string
|
||||
certs?: string
|
||||
certsDirIsCustom: boolean
|
||||
modules: string
|
||||
avatars: string
|
||||
exec?: string
|
||||
|
@ -12,7 +12,8 @@ async function _ensureWorkingDir (
|
||||
options: RegisterServerOptions,
|
||||
workingDir: string,
|
||||
dataDir: string,
|
||||
certsDir: string,
|
||||
certsDir: string | undefined,
|
||||
certsDirIsCustom: boolean,
|
||||
appImageExtractPath: string
|
||||
): Promise<string> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
@ -33,7 +34,7 @@ async function _ensureWorkingDir (
|
||||
logger.debug(`data dir ${dataDir} was created`)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(certsDir)) {
|
||||
if (certsDir && !certsDirIsCustom && !fs.existsSync(certsDir)) {
|
||||
// Certificates dir for Prosody.
|
||||
// Note: not used yet, but we create the directory to avoid errors in prosody logs.
|
||||
logger.info(`The certs dir ${certsDir} does not exists, trying to create it`)
|
||||
@ -61,7 +62,14 @@ async function prepareProsody (options: RegisterServerOptions): Promise<void> {
|
||||
const filePaths = await getProsodyFilePaths(options)
|
||||
|
||||
logger.debug('Ensuring that the working dir exists')
|
||||
await _ensureWorkingDir(options, filePaths.dir, filePaths.data, filePaths.certs, filePaths.appImageExtractPath)
|
||||
await _ensureWorkingDir(
|
||||
options,
|
||||
filePaths.dir,
|
||||
filePaths.data,
|
||||
filePaths.certs,
|
||||
filePaths.certsDirIsCustom,
|
||||
filePaths.appImageExtractPath
|
||||
)
|
||||
|
||||
const appImageToExtract = filePaths.appImageToExtract
|
||||
if (!appImageToExtract) {
|
||||
|
@ -329,6 +329,15 @@ Please read
|
||||
descriptionHTML: loc('prosody_s2s_interfaces_description')
|
||||
})
|
||||
|
||||
registerSetting({
|
||||
name: 'prosody-certificates-dir',
|
||||
label: loc('prosody_certificates_dir_label'),
|
||||
type: 'input',
|
||||
default: '',
|
||||
private: true,
|
||||
descriptionHTML: loc('prosody_certificates_dir_description')
|
||||
})
|
||||
|
||||
registerSetting({
|
||||
name: 'prosody-c2s',
|
||||
label: loc('prosody_c2s_label'),
|
||||
|
@ -139,6 +139,36 @@ indem Sie seine Eigenschaften bearbeiten.
|
||||
Hier können Sie die Ablaufzeit für Raumprotokolle einstellen.
|
||||
Siehe die Online-Hilfe für akzeptierte Werte.
|
||||
|
||||
### Enable connection to room using external XMPP accounts
|
||||
|
||||
By enabling this option, it will be possible to connect to rooms using external XMPP accounts and XMPP clients.<br>
|
||||
Warning, enabling this option can request extra server and DNS configuration.
|
||||
Please refer to the documentation: [Enable external XMPP account connections](/peertube-plugin-livechat/documentation/admin/advanced/xmpp_clients/).
|
||||
|
||||
### Prosody server to server port
|
||||
|
||||
The port that will be used for XMPP s2s (server to server) connections.<br>
|
||||
You should use the standard 5269 port.
|
||||
Otherwise you should [setup a specific DNS record](https://prosody.im/doc/s2s).
|
||||
|
||||
### Server to server network interfaces
|
||||
|
||||
The network interfaces to listen on for server to server connections.<br>
|
||||
List of IP to listen on, coma separated (spaces will be stripped).<br>
|
||||
You can use «*» to listen on all IPv4 interfaces, and «::» for all IPv6.<br>
|
||||
Examples:
|
||||
|
||||
- `*, ::`
|
||||
- `*`
|
||||
- `127.0.0.1, ::1`
|
||||
- `172.18.0.42`
|
||||
|
||||
### Certificates directory
|
||||
|
||||
If this field is empty, the plugin will generate and use self-signed certificates.<br>
|
||||
If you want to use other certificates, just specify here the folder where
|
||||
Prosody can find them. Note: the `peertube` user must have read access to this directory.
|
||||
|
||||
### Enable client to server connections (Aktivieren von Client-Server-Verbindungen)
|
||||
|
||||
Diese Einstellung ermöglicht es XMPP-Clients, sich mit dem eingebauten Prosody-Server zu verbinden.
|
||||
|
@ -159,6 +159,12 @@ Examples:
|
||||
- `127.0.0.1, ::1`
|
||||
- `172.18.0.42`
|
||||
|
||||
### Certificates directory
|
||||
|
||||
If this field is empty, the plugin will generate and use self-signed certificates.<br>
|
||||
If you want to use other certificates, just specify here the folder where
|
||||
Prosody can find them. Note: the `peertube` user must have read access to this directory.
|
||||
|
||||
### Enable client to server connections
|
||||
|
||||
This setting enable XMPP clients to connect to the built-in Prosody server.
|
||||
|
Loading…
x
Reference in New Issue
Block a user