munnerz / kube-acme

Retrieve certificates for Kubernetes Ingress resources from acme servers and store as secrets
Apache License 2.0
13 stars 0 forks source link

Automatically configure challenge endpoint #9

Open munnerz opened 8 years ago

munnerz commented 8 years ago

I'm opening this issue for discussion on how best to deal with automatic configuration of the /.well-known/acme-challenge endpoint. If we add it as an additional path on the existing Ingress resource, the challenge endpoint will also be encrypted with HTTPS, causing the challenge request to fail.

As an example to demonstrate, the resulting Ingress resource would be as follows:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  labels:
    acme-tls: "true"
spec:
  tls:
  - secretName: example-secret
    hosts:
    - example.com
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: backend-service
          servicePort: 80
      - path: /.well-known/acme-challenge
        backend:
          serverName: kube-acme
          servicePort: 80

This of course does depend upon the ingress controllers implementation, as some may serve standard HTTP on port 80 until the secret is created. With the https://github.com/nginxinc/kubernetes-ingress ingress controller it is possible to create a separate second ingress resource that omits the tls block and only contains the /.well-known/acme-challenge path, however with the GLB (google load balancer) implementation, a second IP address would be allocated thus causing the challenge request to fail.

Not sure how best to handle this situation in a way that makes the project work regardless of ingress controller unless someone is aware of any formal specification for how an Ingress should behave.

bprashanth commented 8 years ago

Tl;dr: I think we should start with the easiest, which is assuming that 80 will serve http and 443 https, or that users are OK with specifying a global default backend for the /.well-known path.

Currently there are at least 3 ways to handle https in a cross platform way:

Not all backends support SNI, or evern multi-cert termination in a single IP, so we can assume this is the LCD for now. Thinking out loud there are a few options:

First option Go all HTTP until we acquire the secret, then use one of the other HTTPS modes. This is relatively simple to achieve with cloud backends. We'd have to document that anytime the secret name is blank the loadbalancer is setup as HTTP but when the secret is populated, it's used to terminate.

What about renewal when 80 redirects to 443? We could make something else aware of when the secret expires, and delete it, so the lb flips to HTTP again. This feels weird.

Second option Use the default backend to point to the renewal nginx, and document that nothing else should catch the /.well-known path.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  labels:
    acme-tls: "true"
spec:
  backend:
    serviceName: acme-secret
    sevicePort: 80
  tls:
  - secretName: example-secret
    hosts:
    - example.com
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: backend-service
          servicePort: 80

This is also easy to do, but doesn't allow us to specify a "global" default backend. Instead, each host needs to specify a "/*" that goes to a 404 service.

Third option The catch with the first option is that some users might want to expose all their urls on HTTP, some want to redirect to HTTPS. To achieve the redirct, they'd probably proxy to something like nginx, since the cloudprovider doesn't know how to redirect yet.

We could document that to acquire certs, this proxy must service the /.well-known path without sending the 301. This suggests that we should make redirects more native to Ingress (https://github.com/kubernetes/contrib/issues/679), but eitherway to achieve a redirect currently we'd have to proxy through a non-cloud lb.

Tangentially