Skip to content

Commit

Permalink
fixes #50 - purge cached cert/key when a replacement is received (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
zsteinkamp authored Mar 25, 2024
1 parent a3367ad commit c0342f8
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
KEY_SUFFIX,
CERTIFICATE_SUFFIX,
CERTIFICATE_REQ_SUFFIX,
purgeCachedCertKey,
} from './utils'
import { AcmeClient } from './client'
import fs from 'fs'
Expand Down Expand Up @@ -203,6 +204,9 @@ async function clientAutoModeInternal(
certInfo = await readCertificateInfo(certificatePem)
fs.writeFileSync(certPath, certificatePem)
log.info(`Wrote certificate to ${certPath}`)

// Purge the cert/key in the shared dict zone if applicable
purgeCachedCertKey(r)
}

retVal.success = true
Expand Down
78 changes: 68 additions & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -991,37 +991,40 @@ export function readKey(r: NginxHTTPRequest): string {
* Given a request and suffix that indicates whether the caller wants the cert
* or key, return the requested object from cache if possible, falling back to
* disk.
* @param {NginxHTTPRequest} r - The Nginx HTTP request object.
* @param {string} suffix - The file suffix that indicates whether we want a cert or key
* @returns {string} - The contents of the cert or key
* @param {NginxHTTPRequest} r The Nginx HTTP request object.
* @param {string} suffix The file suffix that indicates whether we want a cert or key
* @returns {string} The contents of the cert or key
*/
function readCertOrKey(
r: NginxHTTPRequest,
suffix: typeof CERTIFICATE_SUFFIX | typeof KEY_SUFFIX
): string {
let data = ''
const prefix = acmeDir(r)
const commonName = acmeCommonName(r)
const zone = acmeZoneName(r)
const path = joinPaths(prefix, commonName + suffix)
const key = ['acme', path].join(':')
const base = certOrKeyBase(r)
const path = base + suffix
const key = cacheKey(path)

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

// ensure the shared dict zone is configured before checking cache
if (cache) {
data = (cache.get(key) as string) || ''
if (data) {
// Return cached value
return data
}
}

// filesystem fallback
try {
data = fs.readFileSync(path, 'utf8')
} catch (e) {
log.error('error reading from file:', path, `. Error=${e}`)
return ''
}
// try caching value read from disk in the shared dict zone, if configured
if (cache && data) {
const zone = acmeZoneName(r)
try {
cache.set(key, data)
log.debug(`wrote to cache: ${key} zone: ${zone}`)
Expand All @@ -1032,3 +1035,58 @@ function readCertOrKey(
}
return data
}

/**
* Returns the NGINX shared dict zone if configured.
* @param r - The request or periodic session
* @returns Shared dict zone or `null`
*/
function ngxSharedDict(
r: NginxHTTPRequest | NginxPeriodicSession
): NgxSharedDict | null {
const zone = acmeZoneName(r)
if (zone && ngx.shared) {
const sharedZone = ngx.shared[zone]
if (sharedZone) {
return sharedZone
}
}
return null
}

/**
* Removes cached cert and key from the shared dict zone, if applicable.
* @param r - The request or periodic session
*/
export function purgeCachedCertKey(
r: NginxHTTPRequest | NginxPeriodicSession
): void {
const objPrefix = certOrKeyBase(r)
const cache = ngxSharedDict(r)

if (cache) {
cache.delete(cacheKey(objPrefix + CERTIFICATE_SUFFIX))
cache.delete(cacheKey(objPrefix + KEY_SUFFIX))
}
}

/**
* Prepend our namespace to a given cache key
* @param key Path to the cert or key
* @returns Shared dict cache ke
*/
function cacheKey(key: string) {
return 'acme:' + key
}

/**
* Returns the base path to store a cert or key
* @param path Path to the cert or key
* @returns Path string
*/
function certOrKeyBase(r: NginxHTTPRequest | NginxPeriodicSession): string {
const prefix = acmeDir(r)
const commonName = acmeCommonName(r)
const path = joinPaths(prefix, commonName)
return path
}

0 comments on commit c0342f8

Please sign in to comment.