Linkerd 2.10 series

  • Linkerd V2 Service Mesh
  • Tencent Cloud K8S deployment Service Mesh — Linkerd2 & Traefik2 deployment emojivoto application
  • Learn about the basic features of Linkerd 2.10 and step into the era of Service Mesh
  • Linkerd 2.10(Step by Step) — 1. Add your service to Linkerd
  • Linkerd 2.10(Step by Step) — 2. Automated Canary publishing

Linkerd 2.10 中文 版

  • linkerd.hacker-linner.com

Automatic rotation control plane TLS credentials

Linkerd’s automatic mTLS function generates TLS certificates for agents using a set of TLS credentials: Trust anchors, Issuer certificates, and private keys. While Linkerd automatically rotates the TLS certificates for the data plane agent every 24 hours, it does not rotate the TLS credentials used to issue those certificates. In this document, we describe how to automatically rotate issuer certificates and private keys using an external solution.

(Note that Linkerd’s trust anchor must still be manually rotated on the long-lived cluster)

Cert manager

Cert-manager is a popular project for making TLS credentials from external sources available to Kubernetes clusters.

First, install cert-Manager on your cluster.

If you want to install cert-Manager >= 1.0, you need Kubernetes >= 1.16. The traditional custom resource definition for Kubernetes <= 1.15 in cert-Manager does not have the keyAlgorithm option, so the certificate will be generated using RSA and not compatible with Linkerd.

For more details on version requirements, see the V0.16 to V1.0 upgrade notes.

Cert Manager acts as a Certificate Authority (CA) on the cluster

In this case, instead of getting credentials from an external source, we configure them as a CA on the cluster and have it periodically reissue Linkerd’s Issuer Certificate and private key.

First, create the namespace that cert-Manager will use to store its Linkerd-related resources. For simplicity, we recommend using the default Linkerd control plane namespace:

kubectl create namespace linkerd
Copy the code

Save the signing key pair as Secret

Next, using the STEP tool, create a signing key pair and store it in Kubernetes Secret in the namespace created above:

step certificate create root.linkerd.cluster.local ca.crt ca.key \ --profile root-ca --no-password --insecure && kubectl  create secret tls \ linkerd-trust-anchor \ --cert=ca.crt \ --key=ca.key \ --namespace=linkerdCopy the code

For longer-lived trust anchor certificates, the –not-after argument is passed to the step command with the desired value (for example –not-after=87600h).

The Issuer of the referencing the secret

With Secret, we can create a cert-manager “Issuer” resource that references it:

cat <<EOF | kubectl apply -f - apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: linkerd-trust-anchor namespace: linkerd spec: ca: secretName: linkerd-trust-anchor EOF
Copy the code

Issuing certificates and writing them to a secret

Finally, we can create a cert-manager “Certificate” resource that uses the Issuer to generate the required certificates:

cat <<EOF | kubectl apply -f - apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: linkerd-identity-issuer namespace: linkerd spec: secretName: linkerd-identity-issuer duration: 48h renewBefore: 25h issuerRef: name: linkerd-trust-anchor kind: Issuer commonName: identity.linkerd.cluster.local dnsNames: - identity.linkerd.cluster.local isCA: true privateKey: algorithm: ECDSA usages: - cert sign - crl sign - server auth - client auth EOF
Copy the code

In the YAML listing above, the Duration key instructs cert-Manager to treat the certificate as valid for 48 hours, and the renewBefore key instructs cert-Manager to attempt to issue a new certificate 25 hours before the current one expires. These values can be customized to your liking.)

At this point, cert-Manager can now use the Certificate Resource to obtain the TLS credentials. This credential will be stored in secret named Linkerd-identity-Issuer. To verify your newly issued certificate, you can run:

kubectl get secret linkerd-identity-issuer -o yaml -n linkerd
Copy the code

Now we just need to tell Linkerd to use these credentials.

Due to a bug in cert-Manager, if you use version 0.15 of Cert-Manager with experimental controllers, It issues a certificate that is incompatible with Linkerd version <= stable-2.8.1.

Your Linkerd-Identity pod may crash with the following log output:

"Failed to initialize identity service: failed to read CA from disk:
unsupported block type: 'PRIVATE KEY'"
Copy the code

Some possible solutions to this problem are:

  • Upgrade Linkerd to includefixEdge version> = edge - 20.6.4.
  • Upgrade cert-Manager to version> = 0.16. (How to upgrade)
  • Disable the cert-Manager experimental Controllers. (docs)

Alternative CA providers

Instead of using Cert Manager as the CA, you can configure Cert Manager to rely on many other solutions, such as Vault. You can find more details here on how to set up an existing certificate manager to use different types of issuers.

Third-party certificate management solution

It is important to note that the mechanism provided by Linkerd can also be used outside of cert-Manager. Linkerd reads linkerd-identity-issuer Secret, and if it is of type kubernetes. IO/TLS, uses the content as its TLS credentials. This means that any solution that can rotate them by writing TLS certificates to this key can be used to provide dynamic TLS certificate management.

Use these credentials in the CLI installation

For CLI installations, the Linkerd control plane should be installed with the — identity-external-Issuer flag, which instructs Linkerd to read certificates from linkerd-identity-issuer Secret. Whenever the certificate and key stored in Secret are updated, the Identity service automatically detects the change and reloads the new credentials.

Look! We have set up automatic rotation of TLS credentials on the Linkerd control plane. If you want to monitor the update process, you can check the Issue Updated event issued by the service:

kubectl get events --field-selector reason=IssuerUpdated -n linkerd
Copy the code

Installation using Helm

For Helm installation, instead of running Linkerd install, set identityTrustAnchorsPEM to the value of ca.crt in Linkerd-identity-issuer Secret:

helm install linkerd2 \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set identity.issuer.scheme=kubernetes.io/tls \
  --set installNamespace=false \
  linkerd/linkerd2 \
  -n linkerd
Copy the code

For Helm versions lower than V3, the –name flag must be passed specifically. In Helm V3, it is deprecated and is the first parameter specified above.

Automatic rotation of Webhook TLS credentials

The Linkerd control plane contains several components, called Webhooks, which are called directly by Kubernetes itself. Traffic from Kubernetes to Linkerd Webhooks is secured using TLS, so each Webhooks requires a secret containing TLS credentials. These certificates are different from those used by the Linkerd agent to secure poD-to-POD communication and use a completely separate trust chain.

By default, when Linkerd is installed with Linkerd CLI or Linkerd Helm Chart, TLS credentials are automatically generated for all Webhooks. If these certificates expire or need to be regenerated for any reason, performing Linkerd Upgrade (using Linkerd CLI or using Helm) will regenerate them.

This workflow works for most users. However, if you need to automatically rotate these Webhook certificates on a regular basis, you can use cert-Manager to manage them automatically.

Install the Cert manager

First, install cert-Manager on your cluster and create the namespace that cert-Manager will use to store its Webhook related resources. For simplicity, we recommend using the default linkerd namespace: linkerd

# control plane core
kubectl create namespace linkerd

# viz (ignore if not using the viz extension)
kubectl create namespace linkerd-viz

# viz (ignore if not using the jaeger extension)
kubectl create namespace linkerd-jaeger
Copy the code

Save the signing key pair as Secret

Next, we will use the STEP tool to create a signing key pair for each Webhook certificate:

step certificate create webhook.linkerd.cluster.local ca.crt ca.key \
  --profile root-ca --no-password --insecure --san webhook.linkerd.cluster.local

kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd

# ignore if not using the viz extension
kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-viz

# ignore if not using the jaeger extension
kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-jaeger
Copy the code

Creating publishers that reference Secrets (Issuers)

With Secrets, we can create cert-manager “Issuer” resources that reference them:

cat <<EOF | kubectl apply -f - apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: webhook-issuer namespace: linkerd spec: ca: secretName: webhook-issuer-tls --- # ignore if not using the viz extension apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: webhook-issuer namespace: linkerd-viz spec: ca: secretName: webhook-issuer-tls --- # ignore if not using the jaeger extension apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: webhook-issuer namespace: linkerd-jaeger spec: ca: secretName: webhook-issuer-tls EOF
Copy the code

Issue a certificate and write it to secrets

Finally, we can create the cert-Manager “Certificate” resource, which uses the issuer (Issuers) to generate the required Certificate:

cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: linkerd-proxy-injector
  namespace: linkerd
spec:
  secretName: linkerd-proxy-injector-k8s-tls
  duration: 24h
  renewBefore: 1h
  issuerRef:
    name: webhook-issuer
    kind: Issuer
  commonName: linkerd-proxy-injector.linkerd.svc
  dnsNames:
  - linkerd-proxy-injector.linkerd.svc
  isCA: false
  privateKey:
    algorithm: ECDSA
  usages:
  - server auth
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: linkerd-sp-validator
  namespace: linkerd
spec:
  secretName: linkerd-sp-validator-k8s-tls
  duration: 24h
  renewBefore: 1h
  issuerRef:
    name: webhook-issuer
    kind: Issuer
  commonName: linkerd-sp-validator.linkerd.svc
  dnsNames:
  - linkerd-sp-validator.linkerd.svc
  isCA: false
  privateKey:
    algorithm: ECDSA
  usages:
  - server auth
---
# ignore if not using the viz extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: tap
  namespace: linkerd-viz
spec:
  secretName: tap-k8s-tls
  duration: 24h
  renewBefore: 1h
  issuerRef:
    name: webhook-issuer
    kind: Issuer
  commonName: tap.linkerd-viz.svc
  dnsNames:
  - tap.linkerd-viz.svc
  isCA: false
  privateKey:
    algorithm: ECDSA
  usages:
  - server auth
---
# ignore if not using the viz extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: linkerd-tap-injector
  namespace: linkerd-viz
spec:
  secretName: tap-injector-k8s-tls
  duration: 24h
  renewBefore: 1h
  issuerRef:
    name: webhook-issuer
    kind: Issuer
  commonName: tap-injector.linkerd-viz.svc
  dnsNames:
  - tap-injector.linkerd-viz.svc
  isCA: false
  privateKey:
    algorithm: ECDSA
  usages:
  - server auth
---
# ignore if not using the jaeger extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: jaeger-injector
  namespace: linkerd-jaeger
spec:
  secretName: jaeger-injector-k8s-tls
  duration: 24h
  renewBefore: 1h
  issuerRef:
    name: webhook-issuer
    kind: Issuer
  commonName: jaeger-injector.linkerd.svc
  dnsNames:
  - jaeger-injector.linkerd.svc
  isCA: false
  privateKey:
    algorithm: ECDSA
  usages:
  - server auth
EOF
Copy the code

At this point, cert-Manager can now use these Certificate resources to obtain TLS certificates. These credentials are stored in linkerd-proxy-injector-k8s-TLS, Linkerd-SP-validator-k8s-TLS, tap-k8S-TLS, tap-Injector-k8s-TLS, and linkerd-proxy-injector-k8s-TLS, respectively Jaeger – Injector – K8s – TLS these secrets.

Now we just need to tell Linkerd to use these credentials.

Use these credentials in the CLI installation

To configure Linkerd to use credentials from cert-Manager instead of generating its own, we generate a supplementary configuration file:

CA=$(awk '{ print " " $0 }' ca.crt)

cat > config.yml <<EOF proxyInjector: externalSecret: true caBundle: | $CA profileValidator: externalSecret: true caBundle: | $CA EOF

# ignore if not using the viz extension
cat > config-viz.yml <<EOF tap: externalSecret: true caBundle: | $CA tapInjector: externalSecret: true caBundle: | $CA EOF

# ignore if not using the jaeger extension
cat > config-jaeger.yml <<EOF webhook: externalSecret: true caBundle: | $CA EOF
Copy the code

Now we can install Linkerd using these configuration files:

linkerd install --values=config.yml | kubectl apply -f -

# ignore if not using the viz extension
linkerd viz install --values=config-viz.yml | kubectl apply -f -

# ignore if not using the jaeger extension
linkerd jaeger install --values=config-jaeger.yml | kubectl apply -f -
Copy the code

Installation using Helm

For Helm installation, we can directly configure the Helm value:

helm install linkerd2 \
  --set installNamespace=false\ -set proxyInjector.externalSecret=true \
  --set-file proxyInjector.caBundle=ca.crt \
  --set profileValidator.externalSecret=true \
  --set-file profileValidator.caBundle=ca.crt \
  linkerd/linkerd2 \
  -n linkerd

# ignore if not using the viz extension
helm install linkerd-viz \
  --set installNamespace=false\ -set tap.externalSecret=true \
  --set-file tap.caBundle=ca.crt \
  --set tapInjector.externalSecret=true \
  --set-file tapInjector.caBundle=ca.crt \
  linkerd/linkerd-viz \
  -n linkerd-viz

# ignore if not using the jaeger extension
helm install linkerd-jaeger \
  --set installNamespace=false\ -set webhook.externalSecret=true \
  --set-file webhook.caBundle=ca.crt \
  linkerd/linkerd-jaeger \
  -n linkerd-jaeger
Copy the code

When installing Linkerd with Helm, you must also provide issuer trust root and issuer credentials, as described in installing Linkerd with Helm.

For Helm versions lower than V3, the –name flag must be passed specifically. In Helm V3, it is deprecated and is the first parameter specified above.