Fix two wildcard-cert gaps: attaching an existing host, and the wildcard's own base domain
- Host.prototype.update() had no challengeType handling (only create() did), so selecting "Parent Wildcard" on an existing host's edit form silently did nothing. Added the same wildcard-parent lookup to update(), using a new Host.lookUpWildcardParent() -- the existing lookUp() can't be reused here since an already-created host resolves to its own leaf rather than falling through to a sibling wildcard. - A wildcard's issued cert covers both the base domain and *.base domain (altNames), but the lookup tree stores the wildcard one level below its base -- looking up the bare base domain landed on an empty parent node and found nothing. buildLookUpObj() now also stamps that parent node, order-independent (a real host explicitly created at that exact name always still wins). Verified both fixes against a real Redis-backed Host model (not just the mocked lookup-tree tests) -- see PR description. Bumps to v1.1.8. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C
This commit is contained in:
+62
-2
@@ -320,9 +320,30 @@ class Host extends Table{
|
||||
}
|
||||
}
|
||||
|
||||
async update(...args){
|
||||
async update(data, ...args){
|
||||
try{
|
||||
let out = await super.update(...args)
|
||||
// Mirror Host.create()'s challengeType handling (lines above) so an
|
||||
// existing HTTP-01 host can be attached to a parent wildcard's cert
|
||||
// after creation -- previously this was silently dropped since only
|
||||
// create() understood challengeType, leaving no way to convert an
|
||||
// existing host onto a wildcard once one was issued.
|
||||
if(data && data.challengeType === 'wildcardChild'){
|
||||
// Not Host.lookUp() -- this.host already has its own leaf in the
|
||||
// tree (it already exists), so a plain lookUp() would just find
|
||||
// itself. lookUpWildcardParent() checks the sibling "*" slot
|
||||
// instead. See its comment for why create()'s own wildcardChild
|
||||
// branch doesn't need this (a host being newly created hasn't
|
||||
// claimed its own leaf yet, so plain lookUp() already falls
|
||||
// through to the wildcard correctly there).
|
||||
let parentHost = Host.lookUpWildcardParent(this.host);
|
||||
if(parentHost && parentHost.is_wildcard){
|
||||
data.wildcard_parent = parentHost.host;
|
||||
}else{
|
||||
throw new Error(`No parent wild card for ${this.host}`);
|
||||
}
|
||||
}
|
||||
|
||||
let out = await super.update(data, ...args)
|
||||
await this.bustCache(this.host);
|
||||
await Host.buildLookUpObj();
|
||||
|
||||
@@ -385,6 +406,25 @@ class Host extends Table{
|
||||
// #record denotes a leaf node on this tree.
|
||||
if(fragments.length === 0){
|
||||
pointer[fragment]['#record'] = await this.get(host)
|
||||
|
||||
// A single-level wildcard's issued cert also covers its own
|
||||
// base domain (createWildcardCert requests altNames:
|
||||
// [domain, *.domain] -- see utils/letsencrypt.js), but the
|
||||
// base domain sits one level ABOVE the wildcard's own leaf
|
||||
// in this tree (e.g. "*.cool.mysite.com" is a child of the
|
||||
// node for "cool.mysite.com"). Without this, looking up the
|
||||
// bare base domain when it has no host of its own falls
|
||||
// through to nothing, even though the already-issued cert
|
||||
// covers it. `pointer` here is still that parent node
|
||||
// (reassigned to the child only below) -- stamp it too, but
|
||||
// only if a real, explicitly-created host at that exact
|
||||
// name hasn't already claimed this leaf (order-independent:
|
||||
// this only ever fills a gap -- a real host's own pass
|
||||
// through this loop always overwrites #record
|
||||
// unconditionally when it's finalized, see above).
|
||||
if(fragment === '*' && !pointer['#record']){
|
||||
pointer['#record'] = pointer[fragment]['#record'];
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the pointer to the next level of the tree.
|
||||
@@ -445,6 +485,26 @@ class Host extends Table{
|
||||
if(parent && parent['*'] && parent['*']['#record']) return parent['*']['#record'];
|
||||
}
|
||||
|
||||
// Find the wildcard covering @host as its own base domain (e.g.
|
||||
// "*.cool.mysite.com" for host="cool.mysite.com"), regardless of whether
|
||||
// @host is already registered as its own host. Unlike lookUp(), which
|
||||
// walks to and returns @host's own exact-match leaf when one exists, this
|
||||
// walks to that exact position and looks one level deeper at its "*"
|
||||
// child -- the sibling wildcard slot -- so it still finds the parent
|
||||
// wildcard even when @host already has its own (non-wildcard) record.
|
||||
// Used when attaching an already-created host to a wildcard after the
|
||||
// fact (see update() below); Host.create()'s own wildcardChild handling
|
||||
// can keep using plain lookUp() since a host being newly created hasn't
|
||||
// claimed its own leaf yet.
|
||||
static lookUpWildcardParent(host){
|
||||
let place = this.lookUpObj;
|
||||
for(let fragment of host.split('.').reverse()){
|
||||
if(!place[fragment]) return undefined;
|
||||
place = place[fragment];
|
||||
}
|
||||
if(place['*'] && place['*']['#record']) return place['*']['#record'];
|
||||
}
|
||||
|
||||
static async lookUpReady(){
|
||||
/*
|
||||
Wait for the lookup tree to be built.
|
||||
|
||||
Reference in New Issue
Block a user