wildduck/lib/api/acme.js
2021-09-08 16:09:14 +03:00

82 lines
2.7 KiB
JavaScript

'use strict';
const config = require('wild-config');
const log = require('npmlog');
const Joi = require('joi');
const AcmeChallenge = require('../acme/acme-challenge');
const { asyncifyJson, validationErrors, getHostname, normalizeIp } = require('../tools');
module.exports = (db, server, routeOptions) => {
routeOptions = routeOptions || {};
const acmeChallenge = AcmeChallenge.create({ db: db.database });
server.get(
{ name: 'acmeToken', path: '/.well-known/acme-challenge/:token' },
asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const ip = normalizeIp(res.socket.remoteAddress);
const domain = getHostname(req);
const schema = Joi.object().keys({
token: Joi.string().empty('').max(256).required()
});
const result = schema.validate(req.params, {
abortEarly: false,
convert: true,
allowUnknown: true
});
if (result.error) {
res.status(400);
res.json({
error: result.error.message,
code: 'InputValidationError',
details: validationErrors(result)
});
return next();
}
const token = result.value.token;
let challenge;
try {
challenge = await acmeChallenge.get({
challenge: {
token,
identifier: { value: domain }
}
});
} catch (err) {
log.error('ACME', `Error verifying challenge ${domain}: ${token} (${ip}, ${req.url}) ${err.message}`);
let resErr = new Error(`Failed to verify authentication token`);
resErr.responseCode = 500;
throw resErr;
}
if (!challenge || !challenge.keyAuthorization) {
log.error('ACME', `Unknown challenge ${domain}: ${token} (${ip}, ${req.url})`);
let err = new Error(`Unknown challenge`);
err.responseCode = 404;
throw err;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(challenge.keyAuthorization);
})
);
if (!routeOptions.disableRedirect) {
server.on('NotFound', (req, res, err, cb) => {
let remoteAddress = ((req.socket || req.connection).remoteAddress || '').replace(/^::ffff:/, '');
log.http('ACME', `${remoteAddress} ${req.method} ${req.url} 302 [redirect=${config.acme.agent.redirect}]`);
res.redirect(302, config.acme.agent.redirect, cb);
});
}
};