add dig for on change checking rfc2136

This commit is contained in:
ElevenNotes
2024-01-22 15:01:37 +01:00
parent fad60b7019
commit 7e67e971ae
4 changed files with 58 additions and 9 deletions

View File

@@ -75,6 +75,7 @@ docker run --name traefik-rfc2136-demo \
| `LABELS_TIMEOUT` | how many seconds after an interval the keys should stay till they expire in seconds | 30 |
| `LABELS_WEBHOOK` | URL to call on each event or poll for each container | |
| `LABELS_WEBHOOK_AUTH_BASIC` | Basic authentication to use in the form of "username:password" for the webhook | |
| `LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE` | Only update DNS entries if they are new or changed (will use dig on each call!) | false |
## Parent image
* [11notes/node:stable](https://hub.docker.com/r/11notes/node)

25
rootfs/labels/dig.js Normal file
View File

@@ -0,0 +1,25 @@
const { spawn } = require('node:child_process');
exports.dig = async(resolver, type, record) => {
return(new Promise((resolve, reject) => {
const dig = spawn('/usr/bin/dig', ['+short', '+answer', type, record, `@${resolver}`]);
const io = {stdout:'', stderr:''};
dig.stderr.on('data', data => {io.stderr += data.toString()});
dig.stdout.on('data', data => {io.stdout += (data.toString()).replace(/[\r\n]*$/ig, '')});
dig.on('error', error => {reject(error)});
dig.on('close', code =>{
switch(true){
case /no servers could be reached/ig.test(io.stdout): io.stderr += io.stdout; break;
}
if(code === 0){
if(io.stderr.length > 0){
reject(io.stderr);
}else{
resolve(io.stdout);
}
}else{
reject(io.stderr);
}
});
}));
}

View File

@@ -4,12 +4,14 @@ process.once('SIGINT', () => process.exit(0));
const Docker = require('dockerode');
const redis = require('redis');
const { nsupdate } = require('./nsupdate');
const { dig } = require('./dig');
const { elevenLogJSON } = require('/labels/lib/util.js');
const ENV_REDIS_INTERVAL = parseInt(process.env?.LABELS_INTERVAL || 300);
const ENV_REDIS_TIMEOUT = parseInt(process.env?.LABELS_TIMEOUT|| 30);
const ENV_LABELS_WEBHOOK = process.env?.LABELS_WEBHOOK;
const ENV_LABELS_WEBHOOK_AUTH_BASIC = process.env?.LABELS_WEBHOOK_AUTH_BASIC;
const ENV_LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE = process.env?.LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE || false;
elevenLogJSON('info', {config:{
LABELS_INTERVAL :ENV_REDIS_INTERVAL,
@@ -100,6 +102,7 @@ class Labels{
if(!error){
const update = (/start|poll/i.test(status)) ? true : false;
const container = {
name:(data?.Name || data?.id).replace(/^\//i, ''),
event:status,
labels:{
traefik:[],
@@ -112,7 +115,7 @@ class Labels{
LAN:{server:'', key:'', commands:[]},
}
elevenLogJSON('info', `inspect container {${(data?.Name || data?.id).replace(/^\//i, '')}}${(
elevenLogJSON('info', `container {${container.name}}.inspect()${(
(null === status) ? '' : ` event[${status}]`
)}`);
@@ -143,7 +146,9 @@ class Labels{
if(!update){
data.Config.Labels[label] = data.Config.Labels[label].replace(/update add/i, 'update delete');
}
rfc2136[type].commands.push(data.Config.Labels[label]);
if((ENV_LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE && !await this.rfc2136KnownRecord(rfc2136[type].server, data.Config.Labels[label])) || true){
rfc2136[type].commands.push(data.Config.Labels[label]);
}
}
break;
}
@@ -152,6 +157,7 @@ class Labels{
for(const type in rfc2136){
if(rfc2136[type].commands.length > 0 && rfc2136[type].server && rfc2136[type].key){
try{
elevenLogJSON('info', `container {${container.name}}.rfc2136() update ${type} DNS entries`);
await nsupdate(rfc2136[type].server, rfc2136[type].key, rfc2136[type].commands);
}catch(e){
elevenLogJSON('error', e);
@@ -174,6 +180,19 @@ class Labels{
});
}));
}
async rfc2136KnownRecord(server, nsupdate){
const matches = nsupdate.match(/update add (\S+) \d+ (\S+) (\S+)/i);
if(matches && matches.length >= 4){
try{
const record = await dig(server, matches[2], matches[1]);
return(matches[1].match(new RegExp(record, 'ig')));
}catch(e){
elevenLogJSON('error', e);
return(false);
}
}
}
}
new Labels().watch();

View File

@@ -1,22 +1,26 @@
const { spawn } = require('node:child_process');
exports.nsupdate = async(server, key, commands) => {
commands.unshift(`server ${server}`);
commands.push('send');
commands.push('quit');
return(new Promise((resolve, reject) => {
const nsupdate = spawn('/usr/bin/nsupdate', ['-y', key]);
let errors = '';
nsupdate.stderr.on('data', data =>{errors += data.toString()});
const io = {stdout:'', stderr:''};
nsupdate.stderr.on('data', data => {io.stderr += data.toString()});
nsupdate.stdout.on('data', data => {io.stdout += (data.toString()).replace(/[\r\n]*$/ig, '')});
nsupdate.on('error', error => {reject(error)});
nsupdate.on('exit', code =>{
nsupdate.on('close', code =>{
if(code === 0){
if(errors.length > 0){
reject(errors);
if(io.stderr.length > 0){
reject(io.stderr);
}else{
resolve(true);
resolve(io.stdout);
}
}else{
reject(errors);
reject(io.stderr);
}
});
for(const command of commands){