Aller au contenu

Installation et configuration

Ce chapitre couvre le déploiement de CoreDNS dans deux contextes : en standalone sur un serveur (via Podman) et comme cluster DNS Kubernetes.

Déploiement standalone avec Podman

Prerequis

Composant Version minimum Vérification
Podman 4.0+ podman --version
Réseau Port 53/UDP+TCP disponible ss -ulnp \| grep :53
Stockage 100 Mo pour les fichiers de zone df -h

Structure des fichiers

# Creer l'arborescence de configuration
mkdir -p /opt/coredns/{config,zones}
/opt/coredns/
├── config/
│   └── Corefile          # Configuration principale
└── zones/
    ├── db.internal.company.io         # Zone racine interne
    ├── db.production.internal         # Zone production
    ├── db.enterprise.internal         # Zone entreprise
    └── db.build.internal              # Zone chaine logicielle

Configuration du Corefile

Le Corefile est le fichier de configuration central de CoreDNS. Chaque bloc définit une zone et les plugins a appliquer :

# /opt/coredns/config/Corefile

# Zone interne principale (autoritatif)
internal.company.io {
    file /etc/coredns/zones/db.internal.company.io
    log
    errors
    prometheus :9153
    health :8080
    ready :8181
    cache 3600
}

# Zone production
production.internal.company.io {
    file /etc/coredns/zones/db.production.internal
    log
    errors
    cache 300
}

# Zone entreprise
enterprise.internal.company.io {
    file /etc/coredns/zones/db.enterprise.internal
    log
    errors
    cache 300
}

# Zone chaine logicielle
build.internal.company.io {
    file /etc/coredns/zones/db.build.internal
    log
    errors
    cache 300
}

# Tout le reste : forward vers DNS publics
. {
    forward . 1.1.1.1 8.8.8.8 {
        tls_servername cloudflare-dns.com
        health_check 30s
    }
    log
    errors
    prometheus :9153
    cache 600
}

Fichier de zone exemple

; /opt/coredns/zones/db.production.internal
$ORIGIN production.internal.company.io.
$TTL 300

@   IN  SOA ns1.internal.company.io. admin.company.io. (
        2026041601  ; Serial (YYYYMMDDNN)
        3600        ; Refresh (1h)
        900         ; Retry (15min)
        604800      ; Expire (7 jours)
        300         ; Negative cache TTL (5min)
    )

; Serveurs de noms
    IN  NS  ns1.internal.company.io.
    IN  NS  ns2.internal.company.io.

; Services de production
app-api         IN  A       10.30.1.10
app-api         IN  AAAA    fd00:30::1:10
app-frontend    IN  A       10.30.1.11
db-master       IN  A       10.30.2.10
db-replica      IN  A       10.30.2.11
cache-redis     IN  A       10.30.3.10
mq-rabbit       IN  A       10.30.3.20

; Alias
api             IN  CNAME   app-api.production.internal.company.io.
frontend        IN  CNAME   app-frontend.production.internal.company.io.

; Service discovery SRV
_http._tcp.app-api  IN  SRV 10 100 8080 app-api.production.internal.company.io.
_amqp._tcp.mq       IN  SRV 10 100 5672 mq-rabbit.production.internal.company.io.

Lancement avec Podman

# Telecharger l'image CoreDNS
podman pull docker.io/coredns/coredns:1.12

# Lancer le conteneur
podman run -d \
  --name coredns \
  --restart=always \
  -p 53:53/udp \
  -p 53:53/tcp \
  -p 9153:9153/tcp \
  -p 8080:8080/tcp \
  -v /opt/coredns/config/Corefile:/etc/coredns/Corefile:ro,Z \
  -v /opt/coredns/zones:/etc/coredns/zones:ro,Z \
  docker.io/coredns/coredns:1.12 \
  -conf /etc/coredns/Corefile

Vérification

# Verifier que le conteneur tourne
podman ps --filter name=coredns

# Tester la resolution d'un nom interne
dig @127.0.0.1 app-api.production.internal.company.io A +short
# Attendu : 10.30.1.10

# Tester la resolution d'un nom externe (forward)
dig @127.0.0.1 google.com A +short

# Verifier les metriques Prometheus
curl -s http://127.0.0.1:9153/metrics | head -20

# Verifier le health check
curl -s http://127.0.0.1:8080/health
# Attendu : OK

Service systemd

Pour que CoreDNS demarre automatiquement :

# Generer le fichier systemd
podman generate systemd --name coredns --new --files

# Installer le service (rootless)
mkdir -p ~/.config/systemd/user/
mv container-coredns.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now container-coredns.service
systemctl --user status container-coredns.service

# Permettre le demarrage sans session active
loginctl enable-linger $(whoami)

Port 53 et rootless

Le port 53 est un port privilegie (\<1024). En mode rootless, il faut soit utiliser sysctl net.ipv4.ip_unprivileged_port_start=53, soit mapper vers un port non privilegie (ex: 5353) et rediriger avec iptables/nftables.

Déploiement Kubernetes (cluster DNS)

CoreDNS est déployé par defaut dans les clusters Kubernetes. Cette section couvre la personnalisation.

ConfigMap CoreDNS

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153

        # Zones internes : forward vers le DNS interne standalone
        forward internal.company.io 10.30.2.10 10.30.2.11 {
            policy round_robin
            health_check 10s
        }

        # Tout le reste : forward vers DNS publics
        forward . /etc/resolv.conf {
            max_concurrent 1000
        }

        cache 30
        loop
        reload
        loadbalance
    }

Application de la configuration

# Editer la ConfigMap
kubectl edit configmap coredns -n kube-system

# Ou appliquer depuis un fichier
kubectl apply -f coredns-configmap.yaml

# CoreDNS recharge automatiquement grace au plugin "reload"
# Verifier les logs pour confirmer
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20

Zones personnalisees dans Kubernetes

Pour servir des zones internes directement depuis le CoreDNS du cluster :

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-custom
  namespace: kube-system
data:
  custom.server: |
    production.internal.company.io:53 {
        file /etc/coredns/custom/db.production.internal
        errors
        cache 300
    }
  db.production.internal: |
    $ORIGIN production.internal.company.io.
    $TTL 300
    @   IN  SOA ns1.internal.company.io. admin.company.io. (
            2026041601 3600 900 604800 300 )
        IN  NS  ns1.internal.company.io.
    app-api     IN  A   10.30.1.10
    db-master   IN  A   10.30.2.10

CoreDNS cluster vs standalone

Dans une architecture avec un DNS standalone existant, preferez le forward depuis le CoreDNS Kubernetes vers le DNS standalone (première approche ci-dessus). Cela evite de dupliquer les zones et centralise la gestion des enregistrements.

Health checks et readiness

# Verifier que CoreDNS repond dans le cluster
kubectl run dns-test --rm -it --image=busybox:1.36 --restart=Never -- \
  nslookup kubernetes.default.svc.cluster.local

# Verifier la resolution d'une zone interne
kubectl run dns-test --rm -it --image=busybox:1.36 --restart=Never -- \
  nslookup app-api.production.internal.company.io

# Verifier les metriques CoreDNS
kubectl port-forward -n kube-system svc/kube-dns 9153:9153 &
curl -s http://127.0.0.1:9153/metrics | grep coredns_dns_requests_total

Recharger les zones sans redemarrage

CoreDNS supporte le rechargement a chaud des zones :

# Dans le Corefile, le plugin "reload" surveille les changements
production.internal.company.io {
    file /etc/coredns/zones/db.production.internal
    reload 30s    # Verifier les changements toutes les 30 secondes
    log
    errors
    cache 300
}

Pour forcer un rechargement immédiat, incrementer le serial dans le fichier de zone et CoreDNS detectera le changement au prochain cycle de reload.

Upstream forwarding securise

Pour forwarder vers les DNS publics en DNS-over-TLS :

. {
    forward . tls://1.1.1.1 tls://1.0.0.1 {
        tls_servername cloudflare-dns.com
        health_check 30s
    }
    cache 600
}

Choix du resolver upstream

Pour un DNS interne d'entreprise, le forward vers les DNS publics devrait passer par un resolver intermédiaire controlable (ex: Unbound local) plutôt que directement vers Cloudflare ou Google. Cela permet de journaliser et filtrer les requêtes sortantes.