diff --git a/client/admin-plugin-client-plugin.ts b/client/admin-plugin-client-plugin.ts index 9c5c2810..67646642 100644 --- a/client/admin-plugin-client-plugin.ts +++ b/client/admin-plugin-client-plugin.ts @@ -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': diff --git a/languages/settings/en.yml b/languages/settings/en.yml index a6250d50..33ca19f3 100644 --- a/languages/settings/en.yml +++ b/languages/settings/en.yml @@ -170,6 +170,12 @@ prosody_s2s_interfaces_description: |
  • 172.18.0.42
  • +prosody_certificates_dir_label: "Certificates directory" +prosody_certificates_dir_description: | + If this field is empty, the plugin will generate and use self-signed certificates.
    + 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.
    diff --git a/languages/settings/fr.yml b/languages/settings/fr.yml index 672b3d6a..c8f4537b 100644 --- a/languages/settings/fr.yml +++ b/languages/settings/fr.yml @@ -180,6 +180,12 @@ prosody_s2s_interfaces_description: |
  • 172.18.0.42
  • +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.
    + 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.
    diff --git a/server/lib/diagnostic/prosody.ts b/server/lib/diagnostic/prosody.ts index 28039e68..f8c3961b 100644 --- a/server/lib/diagnostic/prosody.ts +++ b/server/lib/diagnostic/prosody.ts @@ -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, { diff --git a/server/lib/prosody/certificates.ts b/server/lib/prosody/certificates.ts index 1855afcc..45e37d5a 100644 --- a/server/lib/prosody/certificates.ts +++ b/server/lib/prosody/certificates.ts @@ -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 { - 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 { - 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 { - 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 { + 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 { + 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 { + 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 { + // 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, diff --git a/server/lib/prosody/config.ts b/server/lib/prosody/config.ts index b9efa12e..01f9d46e 100644 --- a/server/lib/prosody/config.ts +++ b/server/lib/prosody/config.ts @@ -27,7 +27,9 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise { 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 { 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) { diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 3fa65473..735e2eff 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -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'), diff --git a/support/documentation/content/documentation/admin/settings.de.md b/support/documentation/content/documentation/admin/settings.de.md index 07539e27..a4d997b0 100644 --- a/support/documentation/content/documentation/admin/settings.de.md +++ b/support/documentation/content/documentation/admin/settings.de.md @@ -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.
    +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.
    +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.
    +List of IP to listen on, coma separated (spaces will be stripped).
    +You can use «*» to listen on all IPv4 interfaces, and «::» for all IPv6.
    +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.
    +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. diff --git a/support/documentation/content/documentation/admin/settings.en.md b/support/documentation/content/documentation/admin/settings.en.md index 749dfe90..860cd82b 100644 --- a/support/documentation/content/documentation/admin/settings.en.md +++ b/support/documentation/content/documentation/admin/settings.en.md @@ -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.
    +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.