{"id":553,"date":"2018-04-07T21:53:43","date_gmt":"2018-04-07T13:53:43","guid":{"rendered":"https:\/\/vinta.ws\/code\/?p=553"},"modified":"2026-03-17T01:20:55","modified_gmt":"2026-03-16T17:20:55","slug":"kube-lego-automatically-provision-tls-certificates-in-kubernetes","status":"publish","type":"post","link":"https:\/\/vinta.ws\/code\/kube-lego-automatically-provision-tls-certificates-in-kubernetes.html","title":{"rendered":"kube-lego: Automatically provision TLS certificates in Kubernetes"},"content":{"rendered":"<p>kube-lego automatically requests certificates for Kubernetes Ingress resources from Let's Encrypt.<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/jetstack\/kube-lego\">https:\/\/github.com\/jetstack\/kube-lego<\/a><br \/>\n<a href=\"https:\/\/letsencrypt.org\/\">https:\/\/letsencrypt.org\/<\/a><\/p>\n<p>I run kube-lego v0.1.5 with Kubernetes v1.9.4, everything works very well.<\/p>\n<h2>Deploy kube-lego<\/h2>\n<p>It is strongly recommended to try Let's Encrypt Staging API first.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-yaml\"># kube-lego\/deployment.yaml\nkind: Namespace\napiVersion: v1\nmetadata:\n  name: kube-lego\n---\nkind: ConfigMap\napiVersion: v1\nmetadata:\n  name: kube-lego\n  namespace: kube-lego\ndata:\n  LEGO.EMAIL: \"your@email.com\"\n  # LEGO.URL: \"https:\/\/acme-v01.api.letsencrypt.org\/directory\"\n  LEGO.URL: \"https:\/\/acme-staging.api.letsencrypt.org\/directory\"\n---\nkind: Deployment\napiVersion: apps\/v1\nmetadata:\n  name: kube-lego\n  namespace: kube-lego\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: kube-lego\n  template:\n    metadata:\n      labels:\n        app: kube-lego\n    spec:\n      containers:\n      - name: kube-lego\n        image: jetstack\/kube-lego:0.1.5\n        ports:\n        - containerPort: 8080\n        env:\n        - name: LEGO_LOG_LEVEL\n          value: debug\n        - name: LEGO_EMAIL\n          valueFrom:\n            configMapKeyRef:\n              name: kube-lego\n              key: LEGO.EMAIL\n        - name: LEGO_URL\n          valueFrom:\n            configMapKeyRef:\n              name: kube-lego\n              key: LEGO.URL\n        - name: LEGO_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        - name: LEGO_POD_IP\n          valueFrom:\n            fieldRef:\n              fieldPath: status.podIP\n        readinessProbe:\n          httpGet:\n            path: \/healthz\n            port: 8080\n          initialDelaySeconds: 5\n          timeoutSeconds: 1<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/jetstack\/kube-lego\/tree\/master\/examples\">https:\/\/github.com\/jetstack\/kube-lego\/tree\/master\/examples<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ kubectl apply -f kube-lego\/ -R<\/code><\/pre>\n<h2>Configure the Ingress<\/h2>\n<ul>\n<li>Add an annotation <code>kubernetes.io\/tls-acme: &quot;true&quot;<\/code> to <code>metadata.annotations<\/code><\/li>\n<li>Add domains to <code>spec.tls.hosts<\/code>.<\/li>\n<\/ul>\n<p><code>spec.tls.secretName<\/code> is the Secret used to store the certificate received from Let's Encrypt, i.e., <code>tls.key<\/code> and <code>tls.crt<\/code>. If no Secret exists with that name, it will be created by kube-lego.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-yaml\"># ingress.yaml\nkind: Ingress\napiVersion: extensions\/v1beta1\nmetadata:\n  name: simple-project\n  annotations:\n    kubernetes.io\/ingress.class: \"gce\"\n    kubernetes.io\/tls-acme: \"true\"\nspec:\n  tls:\n  - secretName: kittenphile-com-tls\n    hosts:\n    - kittenphile.com\n    - www.kittenphile.com\n    - api.kittenphile.com\n  rules:\n  - host: kittenphile.com\n    http:\n      paths:\n      - path: \/*\n        backend:\n          serviceName: simple-frontend\n          servicePort: http\n  - host: www.kittenphile.com\n    http:\n      paths:\n      - path: \/*\n        backend:\n          serviceName: simple-frontend\n          servicePort: http\n  - host: api.kittenphile.com\n    http:\n      paths:\n      - path: \/*\n        backend:\n          serviceName: simple-api\n          servicePort: http<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/services-networking\/ingress\/#tls\">https:\/\/kubernetes.io\/docs\/concepts\/services-networking\/ingress\/#tls<\/a><\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ kubectl apply -f ingress.yaml<\/code><\/pre>\n<p>You could find exact ACME challenge paths by inspecting your Ingress resource.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ kubectl describe ing simple-project\n...\nTLS:\n  kittenphile-com-tls terminates kittenphile.com,www.kittenphile.com,api.kittenphile.com\nRules:\n  Host                 Path  Backends\n  ----                 ----  --------\nkittenphile.com\n                       \/.well-known\/acme-challenge\/*   kube-lego-gce:8080 (&lt;none&gt;)\n                       \/*                              simple-frontend:http (&lt;none&gt;)\nwww.kittenphile.com\n                       \/.well-known\/acme-challenge\/*   kube-lego-gce:8080 (&lt;none&gt;)\n                       \/*                              simple-frontend:http (&lt;none&gt;)\napi.kittenphile.com\n                       \/.well-known\/acme-challenge\/*   kube-lego-gce:8080 (&lt;none&gt;)\n                       \/*                              simple-api:http (&lt;none&gt;)\n...<\/code><\/pre>\n<p>You might want to see logs of kube-lego Pods for observing the progress.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ kubectl logs -f deploy\/kube-lego --namespace kube-lego<\/code><\/pre>\n<h2>Create a Production Certificate<\/h2>\n<p>After you make sure everything works ok, you are able to request production certificates for your domains.<\/p>\n<p>Follow these instructions:<\/p>\n<ul>\n<li>Change <code>LEGO_URL<\/code> to <code>https:\/\/acme-v01.api.letsencrypt.org\/directory<\/code><\/li>\n<li>Delete account secret <code>kube-lego-account<\/code><\/li>\n<li>Delete certificate secret <code>kittenphile-com-tls<\/code><\/li>\n<li>Restart <code>kube-lego<\/code><\/li>\n<\/ul>\n<pre class=\"line-numbers\"><code class=\"language-console\">$ kubectl get secrets --all-namespaces\n$ kubectl delete secret kube-lego-account --namespace kube-lego &amp;&amp; \n  kubectl delete secret kittenphile-com-tls\n\n$ kubectl replace --force -f kube-lego\/ -R\n$ kubectl logs -f deploy\/kube-lego --namespace kube-lego<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/jetstack\/kube-lego#switching-from-staging-to-production\">https:\/\/github.com\/jetstack\/kube-lego#switching-from-staging-to-production<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>kube-lego automatically requests certificates for Kubernetes Ingress resources from Let's Encrypt. By the way, I run kube-lego v0.1.5 with Kubernetes v1.9.4, everything works very fine.<\/p>\n","protected":false},"author":1,"featured_media":555,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38],"tags":[114,123],"class_list":["post-553","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-devops","tag-google-cloud-platform","tag-kubernetes"],"_links":{"self":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/553","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/comments?post=553"}],"version-history":[{"count":0,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/553\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media\/555"}],"wp:attachment":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media?parent=553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/categories?post=553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/tags?post=553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}