These instructions assume you have already setup a Knative cluster and installed cert-manager into your cluster. For more information, see using an SSL certificate. They also assume you have already set up your managed zone with Cloud DNS as part of configuring the domain to map to your IP address.
To automate the generation of a certificate with cert-manager and LetsEncrypt,
we will use a DNS01
challenge type, which requires the domain owner to add a
TXT record to their zone to prove ownership. Other challenge types are not
currently supported by Knative.
To add the TXT record, configure Knative with a service account that can be used by cert-manager to create and update the DNS record.
To begin, create a new service account with the project role dns.admin
:
# Set this to your GCP project ID
export PROJECT_ID=<your-project-id>
# Name of the service account you want to create.
export CLOUD_DNS_SA=cert-manager-cloud-dns-admin
gcloud --project $PROJECT_ID iam service-accounts \
create $CLOUD_DNS_SA \
--display-name "Service Account to support ACME DNS-01 challenge."
# Fully-qualified service account name also has project-id information.
export CLOUD_DNS_SA=$CLOUD_DNS_SA@$PROJECT_ID.iam.gserviceaccount.com
# Bind the role dns.admin to this service account, so it can be used to support
# the ACME DNS01 challenge.
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member serviceAccount:$CLOUD_DNS_SA \
--role roles/dns.admin
# Download the secret key file for your service account.
gcloud iam service-accounts keys create ~/key.json \
--iam-account=$CLOUD_DNS_SA
After obtaining the service account secret, publish it to your cluster. This
command uses the secret name cloud-dns-key
, but you can choose a different
name.
# Upload that as a secret in your Kubernetes cluster.
kubectl create secret --namespace cert-manager generic cloud-dns-key \
--from-file=key.json=$HOME/key.json
# Delete the local secret
rm ~/key.json
Next, configure cert-manager to request new certificates and verify the challenges using DNS.
This example configures cert-manager to use LetsEncrypt, but you can use any certificate provider that supports the ACME protocol.
This example uses the dns01
challenge type, which will enable certificate
generation and wildcard certificates.
kubectl apply --filename - <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-issuer
namespace: cert-manager
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
# This will register an issuer with LetsEncrypt. Replace
# with your admin email address.
email: [email protected]
privateKeySecretRef:
# Set privateKeySecretRef to any unused secret name.
name: letsencrypt-issuer
dns01:
providers:
- name: cloud-dns-provider
clouddns:
# Set this to your GCP project-id
project: $PROJECT_ID
# Set this to the secret that we publish our service account key
# in the previous step.
serviceAccountSecretRef:
name: cloud-dns-key
key: key.json
EOF
To check if your ClusterIssuer is valid, enter:
kubectl get clusterissuer --namespace cert-manager letsencrypt-issuer --output yaml
Then confirm that its conditions have Ready=True
. For example:
status:
acme:
uri: https://acme-v02.api.letsencrypt.org/acme/acct/40759665
conditions:
- lastTransitionTime: 2018-08-23T01:44:54Z
message: The ACME account was registered with the ACME server
reason: ACMEAccountRegistered
status: "True"
type: Ready
Next, configure which certificate issuer to use and which secret you will
publish the certificate into. Use the Secret istio-ingressgateway-certs
. The
following steps will overwrite this Secret if it already exists.
# Change this value to the domain you want to use.
export DOMAIN=your-domain.com
kubectl apply --filename - <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: my-certificate
# Istio certs secret lives in the istio-system namespace, and
# a cert-manager Certificate is namespace-scoped.
namespace: istio-system
spec:
# Reference to the Istio default cert secret.
secretName: istio-ingressgateway-certs
acme:
config:
# Each certificate could rely on different ACME challenge
# solver. In this example we are using one provider for all
# the domains.
- dns01:
provider: cloud-dns-provider
domains:
# Since certificate wildcards only allow one level, we will
# need to one for every namespace that Knative is used in.
# We don't need to use wildcard here, fully-qualified domains
# will work fine too.
- "*.default.$DOMAIN"
- "*.other-namespace.$DOMAIN"
# The certificate common name, use one from your domains.
commonName: "*.default.$DOMAIN"
dnsNames:
# Provide same list as `domains` section.
- "*.default.$DOMAIN"
- "*.other-namespace.$DOMAIN"
# Reference to the ClusterIssuer we created in the previous step.
issuerRef:
kind: ClusterIssuer
name: letsencrypt-issuer
EOF
To check that your certificate setting is valid, enter:
kubectl get certificate --namespace istio-system my-certificate --output yaml
Verify that its Status.Conditions
have Ready=True
. For example:
status:
acme:
order:
url: https://acme-v02.api.letsencrypt.org/acme/order/40759665/45358362
conditions:
- lastTransitionTime: 2018-08-23T02:28:44Z
message: Certificate issued successfully
reason: CertIssued
status: "True"
type: Ready
A condition with Ready=False
is a failure to obtain certificate, and such
condition usually has an error message to indicate the reason of failure.
In the last step, configure the knative-ingress-gateway
(or
knative-shared-gateway
if using Knative Serving 0.2.x or prior version) to use
the certificate that is generated and stored automatically by cert-manager.
The key edit here is adding the tls:
section to the end of the HTTPS port
configuration.
kubectl apply --filename - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: knative-ingress-gateway
namespace: knative-serving
spec:
selector:
knative: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "*"
tls:
httpsRedirect: true # sends 301 redirect for http requests.
mode: SIMPLE
privateKey: /etc/istio/ingressgateway-certs/tls.key
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
EOF
Now you can access your services via HTTPS; cert-manager will keep your certificates up-to-date, replacing them before the certificate expires.