Compare commits

...
12 Commits
Author SHA1 Message Date
ElevenNotes 085de10ca1 add debug info 2024-01-23 09:14:35 +01:00
ElevenNotes 85575ce073 add skip info 2024-01-22 17:09:00 +01:00
ElevenNotes b754494a98 debug 2024-01-22 17:05:18 +01:00
ElevenNotes 6ac866ef5e match all TXT 2024-01-22 16:53:33 +01:00
ElevenNotes 426826532d obfuscate key in logs 2024-01-22 16:50:56 +01:00
ElevenNotes 82b132c68e add debug 2024-01-22 16:42:00 +01:00
ElevenNotes 3f89d3ddd9 add debug 2024-01-22 16:33:25 +01:00
ElevenNotes ff8e1d9cd9 0.1.9 > latest 2024-01-22 15:35:11 +01:00
ElevenNotes 2d5ef49071 add dig 2024-01-22 15:33:28 +01:00
ElevenNotes 76a9a7261f log container layout 2024-01-22 15:06:34 +01:00
ElevenNotes 7e67e971ae add dig for on change checking rfc2136 2024-01-22 15:01:37 +01:00
ElevenNotes fad60b7019 latest 2024-01-21 09:08:56 +01:00
6 changed files with 100 additions and 17 deletions
+6 -4
View File
@@ -1,5 +1,5 @@
# Alpine :: Traefik Labels
![size](https://img.shields.io/docker/image-size/11notes/traefik-labels/amd64-0.1.8?color=0eb305) ![version](https://img.shields.io/docker/v/11notes/traefik-labels/amd64-0.1.8?color=eb7a09) ![pulls](https://img.shields.io/docker/pulls/11notes/traefik-labels?color=2b75d6) ![activity](https://img.shields.io/github/commit-activity/m/11notes/docker-traefik-labels?color=c91cb8) ![commit-last](https://img.shields.io/github/last-commit/11notes/docker-traefik-labels?color=c91cb8)
![size](https://img.shields.io/docker/image-size/11notes/traefik-labels/amd64-0.1.9?color=0eb305) ![version](https://img.shields.io/docker/v/11notes/traefik-labels/amd64-0.1.9?color=eb7a09) ![pulls](https://img.shields.io/docker/pulls/11notes/traefik-labels?color=2b75d6) ![activity](https://img.shields.io/github/commit-activity/m/11notes/docker-traefik-labels?color=c91cb8) ![commit-last](https://img.shields.io/github/last-commit/11notes/docker-traefik-labels?color=c91cb8)
Run Traefik Labels based on Alpine Linux. Small, lightweight, secure and fast 🏔️
@@ -49,11 +49,11 @@ docker run --name traefik-rfc2136-demo \
-l "traefik/http/services/demo.domain.com/loadbalancer/servers/0/url=http://fqdn-of-docker-node:8080" \
-l "rfc2136/WAN/server=ns.domain.com" \
-l "rfc2136/WAN/key=algo:name:secret" \
-l "rfc2136/WAN/nsupdate=update add foo.domain.com A 175.12.41.11" \
-l "rfc2136/WAN/nsupdate=update add foo.domain.com TXT \"hello from traefik-labels!\"" \
-l "rfc2136/WAN/nsupdate=update add foo.domain.com 300 A 175.12.41.11" \
-l "rfc2136/WAN/nsupdate=update add foo.domain.com 300 TXT \"hello from traefik-labels!\"" \
-l "rfc2136/LAN/server=ns.domain.local" \
-l "rfc2136/LAN/key=algo:name:secret" \
-l "rfc2136/LAN/nsupdate=update add foo.domain.local A 192.168.12.54" \
-l "rfc2136/LAN/nsupdate=update add foo.domain.local 300 A 192.168.12.54" \
-d 11notes/nginx:stable
```
@@ -70,11 +70,13 @@ docker run --name traefik-rfc2136-demo \
| Parameter | Value | Default |
| --- | --- | --- |
| `TZ` | [Time Zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | null |
| `DEBUG` | Show debug information | null |
| `LABELS_REDIS_URL` | the redis URL to connect, use rediss:// for SSL | rediss://localhost:6379/0 |
| `LABELS_INTERVAL` | in what interval container information is pulled in seconds | 300 |
| `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 to the set server) | false |
## Parent image
* [11notes/node:stable](https://hub.docker.com/r/11notes/node)
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
curl -Lo manifest-tool https://github.com/estesp/manifest-tool/releases/download/v0.9.0/manifest-tool-linux-amd64
chmod +x manifest-tool
./manifest-tool push from-spec multi-arch-manifest.yaml
+11
View File
@@ -0,0 +1,11 @@
image: 11notes/traefik-labels:latest
manifests:
- image: 11notes/traefik-labels:amd64-0.1.9
platform:
architecture: amd64
os: linux
- image: 11notes/traefik-labels:arm64v8-0.1.9
platform:
architecture: arm64
os: linux
variant: v8
+27
View File
@@ -0,0 +1,27 @@
const { spawn } = require('node:child_process');
const { elevenLogJSON } = require('/labels/lib/util.js');
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 =>{
elevenLogJSON('debug', {method:'dig()', params:{resolver:resolver, type:type, record:record}, io:io});
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);
}
});
}));
}
+39 -6
View File
@@ -4,18 +4,21 @@ 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,
LABELS_TIMEOUT:ENV_REDIS_TIMEOUT,
LABELS_WEBHOOK:ENV_LABELS_WEBHOOK,
LABELS_WEBHOOK_AUTH_BASIC:((ENV_LABELS_WEBHOOK_AUTH_BASIC) ? true: false)
LABELS_WEBHOOK_AUTH_BASIC:((ENV_LABELS_WEBHOOK_AUTH_BASIC) ? true: false),
LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE:ENV_LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE
}});
class Labels{
@@ -100,6 +103,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,9 +116,7 @@ class Labels{
LAN:{server:'', key:'', commands:[]},
}
elevenLogJSON('info', `inspect container {${(data?.Name || data?.id).replace(/^\//i, '')}}${(
(null === status) ? '' : ` event[${status}]`
)}`);
elevenLogJSON('info', {container:container.name, event:status, method:'inspect'});
for(const label in data?.Config?.Labels){
switch(true){
@@ -151,8 +153,22 @@ class Labels{
for(const type in rfc2136){
if(rfc2136[type].commands.length > 0 && rfc2136[type].server && rfc2136[type].key){
if(ENV_LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE){
for(let i=0; i<rfc2136[type].commands.length; i++){
if(await this.rfc2136KnownRecord(rfc2136[type].server, rfc2136[type].commands[i])){
rfc2136[type].commands.splice(i, 1);
}
}
}
try{
await nsupdate(rfc2136[type].server, rfc2136[type].key, rfc2136[type].commands);
if(rfc2136[type].commands.length > 0){
elevenLogJSON('info', {container:container.name, event:status, method:`nsupdate ${rfc2136[type].server}`});
await nsupdate(rfc2136[type].server, rfc2136[type].key, rfc2136[type].commands);
}else{
if(ENV_LABELS_RFC2136_ONLY_UPDATE_ON_CHANGE){
elevenLogJSON('info', {container:container.name, event:status, method:`nsupdate ${rfc2136[type].server} skipped due to same record data`});
}
}
}catch(e){
elevenLogJSON('error', e);
}
@@ -165,7 +181,7 @@ class Labels{
(update) ? 'PUT' : 'DELETE'
), body:JSON.stringify(container), headers:this.#webhook.headers, signal:AbortSignal.timeout(2500)});
}catch(e){
elevenLogJSON('error', e);
elevenLogJSON('error', {method:'fetch(webhook)', error:e});
}
}
@@ -174,6 +190,23 @@ 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]);
const match = (
(record.match(new RegExp(matches[3], 'ig'))) ? true : false
);
elevenLogJSON('debug', {method:'rfc2136KnownRecord()', params:{server:server, nsupdate:nsupdate}, match:{A:record, B:matches[3], match:match}});
return(match);
}catch(e){
elevenLogJSON('error', e);
return(false);
}
}
}
}
new Labels().watch();
+13 -7
View File
@@ -1,22 +1,28 @@
const { spawn } = require('node:child_process');
const { elevenLogJSON } = require('/labels/lib/util.js');
exports.nsupdate = async(server, key, commands) => {
commands.unshift(`server ${server}`);
commands.push('send');
commands.push('quit');
return(new Promise((resolve, reject) => {
elevenLogJSON('debug', {method:'nsupdate()', params:{server:server, key:'******', commands:commands}});
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){