
In a test multi-tenant cluster, a Pod belonging to Tenant A was able to read Secrets that belonged to Tenant B. When the root cause was traced, neither tenant's Namespace had any of the special misconfigurations usually discussed in "k3s vs k8s" comparison articles. What was actually there was simpler: the default ServiceAccount token, automatically created for every Namespace, had been quietly automounted into Pods the whole time, with nobody giving it a second thought.
1. The Incident: "The Neighboring Tenant's Secrets Were Readable"
Here is how it happened. Multiple teams shared the same K3s cluster for a test environment, and each team assumed that splitting workloads into separate Namespaces was enough for isolation. But part of a RoleBinding had been configured too broadly, tied to a ClusterRole through a ClusterRoleBinding. Querying the API server with a Pod's default ServiceAccount token turned out to enumerate resources in a different Namespace that should have been completely out of reach.
What an attacker looks for immediately after compromising a Pod is exactly this kind of credential: one that exists but is never actually used. Most Pods have no code path that ever calls the Kubernetes API, yet the token gets dutifully mounted anyway. This lateral-movement pattern is repeatedly called out from a MITRE ATT&CK perspective as well (Red Hat's write-up). A managed K8s platform makes it easier for operators to notice these unused, leftover credentials in the first place. An environment like Kubo, where the cluster configuration is visible through Captain UI's dashboard, is one option for reducing this kind of "invisible gap."
2. Compare k3s vs k8s All You Want — the Default ServiceAccount Has the Same Trap

This is the part that's easy to miss. When people discuss "k3s vs k8s," they're usually talking about differences in CNI choice, control-plane architecture, or CI/CD compatibility — not RBAC mechanics, which don't differ at all. Role, ClusterRole, RoleBinding, and ClusterRoleBinding behave identically whether you're running k3s or vanilla Kubernetes.
So what is different? Kubernetes automatically creates a ServiceAccount named default in every Namespace, and unless a Pod spec explicitly opts out, that ServiceAccount's API credential gets automounted at /var/run/secrets/kubernetes.io/serviceaccount/token. This default behavior is documented in the official Kubernetes documentation, and it applies equally to k3s and plain Kubernetes.
There's also a precedence trap that makes this worse. Even if you set automountServiceAccountToken: false on the ServiceAccount, a Pod's .spec setting of true takes precedence. "I thought I'd closed this off at the ServiceAccount level, but one particular Pod still had a hole" is an incident that reliably recurs for anyone who doesn't know about this precedence rule.
k3s does add its own wrinkle, though. k3s ships with a powerful admin kubeconfig bundled at /etc/rancher/k3s/k3s.yaml right out of the box. That's convenient for testing and homelab use, but when the same mindset carries over into a multi-tenant production rollout, the habit of reaching for admin credentials instead of thinking about RBAC tends to leave behind exactly the kind of "loose RoleBinding" described above. In fact, k3s's own CIS Hardening Guide states plainly that NetworkPolicy and Pod Security Standards are not applied by default — they require explicit configuration per Namespace. The assumption that "k3s is lightweight, so I can deal with this later" leads to the same trap every time.
3. This Isn't Hypothetical: What CVE-2026-61549 Shows About Externally-Specified serviceAccountName

A real-world example of "what happens when you let ServiceAccount selection come from external input" is a 2026 vulnerability in the Kubernetes backend of the CI/CD tool Woodpecker. The pipeline YAML's backend_options.kubernetes.serviceAccountName field was passed straight through to the Pod spec without validation.
As a result, anyone with push access to the connected repository could specify an arbitrary ServiceAccount within the pipeline Namespace. If a privileged ServiceAccount happened to exist in that Namespace, an attacker's pipeline Pod could obtain its token and escalate all the way to reading Secrets, enumerating cluster resources, and gaining cluster-admin privileges. The GitHub Security Advisory (GHSA-qf34-295c-26v8) rates this as CVSS 8.2 (High), and according to a detailed technical write-up, every version prior to v3.16.0 was affected. The fix introduced a gate called ServiceAccountNameAllowFromStep, disabled by default and only active when an operator explicitly sets the corresponding environment variable.
This isn't unique to CI/CD pipelines — any design where "ServiceAccount name is decided by some external input" shares the same root cause as the default ServiceAccount trap. Even when running GitOps pipelines through something like Kubo's standard ArgoCD/Flux integration, there's value in fixing ServiceAccount assignment rules at the template level rather than letting pipeline definitions choose freely.
4. This Isn't Rare — It's on the Rise (Threat Statistics)

It's tempting to assume this only happens in unusual configurations, but the data says otherwise. According to Palo Alto Networks Unit 42's threat report, threat-actor activity involving Kubernetes token theft increased 282% year over year, and suspicious ServiceAccount token-theft activity was detected in 22% of cloud environments. Kubernetes-related alert volume also reportedly jumped from roughly 122,000 in 2024 to about 467,000 in 2025 — nearly a 3.8x increase.
What these numbers show is that leaving the default ServiceAccount unattended isn't an obscure misconfiguration — it's an increasingly common target for attackers. In multi-tenant environments, a single lax Namespace can spill over to affect every other tenant.
5. Four Layers That Actually Protect a Multi-Tenant Environment

RBAC alone isn't enough. Protecting a multi-tenant environment realistically requires combining at least these four layers:
- Explicitly disable automountServiceAccountToken: Set it on both the ServiceAccount and the Pod, keeping in mind that the Pod-level setting wins, and check for gaps accordingly.
- Default-deny NetworkPolicy: The "block everything first, then explicitly allow only the routes you actually need" approach is effective at cutting off inter-tenant traffic.
- Resource isolation with ResourceQuota / LimitRange: Set explicit total request and limit values for CPU and memory per tenant so that one tenant can't degrade another's performance.
- Enforcement through a policy engine: A policy engine like Kyverno can automatically patch
automountServiceAccountToken: falseonto thedefaultServiceAccount created whenever a new Namespace is created, structurally preventing configuration gaps.
This combination is echoed in an article on multi-tenant isolation best practices, which lists RBAC, NetworkPolicy, and ResourceQuota together as the baseline for "practical isolation." Using something like Kyverno's automated mutation policy to apply this consistently across the whole cluster removes the human error of per-Pod configuration gaps.
In a managed environment like Kubo Cloud, Captain UI's visual dashboard keeps the cluster state from becoming a black box, which makes it easier to notice this kind of "I thought I'd configured it" gap. For industries with strict data-sovereignty requirements, such as finance or healthcare, an option like Kubo On-Premise lets you apply the same multi-tenant design while keeping everything under your own control.
Summary
When people compare k3s and Kubernetes, the discussion tends to focus on CNI choices and control-plane architecture — but when it comes to the default behavior of RBAC and ServiceAccounts, there's no difference between the two at all. Continuing to use the default ServiceAccount as-is turns into a real risk the moment a cluster moves from a test environment into multi-tenant production. As CVE-2026-61549 shows, and as the 282% year-over-year increase in threat activity confirms, this isn't a theoretical concern — it's an attack surface that's actually growing.
Rather than manually checking automount settings, NetworkPolicy, ResourceQuota, and policy-engine enforcement every time a Pod is created, it's better to template these four layers into the platform itself. Kubo is a full-featured Kubernetes distribution built on K3s that lets you build in this kind of multi-tenant design from the start, making it one option for closing this "invisible gap." A good place to start is simply checking what state your own cluster's default ServiceAccount is in right now. You can also reach out via our contact page to talk it through.