84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import type { IChallengeHandler } from './IChallengeHandler.js';
|
|
|
|
/**
|
|
* HTTP-01 ACME challenge handler using in-memory storage.
|
|
* Stores challenge tokens and key authorizations in memory
|
|
* and serves them via handleRequest for arbitrary HTTP servers.
|
|
*/
|
|
export interface Http01MemoryHandlerChallenge {
|
|
type: string;
|
|
token: string;
|
|
keyAuthorization: string;
|
|
webPath: string;
|
|
}
|
|
|
|
export class Http01MemoryHandler implements IChallengeHandler<Http01MemoryHandlerChallenge> {
|
|
private smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
|
|
private smartdnsClient = new plugins.smartdnsClient.Smartdns({});
|
|
private store: Map<string, string> = new Map();
|
|
|
|
public getSupportedTypes(): string[] {
|
|
return ['http-01'];
|
|
}
|
|
|
|
public async prepare(ch: Http01MemoryHandlerChallenge): Promise<void> {
|
|
this.store.set(ch.token, ch.keyAuthorization);
|
|
}
|
|
|
|
public async verify(_ch: Http01MemoryHandlerChallenge): Promise<void> {
|
|
// No-op
|
|
return;
|
|
}
|
|
|
|
public async cleanup(ch: Http01MemoryHandlerChallenge): Promise<void> {
|
|
this.store.delete(ch.token);
|
|
}
|
|
|
|
/**
|
|
* HTTP request handler for serving ACME HTTP-01 challenges.
|
|
* @param req HTTP request object (should have url property)
|
|
* @param res HTTP response object
|
|
* @param next Optional next() callback for Express-style fallthrough
|
|
*/
|
|
public handleRequest(req: any, res: any, next?: () => void): void {
|
|
const url = req.url || '';
|
|
const prefix = '/.well-known/acme-challenge/';
|
|
if (!url.startsWith(prefix)) {
|
|
if (next) {
|
|
return next();
|
|
}
|
|
res.statusCode = 404;
|
|
return res.end();
|
|
}
|
|
const token = url.slice(prefix.length);
|
|
const keyAuth = this.store.get(token);
|
|
if (keyAuth !== undefined) {
|
|
if (typeof res.status === 'function' && typeof res.send === 'function') {
|
|
return res.status(200).send(keyAuth);
|
|
}
|
|
res.statusCode = 200;
|
|
res.setHeader('content-type', 'text/plain');
|
|
return res.end(keyAuth);
|
|
}
|
|
if (next) {
|
|
return next();
|
|
}
|
|
res.statusCode = 404;
|
|
return res.end();
|
|
}
|
|
|
|
public async checkWetherDomainIsSupported(domainArg: string): Promise<boolean> {
|
|
const publicIps = await this.smartnetworkInstance.getPublicIps();
|
|
const aRecords = await this.smartdnsClient.getRecordsA(domainArg);
|
|
const aaaaRecords = await this.smartdnsClient.getRecordsAAAA(domainArg);
|
|
if (aRecords.length && aRecords.some((record) => record.value !== publicIps.v4)) {
|
|
return false;
|
|
}
|
|
if (aaaaRecords.length && aaaaRecords.some((record) => record.value !== publicIps.v6)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|