mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-09-24 15:30:35 +00:00
404 hosts add update complete, fix certbot renewals
and remove the need for email and agreement on cert requests
This commit is contained in:
@@ -13,6 +13,7 @@ import utils from "../lib/utils.js";
|
||||
import { ssl as logger } from "../logger.js";
|
||||
import certificateModel from "../models/certificate.js";
|
||||
import tokenModel from "../models/token.js";
|
||||
import userModel from "../models/user.js";
|
||||
import internalAuditLog from "./audit-log.js";
|
||||
import internalHost from "./host.js";
|
||||
import internalNginx from "./nginx.js";
|
||||
@@ -81,7 +82,7 @@ const internalCertificate = {
|
||||
Promise.resolve({
|
||||
permission_visibility: "all",
|
||||
}),
|
||||
token: new tokenModel(),
|
||||
token: tokenModel(),
|
||||
},
|
||||
{ id: certificate.id },
|
||||
)
|
||||
@@ -118,10 +119,7 @@ const internalCertificate = {
|
||||
data.nice_name = data.domain_names.join(", ");
|
||||
}
|
||||
|
||||
const certificate = await certificateModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
|
||||
|
||||
if (certificate.provider === "letsencrypt") {
|
||||
// Request a new Cert from LE. Let the fun begin.
|
||||
@@ -139,12 +137,19 @@ const internalCertificate = {
|
||||
// 2. Disable them in nginx temporarily
|
||||
await internalCertificate.disableInUseHosts(inUseResult);
|
||||
|
||||
const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
|
||||
if (!user || !user.email) {
|
||||
throw new error.ValidationError(
|
||||
"A valid email address must be set on your user account to use Let's Encrypt",
|
||||
);
|
||||
}
|
||||
|
||||
// With DNS challenge no config is needed, so skip 3 and 5.
|
||||
if (certificate.meta?.dns_challenge) {
|
||||
try {
|
||||
await internalNginx.reload();
|
||||
// 4. Request cert
|
||||
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
|
||||
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
|
||||
await internalNginx.reload();
|
||||
// 6. Re-instate previously disabled hosts
|
||||
await internalCertificate.enableInUseHosts(inUseResult);
|
||||
@@ -159,9 +164,9 @@ const internalCertificate = {
|
||||
try {
|
||||
await internalNginx.generateLetsEncryptRequestConfig(certificate);
|
||||
await internalNginx.reload();
|
||||
setTimeout(() => {}, 5000)
|
||||
setTimeout(() => {}, 5000);
|
||||
// 4. Request cert
|
||||
await internalCertificate.requestLetsEncryptSsl(certificate);
|
||||
await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
|
||||
// 5. Remove LE config
|
||||
await internalNginx.deleteLetsEncryptRequestConfig(certificate);
|
||||
await internalNginx.reload();
|
||||
@@ -204,13 +209,12 @@ const internalCertificate = {
|
||||
data.meta = _.assign({}, data.meta || {}, certificate.meta);
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog
|
||||
.add(access, {
|
||||
action: "created",
|
||||
object_type: "certificate",
|
||||
object_id: certificate.id,
|
||||
meta: data,
|
||||
});
|
||||
await internalAuditLog.add(access, {
|
||||
action: "created",
|
||||
object_type: "certificate",
|
||||
object_id: certificate.id,
|
||||
meta: data,
|
||||
});
|
||||
|
||||
return certificate;
|
||||
},
|
||||
@@ -248,13 +252,12 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog
|
||||
.add(access, {
|
||||
action: "updated",
|
||||
object_type: "certificate",
|
||||
object_id: row.id,
|
||||
meta: _.omit(data, ["expires_on"]), // this prevents json circular reference because expires_on might be raw
|
||||
});
|
||||
await internalAuditLog.add(access, {
|
||||
action: "updated",
|
||||
object_type: "certificate",
|
||||
object_id: row.id,
|
||||
meta: _.omit(data, ["expires_on"]), // this prevents json circular reference because expires_on might be raw
|
||||
});
|
||||
|
||||
return savedRow;
|
||||
},
|
||||
@@ -268,7 +271,7 @@ const internalCertificate = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
get: async (access, data) => {
|
||||
const accessData = await access.can("certificates:get", data.id)
|
||||
const accessData = await access.can("certificates:get", data.id);
|
||||
const query = certificateModel
|
||||
.query()
|
||||
.where("is_deleted", 0)
|
||||
@@ -367,12 +370,9 @@ const internalCertificate = {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
await certificateModel
|
||||
.query()
|
||||
.where("id", row.id)
|
||||
.patch({
|
||||
is_deleted: 1,
|
||||
});
|
||||
await certificateModel.query().where("id", row.id).patch({
|
||||
is_deleted: 1,
|
||||
});
|
||||
|
||||
// Add to audit log
|
||||
row.meta = internalCertificate.cleanMeta(row.meta);
|
||||
@@ -435,10 +435,7 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: async (userId, visibility) => {
|
||||
const query = certificateModel
|
||||
.query()
|
||||
.count("id as count")
|
||||
.where("is_deleted", 0);
|
||||
const query = certificateModel.query().count("id as count").where("is_deleted", 0);
|
||||
|
||||
if (visibility !== "all") {
|
||||
query.andWhere("owner_user_id", userId);
|
||||
@@ -501,12 +498,10 @@ const internalCertificate = {
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Array} data.domain_names
|
||||
* @param {String} data.meta.letsencrypt_email
|
||||
* @param {Boolean} data.meta.letsencrypt_agree
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createQuickCertificate: async (access, data) => {
|
||||
return internalCertificate.create(access, {
|
||||
return await internalCertificate.create(access, {
|
||||
provider: "letsencrypt",
|
||||
domain_names: data.domain_names,
|
||||
meta: data.meta,
|
||||
@@ -652,7 +647,7 @@ const internalCertificate = {
|
||||
const certData = {};
|
||||
|
||||
try {
|
||||
const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"])
|
||||
const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
|
||||
// Examples:
|
||||
// subject=CN = *.jc21.com
|
||||
// subject=CN = something.example.com
|
||||
@@ -739,9 +734,10 @@ const internalCertificate = {
|
||||
/**
|
||||
* Request a certificate using the http challenge
|
||||
* @param {Object} certificate the certificate row
|
||||
* @param {String} email the email address to use for registration
|
||||
* @returns {Promise}
|
||||
*/
|
||||
requestLetsEncryptSsl: async (certificate) => {
|
||||
requestLetsEncryptSsl: async (certificate, email) => {
|
||||
logger.info(
|
||||
`Requesting LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(", ")}`,
|
||||
);
|
||||
@@ -760,7 +756,7 @@ const internalCertificate = {
|
||||
"--authenticator",
|
||||
"webroot",
|
||||
"--email",
|
||||
certificate.meta.letsencrypt_email,
|
||||
email,
|
||||
"--preferred-challenges",
|
||||
"dns,http",
|
||||
"--domains",
|
||||
@@ -779,9 +775,10 @@ const internalCertificate = {
|
||||
|
||||
/**
|
||||
* @param {Object} certificate the certificate row
|
||||
* @param {String} email the email address to use for registration
|
||||
* @returns {Promise}
|
||||
*/
|
||||
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
||||
requestLetsEncryptSslWithDnsChallenge: async (certificate, email) => {
|
||||
await installPlugin(certificate.meta.dns_provider);
|
||||
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||
logger.info(
|
||||
@@ -807,7 +804,7 @@ const internalCertificate = {
|
||||
`npm-${certificate.id}`,
|
||||
"--agree-tos",
|
||||
"--email",
|
||||
certificate.meta.letsencrypt_email,
|
||||
email,
|
||||
"--domains",
|
||||
certificate.domain_names.join(","),
|
||||
"--authenticator",
|
||||
@@ -847,7 +844,7 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renew: async (access, data) => {
|
||||
await access.can("certificates:update", data)
|
||||
await access.can("certificates:update", data);
|
||||
const certificate = await internalCertificate.get(access, data);
|
||||
|
||||
if (certificate.provider === "letsencrypt") {
|
||||
@@ -860,11 +857,9 @@ const internalCertificate = {
|
||||
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
|
||||
);
|
||||
|
||||
const updatedCertificate = await certificateModel
|
||||
.query()
|
||||
.patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
|
||||
});
|
||||
const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
|
||||
});
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog.add(access, {
|
||||
@@ -1159,7 +1154,9 @@ const internalCertificate = {
|
||||
return "no-host";
|
||||
}
|
||||
// Other errors
|
||||
logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
|
||||
logger.info(
|
||||
`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
|
||||
);
|
||||
return `other:${result.responsecode}`;
|
||||
}
|
||||
|
||||
@@ -1201,7 +1198,7 @@ const internalCertificate = {
|
||||
|
||||
getLiveCertPath: (certificateId) => {
|
||||
return `/etc/letsencrypt/live/npm-${certificateId}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default internalCertificate;
|
||||
|
@@ -54,10 +54,21 @@ const internalDeadHost = {
|
||||
thisData.advanced_config = "";
|
||||
}
|
||||
|
||||
const row = await deadHostModel.query().insertAndFetch(thisData).then(utils.omitRow(omissions()));
|
||||
const row = await deadHostModel.query()
|
||||
.insertAndFetch(thisData)
|
||||
.then(utils.omitRow(omissions()));
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog.add(access, {
|
||||
action: "created",
|
||||
object_type: "dead-host",
|
||||
object_id: row.id,
|
||||
meta: _.assign({}, data.meta || {}, row.meta),
|
||||
});
|
||||
|
||||
if (createCertificate) {
|
||||
const cert = await internalCertificate.createQuickCertificate(access, data);
|
||||
|
||||
// update host with cert id
|
||||
await internalDeadHost.update(access, {
|
||||
id: row.id,
|
||||
@@ -71,17 +82,13 @@ const internalDeadHost = {
|
||||
expand: ["certificate", "owner"],
|
||||
});
|
||||
|
||||
// Sanity check
|
||||
if (createCertificate && !freshRow.certificate_id) {
|
||||
throw new errs.InternalValidationError("The host was created but the Certificate creation failed.");
|
||||
}
|
||||
|
||||
// Configure nginx
|
||||
await internalNginx.configure(deadHostModel, "dead_host", freshRow);
|
||||
data.meta = _.assign({}, data.meta || {}, freshRow.meta);
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog.add(access, {
|
||||
action: "created",
|
||||
object_type: "dead-host",
|
||||
object_id: freshRow.id,
|
||||
meta: data,
|
||||
});
|
||||
|
||||
return freshRow;
|
||||
},
|
||||
@@ -94,7 +101,6 @@ const internalDeadHost = {
|
||||
*/
|
||||
update: async (access, data) => {
|
||||
const createCertificate = data.certificate_id === "new";
|
||||
|
||||
if (createCertificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
@@ -147,6 +153,13 @@ const internalDeadHost = {
|
||||
|
||||
thisData = internalHost.cleanSslHstsData(thisData, row);
|
||||
|
||||
|
||||
// do the row update
|
||||
await deadHostModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
.patch(data);
|
||||
|
||||
// Add to audit log
|
||||
await internalAuditLog.add(access, {
|
||||
action: "updated",
|
||||
|
Reference in New Issue
Block a user