Commit 10966e45 authored by nanahira's avatar nanahira

auto check certs and enable https by default

parent b5c2f04f
Pipeline #20345 passed with stages
in 38 minutes and 33 seconds
......@@ -3,14 +3,17 @@ import Mustache from 'mustache';
import path from 'path';
import { getData } from './src/site';
console.log(
Mustache.render(
fs.readFileSync(
path.join(__dirname, '..', 'views', 'nginx.conf.mustache'),
'utf8',
async function main() {
console.log(
Mustache.render(
fs.readFileSync(
path.join(__dirname, '..', 'views', 'nginx.conf.mustache'),
'utf8',
),
await getData(process.env),
undefined,
{ escape: (v) => v },
),
getData(process.env),
undefined,
{ escape: (v) => v },
),
);
);
}
main();
{
"name": "myproject",
"name": "nginx-proxy",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "myproject",
"name": "nginx-proxy",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"lodash": "^4.17.21",
"mustache": "^4.2.0"
},
"devDependencies": {
"@types/jest": "^28.1.6",
"@types/lodash": "^4.14.191",
"@types/mustache": "^4.2.1",
"@types/node": "^18.7.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
......@@ -1208,6 +1210,12 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"dev": true
},
"node_modules/@types/mustache": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.1.tgz",
......@@ -3512,6 +3520,11 @@
"node": ">=8"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
......@@ -5709,6 +5722,12 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"@types/lodash": {
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"dev": true
},
"@types/mustache": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.1.tgz",
......@@ -7406,6 +7425,11 @@
"p-locate": "^4.1.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
......
......@@ -41,6 +41,7 @@
},
"devDependencies": {
"@types/jest": "^28.1.6",
"@types/lodash": "^4.14.191",
"@types/mustache": "^4.2.1",
"@types/node": "^18.7.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
......@@ -55,6 +56,7 @@
"typescript": "^4.7.4"
},
"dependencies": {
"lodash": "^4.17.21",
"mustache": "^4.2.0"
}
}
import { X509Certificate } from 'crypto';
import * as fs from 'fs';
import path from 'path';
import _ from 'lodash';
class Cert {
public cert = new X509Certificate(
fs.readFileSync(path.join('/etc/nginx/certs', this.dir, 'fullchain.pem')),
);
constructor(public dir: string) {}
isNotExpired() {
const now = new Date();
const validFrom = new Date(this.cert.validFrom);
const validTo = new Date(this.cert.validTo);
return now > validFrom && now < validTo;
}
isOkWithDomain(domain: string) {
return this.cert.checkHost(domain);
}
isOkWithDomains(domains: string[]) {
return domains.every((domain) => this.isOkWithDomain(domain));
}
}
const certs = fs
.readdirSync('/etc/nginx/certs')
.filter((dir) =>
fs.existsSync(path.join('/etc/nginx/certs', dir, 'fullchain.pem')),
)
.map((dir) => new Cert(dir))
.filter((cert) => cert.isNotExpired());
console.error(certs);
export function pickCert(domains: string[]) {
const okCerts = certs.filter((cert) => cert.isOkWithDomains(domains));
if (!okCerts.length) {
return;
}
return _.maxBy(okCerts, (cert) => cert.cert.validTo).dir;
}
import { pickCert } from './check-cert';
import { Parser } from './parser';
import { getSiteNames } from './utility';
......@@ -45,23 +46,33 @@ export interface RenderData {
httpExtra?: string[];
}
function getSiteData(
async function getSiteData(
domain: string,
input: Record<string, string> = process.env,
): SiteRenderData {
): Promise<SiteRenderData> {
const parser = new Parser(`SITE_${domain}_`, input);
let https: SiteHttps;
const httpsCert = parser.getString('HTTPS');
if (httpsCert) {
https = {
cert: httpsCert,
ports: parser.getArrayNumber('HTTPS_PORT') || [443],
redirect: !parser.getBoolean('HTTPS_NOREDIR'),
hsts: parser.getBoolean('HSTS'),
};
const domains = domain.split('+');
if (httpsCert !== '0' && httpsCert !== 'false') {
const cert =
!httpsCert ||
httpsCert === '1' ||
httpsCert === 'true' ||
httpsCert === 'auto'
? pickCert(domains)
: httpsCert;
if (cert) {
https = {
cert,
ports: parser.getArrayNumber('HTTPS_PORT') || [443],
redirect: !parser.getBoolean('HTTPS_NOREDIR'),
hsts: parser.getBoolean('HSTS'),
};
}
}
return {
domains: domain.split('+'),
domains: domains,
ports: parser.getArrayNumber('PORT') || [80],
https,
headers: Object.entries(parser.getDict('HEADER')).map(([name, value]) => ({
......@@ -82,9 +93,9 @@ function getSiteData(
};
}
export function getData(
export async function getData(
input: Record<string, string> = process.env,
): RenderData {
): Promise<RenderData> {
const parser = new Parser('', input);
return {
purgeAllowed: parser.getArray('PURGE_ALLOWED'),
......@@ -98,7 +109,9 @@ export function getData(
ticketKeyPath:
parser.getString('TICKET_KEY_PATH') || '/etc/nginx/generated/ticket.key',
certsPath: parser.getString('CERTS_PATH') || '/etc/nginx/certs',
sites: getSiteNames().map((domain) => getSiteData(domain, input)),
sites: await Promise.all(
getSiteNames().map((domain) => getSiteData(domain, input)),
),
httpExtra: parser.getArray('HTTP_EXTRA'),
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment