Prosody: Generating self-signed certificates for s2s connections.

This commit is contained in:
John Livingston 2023-04-13 12:41:54 +02:00 committed by John Livingston
parent 38023df6be
commit 772689f9ce
2 changed files with 59 additions and 3 deletions

View File

@ -91,6 +91,7 @@ interface ProsodyConfig {
logByDefault: boolean logByDefault: boolean
logExpiration: ConfigLogExpiration logExpiration: ConfigLogExpiration
valuesToHideInDiagnostic: Map<string, string> valuesToHideInDiagnostic: Map<string, string>
needCerticates: boolean
} }
async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<ProsodyConfig> { async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<ProsodyConfig> {
const logger = options.peertubeHelpers.logger const logger = options.peertubeHelpers.logger
@ -127,6 +128,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
const prosodyDomain = await getProsodyDomain(options) const prosodyDomain = await getProsodyDomain(options)
const paths = await getProsodyFilePaths(options) const paths = await getProsodyFilePaths(options)
const roomType = settings['prosody-room-type'] === 'channel' ? 'channel' : 'video' const roomType = settings['prosody-room-type'] === 'channel' ? 'channel' : 'video'
let needCerticates: boolean = false
const apikey = await getAPIKey(options) const apikey = await getAPIKey(options)
valuesToHideInDiagnostic.set('APIKey', apikey) valuesToHideInDiagnostic.set('APIKey', apikey)
@ -174,6 +176,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
} }
if (enableRoomS2S) { if (enableRoomS2S) {
needCerticates = true
const s2sPort = (settings['prosody-s2s-port'] as string) || '5269' const s2sPort = (settings['prosody-s2s-port'] as string) || '5269'
if (!/^\d+$/.test(s2sPort)) { if (!/^\d+$/.test(s2sPort)) {
throw new Error('Invalid s2s port') throw new Error('Invalid s2s port')
@ -228,7 +231,8 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
roomType, roomType,
logByDefault, logByDefault,
logExpiration, logExpiration,
valuesToHideInDiagnostic valuesToHideInDiagnostic,
needCerticates
} }
} }
@ -309,6 +313,7 @@ function getProsodyConfigContentForDiagnostic (config: ProsodyConfig, content?:
} }
export { export {
ProsodyConfig,
getProsodyConfig, getProsodyConfig,
getWorkingDir, getWorkingDir,
getProsodyFilePaths, getProsodyFilePaths,

View File

@ -1,5 +1,5 @@
import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RegisterServerOptions } from '@peertube/peertube-types'
import { getProsodyConfig, getProsodyFilePaths, writeProsodyConfig } from './config' import { getProsodyConfig, getProsodyFilePaths, writeProsodyConfig, ProsodyConfig } from './config'
import { startProsodyLogRotate, stopProsodyLogRotate } from './logrotate' import { startProsodyLogRotate, stopProsodyLogRotate } from './logrotate'
import { disableProxyRoute, enableProxyRoute } from '../routers/webchat' import { disableProxyRoute, enableProxyRoute } from '../routers/webchat'
import * as fs from 'fs' import * as fs from 'fs'
@ -91,7 +91,14 @@ interface ProsodyCtlResult {
sterr: string sterr: string
message: string message: string
} }
async function prosodyCtl (options: RegisterServerOptions, command: string): Promise<ProsodyCtlResult> { interface ProsodyCtlOptions {
additionalArgs?: string[]
yesMode?: boolean
stdErrFilter?: (data: string) => boolean
}
async function prosodyCtl (
options: RegisterServerOptions, command: string, prosodyCtlOptions?: ProsodyCtlOptions
): Promise<ProsodyCtlResult> {
const logger = options.peertubeHelpers.logger const logger = options.peertubeHelpers.logger
logger.debug('Calling prosodyCtl with command ' + command) logger.debug('Calling prosodyCtl with command ' + command)
@ -113,6 +120,10 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro
filePaths.config, filePaths.config,
command command
] ]
prosodyCtlOptions?.additionalArgs?.forEach(arg => {
// No need to check for code injection, child_process.spawn will escape args correctly.
cmdArgs.push(arg)
})
const spawned = child_process.spawn(filePaths.execCtl, cmdArgs, { const spawned = child_process.spawn(filePaths.execCtl, cmdArgs, {
cwd: filePaths.dir, cwd: filePaths.dir,
env: { env: {
@ -120,11 +131,25 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro
PROSODY_CONFIG: filePaths.config PROSODY_CONFIG: filePaths.config
} }
}) })
let yesModeInterval: NodeJS.Timer
if (prosodyCtlOptions?.yesMode) {
yesModeInterval = setInterval(() => {
options.peertubeHelpers.logger.debug('ProsodyCtl was called in yesMode, writing to standard input.')
spawned.stdin.write('\n')
}, 10)
}
spawned.stdout.on('data', (data) => { spawned.stdout.on('data', (data) => {
d += data as string d += data as string
m += data as string m += data as string
}) })
spawned.stderr.on('data', (data) => { spawned.stderr.on('data', (data) => {
if (prosodyCtlOptions?.stdErrFilter) {
if (!prosodyCtlOptions.stdErrFilter('' + (data as string))) {
return
}
}
options.peertubeHelpers.logger.error(`Spawned command ${command} has errors: ${data as string}`) options.peertubeHelpers.logger.error(`Spawned command ${command} has errors: ${data as string}`)
e += data as string e += data as string
m += data as string m += data as string
@ -134,6 +159,7 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro
// on 'close' and not 'exit', to be sure everything is done // on 'close' and not 'exit', to be sure everything is done
// (else it can cause trouble by cleaning AppImage extract too soon) // (else it can cause trouble by cleaning AppImage extract too soon)
spawned.on('close', (code) => { spawned.on('close', (code) => {
if (yesModeInterval) { clearInterval(yesModeInterval) }
resolve({ resolve({
code: code, code: code,
stdout: d, stdout: d,
@ -261,6 +287,9 @@ async function ensureProsodyRunning (options: RegisterServerOptions): Promise<vo
return return
} }
// Check certicates if needed.
await ensureProsodyCertificates(options, config)
// launch prosody // launch prosody
const execCmd = filePaths.exec + (filePaths.execArgs.length ? ' ' + filePaths.execArgs.join(' ') : '') const execCmd = filePaths.exec + (filePaths.execArgs.length ? ' ' + filePaths.execArgs.join(' ') : '')
logger.info('Going to launch prosody (' + execCmd + ')') logger.info('Going to launch prosody (' + execCmd + ')')
@ -344,6 +373,28 @@ async function ensureProsodyNotRunning (options: RegisterServerOptions): Promise
logger.info(`ProsodyCtl command returned: ${status.message}`) logger.info(`ProsodyCtl command returned: ${status.message}`)
} }
async function ensureProsodyCertificates (options: RegisterServerOptions, config: ProsodyConfig): Promise<void> {
if (!config.needCerticates) { return }
options.peertubeHelpers.logger.info('Prosody needs certicicates, checking if certificates are okay...')
// FIXME: don't generate certicicated everytime, just if it is missing or expired.
const prosodyDomain = config.host
// 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
}
})
}
export { export {
getProsodyAbout, getProsodyAbout,
checkProsody, checkProsody,