mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2026-09-24 20:10:26 +01:00
setupCertbotPlugins() wrote a credentials file for every DNS-01 certificate each time the backend started, using flag "wx" so it only filled in missing ones. That existed because the renew path did not write the file itself, so something had to put it back before `certbot renew` looked for it. With the previous commit the renew path writes the file immediately before invoking certbot, so this is now the only thing putting those credentials back on disk - and it does so for every certificate on every restart, which undoes the cleanup entirely. Removing the write leaves the `fs` import and the `promises` array unused. The "Added Certbot plugins" log line is kept but now gates on plugins.length, since it was previously gated on a promise array that only ever held credential writes.
152 lines
4.5 KiB
JavaScript
152 lines
4.5 KiB
JavaScript
import { installPlugins } from "./lib/certbot.js";
|
|
import utils from "./lib/utils.js";
|
|
import { setup as logger } from "./logger.js";
|
|
import authModel from "./models/auth.js";
|
|
import certificateModel from "./models/certificate.js";
|
|
import settingModel from "./models/setting.js";
|
|
import userModel from "./models/user.js";
|
|
import userPermissionModel from "./models/user_permission.js";
|
|
|
|
export const isSetup = async () => {
|
|
const row = await userModel.query().select("id").where("is_deleted", 0).first();
|
|
return row?.id > 0;
|
|
};
|
|
|
|
/**
|
|
* Creates a default admin users if one doesn't already exist in the database
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
const setupDefaultUser = async () => {
|
|
const initialAdminEmail = process.env.INITIAL_ADMIN_EMAIL;
|
|
const initialAdminPassword = process.env.INITIAL_ADMIN_PASSWORD;
|
|
|
|
// This will only create a new user when there are no active users in the database
|
|
// and the INITIAL_ADMIN_EMAIL and INITIAL_ADMIN_PASSWORD environment variables are set.
|
|
// Otherwise, users should be shown the setup wizard in the frontend.
|
|
// I'm keeping this legacy behavior in case some people are automating deployments.
|
|
|
|
if (!initialAdminEmail || !initialAdminPassword) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
const userIsetup = await isSetup();
|
|
if (!userIsetup) {
|
|
// Create a new user and set password
|
|
logger.info(`Creating a new user: ${initialAdminEmail} with password: ${initialAdminPassword}`);
|
|
|
|
const data = {
|
|
is_deleted: 0,
|
|
email: initialAdminEmail,
|
|
name: "Administrator",
|
|
nickname: "Admin",
|
|
avatar: "",
|
|
roles: ["admin"],
|
|
};
|
|
|
|
const user = await userModel.query().insertAndFetch(data);
|
|
|
|
await authModel.query().insert({
|
|
user_id: user.id,
|
|
type: "password",
|
|
secret: initialAdminPassword,
|
|
meta: {},
|
|
});
|
|
|
|
await userPermissionModel.query().insert({
|
|
user_id: user.id,
|
|
visibility: "all",
|
|
proxy_hosts: "manage",
|
|
redirection_hosts: "manage",
|
|
dead_hosts: "manage",
|
|
streams: "manage",
|
|
access_lists: "manage",
|
|
certificates: "manage",
|
|
});
|
|
logger.info("Initial admin setup completed");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates default settings if they don't already exist in the database
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
const setupDefaultSettings = async () => {
|
|
const row = await settingModel.query().select("id").where({ id: "default-site" }).first();
|
|
|
|
if (!row?.id) {
|
|
await settingModel.query().insert({
|
|
id: "default-site",
|
|
name: "Default Site",
|
|
description: "What to show when Nginx is hit with an unknown Host",
|
|
value: "congratulations",
|
|
meta: {},
|
|
});
|
|
logger.info("Default settings added");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Installs all Certbot plugins which are required for an installed certificate
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
const setupCertbotPlugins = async () => {
|
|
const certificates = await certificateModel.query().where("is_deleted", 0).andWhere("provider", "letsencrypt");
|
|
|
|
if (certificates?.length) {
|
|
const plugins = [];
|
|
|
|
certificates.map((certificate) => {
|
|
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
|
if (plugins.indexOf(certificate.meta.dns_provider) === -1) {
|
|
plugins.push(certificate.meta.dns_provider);
|
|
}
|
|
|
|
// Deliberately does NOT write the DNS credentials file here any more.
|
|
//
|
|
// It used to, so that a later `certbot renew` would find the path recorded in its
|
|
// renewal config. The effect was that every backend restart rewrote a plaintext
|
|
// DNS provider API token for every DNS-01 certificate, and left it there.
|
|
//
|
|
// internalCertificate now writes that file immediately before it runs certbot and
|
|
// removes it again afterwards, so there is exactly one writer and the credential
|
|
// is on disk only for the length of a certbot run. Recreating the files at boot
|
|
// would put every one of them straight back.
|
|
}
|
|
return true;
|
|
});
|
|
|
|
await installPlugins(plugins);
|
|
|
|
if (plugins.length) {
|
|
logger.info(`Added Certbot plugins ${plugins.join(", ")}`);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Starts a timer to call run the logrotation binary every two days
|
|
* @returns {Promise}
|
|
*/
|
|
const setupLogrotation = () => {
|
|
const intervalTimeout = 1000 * 60 * 60 * 24 * 2; // 2 days
|
|
|
|
const runLogrotate = async () => {
|
|
try {
|
|
await utils.exec("logrotate /etc/logrotate.d/nginx-proxy-manager");
|
|
logger.info("Logrotate completed.");
|
|
} catch (e) {
|
|
logger.warn(e);
|
|
}
|
|
};
|
|
|
|
logger.info("Logrotate Timer initialized");
|
|
setInterval(runLogrotate, intervalTimeout);
|
|
// And do this now as well
|
|
return runLogrotate();
|
|
};
|
|
|
|
export default () => setupDefaultUser().then(setupDefaultSettings).then(setupCertbotPlugins).then(setupLogrotation);
|