kubectl apply from url is dangerous

Many k8s resource "getting started" guides start with a simple one liner:

kubectl apply -f https://lestak.sh/whoami.yaml

For quick debugging, the ability to kubectl apply -f directly from a URL is a great feature. However for security and reproducible deployments, it is a nightmare.

K8s as a platform provides a consistent control plane to allow systems and services to be pinned to a particular version and defined entirely in Infrastructure as Code (IaC). This means that you should be able to point your CI/CD at any other cluster, run your automation jobs, and everything will be rebuilt exactly as it was before. All changes are version controlled in git, are auditable, and can be rolled back to any previous commit at a moment's notice.

However when you kubectl apply -f https://..., there is never an assurance that the web server hosting your manifest will continue to exist, or if they will continue to provide the same version that you expect at that URL.

Many developers of k8s resources will reference the git release tag in the URL to at least provide an assurance that the resource will be pinned to that particular release.

But even still, this relies on the remote web server being an honest actor.

kubectl apply'ing from a remote server is effectively curl'ing to shell as root (or at least, the level of permissions of the client performing the kubectl).

On a good day this means you are temporarily handing root control of the cluster over to a (hopefully) trusted web server. On a bad day, this means you are effectively opening the door for a malicious actor to deploy anything they want into your environment, including resources that corrupt, exfiltrate, or otherwise harm your data.

To get even more creative, a malicious actor could present a valid k8s manifest when you view the URL in the browser or with curl, but then by inspecting the User-Agent header, they can return the malicious payload only when the client is directly applying the URL with kubectl. This means you could click the URL in the install guide and read the entire manifest for a Pod, and then when you kubectl apply, the server actually creates a new ServiceAccount, ClusterRoleBinding, and Pod which then can create a reverse tunnel and allow an external server to completely take over your cluster. If this was deployed off in kube-system and the resource you expected was installed as well, it could be minutes, hours, or days before you even realized those additional resources were running, stealing your data and running crypto miners in your cluster all the while.

This is clearly a security risk.

How to avoid this?
You should already be internalizing your manifests into your own IaaC repositories. These should be regularly synced with their upstreams - but on your timeline.

Whenever you are testing new tools, while it may be tempting to simply kubectl apply the URL, always first curl / wget the files to your local, inspect them thoroughly to understand what will be deployed in your cluster, and only then kubectl apply the local manifests.

This also allows you more flexibility to update and tweak the configurations to suit your needs and then easily version control your fork, a win-win!

last updated 2022-08-20