I have been trying to get the HTTP01-challenge working with Consul API Gateway & Cert-Manager on Kubernetes, but I am unable to get this to work (or even figure out if it is possible). Anyone have a working example or some insight to share?
I do the following:
Create a Gateway
Create a Deployment (Connect-Inject)
Create HTTPRoute
Create a Certificate Issuer (LetsEncrypt, using http01 solver)
Create/request a Certificate for Gateway TLS
Everything seems to work as expected. A new service for handling the ACME-Challenge is created, and a new HTTPRoute linking the Gateway to this service is created as well.
However, I am not able to see a way for me to include this “created” service into the Consul catalog, and I get an error from the gateway controller:
Gateway Controller Error
[ERROR] service/resolver.go:272: consul-api-gateway-server.k8s.Reconciler: could not resolve consul service: error="consul service default/cm-acme-http-solver-b747j not found"
…this service exists/gets created, but is not inside consul catalog/mesh.
The DNS/gateway works fine with the “demo” deployment on port 80
HTTPRoute (generated; invalid state and cannot bind)
As far as I can read from the referenced issue, the error-message is the same, but case is a bit different.
I don’t think I am able to route to this service, since it’s not created as part of the consul-connect mesh (annotation missing from pod: "'consul.hashicorp.com/connect-inject': 'true'").
I should be able to set this, I guess, so I will have a look at the documentation again, but as far as I can see from the CertManager API Spec, I can’t set custom annotations on the pod.
So I was able to repeat the steps above, and saw the same error.
This error is caused because the annotation consul.hashicorp.com/connect-inject: "true" is missing in the service that is created (as you mentioned in a comment).
There are two possible solutions:
Cert-manager needs to enable pass-through of annotations for gatewayHTTPRoute
It seems that cert-manager allows the pass-through of annotations for their HTTP-01 Ingress Solver (HTTP01 - cert-manager Documentation)
It might be worth your time to put in a feature request to allow this on their gateways too.
We did try to see if we could hack it, we realized that the object used for this internally gets merged into the pod, as shown here. However they do not allow both an ingress and gatewayHTTPRoute solver on an issuer, so that didn’t work.
### This Issuer won't work because it contains both `ingress` and `gatewayHTTPRoute` types
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: example-issuer
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: acme-issuer-key
email: test@yahoo.com
solvers:
- http01:
# not valid to have both ingress and gatewayHTTPRoute
# ingress:
# ingressTemplate:
# metadata:
# annotations:
# "consul.hashicorp.com/connect-inject": "true"
gatewayHTTPRoute:
labels:
name: example-gateway2
parentRefs:
- name: example-gateway # Gateway used for HTTP01 requests
kind: Gateway
Enable connect-inject by default in Consul (not recommend)
You can use this option to enable connect-inject by default.
This will make sure that every service in the cluster has a connect-inject container created as a sidecar. This is not ideal, however, as you may not want every service registered with Consul. You can specifically turn this feature off per deployment, but this will not work out of the box with cert-manager. They would need to add a way for the user to add annotations to
their main deployments:
➜ ~ kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-7bd65658bc-47hrf 0/2 PodInitializing 0 44s
cert-manager-cainjector-867cf7f7cc-khbdh 0/2 Init:0/1 0 44s
cert-manager-startupapicheck-vgrd6 0/2 Init:0/1 0 44s
cert-manager-webhook-55cf6bbd97-rfsd6 0/2 PodInitializing 0 44s
The annotation would be: consul.hashicorp.com/connect-inject: "false"
Also the above annotation would need to be manually added to any deployment in the cluster that you did not want registered with Consul.
I have not been able to try this out any further due to time constraints, but I will hopefully be able to pick it up again at a later time. Probably by creating a pull request to cert-manager repository.
Unfortunately, enabling connect-inject by default is not an option (and I think this also didn’t work, due to service-account requirements, but I’m not 100% sure I remember correctly. I do believe I tried this, though.)
I chased this thread also. It’s more than enabling ‘connect-inject’, I gave it a day of exploration and here are the things I found, (I have ACLs enabled)
Instead of creating randomly named POD and Services you need to:
Change CertManager to:
Create a service account with a random suffix that matches the service name (and not use generateName on the Service and the pod.
add the service account to the pod
Create a service defaults to set type to http (maybe optional)
enable connect inject on the pod
create service intentions to allow gateway to talk to solver pod.
At this point I gave up on possibly creating a pull request for CM. I think it would probably be easier to either have consul API gateway write a static-ish http01 solver and have it present an API that allows the challenges to be loaded into that solver. This would mean not using cert-manager, which does a lot of good for looking at cert expirations etc.
Another answer would be to allow Consul API gateway to serve up non-consul services so that what cert manager is doing is more compatible with Consul. I looked at a terminating gateway, but it made my head spin and since the cert manager services all come up with random names it just seemed like too much of a task.
Following up on what I looked at for Terminating Gateways:
From my reading a terminating gateway will add the envoy ‘sidecar’ proxy to an existing service that is registered already in consul.
The Certificate Manager creates a pod and a service with random suffixes in the name (sine maybe there might be multiple certificates in play at once). It then adds an httproute to this randomly named service.
So how do I register with consul, an ephemeral service created by certificate manager? From my kubernetes yaml I need a consul service name for the terminating endpoint.
A more basic question say I have service xxx on a known DNS name and port? lets say it’s not even kubernetes, like a Postgres Azure Service. How do I mesh that in?
So I think what is really needed here is for Consul API gateway to have an ‘out’ to allow an http path that is just a kubernetes service and not consul.