mirror of
https://github.com/jc21/nginx-proxy-manager.git
synced 2024-08-30 18:22:48 +00:00
Refactor configuration
- No longer use config npm package - Prefer config from env vars, though still has support for config file - No longer writes a config file for database config - Writes keys to a new file in /data folder - Removes a lot of cruft and improves config understanding
This commit is contained in:
parent
dad3e1da7c
commit
2a07445005
@ -2,6 +2,7 @@ const express = require('express');
|
|||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const fileUpload = require('express-fileupload');
|
const fileUpload = require('express-fileupload');
|
||||||
const compression = require('compression');
|
const compression = require('compression');
|
||||||
|
const config = require('./lib/config');
|
||||||
const log = require('./logger').express;
|
const log = require('./logger').express;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
|
|||||||
app.enable('strict routing');
|
app.enable('strict routing');
|
||||||
|
|
||||||
// pretty print JSON when not live
|
// pretty print JSON when not live
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (config.debug()) {
|
||||||
app.set('json spaces', 2);
|
app.set('json spaces', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ app.use(function (err, req, res, next) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
||||||
payload.debug = {
|
payload.debug = {
|
||||||
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
||||||
previous: err.previous
|
previous: err.previous
|
||||||
@ -74,7 +75,7 @@ app.use(function (err, req, res, next) {
|
|||||||
|
|
||||||
// Not every error is worth logging - but this is good for now until it gets annoying.
|
// Not every error is worth logging - but this is good for now until it gets annoying.
|
||||||
if (typeof err.stack !== 'undefined' && err.stack) {
|
if (typeof err.stack !== 'undefined' && err.stack) {
|
||||||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
if (config.debug()) {
|
||||||
log.debug(err.stack);
|
log.debug(err.stack);
|
||||||
} else if (typeof err.public == 'undefined' || !err.public) {
|
} else if (typeof err.public == 'undefined' || !err.public) {
|
||||||
log.warn(err.message);
|
log.warn(err.message);
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
const config = require('config');
|
const config = require('./lib/config');
|
||||||
|
|
||||||
if (!config.has('database')) {
|
if (!config.has('database')) {
|
||||||
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
|
throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDbConfig() {
|
function generateDbConfig() {
|
||||||
if (config.database.engine === 'knex-native') {
|
const cfg = config.get('database');
|
||||||
return config.database.knex;
|
if (cfg.engine === 'knex-native') {
|
||||||
} else
|
return cfg.knex;
|
||||||
return {
|
}
|
||||||
client: config.database.engine,
|
return {
|
||||||
connection: {
|
client: cfg.engine,
|
||||||
host: config.database.host,
|
connection: {
|
||||||
user: config.database.user,
|
host: cfg.host,
|
||||||
password: config.database.password,
|
user: cfg.user,
|
||||||
database: config.database.name,
|
password: cfg.password,
|
||||||
port: config.database.port
|
database: cfg.name,
|
||||||
},
|
port: cfg.port
|
||||||
migrations: {
|
},
|
||||||
tableName: 'migrations'
|
migrations: {
|
||||||
}
|
tableName: 'migrations'
|
||||||
};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = require('knex')(generateDbConfig());
|
||||||
let data = generateDbConfig();
|
|
||||||
|
|
||||||
if (typeof config.database.version !== 'undefined') {
|
|
||||||
data.version = config.database.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = require('knex')(data);
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const logger = require('./logger').global;
|
const logger = require('./logger').global;
|
||||||
|
|
||||||
async function appStart () {
|
async function appStart () {
|
||||||
// Create config file db settings if environment variables have been set
|
|
||||||
await createDbConfigFromEnvironment();
|
|
||||||
|
|
||||||
const migrate = require('./migrate');
|
const migrate = require('./migrate');
|
||||||
const setup = require('./setup');
|
const setup = require('./setup');
|
||||||
const app = require('./app');
|
const app = require('./app');
|
||||||
@ -42,90 +40,6 @@ async function appStart () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createDbConfigFromEnvironment() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
|
||||||
const envMysqlPort = process.env.DB_MYSQL_PORT || null;
|
|
||||||
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
|
||||||
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
|
||||||
let envSqliteFile = process.env.DB_SQLITE_FILE || null;
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
|
||||||
let configData = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
configData = require(filename);
|
|
||||||
} catch (err) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configData.database && configData.database.engine && !configData.database.fromEnv) {
|
|
||||||
logger.info('Manual db configuration already exists, skipping config creation from environment variables');
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
|
|
||||||
envSqliteFile = '/data/database.sqlite';
|
|
||||||
logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
|
|
||||||
const newConfig = {
|
|
||||||
fromEnv: true,
|
|
||||||
engine: 'mysql',
|
|
||||||
host: envMysqlHost,
|
|
||||||
port: envMysqlPort,
|
|
||||||
user: envMysqlUser,
|
|
||||||
password: process.env.DB_MYSQL_PASSWORD,
|
|
||||||
name: envMysqlName,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
|
||||||
// Config is unchanged, skip overwrite
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Generating MySQL knex configuration from environment variables');
|
|
||||||
configData.database = newConfig;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
const newConfig = {
|
|
||||||
fromEnv: true,
|
|
||||||
engine: 'knex-native',
|
|
||||||
knex: {
|
|
||||||
client: 'sqlite3',
|
|
||||||
connection: {
|
|
||||||
filename: envSqliteFile
|
|
||||||
},
|
|
||||||
useNullAsDefault: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
|
||||||
// Config is unchanged, skip overwrite
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Generating SQLite knex configuration');
|
|
||||||
configData.database = newConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write config
|
|
||||||
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Could not write db config to config file: ' + filename);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
logger.debug('Wrote db configuration to config file: ' + filename);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
appStart();
|
appStart();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const tempWrite = require('temp-write');
|
const tempWrite = require('temp-write');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const logger = require('../logger').ssl;
|
const logger = require('../logger').ssl;
|
||||||
const error = require('../lib/error');
|
const config = require('../lib/config');
|
||||||
const utils = require('../lib/utils');
|
const error = require('../lib/error');
|
||||||
const certificateModel = require('../models/certificate');
|
const utils = require('../lib/utils');
|
||||||
const dnsPlugins = require('../global/certbot-dns-plugins');
|
const certificateModel = require('../models/certificate');
|
||||||
const internalAuditLog = require('./audit-log');
|
const dnsPlugins = require('../global/certbot-dns-plugins');
|
||||||
const internalNginx = require('./nginx');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalHost = require('./host');
|
const internalNginx = require('./nginx');
|
||||||
const letsencryptStaging = process.env.NODE_ENV !== 'production';
|
const internalHost = require('./host');
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const path = require('path');
|
||||||
|
const { isArray } = require('lodash');
|
||||||
|
|
||||||
|
const letsencryptStaging = config.useLetsencryptStaging();
|
||||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||||
const certbotCommand = 'certbot';
|
const certbotCommand = 'certbot';
|
||||||
const archiver = require('archiver');
|
|
||||||
const path = require('path');
|
|
||||||
const { isArray } = require('lodash');
|
|
||||||
|
|
||||||
function omissions() {
|
function omissions() {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const logger = require('../logger').nginx;
|
const logger = require('../logger').nginx;
|
||||||
const utils = require('../lib/utils');
|
const config = require('../lib/config');
|
||||||
const error = require('../lib/error');
|
const utils = require('../lib/utils');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const error = require('../lib/error');
|
||||||
|
|
||||||
const internalNginx = {
|
const internalNginx = {
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ const internalNginx = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.error('Nginx test failed:', valid_lines.join('\n'));
|
logger.error('Nginx test failed:', valid_lines.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
test: () => {
|
test: () => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Testing Nginx configuration');
|
logger.info('Testing Nginx configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ const internalNginx = {
|
|||||||
generateConfig: (host_type, host) => {
|
generateConfig: (host_type, host) => {
|
||||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ const internalNginx = {
|
|||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.success('Wrote config:', filename, config_text);
|
logger.success('Wrote config:', filename, config_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ const internalNginx = {
|
|||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn('Could not write ' + filename + ':', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
generateLetsEncryptRequestConfig: (certificate) => {
|
generateLetsEncryptRequestConfig: (certificate) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Generating LetsEncrypt Request Config:', certificate);
|
logger.info('Generating LetsEncrypt Request Config:', certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,14 +292,14 @@ const internalNginx = {
|
|||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.success('Wrote config:', filename, config_text);
|
logger.success('Wrote config:', filename, config_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn('Could not write ' + filename + ':', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,8 +416,8 @@ const internalNginx = {
|
|||||||
* @param {string} config
|
* @param {string} config
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
advancedConfigHasDefaultLocation: function (config) {
|
advancedConfigHasDefaultLocation: function (cfg) {
|
||||||
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
181
backend/lib/config.js
Normal file
181
backend/lib/config.js
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const NodeRSA = require('node-rsa');
|
||||||
|
const { config } = require('process');
|
||||||
|
const logger = require('../logger').global;
|
||||||
|
|
||||||
|
const keysFile = '/data/keys.json';
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
// 1. Load from config file first (not recommended anymore)
|
||||||
|
// 2. Use config env variables next
|
||||||
|
const configure = () => {
|
||||||
|
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
||||||
|
if (fs.existsSync(filename)) {
|
||||||
|
let configData;
|
||||||
|
try {
|
||||||
|
configData = require(filename);
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
if (configData?.database && configData?.database?.engine) {
|
||||||
|
logger.info(`Using configuration from file: ${filename}`);
|
||||||
|
instance = configData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
||||||
|
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
||||||
|
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
||||||
|
if (envMysqlHost && envMysqlUser && envMysqlName) {
|
||||||
|
// we have enough mysql creds to go with mysql
|
||||||
|
logger.info('Using MySQL configuration');
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: 'mysql',
|
||||||
|
host: envMysqlHost,
|
||||||
|
port: process.env.DB_MYSQL_PORT || 3306,
|
||||||
|
user: envMysqlUser,
|
||||||
|
password: process.env.DB_MYSQL_PASSWORD,
|
||||||
|
name: envMysqlName,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
||||||
|
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: 'knex-native',
|
||||||
|
knex: {
|
||||||
|
client: 'sqlite3',
|
||||||
|
connection: {
|
||||||
|
filename: envSqliteFile
|
||||||
|
},
|
||||||
|
useNullAsDefault: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get keys from file
|
||||||
|
if (!fs.existsSync(keysFile)) {
|
||||||
|
generateKeys();
|
||||||
|
} else if (!!process.env.DEBUG) {
|
||||||
|
logger.info('Keys file exists OK');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
instance.keys = require(keysFile);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Configuration: ' + JSON.stringify(instance, null, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateKeys = () => {
|
||||||
|
logger.info('Creating a new JWT key pair...');
|
||||||
|
// Now create the keys and save them in the config.
|
||||||
|
const key = new NodeRSA({ b: 2048 });
|
||||||
|
key.generateKeyPair();
|
||||||
|
|
||||||
|
const keys = {
|
||||||
|
key: key.exportKey('private').toString(),
|
||||||
|
pub: key.exportKey('public').toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write keys config
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
logger.info('Wrote JWT key pair to config file: ' + keysFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} key ie: 'database' or 'database.engine'
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has: function(key) {
|
||||||
|
instance === null && configure();
|
||||||
|
const keys = key.split('.');
|
||||||
|
let level = instance;
|
||||||
|
let has = true;
|
||||||
|
keys.forEach((keyItem) =>{
|
||||||
|
if (typeof level[keyItem] === 'undefined') {
|
||||||
|
has = false;
|
||||||
|
} else {
|
||||||
|
level = level[keyItem];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return has;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific key from the top level
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get: function (key) {
|
||||||
|
instance === null && configure();
|
||||||
|
if (key && typeof instance[key] !== 'undefined') {
|
||||||
|
return instance[key];
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a sqlite configuration?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isSqlite: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.database?.knex && instance.database?.knex?.client === 'sqlite3';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we running in debug mdoe?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
debug: function () {
|
||||||
|
return !!process.env.DEBUG;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a public key
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getPublicKey: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance?.keys?.pub
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a private key
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getPrivateKey: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance?.keys?.key;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
useLetsencryptStaging: function () {
|
||||||
|
return !!process.env.LE_STAGING;
|
||||||
|
}
|
||||||
|
};
|
@ -5,7 +5,7 @@ const definitions = require('../../schema/definitions.json');
|
|||||||
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
||||||
|
|
||||||
const ajv = require('ajv')({
|
const ajv = require('ajv')({
|
||||||
verbose: true, //process.env.NODE_ENV === 'development',
|
verbose: true,
|
||||||
allErrors: true,
|
allErrors: true,
|
||||||
format: 'full', // strict regexes for format checks
|
format: 'full', // strict regexes for format checks
|
||||||
coerceTypes: true,
|
coerceTypes: true,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const config = require('config');
|
const config = require('../lib/config');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
if (config.database.knex && config.database.knex.client === 'sqlite3') {
|
if (config.isSqlite()) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return Model.raw("datetime('now','localtime')");
|
return Model.raw("datetime('now','localtime')");
|
||||||
}
|
}
|
||||||
|
@ -6,44 +6,36 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const config = require('../lib/config');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
|
const logger = require('../logger').global;
|
||||||
const ALGO = 'RS256';
|
const ALGO = 'RS256';
|
||||||
|
|
||||||
let public_key = null;
|
|
||||||
let private_key = null;
|
|
||||||
|
|
||||||
function checkJWTKeyPair() {
|
|
||||||
if (!public_key || !private_key) {
|
|
||||||
let config = require('config');
|
|
||||||
public_key = config.get('jwt.pub');
|
|
||||||
private_key = config.get('jwt.key');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
|
|
||||||
let token_data = {};
|
let token_data = {};
|
||||||
|
|
||||||
let self = {
|
const self = {
|
||||||
/**
|
/**
|
||||||
* @param {Object} payload
|
* @param {Object} payload
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
create: (payload) => {
|
create: (payload) => {
|
||||||
|
if (!config.getPrivateKey()) {
|
||||||
|
logger.error('Private key is empty!')
|
||||||
|
}
|
||||||
// sign with RSA SHA256
|
// sign with RSA SHA256
|
||||||
let options = {
|
const options = {
|
||||||
algorithm: ALGO,
|
algorithm: ALGO,
|
||||||
expiresIn: payload.expiresIn || '1d'
|
expiresIn: payload.expiresIn || '1d'
|
||||||
};
|
};
|
||||||
|
|
||||||
payload.jti = crypto.randomBytes(12)
|
payload.jti = crypto.randomBytes(12)
|
||||||
.toString('base64')
|
.toString('base64')
|
||||||
.substr(-8);
|
.substring(-8);
|
||||||
|
|
||||||
checkJWTKeyPair();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
jwt.sign(payload, private_key, options, (err, token) => {
|
jwt.sign(payload, config.getPrivateKey(), options, (err, token) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@ -62,13 +54,15 @@ module.exports = function () {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
load: function (token) {
|
load: function (token) {
|
||||||
|
if (!config.getPublicKey()) {
|
||||||
|
logger.error('Public key is empty!')
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
checkJWTKeyPair();
|
|
||||||
try {
|
try {
|
||||||
if (!token || token === null || token === 'null') {
|
if (!token || token === null || token === 'null') {
|
||||||
reject(new error.AuthError('Empty token'));
|
reject(new error.AuthError('Empty token'));
|
||||||
} else {
|
} else {
|
||||||
jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
||||||
if (err.name === 'TokenExpiredError') {
|
if (err.name === 'TokenExpiredError') {
|
||||||
@ -132,7 +126,7 @@ module.exports = function () {
|
|||||||
* @returns {Integer}
|
* @returns {Integer}
|
||||||
*/
|
*/
|
||||||
getUserId: (default_value) => {
|
getUserId: (default_value) => {
|
||||||
let attrs = self.get('attrs');
|
const attrs = self.get('attrs');
|
||||||
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
|
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
|
||||||
return attrs.id;
|
return attrs.id;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.3.1",
|
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-fileupload": "^1.1.9",
|
"express-fileupload": "^1.1.9",
|
||||||
"gravatar": "^1.8.0",
|
"gravatar": "^1.8.0",
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
const fs = require('fs');
|
const config = require('./lib/config');
|
||||||
const NodeRSA = require('node-rsa');
|
|
||||||
const config = require('config');
|
|
||||||
const logger = require('./logger').setup;
|
const logger = require('./logger').setup;
|
||||||
const certificateModel = require('./models/certificate');
|
const certificateModel = require('./models/certificate');
|
||||||
const userModel = require('./models/user');
|
const userModel = require('./models/user');
|
||||||
@ -9,62 +7,6 @@ const utils = require('./lib/utils');
|
|||||||
const authModel = require('./models/auth');
|
const authModel = require('./models/auth');
|
||||||
const settingModel = require('./models/setting');
|
const settingModel = require('./models/setting');
|
||||||
const dns_plugins = require('./global/certbot-dns-plugins');
|
const dns_plugins = require('./global/certbot-dns-plugins');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JWT RSA Keypair if not alread set on the config
|
|
||||||
*
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
const setupJwt = () => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Now go and check if the jwt gpg keys have been created and if not, create them
|
|
||||||
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
|
|
||||||
logger.info('Creating a new JWT key pair...');
|
|
||||||
|
|
||||||
// jwt keys are not configured properly
|
|
||||||
const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json';
|
|
||||||
let config_data = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
config_data = require(filename);
|
|
||||||
} catch (err) {
|
|
||||||
// do nothing
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.debug(filename + ' config file could not be required');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now create the keys and save them in the config.
|
|
||||||
let key = new NodeRSA({ b: 2048 });
|
|
||||||
key.generateKeyPair();
|
|
||||||
|
|
||||||
config_data.jwt = {
|
|
||||||
key: key.exportKey('private').toString(),
|
|
||||||
pub: key.exportKey('public').toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write config
|
|
||||||
fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Could not write JWT key pair to config file: ' + filename);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
logger.info('Wrote JWT key pair to config file: ' + filename);
|
|
||||||
delete require.cache[require.resolve('config')];
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// JWT key pair exists
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.debug('JWT Keypair already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a default admin users if one doesn't already exist in the database
|
* Creates a default admin users if one doesn't already exist in the database
|
||||||
@ -119,8 +61,8 @@ const setupDefaultUser = () => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('Initial admin setup completed');
|
logger.info('Initial admin setup completed');
|
||||||
});
|
});
|
||||||
} else if (debug_mode) {
|
} else if (config.debug()) {
|
||||||
logger.debug('Admin user setup not required');
|
logger.info('Admin user setup not required');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -151,8 +93,8 @@ const setupDefaultSettings = () => {
|
|||||||
logger.info('Default settings added');
|
logger.info('Default settings added');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.debug('Default setting setup not required');
|
logger.info('Default setting setup not required');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -225,8 +167,7 @@ const setupLogrotation = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
return setupJwt()
|
return setupDefaultUser()
|
||||||
.then(setupDefaultUser)
|
|
||||||
.then(setupDefaultSettings)
|
.then(setupDefaultSettings)
|
||||||
.then(setupCertbotPlugins)
|
.then(setupCertbotPlugins)
|
||||||
.then(setupLogrotation);
|
.then(setupLogrotation);
|
||||||
|
@ -677,13 +677,6 @@ concat-map@0.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||||
|
|
||||||
config@^3.3.1:
|
|
||||||
version "3.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19"
|
|
||||||
integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==
|
|
||||||
dependencies:
|
|
||||||
json5 "^2.1.1"
|
|
||||||
|
|
||||||
configstore@^5.0.1:
|
configstore@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
|
resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
|
||||||
@ -1769,11 +1762,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||||
|
|
||||||
json5@^2.1.1:
|
|
||||||
version "2.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
|
||||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
|
||||||
|
|
||||||
jsonwebtoken@^9.0.0:
|
jsonwebtoken@^9.0.0:
|
||||||
version "9.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d"
|
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d"
|
||||||
|
@ -16,14 +16,17 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PUID: 1000
|
PUID: 1000
|
||||||
PGID: 1000
|
PGID: 1000
|
||||||
NODE_ENV: "development"
|
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
DEVELOPMENT: "true"
|
# specifically for dev:
|
||||||
DB_MYSQL_HOST: "db"
|
DEBUG: 'true'
|
||||||
DB_MYSQL_PORT: 3306
|
DEVELOPMENT: 'true'
|
||||||
DB_MYSQL_USER: "npm"
|
LE_STAGING: 'true'
|
||||||
DB_MYSQL_PASSWORD: "npm"
|
# db:
|
||||||
DB_MYSQL_NAME: "npm"
|
DB_MYSQL_HOST: 'db'
|
||||||
|
DB_MYSQL_PORT: '3306'
|
||||||
|
DB_MYSQL_USER: 'npm'
|
||||||
|
DB_MYSQL_PASSWORD: 'npm'
|
||||||
|
DB_MYSQL_NAME: 'npm'
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
# DISABLE_IPV6: "true"
|
# DISABLE_IPV6: "true"
|
||||||
volumes:
|
volumes:
|
||||||
@ -44,10 +47,10 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: "npm"
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: "npm"
|
MYSQL_PASSWORD: 'npm'
|
||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/mysql
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user