22 ธันวาคม 2568

traefix wildcard SSL with muli provider

docker-compose.yml
services:
  traefik:
    image: traefik:v3.6
    container_name: traefik-test
    restart: unless-stopped
    networks:
      - traefik_net
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    environment:
      - CF_DNS_API_TOKEN=${CLOUDFLARE_TOKEN}
      - SPACESHIP_API_KEY=${SPACESHIP_API_KEY}
      - SPACESHIP_API_SECRET=${SPACESHIP_API_SECRET}
    volumes:
      - ./traefik:/etc/traefik:ro
      - ./letsencrypt:/letsencrypt
    command:
      - --configFile=/etc/traefik/traefik_config.yml

  php-apache:
    image: php:8.4-apache
    container_name: php-apache-test
    restart: unless-stopped
    networks:
      - traefik_net
    volumes:
      - ./php-src:/var/www/html

networks:
  traefik_net:
    driver: bridge


traefik_config.yml
entryPoints:
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: cloudflare-resolver
        domains:
          - main: "domain1.com"
            sans:
              - "*.domain1.com"

providers:
  file:
    filename: "/etc/traefik/dynamic_conf.yml"
    watch: true

certificatesResolvers:
  cloudflare-resolver:
    acme:
      email: your-email@example.com
      storage: /etc/traefik/acme-cloudflare.json
      dnsChallenge:
        provider: cloudflare

  spaceship-resolver:
    acme:
      email: your-email@example.com
      storage: /etc/traefik/acme-spaceship.json
      dnsChallenge:
        provider: spaceship # New in lego/traefik 2025


dynamic_config.yml
http:
  routers:
    # --- CLOUDFLARE DOMAIN (Inherits from Entrypoint) ---
    router1:
      rule: "Host(`app1.domain1.com`)"
      entryPoints: ["websecure"]
      service: "service1"
      tls: {} # Uses cloudflare-resolver & wildcard automatically

    router2:
      rule: "Host(`app2.domain1.com`)"
      entryPoints: ["websecure"]
      service: "service2"
      tls: {} # Uses cloudflare-resolver & wildcard automatically

    # --- SPACESHIP DOMAIN (Manual Override) ---
    router3:
      rule: "Host(`app3.domain2.com`)"
      entryPoints: ["websecure"]
      service: "service3"
      tls:
        certResolver: "spaceship-resolver"
        domains:
          - main: "domain2.com"
            sans: ["*.domain2.com"] # Triggers wildcard issuance

    router4:
      rule: "Host(`app4.domain2.com`)"
      entryPoints: ["websecure"]
      service: "service4"
      tls:
        certResolver: "spaceship-resolver"
        # No domains block needed! It reuses the cert from Router 3
        
  services:
    service1:
      loadBalancer:
        servers: [{ url: "http://172.18.0.11:80" }]
    service2:
      loadBalancer:
        servers: [{ url: "http://172.18.0.12:80" }]
    service3:
      loadBalancer:
        servers: [{ url: "http://172.18.0.13:80" }]
    service4:
      loadBalancer:
        servers: [{ url: "http://172.18.0.14:80" }]

20 ธันวาคม 2568

Unifi Network Application docker compose

 name: unifi-network-application
services:
  unifi-db:
    container_name: unifi-db
    image: docker.io/mongo:8.0
    configs:
      - source: init-mongo.js
        target: /docker-entrypoint-initdb.d/init-mongo.js
    environment:
      - PGID=1000
      - PUID=1000
      - TZ=Asia/Bangkok
    restart: unless-stopped
    volumes:
      - type: bind
        source: ./unifi-db
        target: /data/db
    networks:
     - unifi-bridge
    privileged: false
  unifi-network-application:
    container_name: unifi-network-application
    depends_on:
      unifi-db:
        condition: service_started
        required: true
    environment:
      - MONGO_DBNAME=unifi-db
      - MONGO_HOST=unifi-db
      - MONGO_PASS=pass
      - MONGO_PORT=27017
      - MONGO_USER=unifi
      - PGID=1000
      - PUID=1000
      - TZ=Asia/Bangkok
    image: lscr.io/linuxserver/unifi-network-application:latest
    ports:
      - target: 8443
        published: "8443"
        protocol: tcp
      - target: 3478
        published: "3478"
        protocol: udp
      - target: 10001
        published: "10001"
        protocol: udp
      - target: 8080
        published: "8080"
        protocol: tcp
      - target: 1900 #optional
        published: "1900"
        protocol: udp
      - target: 8843 #optional
        published: "8843"
        protocol: tcp
      - target: 8880 #optional
        published: "8880"
        protocol: tcp
      - target: 6789 #optional
        published: "6789"
        protocol: tcp
      - target: 5514 #optional
        published: "5514"
        protocol: udp
    restart: unless-stopped
    volumes:
      - type: bind
        source: ./unifi-network-application
        target: /config
    networks:
     - unifi-bridge
    privileged: false

networks:
  unifi-bridge:
    driver: bridge

configs:
  init-mongo.js:
    content: |
      db.getSiblingDB("unifi-db").createUser({user: "unifi", pwd: "pass", roles: [{role: "dbOwner", db: "unifi-db"}]}); 
      db.getSiblingDB("unifi-db_stat").createUser({user: "unifi", pwd: "pass", roles: [{role: "dbOwner", db: "unifi-db_stat"}]});


P.S. Override inform host with host ip
UniFi Devices -> Device Updates and Settings -> Inform Host Override

14 ธันวาคม 2568

Enable IPv6 in docker compose

sudo tee /etc/sysctl.d/99-docker-ipv6.conf <<EOF
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.default.forwarding = 1
EOF

Configure the Docker Daemon
/etc/docker/daemon.json
{
  "ipv6": true,
  "ip6tables": true 
}

Add IPv6 to Docker Compose Files
docker-compose.yml

    cap_add:
      - NET_ADMIN

networks:
  custom_network:
    driver: bridge
    enable_ipv6: true

13 ธันวาคม 2568

Traefik dnsChallenge with RFC2136 (Bind9)

 .env
RFC2136_NAMESERVER=<your_nameserver>
RFC2136_TSIG_KEY=<tsig_key>
RFC2136_TSIG_ALGORITHM=<tsig_algorithm>
RFC2136_TSIG_SECRET=<tsig_secret>

docker-compose.yml
- TRAEFIK_CERTIFICATESRESOLVERS_RFC2136_ACME_DNSCHALLENGE_RFC2136_NAMESERVER=${RFC2136_NAMESERVER}
- TRAEFIK_CERTIFICATESRESOLVERS_RFC2136_ACME_DNSCHALLENGE_RFC2136_TSIG_KEY=${RFC2136_TSIG_KEY}
- TRAEFIK_CERTIFICATESRESOLVERS_RFC2136_ACME_DNSCHALLENGE_RFC2136_TSIG_ALGORITHM=${RFC2136_TSIG_ALGORITHM}
- TRAEFIK_CERTIFICATESRESOLVERS_RFC2136_ACME_DNSCHALLENGE_RFC2136_TSIG_SECRET=${RFC2136_TSIG_SECRET}

traefik_config.yml
certificatesResolvers:
  rfc2136:
    acme:
      dnsChallenge:
        provider: "rfc2136"
      email: "<your_email>"
      storage: "/letsencrypt/rfc2136.json"

ref: https://go-acme.github.io/lego/dns/rfc2136/

HmacSHA1   = "hmac-sha1."
HmacSHA224 = "hmac-sha224."
HmacSHA256 = "hmac-sha256."
HmacSHA384 = "hmac-sha384."
HmacSHA512 = "hmac-sha512."

Traefik dnsChallenge with Spaceship DNS

 .env
SPACESHIP_API_KEY=<your_spaceship_api_key>
SPACESHIP_API_SECRET=<your_spaceship_api_secret>

docker-compose.yml
- TRAEFIK_CERTIFICATESRESOLVERS_SPACESHIP_ACME_DNSCHALLENGE_SPACESHIP_API_KEY=${SPACESHIP_API_KEY}
- TRAEFIK_CERTIFICATESRESOLVERS_SPACESHIP_ACME_DNSCHALLENGE_SPACESHIP_API_SECRET=$(SPACESHIP_API_SECRET)

traefik_config.yml
certificatesResolvers:
  spaceship:
    acme:
      dnsChallenge:
        provider: "spaceship"
      email: "<your_email>"
      storage: "letsencrypt/spaceship.json"

ref: https://go-acme.github.io/lego/dns/spaceship/

Traefik dnsChallenge with Clouflare DNS

 .env
LETSENCRYPT_TOKEN=<your_cloudflare_api_token>

docker-compose.yml
- TRAEFIK_CERTIFICATESRESOLVERS_CLOUDFLARE_ACME_DNSCHALLENGE_CLOUDFLARE_DNS_API_TOKEN=${LETSENCRYPT_TOKEN}

traefik_config.yml
certificatesResolvers:
  cloudflare:
    acme:
      dnsChallenge:
        provider: "cloudflare"
      email: "<your_email>"
      storage: "/letsencrypt/cloudflare.json"

ref: https://go-acme.github.io/lego/dns/cloudflare/

04 ธันวาคม 2568

SSL script with Certbot and RFC2136

 #!/bin/bash

# Certificate management script with Certbot and RFC2136

DOMAIN='*.example.com'  # Replace with your domain
EMAIL='your-email@example.com' # Replace with your email for notifications
DNS_SERVER='10.0.0.1' # Replace with your DNS server IP
KEY_NAME='your-key-name'  # Must match the key name on your DNS server
KEY_SECRET='your-base64-secret'  # Base64 encoded secret
KEY_ALGORITHM='HMAC-SHA512'  # or HMAC-MD5, HMAC-SHA1, HMAC-SHA256, HMAC-SHA384
PROPAGATION_SECONDS=20  # Time to wait for DNS propagation

# Create temporary rfc2136 credentials file on host
TMP_CREDS=$(mktemp)
cat > "$TMP_CREDS" <<EOF
dns_rfc2136_server = $DNS_SERVER
dns_rfc2136_name = $KEY_NAME
dns_rfc2136_secret = $KEY_SECRET
dns_rfc2136_algorithm = $KEY_ALGORITHM
EOF
chmod 600 "$TMP_CREDS"

echo "Created temporary credentials file: $TMP_CREDS"
echo "Issuing new certificate for $DOMAIN with Certbot (RFC2136)..."

docker run --rm -it \
    -v ./letsencrypt:/etc/letsencrypt \
    -v "$TMP_CREDS:/tmp/rfc2136.ini:ro" \
    certbot/dns-rfc2136 \
    certonly \
    --dns-rfc2136 \
    --dns-rfc2136-credentials /tmp/rfc2136.ini \
    --dns-rfc2136-propagation-seconds $PROPAGATION_SECONDS \
    --email "$EMAIL" \
    --agree-tos \
    --non-interactive \
    -d "$DOMAIN"

# Clean up temporary file
rm -f "$TMP_CREDS"
echo "Cleaned up temporary credentials file"
echo "Done!"