docs: README, changelog, contributing, security, deployment, operations, CI
This commit is contained in:
commit
bdfd8027f4
12 changed files with 551 additions and 0 deletions
48
.gitea/workflows/build.yml
Normal file
48
.gitea/workflows/build.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Forgejo registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: <REGISTRY>
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: <REGISTRY>/laurence/dns-traefik-sync
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=sha,prefix=sha-
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.keytab
|
||||||
|
*.ccache
|
||||||
|
/tmp/
|
||||||
|
.env
|
||||||
23
CHANGELOG.md
Normal file
23
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented here.
|
||||||
|
|
||||||
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-05-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Initial release
|
||||||
|
- Poll Traefik API for docker-provider routers with Host() rules
|
||||||
|
- Filter hostnames to configured DNS zone
|
||||||
|
- Create and remove A records via GSS-TSIG authenticated RFC 2136 dynamic DNS update
|
||||||
|
- Kerberos authentication using keytab (no passwords, no SSH)
|
||||||
|
- State tracking across poll cycles to minimise DNS writes
|
||||||
|
- Docker secret support for keytab
|
||||||
|
- Configurable via environment variables
|
||||||
|
- Debian bookworm-slim base image
|
||||||
|
- Runs as nobody
|
||||||
41
CONTRIBUTING.md
Normal file
41
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Development setup
|
||||||
|
|
||||||
|
Clone the repo and set up a venv:
|
||||||
|
|
||||||
|
git clone https://<REGISTRY>/laurence/dns-traefik-sync.git
|
||||||
|
cd dns-traefik-sync
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
No external Python dependencies - stdlib only by design.
|
||||||
|
|
||||||
|
## Testing locally
|
||||||
|
|
||||||
|
Set environment variables to point at a test Traefik instance and DNS zone, then run:
|
||||||
|
|
||||||
|
python3 dns_sync.py
|
||||||
|
|
||||||
|
You will need a valid Kerberos ticket or keytab. For dry-run testing without a real Samba DC,
|
||||||
|
set DNS_SERVER to a non-existent host and observe that nsupdate errors are caught and logged cleanly.
|
||||||
|
|
||||||
|
## Building the image
|
||||||
|
|
||||||
|
docker build -t dns-traefik-sync:dev .
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
Tag with vMAJOR.MINOR.PATCH - the Gitea Actions workflow will build and push automatically:
|
||||||
|
|
||||||
|
git tag v1.0.1
|
||||||
|
git push origin v1.0.1
|
||||||
|
|
||||||
|
Update CHANGELOG.md before tagging.
|
||||||
|
|
||||||
|
## Code style
|
||||||
|
|
||||||
|
- stdlib only, no third-party dependencies
|
||||||
|
- All configuration via environment variables
|
||||||
|
- Errors caught per-hostname, never crash the whole sync cycle
|
||||||
|
- Log every DNS write at INFO level
|
||||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
python3 \
|
||||||
|
krb5-user \
|
||||||
|
dnsutils \
|
||||||
|
ca-certificates && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY dns_sync.py .
|
||||||
|
COPY krb5.conf /etc/krb5.conf
|
||||||
|
RUN chmod +x dns_sync.py
|
||||||
|
|
||||||
|
USER nobody
|
||||||
|
ENTRYPOINT ["python3", "/app/dns_sync.py"]
|
||||||
72
README.md
Normal file
72
README.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# dns-traefik-sync
|
||||||
|
|
||||||
|
Automatic DNS registration for Traefik-managed containers using Kerberos-authenticated
|
||||||
|
RFC 2136 dynamic updates against a Samba Active Directory DNS server.
|
||||||
|
|
||||||
|
Polls the Traefik API, extracts Host() rules from docker-provider routers, and
|
||||||
|
creates/removes A records in Samba AD DNS using GSS-TSIG authentication.
|
||||||
|
No SSH. No passwords. No broad privilege.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
Traefik API (/api/http/routers)
|
||||||
|
└── docker provider routers with Host() rules
|
||||||
|
└── filtered to target zone
|
||||||
|
└── diff against last known state
|
||||||
|
└── nsupdate -g (GSS-TSIG via Kerberos keytab)
|
||||||
|
└── Samba AD DNS (vetinari)
|
||||||
|
|
||||||
|
On each poll cycle:
|
||||||
|
|
||||||
|
- Fetches all routers from the Traefik API
|
||||||
|
- Filters to docker provider only, extracts hostnames matching the target zone
|
||||||
|
- Compares against state from the previous cycle
|
||||||
|
- Adds A records for new hostnames, removes A records for gone hostnames
|
||||||
|
- Renews the Kerberos ticket before each cycle
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Traefik with API enabled
|
||||||
|
- Samba AD DC with a service account in DnsAdmins
|
||||||
|
- Keytab for the service account stored as a Docker secret
|
||||||
|
- Docker Swarm mode (for secrets)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All configuration via environment variables:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|------------------|------------------------------------|------------------------------------|
|
||||||
|
| TRAEFIK_API | http://<TARGET_IP>:8080 | Traefik API base URL |
|
||||||
|
| DNS_SERVER | <DNS_SERVER_IP> | Samba AD DNS server IP |
|
||||||
|
| DNS_ZONE | <DNS_ZONE> | Zone to manage records in |
|
||||||
|
| TARGET_IP | <TARGET_IP> | IP address for created A records |
|
||||||
|
| TTL | 300 | TTL for created records (seconds) |
|
||||||
|
| POLL_INTERVAL | 60 | Poll interval (seconds) |
|
||||||
|
| KEYTAB | /run/secrets/dns-updater-keytab | Path to Kerberos keytab |
|
||||||
|
| KRB_PRINCIPAL | dns-updater@<REALM> | Kerberos principal |
|
||||||
|
| STATE_FILE | /tmp/dns_state.json | State persistence path |
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
See docs/deployment.md for full setup instructions.
|
||||||
|
See docs/operations.md for day-to-day operational guidance.
|
||||||
|
|
||||||
|
## Security model
|
||||||
|
|
||||||
|
- Service account scoped to DnsAdmins only
|
||||||
|
- Keytab stored as a Docker secret, never on disk
|
||||||
|
- GSS-TSIG: Kerberos mutual authentication, updates signed and verified by the KDC
|
||||||
|
- No SSH, no HTTP webhooks, no plaintext credentials
|
||||||
|
- Container runs as nobody
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- A records only (no CNAME, no AAAA)
|
||||||
|
- Single target IP (all records point to the same host)
|
||||||
|
- Single zone
|
||||||
|
- On restart, re-syncs from Traefik - safe and idempotent
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
See CHANGELOG.md
|
||||||
22
SECURITY.md
Normal file
22
SECURITY.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
## Reporting
|
||||||
|
|
||||||
|
Raise a private issue in Forgejo or contact the repo owner directly.
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
- Never commit keytabs, .ccache files, or .env files - all are in .gitignore
|
||||||
|
- The keytab must be stored as a Docker secret only
|
||||||
|
- The service account (dns-updater) should have no other privileges beyond DnsAdmins
|
||||||
|
- Rotate the keytab periodically - see docs/operations.md
|
||||||
|
|
||||||
|
## Threat model
|
||||||
|
|
||||||
|
| Threat | Mitigation |
|
||||||
|
|---|---|
|
||||||
|
| Keytab compromise | Scoped to DnsAdmins only; rotate via samba-tool user setpassword |
|
||||||
|
| Container escape | Runs as nobody; no capabilities; no host mounts beyond the secret |
|
||||||
|
| DNS spoofing | GSS-TSIG: updates are Kerberos-signed and verified by the KDC |
|
||||||
|
| Unauthorised DNS writes | Only the dns-updater principal can authenticate with this keytab |
|
||||||
|
| Traefik API abuse | API is read-only; no writes to Traefik from this service |
|
||||||
119
dns_sync.py
Normal file
119
dns_sync.py
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s %(levelname)s %(message)s'
|
||||||
|
)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
TRAEFIK_API = os.environ.get('TRAEFIK_API', 'http://<TARGET_IP>:8080')
|
||||||
|
DNS_SERVER = os.environ.get('DNS_SERVER', '<DNS_SERVER_IP>')
|
||||||
|
DNS_ZONE = os.environ.get('DNS_ZONE', '<DNS_ZONE>')
|
||||||
|
TARGET_IP = os.environ.get('TARGET_IP', '<TARGET_IP>')
|
||||||
|
TTL = int(os.environ.get('TTL', '300'))
|
||||||
|
POLL_INTERVAL = int(os.environ.get('POLL_INTERVAL', '60'))
|
||||||
|
KEYTAB = os.environ.get('KEYTAB', '/run/secrets/dns-updater-keytab')
|
||||||
|
KRB_PRINCIPAL = os.environ.get('KRB_PRINCIPAL', 'dns-updater@<REALM>')
|
||||||
|
STATE_FILE = os.environ.get('STATE_FILE', '/tmp/dns_state.json')
|
||||||
|
|
||||||
|
def kinit():
|
||||||
|
result = subprocess.run(
|
||||||
|
['kinit', '-kt', KEYTAB, KRB_PRINCIPAL],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"kinit failed: {result.stderr}")
|
||||||
|
log.info("Kerberos ticket obtained")
|
||||||
|
|
||||||
|
def get_traefik_hostnames():
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(f"{TRAEFIK_API}/api/http/routers", timeout=10) as r:
|
||||||
|
routers = json.loads(r.read())
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
log.error(f"Failed to reach Traefik API: {e}")
|
||||||
|
return set()
|
||||||
|
|
||||||
|
hostnames = set()
|
||||||
|
for router in routers:
|
||||||
|
if router.get('provider') != 'docker':
|
||||||
|
continue
|
||||||
|
rule = router.get('rule', '')
|
||||||
|
for match in re.finditer(r'Host\(`([^`]+)`\)', rule):
|
||||||
|
host = match.group(1)
|
||||||
|
if host.endswith(DNS_ZONE):
|
||||||
|
hostnames.add(host)
|
||||||
|
return hostnames
|
||||||
|
|
||||||
|
def nsupdate(commands):
|
||||||
|
input_data = f"gss-tsig\n{commands}\n"
|
||||||
|
result = subprocess.run(
|
||||||
|
['nsupdate', '-g'],
|
||||||
|
input=input_data, capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"nsupdate failed: {result.stderr}")
|
||||||
|
|
||||||
|
def add_record(hostname):
|
||||||
|
log.info(f"Adding {hostname} -> {TARGET_IP}")
|
||||||
|
nsupdate(f"""server {DNS_SERVER}
|
||||||
|
zone {DNS_ZONE}
|
||||||
|
update add {hostname}. {TTL} A {TARGET_IP}
|
||||||
|
send""")
|
||||||
|
|
||||||
|
def remove_record(hostname):
|
||||||
|
log.info(f"Removing {hostname}")
|
||||||
|
nsupdate(f"""server {DNS_SERVER}
|
||||||
|
zone {DNS_ZONE}
|
||||||
|
update delete {hostname}. A
|
||||||
|
send""")
|
||||||
|
|
||||||
|
def load_state():
|
||||||
|
try:
|
||||||
|
return set(json.loads(Path(STATE_FILE).read_text()))
|
||||||
|
except Exception:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def save_state(hostnames):
|
||||||
|
Path(STATE_FILE).write_text(json.dumps(list(hostnames)))
|
||||||
|
|
||||||
|
def sync():
|
||||||
|
kinit()
|
||||||
|
current = get_traefik_hostnames()
|
||||||
|
previous = load_state()
|
||||||
|
|
||||||
|
to_add = current - previous
|
||||||
|
to_remove = previous - current
|
||||||
|
|
||||||
|
for h in to_add:
|
||||||
|
try:
|
||||||
|
add_record(h)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to add {h}: {e}")
|
||||||
|
current.discard(h)
|
||||||
|
|
||||||
|
for h in to_remove:
|
||||||
|
try:
|
||||||
|
remove_record(h)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to remove {h}: {e}")
|
||||||
|
|
||||||
|
save_state(current)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
log.info("dns-traefik-sync starting")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
sync()
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Sync failed: {e}")
|
||||||
|
time.sleep(POLL_INTERVAL)
|
||||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
dns-traefik-sync:
|
||||||
|
image: <REGISTRY>/laurence/dns-traefik-sync:latest
|
||||||
|
environment:
|
||||||
|
TRAEFIK_API: "http://<TARGET_IP>:8080"
|
||||||
|
DNS_SERVER: "<DNS_SERVER_IP>"
|
||||||
|
DNS_ZONE: "<DNS_ZONE>"
|
||||||
|
TARGET_IP: "<TARGET_IP>"
|
||||||
|
TTL: "300"
|
||||||
|
POLL_INTERVAL: "60"
|
||||||
|
KEYTAB: "/run/secrets/dns-updater-keytab"
|
||||||
|
KRB_PRINCIPAL: "dns-updater@<REALM>"
|
||||||
|
secrets:
|
||||||
|
- dns-updater-keytab
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
dns-updater-keytab:
|
||||||
|
external: true
|
||||||
89
docs/deployment.md
Normal file
89
docs/deployment.md
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# Deployment
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Samba AD DC
|
||||||
|
- Docker host joined to the AD domain
|
||||||
|
- Docker Swarm mode enabled
|
||||||
|
- Traefik running with API enabled
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Samba AD: Create service account and keytab
|
||||||
|
|
||||||
|
On the Samba DC:
|
||||||
|
|
||||||
|
sudo samba-tool user create dns-updater --random-password
|
||||||
|
sudo samba-tool group addmembers DnsAdmins dns-updater
|
||||||
|
sudo samba-tool domain exportkeytab /etc/samba/dns-updater.keytab \
|
||||||
|
--principal=dns-updater@<REALM>
|
||||||
|
sudo kinit -kt /etc/samba/dns-updater.keytab dns-updater@<REALM>
|
||||||
|
klist
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Join the Docker host to the domain
|
||||||
|
|
||||||
|
sudo apt install sssd-ad sssd-tools realmd adcli krb5-user \
|
||||||
|
packagekit samba-common-bin libnss-sss libpam-sss
|
||||||
|
sudo realm join <DOMAIN> -U administrator
|
||||||
|
sudo realm list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Transfer keytab and create Docker secret
|
||||||
|
|
||||||
|
On the DC make the keytab readable temporarily:
|
||||||
|
|
||||||
|
sudo cp /etc/samba/dns-updater.keytab /tmp/dns-updater.keytab
|
||||||
|
sudo chown <youradmin>:<youradmin> /tmp/dns-updater.keytab
|
||||||
|
|
||||||
|
On your local machine:
|
||||||
|
|
||||||
|
scp <dcadmin>@<dc>:/tmp/dns-updater.keytab .
|
||||||
|
scp dns-updater.keytab <dockeradmin>@<dockerhost>:/tmp/
|
||||||
|
shred -u dns-updater.keytab
|
||||||
|
|
||||||
|
On the DC clean up:
|
||||||
|
|
||||||
|
sudo shred -u /tmp/dns-updater.keytab
|
||||||
|
|
||||||
|
On the Docker host:
|
||||||
|
|
||||||
|
sudo docker secret create dns-updater-keytab /tmp/dns-updater.keytab
|
||||||
|
sudo shred -u /tmp/dns-updater.keytab
|
||||||
|
sudo docker secret ls
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Enable Docker Swarm
|
||||||
|
|
||||||
|
sudo docker swarm init --advertise-addr <docker-host-ip>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Build and push the image
|
||||||
|
|
||||||
|
git clone https://<REGISTRY>/laurence/dns-traefik-sync.git
|
||||||
|
cd dns-traefik-sync
|
||||||
|
sudo docker build -t <REGISTRY>/laurence/dns-traefik-sync:latest .
|
||||||
|
sudo docker login <REGISTRY>
|
||||||
|
sudo docker push <REGISTRY>/laurence/dns-traefik-sync:latest
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Deploy the stack
|
||||||
|
|
||||||
|
sudo docker stack deploy -c docker-compose.yml dns-traefik-sync
|
||||||
|
sudo docker stack ps dns-traefik-sync
|
||||||
|
sudo docker service logs dns-traefik-sync_dns-traefik-sync
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Verify DNS records are being created
|
||||||
|
|
||||||
|
dig @<DNS_SERVER_IP> adguard.<DNS_ZONE>
|
||||||
|
|
||||||
|
Or on the DC:
|
||||||
|
|
||||||
|
sudo samba-tool dns query 127.0.0.1 <DNS_ZONE> @ ALL -U administrator
|
||||||
78
docs/operations.md
Normal file
78
docs/operations.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Operations
|
||||||
|
|
||||||
|
## Checking service status
|
||||||
|
|
||||||
|
sudo docker service ls
|
||||||
|
sudo docker stack ps dns-traefik-sync
|
||||||
|
sudo docker service logs dns-traefik-sync_dns-traefik-sync --follow
|
||||||
|
|
||||||
|
## What healthy logs look like
|
||||||
|
|
||||||
|
2026-05-24 18:00:00 INFO dns-traefik-sync starting
|
||||||
|
2026-05-24 18:00:00 INFO Kerberos ticket obtained
|
||||||
|
2026-05-24 18:00:00 INFO Adding adguard.<DNS_ZONE> -> <TARGET_IP>
|
||||||
|
2026-05-24 18:01:00 INFO Kerberos ticket obtained
|
||||||
|
2026-05-24 18:01:00 INFO No changes
|
||||||
|
|
||||||
|
## Manually verifying DNS records
|
||||||
|
|
||||||
|
dig @<DNS_SERVER_IP> grafana.<DNS_ZONE> A
|
||||||
|
sudo samba-tool dns query 127.0.0.1 <DNS_ZONE> @ ALL -U administrator
|
||||||
|
|
||||||
|
## Forcing a re-sync
|
||||||
|
|
||||||
|
sudo docker service update --force dns-traefik-sync_dns-traefik-sync
|
||||||
|
|
||||||
|
## Updating the image
|
||||||
|
|
||||||
|
cd dns-traefik-sync
|
||||||
|
git pull
|
||||||
|
sudo docker build -t <REGISTRY>/laurence/dns-traefik-sync:latest .
|
||||||
|
sudo docker push <REGISTRY>/laurence/dns-traefik-sync:latest
|
||||||
|
sudo docker service update --image \
|
||||||
|
<REGISTRY>/laurence/dns-traefik-sync:latest \
|
||||||
|
dns-traefik-sync_dns-traefik-sync
|
||||||
|
|
||||||
|
## Keytab rotation
|
||||||
|
|
||||||
|
On the Samba DC:
|
||||||
|
|
||||||
|
sudo samba-tool user setpassword dns-updater --random-password
|
||||||
|
sudo samba-tool domain exportkeytab /etc/samba/dns-updater.keytab \
|
||||||
|
--principal=dns-updater@<REALM>
|
||||||
|
|
||||||
|
Update the Docker secret:
|
||||||
|
|
||||||
|
sudo docker secret rm dns-updater-keytab
|
||||||
|
sudo docker secret create dns-updater-keytab /tmp/dns-updater.keytab
|
||||||
|
sudo shred -u /tmp/dns-updater.keytab
|
||||||
|
sudo docker stack deploy -c docker-compose.yml dns-traefik-sync
|
||||||
|
|
||||||
|
## Removing a DNS record manually
|
||||||
|
|
||||||
|
sudo samba-tool dns delete 127.0.0.1 <DNS_ZONE> \
|
||||||
|
<hostname> A <TARGET_IP> -U administrator
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### kinit fails - no suitable keys
|
||||||
|
|
||||||
|
Keytab is stale. Re-export on the DC:
|
||||||
|
|
||||||
|
sudo samba-tool domain exportkeytab /etc/samba/dns-updater.keytab \
|
||||||
|
--principal=dns-updater@<REALM>
|
||||||
|
|
||||||
|
### nsupdate fails with REFUSED
|
||||||
|
|
||||||
|
dns-updater not in DnsAdmins:
|
||||||
|
|
||||||
|
sudo samba-tool group addmembers DnsAdmins dns-updater
|
||||||
|
|
||||||
|
### nsupdate fails with NOTAUTH
|
||||||
|
|
||||||
|
Wrong DNS_ZONE or DNS_SERVER environment variable.
|
||||||
|
|
||||||
|
### Records not being created for a container
|
||||||
|
|
||||||
|
Container must have a Host() rule in its Traefik label matching DNS_ZONE.
|
||||||
|
Internal and non-docker providers are ignored.
|
||||||
14
krb5.conf
Normal file
14
krb5.conf
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[libdefaults]
|
||||||
|
default_realm = <REALM>
|
||||||
|
dns_lookup_realm = false
|
||||||
|
dns_lookup_kdc = true
|
||||||
|
|
||||||
|
[realms]
|
||||||
|
<REALM> = {
|
||||||
|
kdc = <DNS_SERVER_IP>
|
||||||
|
admin_server = <DNS_SERVER_IP>
|
||||||
|
}
|
||||||
|
|
||||||
|
[domain_realm]
|
||||||
|
.<DOMAIN> = <REALM>
|
||||||
|
<DOMAIN> = <REALM>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue