Skip to content

Commit c0342f8

Browse files
authored
fixes #50 - purge cached cert/key when a replacement is received (#51)
1 parent a3367ad commit c0342f8

File tree

2 files changed

+72
-10
lines changed

2 files changed

+72
-10
lines changed

src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
KEY_SUFFIX,
2020
CERTIFICATE_SUFFIX,
2121
CERTIFICATE_REQ_SUFFIX,
22+
purgeCachedCertKey,
2223
} from './utils'
2324
import { AcmeClient } from './client'
2425
import fs from 'fs'
@@ -203,6 +204,9 @@ async function clientAutoModeInternal(
203204
certInfo = await readCertificateInfo(certificatePem)
204205
fs.writeFileSync(certPath, certificatePem)
205206
log.info(`Wrote certificate to ${certPath}`)
207+
208+
// Purge the cert/key in the shared dict zone if applicable
209+
purgeCachedCertKey(r)
206210
}
207211

208212
retVal.success = true

src/utils.ts

+68-10
Original file line numberDiff line numberDiff line change
@@ -991,37 +991,40 @@ export function readKey(r: NginxHTTPRequest): string {
991991
* Given a request and suffix that indicates whether the caller wants the cert
992992
* or key, return the requested object from cache if possible, falling back to
993993
* disk.
994-
* @param {NginxHTTPRequest} r - The Nginx HTTP request object.
995-
* @param {string} suffix - The file suffix that indicates whether we want a cert or key
996-
* @returns {string} - The contents of the cert or key
994+
* @param {NginxHTTPRequest} r The Nginx HTTP request object.
995+
* @param {string} suffix The file suffix that indicates whether we want a cert or key
996+
* @returns {string} The contents of the cert or key
997997
*/
998998
function readCertOrKey(
999999
r: NginxHTTPRequest,
10001000
suffix: typeof CERTIFICATE_SUFFIX | typeof KEY_SUFFIX
10011001
): string {
10021002
let data = ''
1003-
const prefix = acmeDir(r)
1004-
const commonName = acmeCommonName(r)
1005-
const zone = acmeZoneName(r)
1006-
const path = joinPaths(prefix, commonName + suffix)
1007-
const key = ['acme', path].join(':')
1003+
const base = certOrKeyBase(r)
1004+
const path = base + suffix
1005+
const key = cacheKey(path)
10081006

1009-
// if the zone is not defined in nginx.conf, then we will bypass the cache
1010-
const cache = zone && ngx.shared && ngx.shared[zone]
1007+
const cache = ngxSharedDict(r)
10111008

1009+
// ensure the shared dict zone is configured before checking cache
10121010
if (cache) {
10131011
data = (cache.get(key) as string) || ''
10141012
if (data) {
1013+
// Return cached value
10151014
return data
10161015
}
10171016
}
1017+
1018+
// filesystem fallback
10181019
try {
10191020
data = fs.readFileSync(path, 'utf8')
10201021
} catch (e) {
10211022
log.error('error reading from file:', path, `. Error=${e}`)
10221023
return ''
10231024
}
1025+
// try caching value read from disk in the shared dict zone, if configured
10241026
if (cache && data) {
1027+
const zone = acmeZoneName(r)
10251028
try {
10261029
cache.set(key, data)
10271030
log.debug(`wrote to cache: ${key} zone: ${zone}`)
@@ -1032,3 +1035,58 @@ function readCertOrKey(
10321035
}
10331036
return data
10341037
}
1038+
1039+
/**
1040+
* Returns the NGINX shared dict zone if configured.
1041+
* @param r - The request or periodic session
1042+
* @returns Shared dict zone or `null`
1043+
*/
1044+
function ngxSharedDict(
1045+
r: NginxHTTPRequest | NginxPeriodicSession
1046+
): NgxSharedDict | null {
1047+
const zone = acmeZoneName(r)
1048+
if (zone && ngx.shared) {
1049+
const sharedZone = ngx.shared[zone]
1050+
if (sharedZone) {
1051+
return sharedZone
1052+
}
1053+
}
1054+
return null
1055+
}
1056+
1057+
/**
1058+
* Removes cached cert and key from the shared dict zone, if applicable.
1059+
* @param r - The request or periodic session
1060+
*/
1061+
export function purgeCachedCertKey(
1062+
r: NginxHTTPRequest | NginxPeriodicSession
1063+
): void {
1064+
const objPrefix = certOrKeyBase(r)
1065+
const cache = ngxSharedDict(r)
1066+
1067+
if (cache) {
1068+
cache.delete(cacheKey(objPrefix + CERTIFICATE_SUFFIX))
1069+
cache.delete(cacheKey(objPrefix + KEY_SUFFIX))
1070+
}
1071+
}
1072+
1073+
/**
1074+
* Prepend our namespace to a given cache key
1075+
* @param key Path to the cert or key
1076+
* @returns Shared dict cache ke
1077+
*/
1078+
function cacheKey(key: string) {
1079+
return 'acme:' + key
1080+
}
1081+
1082+
/**
1083+
* Returns the base path to store a cert or key
1084+
* @param path Path to the cert or key
1085+
* @returns Path string
1086+
*/
1087+
function certOrKeyBase(r: NginxHTTPRequest | NginxPeriodicSession): string {
1088+
const prefix = acmeDir(r)
1089+
const commonName = acmeCommonName(r)
1090+
const path = joinPaths(prefix, commonName)
1091+
return path
1092+
}

0 commit comments

Comments
 (0)