<p>I am experimenting with service accounts. I believe the following should produce an access error (but it doesn't):</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: v1
kind: ServiceAccount
metadata:
  name: test-sa

---

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  serviceAccountName: test-sa
  containers:
  - image: alpine
    name: test-container
    command: [sh]
    args:
    - -ec
    - |
      apk add curl;
      KUBE_NAMESPACE=&quot;$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)&quot;;
      curl \
        --cacert &quot;/var/run/secrets/kubernetes.io/serviceaccount/ca.crt&quot; \
        -H &quot;Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)&quot; \
        &quot;https://kubernetes.default.svc/api/v1/namespaces/$KUBE_NAMESPACE/services&quot;;
      while true; do sleep 1; done;
</code></pre>
<pre class="lang-sh prettyprint-override"><code>kubectl apply -f test.yml
kubectl logs test-pod
</code></pre>
<p>What I see is a successful listing of services, but I would expect a permissions error because I never created any <code>RoleBinding</code>s or <code>ClusterRoleBinding</code>s for <code>test-sa</code>.</p>
<p>I'm struggling to find ways to list the permissions available to a particular SA, but according to <a href="https://stackoverflow.com/questions/54889458/kubernetes-check-serviceaccount-permissions">Kubernetes check serviceaccount permissions</a>, it should be possible with:</p>
<pre class="lang-sh prettyprint-override"><code>kubectl auth can-i list services --as=system:serviceaccount:default:test-sa
&gt; yes
</code></pre>
<p>Though I'm skeptical whether that command is actually working, because I can replace <code>test-sa</code> with any gibberish and it still says &quot;yes&quot;.</p>
<hr />
<p>According to the documentation, service accounts by default have <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/#service-account-permissions" rel="nofollow noreferrer">&quot;discovery permissions given to all authenticated users&quot;</a>. It doesn't say what that actually means, but from more reading I found this resource which is probably what it's referring to:</p>
<pre class="lang-sh prettyprint-override"><code>kubectl get clusterroles system:discovery -o yaml
&gt; [...]
&gt; rules:
&gt; - nonResourceURLs:
&gt;   - /api
&gt;   - /api/*
&gt; [...]
&gt;   verbs:
&gt;   - get
</code></pre>
<p>Which would imply that all service accounts have <code>get</code> permissions on all API endpoints, though the &quot;nonResourceURLs&quot; bit implies this wouldn't apply to APIs for resources like services, even though those APIs live under that path… (???)</p>
<hr />
<p>If I remove the <code>Authorization</code> header entirely, I see an access error as expected. But I don't understand why it's able to get data using this empty service account. What's my misunderstanding and how can I restrict permissions correctly?</p>

<p>It turns out this is a bug in Docker Desktop for Mac's Kubernetes support.</p>
<p>It automatically adds a <code>ClusterRoleBinding</code> giving <code>cluster-admin</code> to <strong>all service accounts</strong> (!). It only <em>intends</em> to give this to service accounts inside the <code>kube-system</code> namespace.</p>
<p>It was originally raised in <a href="https://github.com/docker/for-mac/issues/3694" rel="nofollow noreferrer">docker/for-mac#3694</a> but fixed incorrectly. I have raised a new issue <a href="https://github.com/docker/for-mac/issues/4774" rel="nofollow noreferrer">docker/for-mac#4774</a> (the original issue is locked due to age).</p>
<p>A quick fix while waiting for the bug to be resolved is to run:</p>
<pre class="lang-sh prettyprint-override"><code>kubectl apply -f - &lt;&lt;EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: docker-for-desktop-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:serviceaccounts:kube-system
EOF
</code></pre>
<p>I don't know if that might cause issues with future Docker Desktop upgrades but it does the job for now.</p>
<p>With that fixed, the code above correctly gives a 403 error, and would require the following to explicitly grant access to the services resource:</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: service-reader
rules:
- apiGroups: [&quot;&quot;]
  resources: [services]
  verbs: [get, list]

---

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test-sa-service-reader-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: service-reader
subjects:
- kind: ServiceAccount
  name: test-sa
</code></pre>
<hr />
<p>A useful command for investigating is <code>kubectl auth can-i --list --as system:serviceaccount</code>, which shows the rogue permissions were applying to <em>all</em> service accounts:</p>
<blockquote>
<pre><code>Resources                       Non-Resource URLs   Resource Names   Verbs
*.*                             []                  []               [*]
                                [*]                 []               [*]
[...]
</code></pre>
</blockquote>
