diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b43178..80fbf3fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 8.0.2 + +### Minor changes and fixes + +* On some Ubuntu server, the self-signed certificates generation fails: + * See [issue #268](https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/268) + * This prevents the bot to connect to the server + * As a fallback, we directly call openssl to generate the certificates + ## 8.0.1 ### Minor changes and fixes diff --git a/package-lock.json b/package-lock.json index 59981338..b52aa4cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "peertube-plugin-livechat", - "version": "8.0.1", + "version": "8.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "peertube-plugin-livechat", - "version": "8.0.1", + "version": "8.0.2", "license": "AGPL-3.0", "dependencies": { "async": "^3.2.2", diff --git a/package.json b/package.json index 42929010..14308da2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "peertube-plugin-livechat", "description": "PeerTube plugin livechat: create chat rooms for your Peertube lives!", - "version": "8.0.1", + "version": "8.0.2", "license": "AGPL-3.0", "author": { "name": "John Livingston", diff --git a/server/lib/debug.ts b/server/lib/debug.ts index 5bfbe83d..1d9da72c 100644 --- a/server/lib/debug.ts +++ b/server/lib/debug.ts @@ -11,6 +11,7 @@ interface ProsodyDebuggerOptions { interface DebugContent { renewCertCheckInterval?: number renewSelfSignedCertInterval?: number + useOpenSSL?: boolean logRotateCheckInterval?: number logRotateEvery?: number remoteServerInfosMaxAge?: number @@ -25,7 +26,7 @@ type DebugNumericValue = 'renewCertCheckInterval' | 'logRotateCheckInterval' | 'remoteServerInfosMaxAge' -type DebugBooleanValue = 'alwaysPublishXMPPRoom' | 'enablePodcastChatTagForNonLive' +type DebugBooleanValue = 'alwaysPublishXMPPRoom' | 'enablePodcastChatTagForNonLive' | 'useOpenSSL' let debugContent: DebugContent | null | false = null function _readDebugFile (options: RegisterServerOptions): DebugContent | false { @@ -58,6 +59,7 @@ function _readDebugFile (options: RegisterServerOptions): DebugContent | false { debugContent.logRotateEvery = _getNumericOptions(options, json, 'log_rotate_every') debugContent.renewCertCheckInterval = _getNumericOptions(options, json, 'renew_cert_check_interval') debugContent.renewSelfSignedCertInterval = _getNumericOptions(options, json, 'renew_self_signed_cert_interval') + debugContent.useOpenSSL = json.use_openssl === true debugContent.remoteServerInfosMaxAge = _getNumericOptions(options, json, 'remote_server_infos_max_age') debugContent.alwaysPublishXMPPRoom = json.always_publish_xmpp_room === true debugContent.enablePodcastChatTagForNonLive = json.enable_podcast_chat_tag_for_nonlive === true diff --git a/server/lib/prosody/certificates.ts b/server/lib/prosody/certificates.ts index 0b3b8628..59d31ba3 100644 --- a/server/lib/prosody/certificates.ts +++ b/server/lib/prosody/certificates.ts @@ -1,9 +1,10 @@ import type { RegisterServerOptions } from '@peertube/peertube-types' import type { ProsodyConfig } from './config' -import { debugNumericParameter } from '../debug' +import { debugNumericParameter, isDebugMode } from '../debug' import { prosodyCtl, reloadProsody } from './ctl' import * as path from 'path' import * as fs from 'fs' +import * as child_process from 'child_process' interface Renew { timer: NodeJS.Timer @@ -61,18 +62,28 @@ async function ensureProsodyCertificates (options: RegisterServerOptions, config } // 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 - } - }) + if (!isDebugMode(options, 'useOpenSSL')) { + 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 + } + }) + } + + // Note: it seems that on Ubuntu, "prosocyctl cert generate" won't work. + // See https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/268 + // So, if the file still does not exist, we will fallback to openssl: + if (!fs.existsSync(filepath)) { + logger.warn(`The certificate ${filepath} creation (using prosodyctl) failed, trying to use openssl`) + await _generateSelfSignedUsingOpenSSL(options, path.dirname(filepath), prosodyDomain) + } } async function renewCheck (options: RegisterServerOptions, config: ProsodyConfig): Promise { @@ -159,6 +170,63 @@ function _filePathToTest (options: RegisterServerOptions, config: ProsodyConfig) return path.resolve(config.paths.certs, config.host + '.crt') } +async function _generateSelfSignedUsingOpenSSL ( + options: RegisterServerOptions, + dir: string, + prosodyDomain: string +): Promise { + const logger = options.peertubeHelpers.logger + logger.info('Calling openssl to generate a self-signed certificate.') + + return new Promise((resolve, reject) => { + // Here we want to launch something like: + // eslint-disable-next-line max-len + // openssl req -new -x509 -newkey rsa:2048 -nodes -keyout prosodyDomain.key -days 365 -sha256 -out prosodyDomain.crt -utf8 -subj /CN=prosodyDomain + const cmdArgs = [ + 'req', + '-new', + '-x509', + '-newkey', + 'rsa:2048', + '-nodes', + '-keyout', + path.resolve(dir, `${prosodyDomain}.key`), + '-days', + '365', + '-sha256', + '-out', + path.resolve(dir, `${prosodyDomain}.crt`), + '-utf8', + '-subj', + `/CN=${prosodyDomain}` + ] + const spawned = child_process.spawn('openssl', cmdArgs, { + cwd: dir, + env: { + ...process.env + } + }) + + // spawned.stdout.on('data', (data) => { + // logger.debug(`OpenSSL output: ${data as string}`) + // }) + // spawned.stderr.on('data', (data) => { + // // Note: openssl writes on stderr... not loging anything. + // // logger.error(`Spawned openssl command has errors: ${data as string}`) + // }) + spawned.on('error', (err) => { + logger.error(`Spawned openssl command failed: ${err as unknown as string}`) + reject(err) + }) + + // on 'close' and not 'exit', to be sure everything is done + // (else it can cause trouble by cleaning AppImage extract too soon) + spawned.on('close', () => { + resolve() + }) + }) +} + export { ensureProsodyCertificates, startProsodyCertificatesRenewCheck,