@depup/redbird 1.0.2-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +35 -0
- package/changes.json +26 -0
- package/dist/docker.d.ts +30 -0
- package/dist/docker.js +136 -0
- package/dist/docker.js.map +1 -0
- package/dist/etcd-backend.d.ts +12 -0
- package/dist/etcd-backend.js +80 -0
- package/dist/etcd-backend.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +2 -0
- package/dist/interfaces/index.js +3 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/proxy-options.d.ts +36 -0
- package/dist/interfaces/proxy-options.js +2 -0
- package/dist/interfaces/proxy-options.js.map +1 -0
- package/dist/interfaces/proxy-route.d.ts +14 -0
- package/dist/interfaces/proxy-route.js +2 -0
- package/dist/interfaces/proxy-route.js.map +1 -0
- package/dist/interfaces/proxy-target-url.d.ts +8 -0
- package/dist/interfaces/proxy-target-url.js +2 -0
- package/dist/interfaces/proxy-target-url.js.map +1 -0
- package/dist/interfaces/resolver.d.ts +12 -0
- package/dist/interfaces/resolver.js +2 -0
- package/dist/interfaces/resolver.js.map +1 -0
- package/dist/interfaces/route-options.d.ts +15 -0
- package/dist/interfaces/route-options.js +2 -0
- package/dist/interfaces/route-options.js.map +1 -0
- package/dist/letsencrypt.d.ts +19 -0
- package/dist/letsencrypt.js +127 -0
- package/dist/letsencrypt.js.map +1 -0
- package/dist/proxy.d.ts +122 -0
- package/dist/proxy.js +812 -0
- package/dist/proxy.js.map +1 -0
- package/dist/third-party/le-challenge-fs.d.ts +8 -0
- package/dist/third-party/le-challenge-fs.js +140 -0
- package/dist/third-party/le-challenge-fs.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +143 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Letsecript module for Redbird (c) Optimalbits 2016-2024
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from 'http';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import url from 'url';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import LeChallengeFs from './third-party/le-challenge-fs.js';
|
|
12
|
+
/**
|
|
13
|
+
* LetsEncrypt certificates are stored like the following:
|
|
14
|
+
*
|
|
15
|
+
* /example.com
|
|
16
|
+
* /
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
let leStoreConfig = {};
|
|
22
|
+
const webrootPath = ':configDir/:hostname/.well-known/acme-challenge';
|
|
23
|
+
function init(certPath, port, logger) {
|
|
24
|
+
logger === null || logger === void 0 ? void 0 : logger.info('Initializing letsencrypt, path %s, port: %s', certPath, port);
|
|
25
|
+
leStoreConfig = {
|
|
26
|
+
configDir: certPath,
|
|
27
|
+
privkeyPath: ':configDir/:hostname/privkey.pem',
|
|
28
|
+
fullchainPath: ':configDir/:hostname/fullchain.pem',
|
|
29
|
+
certPath: ':configDir/:hostname/cert.pem',
|
|
30
|
+
chainPath: ':configDir/:hostname/chain.pem',
|
|
31
|
+
workDir: ':configDir/letsencrypt/var/lib',
|
|
32
|
+
logsDir: ':configDir/letsencrypt/var/log',
|
|
33
|
+
webrootPath,
|
|
34
|
+
debug: false,
|
|
35
|
+
};
|
|
36
|
+
// we need to proxy for example: 'example.com/.well-known/acme-challenge' -> 'localhost:port/example.com/'
|
|
37
|
+
return createServer(function (req, res) {
|
|
38
|
+
if (req.method !== 'GET') {
|
|
39
|
+
res.statusCode = 405; // Method Not Allowed
|
|
40
|
+
res.end();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const reqPath = url.parse(req.url).pathname;
|
|
44
|
+
const basePath = path.resolve(certPath);
|
|
45
|
+
const safePath = path.normalize(reqPath).replace(/^(\.\.[\/\\])+/, ''); // Prevent directory traversal
|
|
46
|
+
const fullPath = path.join(basePath, safePath);
|
|
47
|
+
if (!fullPath.startsWith(basePath)) {
|
|
48
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`Attempted directory traversal attack: ${req.url}`);
|
|
49
|
+
res.statusCode = 403; // Forbidden
|
|
50
|
+
res.end('Access denied');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
logger === null || logger === void 0 ? void 0 : logger.info('LetsEncrypt CA trying to validate challenge %s', fullPath);
|
|
54
|
+
fs.stat(fullPath, function (err, stats) {
|
|
55
|
+
if (err || !stats.isFile()) {
|
|
56
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
57
|
+
res.write('404 Not Found\n');
|
|
58
|
+
res.end();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
res.writeHead(200);
|
|
62
|
+
fs.createReadStream(fullPath, 'binary').pipe(res);
|
|
63
|
+
});
|
|
64
|
+
}).listen(port);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets the certificates for the given domain.
|
|
68
|
+
* Handles all the LetsEncrypt protocol. Uses
|
|
69
|
+
* existing certificates if any, or negotiates a new one.
|
|
70
|
+
* Returns a promise that resolves to an object with the certificates.
|
|
71
|
+
* TODO: We should use something like https://github.com/PaquitoSoft/memored/blob/master/index.js
|
|
72
|
+
* to avoid
|
|
73
|
+
*/
|
|
74
|
+
async function getCertificates(domain, email, loopbackPort, production, renew, logger) {
|
|
75
|
+
const LE = (await import('greenlock')).default;
|
|
76
|
+
// Storage Backend
|
|
77
|
+
const leStore = (await import('le-store-certbot')).create(leStoreConfig);
|
|
78
|
+
// ACME Challenge Handlers
|
|
79
|
+
const leChallenge = LeChallengeFs.create({
|
|
80
|
+
loopbackPort: loopbackPort,
|
|
81
|
+
webrootPath,
|
|
82
|
+
debug: false,
|
|
83
|
+
});
|
|
84
|
+
const le = LE.create({
|
|
85
|
+
server: production
|
|
86
|
+
? 'https://acme-v02.api.letsencrypt.org/directory'
|
|
87
|
+
: 'https://acme-staging-v02.api.letsencrypt.org/directory',
|
|
88
|
+
store: leStore, // handles saving of config, accounts, and certificates
|
|
89
|
+
challenges: { 'http-01': leChallenge }, // handles /.well-known/acme-challege keys and tokens
|
|
90
|
+
challengeType: 'http-01', // default to this challenge type
|
|
91
|
+
debug: false,
|
|
92
|
+
log: function () {
|
|
93
|
+
logger === null || logger === void 0 ? void 0 : logger.info(arguments, 'Lets encrypt debugger');
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
// Check in-memory cache of certificates for the named domain
|
|
97
|
+
const cert = await le.check({ domains: [domain] });
|
|
98
|
+
const opts = {
|
|
99
|
+
domains: [domain],
|
|
100
|
+
email: email,
|
|
101
|
+
agreeTos: true,
|
|
102
|
+
rsaKeySize: 2048, // 2048 or higher
|
|
103
|
+
challengeType: 'http-01',
|
|
104
|
+
};
|
|
105
|
+
if (cert) {
|
|
106
|
+
if (renew) {
|
|
107
|
+
logger && logger.info('renewing cert for ' + domain);
|
|
108
|
+
opts.duplicate = true;
|
|
109
|
+
return le.renew(opts, cert).catch(function (err) {
|
|
110
|
+
logger && logger.error(err, 'Error renewing certificates for ', domain);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
logger && logger.info('Using cached cert for ' + domain);
|
|
115
|
+
return cert;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Register Certificate manually
|
|
120
|
+
logger === null || logger === void 0 ? void 0 : logger.info('Manually registering certificate for %s', domain);
|
|
121
|
+
return le.register(opts).catch(function (err) {
|
|
122
|
+
logger === null || logger === void 0 ? void 0 : logger.error(err, 'Error registering LetsEncrypt certificates');
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export { init, getCertificates };
|
|
127
|
+
//# sourceMappingURL=letsencrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"letsencrypt.js","sourceRoot":"","sources":["../lib/letsencrypt.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { URL } from 'url';
|
|
2
|
+
import http, { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import httpProxy from 'http-proxy';
|
|
4
|
+
import { pino, Logger } from 'pino';
|
|
5
|
+
import { ProxyOptions } from './interfaces/proxy-options.js';
|
|
6
|
+
import { ProxyRoute } from './interfaces/proxy-route.js';
|
|
7
|
+
import { RouteOptions } from './interfaces/route-options.js';
|
|
8
|
+
import { Resolver, ResolverFn } from './interfaces/resolver.js';
|
|
9
|
+
export declare class Redbird {
|
|
10
|
+
private opts;
|
|
11
|
+
logger?: Logger;
|
|
12
|
+
routing: any;
|
|
13
|
+
resolvers: Resolver[];
|
|
14
|
+
certs: any;
|
|
15
|
+
lazyCerts: {
|
|
16
|
+
[key: string]: {
|
|
17
|
+
email: string;
|
|
18
|
+
production: boolean;
|
|
19
|
+
renewWithin: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
private _defaultResolver;
|
|
23
|
+
private proxy;
|
|
24
|
+
private agent;
|
|
25
|
+
private server;
|
|
26
|
+
private httpsServer;
|
|
27
|
+
private letsencryptHost;
|
|
28
|
+
private letsencryptServer;
|
|
29
|
+
get defaultResolver(): any;
|
|
30
|
+
constructor(opts?: ProxyOptions);
|
|
31
|
+
setupHttpProxy(proxy: httpProxy, websocketsUpgrade: any, log: pino.Logger, opts: ProxyOptions): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
32
|
+
/**
|
|
33
|
+
* Special resolver for handling Let's Encrypt ACME challenges.
|
|
34
|
+
* @param opts
|
|
35
|
+
*/
|
|
36
|
+
setupLetsencrypt(opts: ProxyOptions): void;
|
|
37
|
+
setupHttpsProxy(proxy: httpProxy, websocketsUpgrade: any, sslOpts: any): void;
|
|
38
|
+
addResolver(resolverFn: ResolverFn, priority?: number): this;
|
|
39
|
+
removeResolver(resolverFn: ResolverFn): this;
|
|
40
|
+
/**
|
|
41
|
+
Register a new route.
|
|
42
|
+
|
|
43
|
+
@src {String|URL} A string or a url parsed by node url module.
|
|
44
|
+
Note that port is ignored, since the proxy just listens to one port.
|
|
45
|
+
|
|
46
|
+
@target {String|URL} A string or a url parsed by node url module.
|
|
47
|
+
@opts {Object} Route options.
|
|
48
|
+
*/
|
|
49
|
+
register(opts: {
|
|
50
|
+
src: string | URL;
|
|
51
|
+
target: string | URL;
|
|
52
|
+
} & RouteOptions): Promise<void>;
|
|
53
|
+
register(src: string, opts: any): Promise<void>;
|
|
54
|
+
register(src: string | URL, target: string | URL, opts: RouteOptions): Promise<void>;
|
|
55
|
+
updateCertificates(domain: string, email: string, production: boolean, renewWithin: number, renew?: boolean): Promise<void>;
|
|
56
|
+
unregister(src: string | URL, target?: string | URL): Redbird;
|
|
57
|
+
private applyResolvers;
|
|
58
|
+
/**
|
|
59
|
+
* Resolves to route
|
|
60
|
+
* @param host
|
|
61
|
+
* @param url
|
|
62
|
+
* @returns {*}
|
|
63
|
+
*/
|
|
64
|
+
resolve(host: string, url?: string, req?: IncomingMessage): Promise<ProxyRoute | undefined>;
|
|
65
|
+
getTarget(src: string, req: IncomingMessage, res?: ServerResponse): Promise<void | import("./interfaces/proxy-target-url.js").ProxyTargetUrl>;
|
|
66
|
+
getSource(req: IncomingMessage): string;
|
|
67
|
+
close(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
Routing table structure. An object with hostname as key, and an array as value.
|
|
70
|
+
The array has one element per path associated to the given hostname.
|
|
71
|
+
Every path has a Round-Robin value (rr) and urls array, with all the urls available
|
|
72
|
+
for this target route.
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
hostA :
|
|
76
|
+
[
|
|
77
|
+
{
|
|
78
|
+
path: '/',
|
|
79
|
+
rr: 3,
|
|
80
|
+
urls: []
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
*/
|
|
85
|
+
notFound(callback: any): void;
|
|
86
|
+
shouldRedirectToHttps(target: any): boolean;
|
|
87
|
+
}
|
|
88
|
+
export declare const buildRoute: (route: string | ProxyRoute) => ProxyRoute | null;
|
|
89
|
+
export declare const buildTarget: (target: string | URL, opts?: {
|
|
90
|
+
ssl?: any;
|
|
91
|
+
useTargetHostHeader?: boolean;
|
|
92
|
+
}) => {
|
|
93
|
+
sslRedirect: boolean;
|
|
94
|
+
useTargetHostHeader: boolean;
|
|
95
|
+
hash: string;
|
|
96
|
+
host: string;
|
|
97
|
+
hostname: string;
|
|
98
|
+
href: string;
|
|
99
|
+
origin: string;
|
|
100
|
+
password: string;
|
|
101
|
+
pathname: string;
|
|
102
|
+
port: string;
|
|
103
|
+
protocol: string;
|
|
104
|
+
search: string;
|
|
105
|
+
searchParams: import("url").URLSearchParams;
|
|
106
|
+
username: string;
|
|
107
|
+
} | {
|
|
108
|
+
sslRedirect: boolean;
|
|
109
|
+
useTargetHostHeader: boolean;
|
|
110
|
+
query: string | null;
|
|
111
|
+
auth: string | null;
|
|
112
|
+
hash: string | null;
|
|
113
|
+
host: string | null;
|
|
114
|
+
hostname: string | null;
|
|
115
|
+
href: string;
|
|
116
|
+
path: string | null;
|
|
117
|
+
pathname: string | null;
|
|
118
|
+
protocol: string | null;
|
|
119
|
+
search: string | null;
|
|
120
|
+
slashes: boolean | null;
|
|
121
|
+
port: string | null;
|
|
122
|
+
};
|