2019-09-26 02:13:20 +00:00
|
|
|
/*
|
2021-10-07 12:42:08 +00:00
|
|
|
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
|
2019-09-26 02:13:20 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-08-16 14:51:18 +00:00
|
|
|
import * as fs from "fs";
|
|
|
|
import { load } from "js-yaml";
|
2022-12-06 17:17:40 +00:00
|
|
|
import { MatrixClient, LogService } from "matrix-bot-sdk";
|
2022-11-23 10:55:22 +00:00
|
|
|
import Config from "config";
|
2019-09-26 02:13:20 +00:00
|
|
|
|
2023-01-05 07:37:54 +00:00
|
|
|
export interface IHealthConfig {
|
|
|
|
health?: {
|
|
|
|
// If specified, attempt to upload any crash statistics to sentry.
|
|
|
|
sentry?: {
|
|
|
|
dsn: string;
|
|
|
|
|
|
|
|
// Frequency of performance monitoring.
|
|
|
|
//
|
|
|
|
// A number in [0.0, 1.0], where 0.0 means "don't bother with tracing"
|
|
|
|
// and 1.0 means "trace performance at every opportunity".
|
|
|
|
tracesSampleRate: number;
|
|
|
|
};
|
|
|
|
openMetrics?: {
|
|
|
|
/**
|
|
|
|
* If `true`, expose a web server for server metrics, e.g. performance.
|
|
|
|
*
|
|
|
|
* Intended to be used with Prometheus or another Open Metrics scrapper.
|
|
|
|
*/
|
|
|
|
enabled: boolean;
|
|
|
|
/**
|
|
|
|
* The port on which to expose server metrics.
|
|
|
|
*/
|
|
|
|
port: number;
|
|
|
|
/**
|
|
|
|
* The path at which to collect health metrics.
|
|
|
|
*
|
|
|
|
* If unspecified, use `"/metrics"`.
|
|
|
|
*/
|
|
|
|
endpoint: string;
|
|
|
|
/**
|
|
|
|
* If specified, only serve this address mask.
|
|
|
|
*
|
|
|
|
* If unspecified, use 0.0.0.0 (accessible by any host).
|
|
|
|
*/
|
|
|
|
address: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-07 12:42:08 +00:00
|
|
|
/**
|
|
|
|
* The configuration, as read from production.yaml
|
|
|
|
*
|
|
|
|
* See file default.yaml for the documentation on individual options.
|
|
|
|
*/
|
|
|
|
// The object is magically generated by external lib `config`
|
|
|
|
// from the file specified by `NODE_ENV`, e.g. production.yaml
|
|
|
|
// or harness.yaml.
|
2022-08-09 10:29:27 +00:00
|
|
|
export interface IConfig {
|
2019-09-26 02:13:20 +00:00
|
|
|
homeserverUrl: string;
|
2021-10-07 12:42:08 +00:00
|
|
|
rawHomeserverUrl: string;
|
2019-09-26 02:13:20 +00:00
|
|
|
accessToken: string;
|
2019-10-03 04:20:37 +00:00
|
|
|
pantalaimon: {
|
|
|
|
use: boolean;
|
|
|
|
username: string;
|
|
|
|
password: string;
|
|
|
|
};
|
2019-09-26 02:13:20 +00:00
|
|
|
dataPath: string;
|
2022-12-07 17:00:05 +00:00
|
|
|
/**
|
|
|
|
* If true, Mjolnir will only accept invites from users present in managementRoom.
|
|
|
|
* Otherwise a space must be provided to `acceptInvitesFromSpace`.
|
|
|
|
*/
|
2020-01-21 20:53:02 +00:00
|
|
|
autojoinOnlyIfManager: boolean;
|
2022-12-07 17:00:05 +00:00
|
|
|
/** Mjolnir will accept invites from members of this space if `autojoinOnlyIfManager` is false. */
|
|
|
|
acceptInvitesFromSpace: string;
|
2020-03-05 22:38:09 +00:00
|
|
|
recordIgnoredInvites: boolean;
|
2019-09-27 19:57:36 +00:00
|
|
|
managementRoom: string;
|
2019-10-05 02:59:30 +00:00
|
|
|
verboseLogging: boolean;
|
2019-10-31 15:55:34 +00:00
|
|
|
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
|
2019-10-05 03:02:37 +00:00
|
|
|
syncOnStartup: boolean;
|
2019-10-05 03:22:34 +00:00
|
|
|
verifyPermissionsOnStartup: boolean;
|
2019-10-09 13:51:30 +00:00
|
|
|
noop: boolean;
|
2019-09-27 20:36:23 +00:00
|
|
|
protectedRooms: string[]; // matrix.to urls
|
2019-11-07 01:54:59 +00:00
|
|
|
fasterMembershipChecks: boolean;
|
2019-12-10 02:56:12 +00:00
|
|
|
automaticallyRedactForReasons: string[]; // case-insensitive globs
|
2020-01-21 22:19:03 +00:00
|
|
|
protectAllJoinedRooms: boolean;
|
2022-06-08 09:49:43 +00:00
|
|
|
/**
|
|
|
|
* Backgrounded tasks: number of milliseconds to wait between the completion
|
|
|
|
* of one background task and the start of the next one.
|
|
|
|
*/
|
|
|
|
backgroundDelayMS: number;
|
2022-07-04 14:06:36 +00:00
|
|
|
pollReports: boolean;
|
2022-07-07 11:55:03 +00:00
|
|
|
/**
|
|
|
|
* Whether or not new reports, received either by webapi or polling,
|
|
|
|
* should be printed to our managementRoom.
|
|
|
|
*/
|
|
|
|
displayReports: boolean;
|
2022-03-07 09:14:06 +00:00
|
|
|
admin?: {
|
|
|
|
enableMakeRoomAdminCommand?: boolean;
|
|
|
|
}
|
2020-02-12 22:27:27 +00:00
|
|
|
commands: {
|
|
|
|
allowNoPrefix: boolean;
|
|
|
|
additionalPrefixes: string[];
|
2021-02-05 16:04:06 +00:00
|
|
|
confirmWildcardBan: boolean;
|
2020-02-12 22:27:27 +00:00
|
|
|
};
|
2020-06-21 19:18:34 +00:00
|
|
|
protections: {
|
|
|
|
wordlist: {
|
|
|
|
words: string[];
|
|
|
|
minutesBeforeTrusting: number;
|
|
|
|
};
|
|
|
|
};
|
2020-06-12 14:03:08 +00:00
|
|
|
health: {
|
|
|
|
healthz: {
|
|
|
|
enabled: boolean;
|
|
|
|
port: number;
|
|
|
|
address: string;
|
|
|
|
endpoint: string;
|
|
|
|
healthyStatus: number;
|
|
|
|
unhealthyStatus: number;
|
|
|
|
};
|
2022-11-30 15:06:02 +00:00
|
|
|
// If specified, attempt to upload any crash statistics to sentry.
|
|
|
|
sentry?: {
|
|
|
|
dsn: string;
|
|
|
|
|
|
|
|
// Frequency of performance monitoring.
|
|
|
|
//
|
|
|
|
// A number in [0.0, 1.0], where 0.0 means "don't bother with tracing"
|
|
|
|
// and 1.0 means "trace performance at every opportunity".
|
|
|
|
tracesSampleRate: number;
|
|
|
|
};
|
2023-01-05 07:37:54 +00:00
|
|
|
openMetrics?: {
|
|
|
|
/**
|
|
|
|
* If `true`, expose a web server for server metrics, e.g. performance.
|
|
|
|
*
|
|
|
|
* Intended to be used with Prometheus or another Open Metrics scrapper.
|
|
|
|
*/
|
|
|
|
enabled: boolean;
|
|
|
|
/**
|
|
|
|
* The port on which to expose server metrics.
|
|
|
|
*/
|
|
|
|
port: number;
|
|
|
|
/**
|
|
|
|
* The path at which to collect health metrics.
|
|
|
|
*
|
|
|
|
* If unspecified, use `"/metrics"`.
|
|
|
|
*/
|
|
|
|
endpoint: string;
|
|
|
|
/**
|
|
|
|
* If specified, only serve this address mask.
|
|
|
|
*
|
|
|
|
* If unspecified, use 0.0.0.0 (accessible by any host).
|
|
|
|
*/
|
|
|
|
address: string;
|
|
|
|
}
|
2020-06-12 14:03:08 +00:00
|
|
|
};
|
2021-10-07 12:42:08 +00:00
|
|
|
web: {
|
|
|
|
enabled: boolean;
|
|
|
|
port: number;
|
|
|
|
address: string;
|
|
|
|
abuseReporting: {
|
|
|
|
enabled: boolean;
|
|
|
|
}
|
2022-02-01 13:20:26 +00:00
|
|
|
ruleServer?: {
|
2021-10-22 08:47:05 +00:00
|
|
|
enabled: boolean;
|
|
|
|
}
|
2021-10-07 12:42:08 +00:00
|
|
|
}
|
2019-11-07 01:46:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Config options only set at runtime. Try to avoid using the objects
|
|
|
|
* here as much as possible.
|
|
|
|
*/
|
|
|
|
RUNTIME: {
|
2021-07-22 06:38:44 +00:00
|
|
|
client?: MatrixClient;
|
2019-11-07 01:46:49 +00:00
|
|
|
};
|
2019-09-26 02:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 22:38:52 +00:00
|
|
|
const defaultConfig: IConfig = {
|
|
|
|
homeserverUrl: "http://localhost:8008",
|
2021-10-07 12:42:08 +00:00
|
|
|
rawHomeserverUrl: "http://localhost:8008",
|
2020-02-12 22:38:52 +00:00
|
|
|
accessToken: "NONE_PROVIDED",
|
|
|
|
pantalaimon: {
|
|
|
|
use: false,
|
|
|
|
username: "",
|
|
|
|
password: "",
|
|
|
|
},
|
|
|
|
dataPath: "/data/storage",
|
2022-08-17 09:05:23 +00:00
|
|
|
acceptInvitesFromSpace: '!noop:example.org',
|
2022-12-07 17:00:05 +00:00
|
|
|
autojoinOnlyIfManager: true,
|
2020-03-05 22:38:09 +00:00
|
|
|
recordIgnoredInvites: false,
|
2020-02-12 22:38:52 +00:00
|
|
|
managementRoom: "!noop:example.org",
|
|
|
|
verboseLogging: false,
|
|
|
|
logLevel: "INFO",
|
|
|
|
syncOnStartup: true,
|
|
|
|
verifyPermissionsOnStartup: true,
|
|
|
|
noop: false,
|
|
|
|
protectedRooms: [],
|
|
|
|
fasterMembershipChecks: false,
|
|
|
|
automaticallyRedactForReasons: ["spam", "advertising"],
|
|
|
|
protectAllJoinedRooms: false,
|
2022-06-08 09:49:43 +00:00
|
|
|
backgroundDelayMS: 500,
|
2022-07-04 14:06:36 +00:00
|
|
|
pollReports: false,
|
2022-07-07 11:55:03 +00:00
|
|
|
displayReports: true,
|
2020-02-12 22:38:52 +00:00
|
|
|
commands: {
|
|
|
|
allowNoPrefix: false,
|
|
|
|
additionalPrefixes: [],
|
2021-02-05 16:04:06 +00:00
|
|
|
confirmWildcardBan: true,
|
2020-02-12 22:38:52 +00:00
|
|
|
},
|
2020-06-21 19:18:34 +00:00
|
|
|
protections: {
|
|
|
|
wordlist: {
|
2020-10-31 13:23:18 +00:00
|
|
|
words: [],
|
2020-06-21 19:18:34 +00:00
|
|
|
minutesBeforeTrusting: 20
|
|
|
|
}
|
|
|
|
},
|
2020-06-12 14:03:08 +00:00
|
|
|
health: {
|
|
|
|
healthz: {
|
|
|
|
enabled: false,
|
|
|
|
port: 8080,
|
|
|
|
address: "0.0.0.0",
|
|
|
|
endpoint: "/healthz",
|
|
|
|
healthyStatus: 200,
|
|
|
|
unhealthyStatus: 418,
|
|
|
|
},
|
2023-01-05 07:37:54 +00:00
|
|
|
openMetrics: {
|
|
|
|
enabled: false,
|
|
|
|
port: 9090,
|
|
|
|
address: "0.0.0.0",
|
|
|
|
endpoint: "/metrics",
|
|
|
|
}
|
2020-06-12 14:03:08 +00:00
|
|
|
},
|
2021-10-07 12:42:08 +00:00
|
|
|
web: {
|
|
|
|
enabled: false,
|
|
|
|
port: 8080,
|
|
|
|
address: "localhost",
|
|
|
|
abuseReporting: {
|
|
|
|
enabled: false,
|
2021-10-22 08:47:05 +00:00
|
|
|
},
|
|
|
|
ruleServer: {
|
|
|
|
enabled: false,
|
|
|
|
},
|
2021-10-07 12:42:08 +00:00
|
|
|
},
|
2020-02-12 22:38:52 +00:00
|
|
|
|
|
|
|
// Needed to make the interface happy.
|
|
|
|
RUNTIME: {
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-12-06 17:17:40 +00:00
|
|
|
export function getDefaultConfig(): IConfig {
|
|
|
|
return Config.util.cloneDeep(defaultConfig);
|
|
|
|
}
|
|
|
|
|
2022-11-23 10:55:22 +00:00
|
|
|
/**
|
|
|
|
* Grabs an explicit path provided for mjolnir's config from an arguments vector if provided, otherwise returns undefined.
|
|
|
|
* @param argv An arguments vector sourced from `process.argv`.
|
|
|
|
* @returns The path if one was provided or undefined.
|
|
|
|
*/
|
|
|
|
function configPathFromArguments(argv: string[]): undefined|string {
|
|
|
|
const configOptionIndex = argv.findIndex(arg => arg === "--mjolnir-config");
|
|
|
|
if (configOptionIndex > 0) {
|
|
|
|
const configOptionPath = argv.at(configOptionIndex + 1);
|
|
|
|
if (!configOptionPath) {
|
|
|
|
throw new Error("No path provided with option --mjolnir-config");
|
|
|
|
}
|
|
|
|
return configOptionPath;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2022-08-17 15:17:23 +00:00
|
|
|
|
2022-11-23 10:55:22 +00:00
|
|
|
export function read(): IConfig {
|
|
|
|
const explicitConfigPath = configPathFromArguments(process.argv);
|
|
|
|
if (explicitConfigPath) {
|
|
|
|
const content = fs.readFileSync(explicitConfigPath, "utf8");
|
|
|
|
const parsed = load(content);
|
|
|
|
return Config.util.extendDeep({}, defaultConfig, parsed);
|
|
|
|
} else {
|
|
|
|
const config = Config.util.extendDeep({}, defaultConfig, Config.util.toObject()) as IConfig;
|
|
|
|
return config;
|
|
|
|
}
|
2022-08-16 14:51:18 +00:00
|
|
|
}
|
2022-12-06 17:17:40 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides a config for each newly provisioned mjolnir in appservice mode.
|
|
|
|
* @param managementRoomId A room that has been created to serve as the mjolnir's management room for the owner.
|
|
|
|
* @returns A config that can be directly used by the new mjolnir.
|
|
|
|
*/
|
|
|
|
export function getProvisionedMjolnirConfig(managementRoomId: string): IConfig {
|
|
|
|
// These are keys that are allowed to be configured for provisioned mjolnirs.
|
|
|
|
// We need a restricted set so that someone doesn't accidentally enable webservers etc
|
|
|
|
// on every created Mjolnir, which would result in very confusing error messages.
|
|
|
|
const allowedKeys = [
|
|
|
|
"commands",
|
|
|
|
"verboseLogging",
|
|
|
|
"logLevel",
|
|
|
|
"syncOnStartup",
|
|
|
|
"verifyPermissionsOnStartup",
|
|
|
|
"fasterMembershipChecks",
|
|
|
|
"automaticallyRedactForReasons",
|
|
|
|
"protectAllJoinedRooms",
|
|
|
|
"backgroundDelayMS",
|
|
|
|
];
|
|
|
|
const configTemplate = read(); // we use the standard bot config as a template for every provisioned mjolnir.
|
|
|
|
const unusedKeys = Object.keys(configTemplate).filter(key => !allowedKeys.includes(key));
|
|
|
|
if (unusedKeys.length > 0) {
|
|
|
|
LogService.warn("config", "The config provided for provisioned mjolnirs contains keys which are not used by the appservice.", unusedKeys);
|
|
|
|
}
|
|
|
|
const config = Config.util.extendDeep(
|
|
|
|
getDefaultConfig(),
|
|
|
|
allowedKeys.reduce((existingConfig: any, key: string) => {
|
|
|
|
return { ...existingConfig, [key]: configTemplate[key as keyof IConfig] }
|
|
|
|
}, {})
|
|
|
|
);
|
|
|
|
|
|
|
|
config.managementRoom = managementRoomId;
|
|
|
|
config.protectedRooms = [];
|
|
|
|
return config;
|
|
|
|
}
|