Updated DNS-01 validation

This commit is contained in:
2025-12-31 16:00:21 -05:00
parent d944ca6957
commit ef3fffdef2
3 changed files with 176 additions and 35 deletions
+44 -10
View File
@@ -39,17 +39,12 @@ class Host extends Table{
static async addCache(host, parentOBJ){
try{
console.log('addCache host:', host, 'parentOBJ host', parentOBJ.host)
parentOBJ = await this.get(parentOBJ.host);
if(parentOBJ.is_cache){
console.log('addCache parentOBJ is chace, skipping')
return;
}
console.log('addCache, corrent parent?', parentOBJ.wildcard_parent || parentOBJ.host)
console.log('addCache, got parent', parentOBJ)
await this.create({
...parentOBJ,
host: host,
@@ -63,7 +58,7 @@ class Host extends Table{
parent: parentOBJ.host
});
}catch(error){
console.error('add cache error', {...parentOBJ, host, is_cache: true}, error)
console.error('add cache error', {...parentOBJ, host, is_cache: true}, error);
throw error;
}
}
@@ -81,18 +76,35 @@ class Host extends Table{
}catch(error){
console.error('bust cache error', error)
throw error;
}
}
static async create(data, ...args){
try{
if(data.is_wildcard) await this.validateWildcardCreate(data, args);
// Validate requested host is valid host and domain
if(data.challengeType === 'DNS-01-wildcard') await this.validateWildcardCreate(data, args);
// Validate requested host has a valid wildcard parent
if(data.challengeType === 'wildcardChild'){
let parentHost = await this.lookUp(data.host);
console.log('parentHost:', parentHost)
if(parentHost.is_wildcard){
data.wildcard_parent = parentHost.host;
}else{
throw new Error(`No parent wild card for ${data.host}`);
}
}
// Create the new host entry
let out = await super.create(data, ...args);
// Update the lookup table to reflect new host
await this.buildLookUpObj();
if(out.is_wildcard) out.createWildcardCert();
// Fire the request for the wild card cert
// This is "back ground" job, await is intentionally missing
if(out.challengeType === 'DNS-01-wildcard') out.createWildcardCert();
return out;
@@ -134,7 +146,8 @@ class Host extends Table{
type:'TXT',
name: `_acme-challenge${parts.sub ? `.${parts.sub}` : ''}`,
data: `${keyAuthorization}`
}
},
true // Force the record creation, even if the record exists
);
}catch(error){
console.log('model Host challengeCreateFn error:', error)
@@ -215,6 +228,26 @@ class Host extends Table{
}
}
async checkWildcardForRenew(){
try{
if(this.is_wildcard && Date.now() > this.wildcard_expires - (30 * 24 * 60 * 60 * 1000)){
this.createWildcardCert();
}
}catch(error){
throw error;
}
}
static async checkWildcardForRenew(){
try{
for(let host of await this.listDetail()){
host.createWildcardCert();
}
}catch(error){
throw error;
}
}
async update(...args){
try{
let out = await super.update(...args)
@@ -330,6 +363,7 @@ class Host extends Table{
// Check every 5ms to see if the look up tree is ready
while(!this.__lookUpIsReady) await new Promise(r => setTimeout(r, 5));
return true;
}
}
+11
View File
@@ -41,6 +41,17 @@ router.get('/lookup/:item', async function(req, res, next){
}
});
router.get('/lookupobj', async function(req, res, next){
try{
return res.json({
results: Model.lookUpObj,
});
}catch(error){
return next(error);
}
});
router.get('/:item', async function(req, res, next){
try{
+121 -25
View File
@@ -13,6 +13,7 @@
div.form-group{
margin-bottom: 1em;
}
/* my Div class for my search bar */
.search-wrapper {
display: flex;
@@ -20,6 +21,7 @@
align-items: center;
margin-top: 10px;
}
/* The input bar */
input {
font-size: 1rem;
@@ -28,17 +30,26 @@
border-top-right-radius: 5px !important;
border-bottom-right-radius: 5px !important;
}
.challengeType-container {
pointer-events: none; /* Prevents clicking */
opacity: 0.5; /* Greys it out */
filter: grayscale(1); /* Removes blue/color tint */
cursor: not-allowed;
}
</style>
<script type="text/javascript">
var $editHostForm;
// Parse the JSON object for a host to something the UI wants
function hostParseRow(host) {
host['updated_on_text'] = moment(host['updated_on'], "x").fromNow();
host['wildcard_expires_text'] = moment(host['wildcard_expires'], "x").fromNow();
host['targetssl_text'] = host['targetssl'] ? 'https://' : 'http://';
host['forcessl_text'] = host['forcessl'] ? 'https://' : 'http://';
host['wildcard_text'] = host['is_wildcard'] ? host['wildcard_status'] : 'Auto';
host['wildcard_text'] = host['wildcard_parent'] ? 'Child' : host['wildcard_text'];
host['wildcard_text_bg'] = 'warning';
if(!host['is_wildcard']){
host['wildcard_text_bg'] = 'success';
@@ -80,6 +91,7 @@
function hostEditOpen(btn, host){
hostEditCancle();
console.log('host:', host)
host = $.scope.hosts.getByKey(host);
host.__jq_$el.addClass('table-warning');
$editHostForm.find('[name=is_wildcard').attr('disabled', true);
@@ -123,15 +135,73 @@
}
};
async function verifyWildcardRequirements(host){
try{
let res = await app.api.get(`dns/domain/${host}`);
return res.results.length === 1;
}catch(error){
return false;
}
}
async function hostMatchWildcard(host){
try{
let res = await app.api.get(`host/lookup/${host}`);
if(res.results && res.results.is_wildcard){
return res.results;
}
}catch(error){
return false;
}
}
$(document).ready(function(){
// Clone the new host form to be used on edit requests.
$editHostForm = $('#addHost').clone();
$editHostForm.find('hr.buttonBreak').nextAll().remove();
// $editHostForm.find('.autoSll').addClass('bg-secondary');
hostPopulate(); //populate the table
// Populate the host UI table
hostPopulate();
// Determine what lets encrypt challenge type the given host name can use
$hostField = $('[name=host');
$hostField.keyup(async function(){
// Reset the allowed types on start
$('#challengeType-child-container').addClass('challengeType-container');
$('#challengeType-DNS-01-wildcard-container').addClass('challengeType-container');
let host = $hostField.val();
// If its a wild card, we must check if the domain has a registered
// provider.
if(host.startsWith("*.") && await verifyWildcardRequirements(host)){
$('#challengeType-DNS-01-wildcard-container').removeClass('challengeType-container');
return;
}
// Check if a wildcard cert is available for the given host.
let wildcardParent = await hostMatchWildcard($hostField.val());
if(wildcardParent){
$('#challengeType-child-container').removeClass('challengeType-container');
$('#challengeType-child-relatedHost').text(wildcardParent.host);
return;
}
// If we hit here, make sure the form is reverted to a valid state
$('#challengeType-child-relatedHost').text('');
$('#challengeType-HTTP-01').prop('checked', true);
});
//
$.scope.hosts.take = function($el, item, list){
$el.addClass('table-danger');
$el.fadeOut(1000, function(){
$el.fadeOut(500, function(){
$el.remove()
});
};
@@ -149,9 +219,9 @@
$el.slideUp();
};
app.subscribe(/^model:Host/, function(data, topic){
console.log(topic, data);
});
// app.subscribe(/^model:Host/, function(data, topic){
// console.log(topic, data);
// });
app.subscribe(/^model:Host:create/, function(data, topic){
let [a,b, action, host] = topic.split(':');
@@ -259,26 +329,8 @@
</div>
</div>
<div class="form-group autoSll">
<label class="form-label">
Auto SSL
</label>
<div class="radio">
<label>
<input type="radio" name="is_wildcard" id="is_wildcard-false" value="false" checked>
On demand certs
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="is_wildcard" id="is_wildcard-true" value="true">
Request Wildcard cert
</label>
</div>
</div>
<div class="form-group">
<label for='host' class="form-label">
<label for="host" class="form-label">
Incoming Host Name
</label>
<div>
@@ -287,6 +339,30 @@
</div>
</div>
<div class="form-group autoSll">
<label class="form-label">
SSL <a href="https://letsencrypt.org/docs/challenge-types/" target="_blank">Validation Type</a>:
</label>
<div class="radio" id="challengeType-HTTP-01-container">
<label>
<input type="radio" name="challengeType" id="challengeType-HTTP-01" value="HTTP-01" checked>
HTTP-01
</label>
</div>
<div class="radio challengeType-container" id="challengeType-DNS-01-wildcard-container">
<label>
<input type="radio" name="challengeType" id="challengeType-DNS-01-wildcard" value="DNS-01-wildcard">
DNS-01 Wildcard
</label>
</div>
<div class="radio challengeType-container" id="challengeType-child-container">
<label>
<input type="radio" name="challengeType" id="challengeType-wildcardChild" value="wildcardChild">
Parent Wildcard from <i id="challengeType-child-relatedHost"></i>
</label>
</div>
</div>
<div class="mb-3 form-group">
<label for="ip" class="form-label">
Target IP or Host Name
@@ -362,6 +438,19 @@
<table class="m-0 card-body table table-striped overflow-x-scroll">
<thead>
<th>
<input type="checkbox"
onclick="$('.host_checkbox:visible').each((i, element)=>{
$(element).prop('checked', $(this).prop('checked'));
})"
/>
<br />
<button type="button" class="btn btn-sm btn-danger" onclick="$('.host_checkbox:checked').each((i, element)=>{
app.api.delete(`host/${$(element).parents('[jq-repeat-index]').attr('jq-repeat-index')}`, function(){});
})">
<i class="fa-solid fa-trash-can"></i>
</button>
</th>
<th>
SSL Expire
</th>
@@ -380,7 +469,10 @@
</thead>
<tbody>
<tr action="api" jq-repeat="hosts" jq-repeat-index='host' style="display:none">
<tr action="api" jq-repeat="hosts" jq-index-key='host' style="display:none">
<td>
<input type="checkbox" class="host_checkbox">
</td>
<td class="table-{{wildcard_text_bg}}">
{{{ wildcard_text }}}
{{#wildcard_expires}}
@@ -395,6 +487,10 @@
<br />
<img width="24px" src="{{displayIconHtml}}" /> {{displayName}} - {{name}}
{{/domain.provider}}
{{#wildcard_parent}}
<i>{{wildcard_parent}}</i>
{{/wildcard_parent}}
</td>
<td>
{{{ targetssl_text }}}{{ ip }}:{{ targetPort }}