LinkerD with Automatic certificate rotation on Ubuntu 22.04

Here are my findings about LinkerD Service Mesh and its mutual automatic TLS feature that encrypts communications between meshed Pods without any modification needed to them.

We'll start from a slightly (ok, severely) outdated K8S cluster to test the appropriate update procedure, too.

This article is meant as a work-in-progress.

References

Cert-Manager installation

[test0|default] ferdi@DESKTOP-NL6I2OD:~$ helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.10.1 \
  --set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Fri Dec  9 09:39:17 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.10.1 has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.io/docs/configuration/

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.io/docs/usage/ingress/

Root CA certificate

We need a (long lasting) top-level certificate to serve as an origin for our mTLS certificates, so we'll create one ourselves with the aid of Step CLI.

Creation

[test0|default] ferdi@DESKTOP-NL6I2OD:~$ step certificate create root.linkerd.cluster.local ca.crt ca.key \
  --profile root-ca \
  --no-password \
  --insecure \
  --not-after=87600h
Your certificate has been saved in ca.crt.
Your private key has been saved in ca.key.

We're creating it with a 10-years validity, plenty of time to experiment!

Loading it to K8S

[test0|default] ferdi@DESKTOP-NL6I2OD:~$ kubectl create namespace linkerd
namespace/linkerd created
[test0|default] ferdi@DESKTOP-NL6I2OD:~$ kubectl create secret tls \
    linkerd-trust-anchor \
    --cert=ca.crt \
    --key=ca.key \
    --namespace=linkerd
secret/linkerd-trust-anchor created

We have created the linkerd namespace in advance, to be able to instruct Linkerd's Helm Chart to use the new certificate.

Hooking it up to Cert-Manager

We're going to need an Issuer resource, referencing our Secret

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: linkerd-trust-anchor
  namespace: linkerd
spec:
  ca:
    secretName: linkerd-trust-anchor

And a Certificate generated by this Issuer

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

After having applied these Custom Resources, the situation will be:

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ kubectl get secrets,issuers,certificates
NAME                             TYPE                                  DATA   AGE
secret/default-token-pbgcg       kubernetes.io/service-account-token   3      8m10s
secret/linkerd-identity-issuer   kubernetes.io/tls                     3      29s
secret/linkerd-trust-anchor      kubernetes.io/tls                     2      7m33s

NAME                                          READY   AGE
issuer.cert-manager.io/linkerd-trust-anchor   True    41s

NAME                                                  READY   SECRET                    AGE
certificate.cert-manager.io/linkerd-identity-issuer   True    linkerd-identity-issuer   29s

LinkerD

First of all, we'll need the CRDs

First installation

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm install linkerd-crds -n linkerd --create-namespace linkerd/linkerd-crds
NAME: linkerd-crds
LAST DEPLOYED: Fri Dec  9 10:08:26 2022
NAMESPACE: linkerd
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The linkerd-crds chart was successfully installed πŸŽ‰

To complete the linkerd core installation, please now proceed to install the
linkerd-control-plane chart in the linkerd namespace.

Looking for more? Visit https://linkerd.io/2/getting-started/

Then LinkerD itself, within the already present Namespace

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm install linkerd-control-plane -n linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set identity.issuer.scheme=kubernetes.io/tls \
  linkerd/linkerd-control-plane
NAME: linkerd-control-plane
LAST DEPLOYED: Fri Dec  9 10:09:58 2022
NAMESPACE: linkerd
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The Linkerd control plane was successfully installed πŸŽ‰

To help you manage your Linkerd service mesh you can install the Linkerd CLI by running:

  curl -sL https://run.linkerd.io/install | sh

Alternatively, you can download the CLI directly via the Linkerd releases page:

  https://github.com/linkerd/linkerd2/releases/

To make sure everything works as expected, run the following:

  linkerd check

The viz extension can be installed by running:

  helm install linkerd-viz linkerd/linkerd-viz

Looking for more? Visit https://linkerd.io/2/getting-started/

Course correction

My single-host K8S test cluster is running on top of Ubuntu 22.04, using Rancher (now SuSE) RKE1 Kubernetes distribution.

This simple fact lead me to this dire situation, after having installed the Helm Chart as shown above.

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ kubectl get po
NAME                                      READY   STATUS                  RESTARTS   AGE
linkerd-destination-54bc79696d-xcw7f      0/4     Init:CrashLoopBackOff   7          14m
linkerd-identity-76578bd5b9-68vrc         0/2     Init:CrashLoopBackOff   7          14m
linkerd-proxy-injector-746646f874-j45l4   0/2     Init:CrashLoopBackOff   7          14m

Let's check what's wrong with all of them!

test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ kubectl describe po linkerd-identity-76578bd5b9-68vrc|grep Message
      Message:   2-12-09T09:21:28Z" level=info msg="iptables-save v1.8.8 (legacy): Cannot initialize: Permission denied (you must be root)\n\n"
  Type     Reason     Age                   From               Message
[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ kubectl describe po linkerd-destination-54bc79696d-xcw7f|grep Message
      Message:   2-12-09T09:21:15Z" level=info msg="iptables-save v1.8.8 (legacy): Cannot initialize: Permission denied (you must be root)\n\n"
  Type     Reason     Age                From               Message

There's a reference to this error, at the thread's bottom there is a suggestion about an Helm Chart parameter that could save the day.

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm upgrade --install linkerd-control-plane -n linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set identity.issuer.scheme=kubernetes.io/tls \
  --set "proxyInit.iptablesMode=nft" \
  linkerd/linkerd-control-plane
Release "linkerd-control-plane" has been upgraded. Happy Helming!
NAME: linkerd-control-plane
LAST DEPLOYED: Fri Dec  9 10:33:13 2022
NAMESPACE: linkerd
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
The Linkerd control plane was successfully installed πŸŽ‰

To help you manage your Linkerd service mesh you can install the Linkerd CLI by running:

  curl -sL https://run.linkerd.io/install | sh

Alternatively, you can download the CLI directly via the Linkerd releases page:

  https://github.com/linkerd/linkerd2/releases/

To make sure everything works as expected, run the following:

  linkerd check

The viz extension can be installed by running:

  helm install linkerd-viz linkerd/linkerd-viz

Looking for more? Visit https://linkerd.io/2/getting-started/

Unfortunately, it didn'help a lot, below you'll find the relevant excerpt of kubectl describe pod

      Reason:    Error
      Message:   ave v1.8.8 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)\n\n"
time="2022-12-09T09:43:09Z" level=error msg="aborting firewall configuration"
Error: exit status 4

Serious troubleshooting here

Ok, let's check the default values of the Helm Chart to find out something that could help us.

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm show values linkerd/linkerd-control-plane > linkerd-values.yml
[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$

Here's the relevant section:

# proxy-init configuration
proxyInit:
  # -- Variant of iptables that will be used to configure routing. Currently,
  # proxy-init can be run either in 'nft' or in 'legacy' mode. The mode will
  # control which utility binary will be called. The host must support
  # whichever mode will be used
  iptablesMode: "legacy"
  # -- Default set of inbound ports to skip via iptables
  # - Galera (4567,4568)
  ignoreInboundPorts: "4567,4568"
  # -- Default set of outbound ports to skip via iptables
  # - Galera (4567,4568)
  ignoreOutboundPorts: "4567,4568"
  # -- Comma-separated list of subnets in valid CIDR format that should be skipped by the proxy
  skipSubnets: ""
  # -- Log level for the proxy-init
  # @default -- info
  logLevel: ""
  # -- Log format (`plain` or `json`) for the proxy-init
  # @default -- plain
  logFormat: ""
  image:
    # -- Docker image for the proxy-init container
    name: cr.l5d.io/linkerd/proxy-init
    # -- Pull policy for the proxy-init container Docker image
    # @default -- imagePullPolicy
    pullPolicy: ""
    # -- Tag for the proxy-init container Docker image
    version: v2.0.0
  resources:
    cpu:
      # -- Maximum amount of CPU units that the proxy-init container can use
      limit: 100m
      # -- Amount of CPU units that the proxy-init container requests
      request: 100m
    memory:
      # -- Maximum amount of memory that the proxy-init container can use
      limit: 20Mi
      # -- Amount of memory that the proxy-init container requests
      request: 20Mi
    ephemeral-storage:
      # -- Maximum amount of ephemeral storage that the proxy-init container can use
      limit: ""
      # -- Amount of ephemeral storage that the proxy-init container requests
      request: ""
  closeWaitTimeoutSecs: 0
  # -- Allow overriding the runAsNonRoot behaviour (<https://github.com/linkerd/linkerd2/issues/7308>)
  runAsRoot: false
  # -- This value is used only if runAsRoot is false; otherwise runAsUser will be 0
  runAsUser: 65534
  xtMountPath:
    mountPath: /run
    name: linkerd-proxy-init-xtables-lock

Let's go with the plain old brute-force approach (running it as root user, in this case)/

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm upgrade --install linkerd-control-plane -n linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set identity.issuer.scheme=kubernetes.io/tls \
  --set "proxyInit.iptablesMode=nft" \
  --set "proxyInit.runAsRoot=true" \
  linkerd/linkerd-control-plane
Release "linkerd-control-plane" does not exist. Installing it now.

Aaand... we did it! Everything's running!

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ kubectl get po
NAME                                      READY   STATUS    RESTARTS   AGE
linkerd-destination-7f5857f549-xsdjf      4/4     Running   0          79s
linkerd-identity-b7d47f674-2mrsm          2/2     Running   0          79s
linkerd-proxy-injector-5c5f849755-4xrz7   2/2     Running   0          79s
[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ 
[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ 
[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ linkerd check
Linkerd core checks
===================

kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API

kubernetes-version
------------------
√ is running the minimum Kubernetes API version
√ is running the minimum kubectl version

linkerd-existence
-----------------
√ 'linkerd-config' config map exists
√ heartbeat ServiceAccount exist
√ control plane replica sets are ready
√ no unschedulable pods
√ control plane pods are ready
√ cluster networks contains all node podCIDRs
√ cluster networks contains all pods
√ cluster networks contains all services

linkerd-config
--------------
√ control plane Namespace exists
√ control plane ClusterRoles exist
√ control plane ClusterRoleBindings exist
√ control plane ServiceAccounts exist
√ control plane CustomResourceDefinitions exist
√ control plane MutatingWebhookConfigurations exist
√ control plane ValidatingWebhookConfigurations exist
√ proxy-init container runs as root user if docker container runtime is used

linkerd-identity
----------------
√ certificate config is valid
√ trust anchors are using supported crypto algorithm
√ trust anchors are within their validity period
√ trust anchors are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
β€Ό issuer cert is valid for at least 60 days
    issuer certificate will expire on 2022-12-11T09:04:05Z
    see https://linkerd.io/2.12/checks/#l5d-identity-issuer-cert-not-expiring-soon for hints
√ issuer cert is issued by the trust anchor

linkerd-webhooks-and-apisvc-tls
-------------------------------
√ proxy-injector webhook has valid cert
√ proxy-injector cert is valid for at least 60 days
√ sp-validator webhook has valid cert
√ sp-validator cert is valid for at least 60 days
√ policy-validator webhook has valid cert
√ policy-validator cert is valid for at least 60 days

linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date

control-plane-version
---------------------
√ can retrieve the control plane version
√ control plane is up-to-date
√ control plane and cli versions match

linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match

Status check results are √

Experiments with LinkerD

Protocols

Here you'll find a detailed explanation of the proxying capabilities of LinkerD, I'm quoting some excerpt

Linkerd is capable of proxying all TCP traffic, including TLS connections, WebSockets, and HTTP tunneling.

In most cases, Linkerd can do this without configuration. To accomplish this, Linkerd performs protocol detection to determine whether traffic is HTTP or HTTP/2 (including gRPC). If Linkerd detects that a connection is HTTP or HTTP/2, Linkerd automatically provides HTTP-level metrics and routing.

If Linkerd cannot determine that a connection is using HTTP or HTTP/2, Linkerd will proxy the connection as a plain TCP connection, applying mTLS and providing byte-level metrics as usual.

(Note that HTTPS calls to or from meshed pods are treated as TCP, not as HTTP. Because the client initiates the TLS connection, Linkerd is not be able to decrypt the connection to observe the HTTP transactions.)

Linkerd maintains a default list of opaque ports that corresponds to the standard ports used by protocols that interact poorly with protocol detection. As of the 2.12 release, that list is: 25 (SMTP), 587 (SMTP), 3306 (MySQL), 4444 (Galera), 5432 (Postgres), 6379 (Redis), 9300 (ElasticSearch), and 11211 (Memcache).

The following table contains common protocols that may require additional configuration.

Protocol Standard port(s) In default list? Notes
SMTP 25, 587 Yes
MySQL 3306 Yes
PostgreSQL 5432 Yes
Redis 6379 Yes
ElasticSearch 9300 Yes
Memcache 11211 Yes
MySQL with Galera 3306, 4444, 4567, 4568 Partially Ports 4567 and 4568 are not in Linkerd’s default set of opaque ports

Using LinkerD to secure the traffic

Here, as usual, there are some references

Enabling default-deny firewall policy

Let's upgrade again our LinkerD Helm Chart configuration:

[test0|linkerd] ferdi@DESKTOP-NL6I2OD:~$ helm upgrade --install linkerd-control-plane \
  --namespace linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set identity.issuer.scheme=kubernetes.io/tls \
  --set "proxyInit.iptablesMode=nft" \
  --set "proxyInit.runAsRoot=true" \
  --set "proxy.defaultInboundPolicy=deny" \
  linkerd/linkerd-control-plane

This way, we ensure that LinkerD-meshed Pods could only be contacted by predetermined entities

Test deployments

We're going to use custom ServiceAccounts for each Deployment (it's a requirement to identify the client side) and we'll show two different ways to select the server side of the allowed connection.

# File: whoami.yml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: whoami1
  namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: whoami2
  namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: whoami3
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: whoami1
  name: whoami1
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami1
  template:
    metadata:
      labels:
        app: whoami1
    spec:
      automountServiceAccountToken: false
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          protocol: TCP
        resources: {}
      - args:
        - -c
        - sleep 999999999
        command:
        - /bin/bash
        image: nicolaka/netshoot
        imagePullPolicy: Always
        name: netshoot
        resources: {}
      serviceAccount: whoami1
      serviceAccountName: whoami1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: whoami2
  name: whoami2
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami2
  template:
    metadata:
      labels:
        app: whoami2
    spec:
      automountServiceAccountToken: false
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          protocol: TCP
      - args:
        - -c
        - sleep 999999999
        command:
        - /bin/bash
        image: nicolaka/netshoot
        imagePullPolicy: Always
        name: netshoot
      serviceAccount: whoami2
      serviceAccountName: whoami2
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: whoami3
  name: whoami3
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami3
  template:
    metadata:
      labels:
        app: whoami3
    spec:
      automountServiceAccountToken: false
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          protocol: TCP
      - args:
        - -c
        - sleep 999999999
        command:
        - /bin/bash
        image: nicolaka/netshoot
        imagePullPolicy: Always
        name: netshoot
      serviceAccount: whoami3
      serviceAccountName: whoami3
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: whoami1
  name: whoami1
  namespace: default
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: whoami1
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: whoami2
  name: whoami2
  namespace: default
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: whoami2
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: whoami3
  name: whoami3
  namespace: default
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: whoami3
  sessionAffinity: None
  type: ClusterIP

LinkerD-izing deployments

The deployments defined above haven't been LinkerD-ized yet! Let's use linkerd CLI tool to produce an edited version of the same YAML.

[test0|default] ferdi@DESKTOP-NL6I2OD:~/linkerdtest$ cat whoami.yml | linkerd inject - | tee whoami-injected.yml

Below there's an excerpt of the new YAML, detailing one of the updated Deployments:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: whoami1
  name: whoami1
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami1
  template:
    metadata:
      annotations:
        linkerd.io/inject: enabled
      labels:
        app: whoami1
    spec:
      automountServiceAccountToken: false
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          protocol: TCP
      - args:
        - -c
        - sleep 999999999
        command:
        - /bin/bash
        image: nicolaka/netshoot
        imagePullPolicy: Always
        name: netshoot
      serviceAccount: whoami1
      serviceAccountName: whoami1

Definining rules

We're going to define the firewall rules (LinkerD calls them ServerAuthorizations) first, and only after that, the corresponding targets (Servers for LinkerD).

This way, we're sure that our defined rules will be applied instantly after the creation of the Server* custom resource.

Identifying the destination (Server) by resource name

---
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
  namespace: default
  name: whoami2-whoami1-auth
  labels:
    app.kubernetes.io/part-of: whoami
    app.kubernetes.io/name: whoami2-whoami1
spec:
  # the Server whoami1 can be contacted by Pods running with whoami2 ServiceAccount
  server:
    name: whoami1-server
  client:
    meshTLS:
      serviceAccounts:
        - name: whoami2

Identifying the destination (Server) by resource label(s)

---
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
  namespace: default
  name: whoami3-whoami1-auth
  labels:
    app.kubernetes.io/part-of: whoami
    app.kubernetes.io/name: whoami3-whoami1
spec:
  # the Server labeled app=whoami1 can be contacted by Pods running with whoami2 ServiceAccount
  server:
    selector:
      matchLabels:
        app: whoami1
  client:
    meshTLS:
      serviceAccounts:
        - name: whoami3

Defining the Server (connection destination)

---
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
  namespace: default
  name: whoami1-server
  labels:
    app: whoami1
spec:
  podSelector:
    matchLabels:
      app: whoami1
  port: 80
  proxyProtocol: HTTP/1

Testing the ServerAuthorizations/Server combination

These are my currently running Pods, for reference

[test0|default] ferdi@DESKTOP-NL6I2OD:~/linkerdtest$ kubectl get po
NAME                       READY   STATUS    RESTARTS   AGE
whoami1-6cccb5f954-mjmgg   3/3     Running   0          3h
whoami2-689d97cf87-7p5s7   3/3     Running   0          3h
whoami3-7689d5fbc6-7n7l9   3/3     Running   0          3h

From whoami2's sidecar container

[test0|default] ferdi@DESKTOP-NL6I2OD:~/linkerdtest$ kubectl exec -ti whoami2-689d97cf87-7p5s7 -c netshoot -- bash
bash-5.2# curl -v http://whoami1/
*   Trying 10.43.253.230:80...
* Connected to whoami1 (10.43.253.230) port 80 (#0)
> GET / HTTP/1.1
> Host: whoami1
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Sat, 10 Dec 2022 14:45:01 GMT
< content-length: 299
< content-type: text/plain; charset=utf-8
<
Hostname: whoami1-6cccb5f954-mjmgg
IP: 127.0.0.1
IP: 10.42.0.31
RemoteAddr: 10.42.0.31:57388
GET / HTTP/1.1
Host: whoami1
User-Agent: curl/7.86.0
Accept: */*
L5d-Client-Id: whoami2.default.serviceaccount.identity.linkerd.cluster.local
L5d-Dst-Canonical: whoami1.default.svc.cluster.local:80

* Connection #0 to host whoami1 left intact
bash-5.2#

Yeah! It works!

From whoami3's sidecar container

[test0|default] ferdi@DESKTOP-NL6I2OD:~/linkerdtest$ kubectl exec -ti whoami3-7689d5fbc6-7n7l9 -c netshoot -- bash
bash-5.2# curl -v http://whoami1
*   Trying 10.43.253.230:80...
* Connected to whoami1 (10.43.253.230) port 80 (#0)
> GET / HTTP/1.1
> Host: whoami1
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< date: Sat, 10 Dec 2022 14:46:24 GMT
< content-length: 299
< content-type: text/plain; charset=utf-8
<
Hostname: whoami1-6cccb5f954-mjmgg
IP: 127.0.0.1
IP: 10.42.0.31
RemoteAddr: 10.42.0.31:51432
GET / HTTP/1.1
Host: whoami1
User-Agent: curl/7.86.0
Accept: */*
L5d-Client-Id: whoami3.default.serviceaccount.identity.linkerd.cluster.local
L5d-Dst-Canonical: whoami1.default.svc.cluster.local:80

* Connection #0 to host whoami1 left intact
bash-5.2#

This one, too

A connection from an unauthorized resource, within the cluster

Whoo-hoo, I'm a bad guy trying to reach our precious whoami1

[test0|default] ferdi@DESKTOP-NL6I2OD:~/linkerdtest$ kubectl run badguy -ti --rm --image=nicolaka/netshoot -- bash
If you don't see a command prompt, try pressing enter.
bash-5.2# curl -v http://whoami1
*   Trying 10.43.253.230:80...
* Connected to whoami1 (10.43.253.230) port 80 (#0)
> GET / HTTP/1.1
> Host: whoami1
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< content-length: 0
< date: Sat, 10 Dec 2022 14:49:24 GMT
<
* Connection #0 to host whoami1 left intact
bash-5.2#

... and I miserably fail to connect!

links

social