Kubernetes @ OVH - Traefik et Cert Manager pour le stockage des certificats en secrets


27/01/2019 - Dernière mise à jour : 13/10/2019 kubernetes traefik ovh secrets cert-manager

L’objectif est de s’appuyer sur Cert-Manager pour la génération et le stockage des certificats Let’s Encrypt qui seront utilisés par Traefik. L’idée est de stocker ces certificats sous la forme de secrets et de ne plus avoir à provisionner un volume pour les stocker.

Installons déjà cert-manager :

# Install the CustomResourceDefinition resources separately
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.11/deploy/manifests/00-crds.yaml

# Create the namespace for cert-manager
kubectl create namespace cert-manager

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update your local Helm chart repository cache
helm repo update

# Install the cert-manager Helm chart
helm install \
  --name cert-manager \
  --namespace cert-manager \
  --version v0.11.0 \
  jetstack/cert-manager

Nous allons ensuite devoir créer un Issuer dans chaque namespace pour avoir un générateur de certificats propre à chaque namespace. Cela est notamment du au fait que Traefik s’attend à ce que le secret et l’ingress utilisant ce secret soient dans le même namespace. Nous spécifions également que nous utiliserons traefik comme ingress pour la génération des certificats.

cert-manager/issuer.yml:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: user@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable HTTP01 validations
    solvers:
    - selector: {}
      http01:
        ingress:
          class: traefik

Puis créons le “issuer” dans la/les namespace(s) voulu(s) :

# Create issuer in a given namespace
kubectl create -n <namespace> -f issuer.yml

Notre contexte de déploiement utilisant Traefik comme ingress, je remets ci-dessous la configuration que j’utilise avec les ajustements nécessaires pour l’utilisation de cert-manager. Il n’est en effet plus possible et il devient désormais inutile de déclarer la section “acme” dans traefik.toml. J’ai aussi supprimé la redirect automatique http vers https, il faudra la gérer au niveau des ingress.

Créons le namespace traefik :

# Create namespace
kubectl create ns traefik
# Change context to this namespace so that all commands are by default run for this namespace
# see https://github.com/ahmetb/kubectx
kubens traefik

Commençons par traefik/rbac.yml - le fichier défini le compte de service (Service Account), le rôle au niveau du cluster (Cluster Role) et la liaison entre le rôle et le compte de service (Cluster Role Binding)

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
  namespace: traefik
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: traefik-ingress-controller
rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
    - extensions
    resources:
    - ingresses/status
    verbs:
    - update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
  name: traefik-ingress-controller
  namespace: traefik
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
  name: traefik-ingress-controller
  namespace: traefik

Ensuite, pour Traefik, j’ai besoin d’un fichier traefik.toml avec la configuration que je mets à disposition sous la forme d’une ConfigMap dans un fichier traefik/traefik-toml-configmap.yml :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-conf
data:
  traefik.toml: |
    defaultEntryPoints = ["http", "https"]

    logLevel = "INFO"

    insecureSkipVerify = true

    [entryPoints]
      [entryPoints.http]
        address = ":80"
      [entryPoints.https]
        address = ":443"
        [entryPoints.https.tls]
      [entryPoints.api]
        address = ":8080"

    [api]
    entryPoint = "api"
    dashboard = true
    debug = false

    [kubernetes]

Le dashboard est à protéger par une authentification pour éviter tout accès non souhaité. Je l’ai supprimé de la configuration par simplicité.

Je peux donc enfin déployer Traefik via le fichier traefik/traefik-deployment.yml :

---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: traefik-ingress-controller
  labels:
    k8s-app: traefik-ingress-lb
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: traefik-ingress-lb
  template:
    metadata:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik:1.7.16
        name: traefik-ingress-lb
        volumeMounts:
        - mountPath: /config
          name: traefik-config
        ports:
        - name: http
          containerPort: 80
        - name: admin
          containerPort: 8080
        - name: secure
          containerPort: 443
        args:
        - --configfile=/config/traefik.toml
      volumes:
        - name: traefik-config
          configMap:
            name: traefik-conf

Nous déployons donc :

  • Traefik en Deployment
  • Les ports 80, 443 et 8080 sont définis
  • La configuration est une ConfigMap

Pour permettre au cluster d’accéder aux différents ports, il faut définir un service via le fichier traefik-service-clusterip.yml :

---
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service-clusterip
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 8080
      name: admin
    - protocol: TCP
      port: 443
      name: secure
  type: ClusterIP

Et pour avoir un accès de l’extérieur, il faut instancier un load-balancer via le fichier traefik/traefik-service-loadbalancer.yml

kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service-lb
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 443
      name: secure
  type: LoadBalancer

Pour donner l’accès au dashboard via une url sécurisée par un certificat Let’s Encrypt, il faut déclarer un Ingress, dans le fichier traefik/traefik-api-ingress.yml :

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: traefik
    cert-manager.io/issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/redirect-entry-point: https
    traefik.ingress.kubernetes.io/redirect-permanent: "true"
    ingress.kubernetes.io/ssl-redirect: "true"
    ingress.kubernetes.io/ssl-temporary-redirect: "false"
  name: traefik-web-ui
spec:
  rules:
  - host: traefik.k8s.cerenit.fr
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ingress-service-clusterip
          servicePort: admin
  tls:
  - hosts:
    - traefik.k8s.cerenit.fr
    secretName: traefik-cert

L’idée est donc de rentre le dashboard accessible via l’url traefik.k8s.cerenit.fr.

La section tls de l’ingress indique le nom d’hôte pour lequel le certificat va être disponible et le nom du secret contenant le certificat du site que nous n’avons pas encore créé.

Les annotations permettent :

  • de déclarer le type d’ingress à utiliser ; ici: traefik
  • de déclarer que le certificat qui doit être fourni par cert-manager est un certificat de type Let’s Encrypt
  • de faire une redirection http vers https systématique.

Les deux premières annotations permettent de ne pas avoir à déclarer soi même le certificat - il est automatiquement généré via ingress-shim. Cela vous fait donc un objet kubernetes en moins à gérer dans votre configuration. Si vous ne souhaitez pas vous appuyer sur ce méchanisme d’ingress-shim, il vous faudra ne pas utiliser ces annotations et gérer vous même un objet “Certificate”.

Il ne reste plus qu'à faire pour instancier le tout :

kubectl create -f traefik/

Pour la génération du certificat, il conviendra de vérifier la sortie de

kubectl describe certificate traefik-cert

Et voilà - maintenant que le problème des certificats est corrigé, je vais pouvoir passer dans un contexte de déploiement multi-nodes.

Le Blog

Nous partageons ici notre veille et nos réflexions

Nuage de tags

docker kubernetes traefik ansible elasticsearch postgres kafka influxdb grafana timeseries python aws redis sécurité tick cloud mysql ovh cassandra helm swarm test chronograf terraform warp10 docker-compose ksql log machine-learning microservice résilience serverless spark telegraf timescaledb angularjs api architecture cncf confluent container git graphql hashicorp java javascript opensource ptsm rancher service-mesh stream windows arm bilan csp cérénit devops dns documentation elastic flux gcp hpkp iac ingress jenkins kafka-streams kapacitor kibana lambda lean licence maintenance microsoft mobile monitoring nginx npm optimisation orientdb perspective prometheus redhat rest rethinkdb reverse-proxy s3 sauvegarde sql ssh agile apm automatisation azure bash big-data cert-manager certificat ci/cd cli cluster containerd continous-delivery continous-integration cookie dashboard diff fluxlang framework gdpr gitlab grav hsts http/3 https hypriot hébergement influxace influxdata istio json k3s kubedb lets-encrypt linux load-balancer meetup molecule mongodb operator php pip pipeline postgresql reaper replication rook rpi rsyslog scale scaleway secrets solr sre systemd tls vault virtualenv vscode vue.js wagtail yarn accessibilité akka alerte alibaba amazon-emr anonymisation anthos ara audit bastion beam beat bigdatahebdo bounded-context branche brigade browser buildkit cahier-des-charges cassandra-reaper cd cdc ceph certificats chart checklist chrome ci cloud-init cloud-native cloud-storage clusterip cnab cockroachdb code codeurs-en-seine confluence consul continous-deployment coreos cors cqrs crash cron crontab csrf css curl d3.js daemonset data-pipelining data.gouv.fr datacenter dataviz date ddd debezium debian delta deployment desktop devoxx distributed-systems dive docker-app docker-hub docker-registry docker-swarm documentdb dokcer draft drop-in déploiement développement-du-site e-commerce ebs ec2 edge elassandra electron elk engineering ergonomie etcd event-sourcing faas facebook faisabilité falcor feature-policy feed filebeat firebase firefox fish flash flask fleet flink fluentd formation foundation frontend fsync fullstack github gke glacier glowroot google google-cloud-next gpu grid géospatial hacker hadoop haproxy hdfs header html html5 http hue ia iaac ibm immutable incident index influxcloud influxdays infrastructure-as-code ingénierie inspec jq jquery jwt k3d k8s k9s kotlin kubeadm kubecon kubectl laravel linky liste-de-diffusion loadbalancer logstash logstatsh loi maesh mailing-list management mariadb message metallb micro-service mot-de-passe multi-cloud médecine métrique newsletter nodeport nomad nosql null object-storage observabilité opendata openebs openmetrics openshit openssh openweb over-engineering packaging pandas partiql password percona performance persistent-volume-claim pipenv pod portainer prediction prescience publicité push pyenv quasardb quay queue quic ram rambleed raml raspberrypi react recaptcha recherche redistimeseries registry reindex reinvent reliability responsive revocation revue-de-code rkt rolespec root rpo rto runc rwd scanner schema sdk search select serverless-architecture service-worker sha1 sharding shell shipyard société souveraineté-numérique spinnaker spécifications sri ssh-agent ssl statistique superset sympa syslog-ng test-unitaire tidb tiers timer timezone training travail tsl ubuntu unikernel unit ux vendredi vie-privée virtualbox virtualisation vitess vm vnc volume voxxeddays vpc web yubikey

Syndication

Restez informé(s) de notre actualité en vous abonnant au flux du blog (Atom)