mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-04-17 04:07:21 +02:00
Move cookie secret to data root. Make config.yaml immutable
This commit is contained in:
parent
3bb8b887e1
commit
7ea2c5f8cf
@ -79,8 +79,6 @@ minLogLevel: 0
|
|||||||
## Set to 0 to expire session when the browser is closed
|
## Set to 0 to expire session when the browser is closed
|
||||||
## Set to a negative number to disable session expiration
|
## Set to a negative number to disable session expiration
|
||||||
sessionTimeout: -1
|
sessionTimeout: -1
|
||||||
# Used to sign session cookies. Will be auto-generated if not set
|
|
||||||
cookieSecret: ''
|
|
||||||
# Disable CSRF protection - NOT RECOMMENDED
|
# Disable CSRF protection - NOT RECOMMENDED
|
||||||
disableCsrfProtection: false
|
disableCsrfProtection: false
|
||||||
# Disable startup security checks - NOT RECOMMENDED
|
# Disable startup security checks - NOT RECOMMENDED
|
||||||
|
@ -104,6 +104,15 @@ const keyMigrationMap = [
|
|||||||
newKey: 'extensions.models.textToSpeech',
|
newKey: 'extensions.models.textToSpeech',
|
||||||
migrate: (value) => value,
|
migrate: (value) => value,
|
||||||
},
|
},
|
||||||
|
// uncommend one release after 1.12.13
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
oldKey: 'cookieSecret',
|
||||||
|
newKey: 'cookieSecret',
|
||||||
|
migrate: () => void 0,
|
||||||
|
remove: true,
|
||||||
|
},
|
||||||
|
*/
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,8 +172,17 @@ function addMissingConfigValues() {
|
|||||||
|
|
||||||
// Migrate old keys to new keys
|
// Migrate old keys to new keys
|
||||||
const migratedKeys = [];
|
const migratedKeys = [];
|
||||||
for (const { oldKey, newKey, migrate } of keyMigrationMap) {
|
for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) {
|
||||||
if (_.has(config, oldKey)) {
|
if (_.has(config, oldKey)) {
|
||||||
|
if (remove) {
|
||||||
|
_.unset(config, oldKey);
|
||||||
|
migratedKeys.push({
|
||||||
|
oldKey,
|
||||||
|
newValue: void 0,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const oldValue = _.get(config, oldKey);
|
const oldValue = _.get(config, oldKey);
|
||||||
const newValue = migrate(oldValue);
|
const newValue = migrate(oldValue);
|
||||||
_.set(config, newKey, newValue);
|
_.set(config, newKey, newValue);
|
||||||
|
@ -274,7 +274,7 @@ const listenAddressIPv4 = cliArguments.listenAddressIPv4 ?? getConfigValue('list
|
|||||||
const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY);
|
const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY);
|
||||||
const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST);
|
const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST);
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
const dataRoot = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');
|
globalThis.DATA_ROOT = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');
|
||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
const disableCsrf = cliArguments.disableCsrf ?? getConfigValue('disableCsrfProtection', DEFAULT_CSRF_DISABLED);
|
const disableCsrf = cliArguments.disableCsrf ?? getConfigValue('disableCsrfProtection', DEFAULT_CSRF_DISABLED);
|
||||||
const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', DEFAULT_BASIC_AUTH);
|
const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', DEFAULT_BASIC_AUTH);
|
||||||
@ -282,7 +282,7 @@ const perUserBasicAuth = getConfigValue('perUserBasicAuth', DEFAULT_PER_USER_BAS
|
|||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);
|
const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);
|
||||||
|
|
||||||
const uploadsPath = path.join(dataRoot, UPLOADS_DIRECTORY);
|
const uploadsPath = path.join(globalThis.DATA_ROOT, UPLOADS_DIRECTORY);
|
||||||
|
|
||||||
|
|
||||||
/** @type {boolean | "auto"} */
|
/** @type {boolean | "auto"} */
|
||||||
@ -466,7 +466,7 @@ app.use(cookieSession({
|
|||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
maxAge: getSessionCookieAge(),
|
maxAge: getSessionCookieAge(),
|
||||||
secret: getCookieSecret(),
|
secret: getCookieSecret(globalThis.DATA_ROOT),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(setUserDataMiddleware);
|
app.use(setUserDataMiddleware);
|
||||||
@ -1137,7 +1137,7 @@ function apply404Middleware() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// User storage module needs to be initialized before starting the server
|
// User storage module needs to be initialized before starting the server
|
||||||
initUserStorage(dataRoot)
|
initUserStorage(globalThis.DATA_ROOT)
|
||||||
.then(ensurePublicDirectoriesExist)
|
.then(ensurePublicDirectoriesExist)
|
||||||
.then(migrateUserData)
|
.then(migrateUserData)
|
||||||
.then(migrateSystemPrompts)
|
.then(migrateSystemPrompts)
|
||||||
|
32
src/users.js
32
src/users.js
@ -15,7 +15,7 @@ import _ from 'lodash';
|
|||||||
import { sync as writeFileAtomicSync } from 'write-file-atomic';
|
import { sync as writeFileAtomicSync } from 'write-file-atomic';
|
||||||
|
|
||||||
import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } from './constants.js';
|
import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } from './constants.js';
|
||||||
import { getConfigValue, color, delay, setConfigValue, generateTimestamp } from './util.js';
|
import { getConfigValue, color, delay, generateTimestamp } from './util.js';
|
||||||
import { readSecret, writeSecret } from './endpoints/secrets.js';
|
import { readSecret, writeSecret } from './endpoints/secrets.js';
|
||||||
import { getContentOfType } from './endpoints/content-manager.js';
|
import { getContentOfType } from './endpoints/content-manager.js';
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');
|
|||||||
*/
|
*/
|
||||||
const DIRECTORIES_CACHE = new Map();
|
const DIRECTORIES_CACHE = new Map();
|
||||||
const PUBLIC_USER_AVATAR = '/img/default-user.png';
|
const PUBLIC_USER_AVATAR = '/img/default-user.png';
|
||||||
|
const COOKIE_SECRET_PATH = 'cookie-secret.txt';
|
||||||
|
|
||||||
const STORAGE_KEYS = {
|
const STORAGE_KEYS = {
|
||||||
csrfSecret: 'csrfSecret',
|
csrfSecret: 'csrfSecret',
|
||||||
@ -412,11 +413,10 @@ export function toAvatarKey(handle) {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export async function initUserStorage(dataRoot) {
|
export async function initUserStorage(dataRoot) {
|
||||||
globalThis.DATA_ROOT = dataRoot;
|
console.log('Using data root:', color.green(dataRoot));
|
||||||
console.log('Using data root:', color.green(globalThis.DATA_ROOT));
|
|
||||||
console.log();
|
console.log();
|
||||||
await storage.init({
|
await storage.init({
|
||||||
dir: path.join(globalThis.DATA_ROOT, '_storage'),
|
dir: path.join(dataRoot, '_storage'),
|
||||||
ttl: false, // Never expire
|
ttl: false, // Never expire
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -430,17 +430,29 @@ export async function initUserStorage(dataRoot) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cookie secret from the config. If it doesn't exist, generate a new one.
|
* Get the cookie secret from the config. If it doesn't exist, generate a new one.
|
||||||
|
* @param {string} dataRoot The root directory for user data
|
||||||
* @returns {string} The cookie secret
|
* @returns {string} The cookie secret
|
||||||
*/
|
*/
|
||||||
export function getCookieSecret() {
|
export function getCookieSecret(dataRoot) {
|
||||||
let secret = getConfigValue(STORAGE_KEYS.cookieSecret);
|
const cookieSecretPath = path.join(dataRoot, COOKIE_SECRET_PATH);
|
||||||
|
|
||||||
if (!secret) {
|
if (fs.existsSync(cookieSecretPath)) {
|
||||||
console.warn(color.yellow('Cookie secret is missing from config.yaml. Generating a new one...'));
|
const stat = fs.statSync(cookieSecretPath);
|
||||||
secret = crypto.randomBytes(64).toString('base64');
|
if (stat.size > 0) {
|
||||||
setConfigValue(STORAGE_KEYS.cookieSecret, secret);
|
return fs.readFileSync(cookieSecretPath, 'utf8');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oldSecret = getConfigValue(STORAGE_KEYS.cookieSecret);
|
||||||
|
if (oldSecret) {
|
||||||
|
console.log('Migrating cookie secret from config.yaml...');
|
||||||
|
writeFileAtomicSync(cookieSecretPath, oldSecret, { encoding: 'utf8' });
|
||||||
|
return oldSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(color.yellow('Cookie secret is missing from data root. Generating a new one...'));
|
||||||
|
const secret = crypto.randomBytes(64).toString('base64');
|
||||||
|
writeFileAtomicSync(cookieSecretPath, secret, { encoding: 'utf8' });
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
src/util.js
14
src/util.js
@ -9,7 +9,6 @@ import { promises as dnsPromise } from 'node:dns';
|
|||||||
|
|
||||||
import yaml from 'yaml';
|
import yaml from 'yaml';
|
||||||
import { sync as commandExistsSync } from 'command-exists';
|
import { sync as commandExistsSync } from 'command-exists';
|
||||||
import { sync as writeFileAtomicSync } from 'write-file-atomic';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import yauzl from 'yauzl';
|
import yauzl from 'yauzl';
|
||||||
import mime from 'mime-types';
|
import mime from 'mime-types';
|
||||||
@ -58,19 +57,6 @@ export function getConfigValue(key, defaultValue = null) {
|
|||||||
return _.get(config, key, defaultValue);
|
return _.get(config, key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a value for the given key in the config object and writes it to the config.yaml file.
|
|
||||||
* @param {string} key Key to set
|
|
||||||
* @param {any} value Value to set
|
|
||||||
*/
|
|
||||||
export function setConfigValue(key, value) {
|
|
||||||
// Reset cache so that the next getConfig call will read the updated config file
|
|
||||||
CACHED_CONFIG = null;
|
|
||||||
const config = getConfig();
|
|
||||||
_.set(config, key, value);
|
|
||||||
writeFileAtomicSync('./config.yaml', yaml.stringify(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes the Basic Auth header value for the given user and password.
|
* Encodes the Basic Auth header value for the given user and password.
|
||||||
* @param {string} auth username:password
|
* @param {string} auth username:password
|
||||||
|
Loading…
x
Reference in New Issue
Block a user