Skip to content

Commit

Permalink
Check if certificate needs to be renewed
Browse files Browse the repository at this point in the history
  • Loading branch information
Netfloex committed Mar 4, 2022
1 parent e936d96 commit 560565b
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 9 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"@types/fs-extra": "^9.0.13",
"@types/jest": "^27.4.1",
"@types/js-yaml": "^4.0.5",
"@types/luxon": "^2.3.0",
"@types/node": "^17.0.21",
"@types/node-forge": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"@webantic/nginx-config-parser": "^1.4.1",
Expand All @@ -40,6 +42,8 @@
"jest": "^27.5.1",
"js-yaml": "^4.1.0",
"json5": "^2.2.0",
"luxon": "^2.3.1",
"node-forge": "^1.2.1",
"prettier": "^2.5.1",
"rollup": "^2.68.0",
"rollup-plugin-terser": "^7.0.2",
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "@utils/cloudflare";
import { createConfigFiles } from "@utils/createConfigFiles";
import { editNginxConfig } from "@utils/editNginxConfig";
import { filterServersWithSslFiles } from "@utils/filterServersWithSslFiles";
import { filterServersWithValidSslFiles } from "@utils/filterServersWithValidSslFiles";
import log from "@utils/log";
import parseUserConfig from "@utils/parseUserConfig";
import settings from "@utils/settings";
Expand Down Expand Up @@ -132,7 +132,7 @@ const main = async (): Promise<ExitCode> => {
);

const sslServers = config.servers.filter((server) => !server.disable_cert);
const serversWithKeys = await filterServersWithSslFiles(sslServers);
const serversWithKeys = await filterServersWithValidSslFiles(sslServers);
const serversWithoutKeys = sslServers.filter(
(server) => !serversWithKeys.includes(server)
);
Expand All @@ -141,8 +141,8 @@ const main = async (): Promise<ExitCode> => {

await Promise.all(promises);

await certbot(serversWithoutKeys);
await filterServersWithSslFiles(serversWithoutKeys, true);
await certbot(serversWithKeys);
await filterServersWithValidSslFiles(serversWithoutKeys, true);
await Promise.all(createConfigFiles(serversWithoutKeys));

return ExitCode.success;
Expand Down
11 changes: 7 additions & 4 deletions src/lib/certbot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@ const hasCertbot = async (): Promise<boolean> => {
try {
await exec("command -v certbot", true);
} catch (error) {
if (error instanceof Error && error.message.endsWith("127")) {
log.noCertbot();
return false;
} else throw error;
log.noCertbot();
return false;
}
return true;
};

export const certbot = async (servers: SimpleServer[]): Promise<void> => {
if (!servers.length) {
return log.certbotNotNeeded();
}

if (!hasMail() || settings.disableCertbot || !(await hasCertbot())) {
return log.skippingCertbot();
}

log.startingCertbot(servers.length);

const certNames = [
...new Set(
servers.map((server) => server.certbot_name ?? server.server_name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { pathExists } from "fs-extra";

import log from "@utils/log";
import { parseCertificateExpiry } from "@utils/parseCertificateExpiry";
import { sslFilesFor } from "@utils/sslFilesFor";

import { SimpleServer } from "@models/ParsedConfig";

export const filterServersWithSslFiles = async (
export const filterServersWithValidSslFiles = async (
servers: SimpleServer[],
last = false
): Promise<SimpleServer[]> => {
Expand All @@ -16,10 +17,22 @@ export const filterServersWithSslFiles = async (

for (const file of sslFilePaths) {
if (!(await pathExists(file))) {
// File does not exists
log.missingSslFiles(server.server_name, last);
continue server;
}
}
const days = (await parseCertificateExpiry(sslFilePaths[0]))
.diffNow()
.as("days");

if (days < 30) {
// Certificate expires in less than 30 days
log.certificateExpiresIn(server.server_name, Math.round(days));
continue server;
}

log.certificateValid(server, days);

out.push(server);
}
Expand Down
25 changes: 25 additions & 0 deletions src/utils/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { ZodIssue } from "zod";

import settings from "@utils/settings";

import { SimpleServer } from "@models/ParsedConfig";

class Log {
public log;

Expand Down Expand Up @@ -86,6 +88,12 @@ class Log {
this.error(chalk`Certbot binary not found.`);
}

public certbotNotNeeded() {
this.info(
chalk`All domains have their valid certificates, skipping certbot.`
);
}

public skippingCertbot() {
this.info(chalk`Skipped requesting certificates`);
}
Expand Down Expand Up @@ -113,6 +121,23 @@ class Log {
);
}

public certificateValid(server: SimpleServer, days: number) {
this.info(
chalk`Certificate for {dim ${
server.certbot_name ?? server.server_name
}} is valid for {bold ${Math.round(days)}} days.`
);
}

public certificateExpiresIn(certificate: string, days: number) {
const hasExpired = days <= 0;
this.info(
chalk`The certificate {dim ${certificate}}, expire${
hasExpired ? "d" : "s in"
} ${days} days ${hasExpired ? "ago" : ""}`
);
}

// Nginx Config

public configDone(config: string) {
Expand Down
17 changes: 17 additions & 0 deletions src/utils/parseCertificateExpiry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { readFile } from "fs-extra";
import { DateTime } from "luxon";
import { pki } from "node-forge";

export const parseCertificateExpiry = async (
certificateFile: string
): Promise<DateTime> => {
const certificate = await readFile(certificateFile, "");

try {
const cert = pki.certificateFromPem(certificate);
return DateTime.fromJSDate(cert.validity.notAfter);
} catch (error) {
console.error(error);
return DateTime.fromMillis(0);
}
};
22 changes: 22 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,18 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=

"@types/luxon@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.3.0.tgz#0f4d912c385e47890374cb694da9bc93bacbe2b0"
integrity sha512-mWXdRlg+5dWvxU+uaijB2RY5NrJtMEXR6j+D6W66hPuezSVXrQqQvWa/JNHntgEYgjzeoVRrQVmMWAbKjUJiFQ==

"@types/node-forge@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.0.0.tgz#0b4e9507209485945115a4db4879f39632230593"
integrity sha512-h0bgwPKq5u99T9Gor4qtV1lCZ41xNkai0pie1n/a2mh2/4+jENWOlo7AJ4YKxTZAnSZ8FRurUpdIN7ohaPPuHA==
dependencies:
"@types/node" "*"

"@types/node@*":
version "16.0.0"
resolved "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz"
Expand Down Expand Up @@ -3290,6 +3302,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"

luxon@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a"
integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==

magic-string@^0.25.7:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
Expand Down Expand Up @@ -3393,6 +3410,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=

node-forge@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.2.1.tgz#82794919071ef2eb5c509293325cec8afd0fd53c"
integrity sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==

node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
Expand Down

0 comments on commit 560565b

Please sign in to comment.