Skip to content

Kubernetes

Connect vScope to your Kubernetes cluster by creating a read-only inventory account and adding its token to vScope. Follow these steps with kubectl on any standard Kubernetes cluster, including K3s.

  • kubectl configured to connect to the cluster.
  • An administrator account allowed to create namespaces, ServiceAccounts, Secrets, ClusterRoles, and ClusterRoleBindings, and grant the permissions below. The permission checks also require permission to impersonate the ServiceAccount.
  • Network access from the vScope server or Discovery Proxy to the Kubernetes API endpoint over HTTPS. Use the endpoint’s configured TCP port, commonly 6443 or 443.
  • Bash with base64, or PowerShell, to retrieve the token.

Confirm that you are working with the intended cluster:

Terminal window
kubectl config current-context
kubectl cluster-info

Run the setup and token retrieval commands using your administrator credentials. The inventory account itself will not be able to read Secrets, including its own token Secret.

1. Create the inventory account and permissions

Section titled “1. Create the inventory account and permissions”

Save the following as vscope-inventory.yaml. Use the same names throughout this guide, or change all matching references if these names are already in use.

apiVersion: v1
kind: Namespace
metadata:
name: vscope-inventory
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: vscope-inventory
namespace: vscope-inventory
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: vscope-inventory
rules:
# Core infrastructure
- apiGroups: [""]
resources:
- nodes
- namespaces
- pods
- services
- endpoints
- persistentvolumeclaims
- persistentvolumes
- events
verbs: ["get", "list", "watch"]
# Workloads
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch"]
# Networking
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "ingressclasses", "networkpolicies"]
verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
# Storage and CSI
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses", "csidrivers", "csinodes", "volumeattachments"]
verbs: ["get", "list", "watch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshots", "volumesnapshotcontents", "volumesnapshotclasses"]
verbs: ["get", "list", "watch"]
# Custom resource definitions (not all custom resource instances)
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vscope-inventory
subjects:
- kind: ServiceAccount
name: vscope-inventory
namespace: vscope-inventory
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: vscope-inventory

Apply the file:

Terminal window
kubectl apply -f vscope-inventory.yaml

This grants read access to the listed resources across all namespaces. It does not grant access to Secrets, ConfigMaps, Pod logs, exec, attach, or port forwarding, or permission to create, change, or delete resources.

If these extensions are installed and you want to allow access to their metadata, append the relevant rules to the ClusterRole’s rules list, before the --- that starts the ClusterRoleBinding, then apply the file again:

# KubeVirt
- apiGroups: ["kubevirt.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
# CDI / DataVolumes
- apiGroups: ["cdi.kubevirt.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
# Longhorn
- apiGroups: ["longhorn.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]

Include only the extension groups you need and keep the core resource list unchanged. These rules grant read access to all resources in the named extension groups. The CSI snapshot rule in the base manifest applies when the snapshot APIs are installed.

For unattended inventory using a stored bearer token, create a separate file named vscope-inventory-token.yaml:

apiVersion: v1
kind: Secret
metadata:
name: vscope-inventory-token
namespace: vscope-inventory
annotations:
kubernetes.io/service-account.name: vscope-inventory
type: kubernetes.io/service-account-token
Terminal window
kubectl apply -f vscope-inventory-token.yaml

Do not add a data section. Kubernetes generates the token. Wait for it to be available:

Terminal window
kubectl -n vscope-inventory wait --for=jsonpath='{.data.token}' secret/vscope-inventory-token --timeout=60s

Run these checks with your administrator credentials. Each should return yes:

Terminal window
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list nodes
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list pods --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list deployments.apps --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list storageclasses.storage.k8s.io

Each of these should return no:

Terminal window
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list secrets --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory get secrets -n vscope-inventory
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list configmaps --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory create pods -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory patch deployments.apps -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory delete pods -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory get pods --subresource=log -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory create pods --subresource=exec -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory create pods --subresource=attach -n default
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory create pods --subresource=portforward -n default

Repeat namespace-specific checks in namespaces with additional bindings. If an unexpected check returns yes, inspect other RoleBindings and ClusterRoleBindings that grant access to the account or its groups before continuing.

For installed extensions whose optional rules you added, these should return yes:

Terminal window
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list virtualmachines.kubevirt.io --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list datavolumes.cdi.kubevirt.io --all-namespaces
kubectl auth can-i --as=system:serviceaccount:vscope-inventory:vscope-inventory list volumes.longhorn.io --all-namespaces

Find the API endpoint in your current context:

Terminal window
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'

Use an HTTPS endpoint reachable from vScope or its Discovery Proxy, such as https://api.example.com:6443. An address like 127.0.0.1 or a local tunnel in your kubeconfig will not work from another machine. For highly available clusters, use the stable API load balancer or virtual IP.

Run the commands for your shell and copy the resulting token for use in vScope.

Terminal window
kubectl -n vscope-inventory get secret vscope-inventory-token -o jsonpath='{.data.token}' | base64 --decode
printf '\n'

On macOS, use base64 -D instead of base64 --decode if your version does not support the long option.

Terminal window
$tokenBase64 = kubectl -n vscope-inventory get secret vscope-inventory-token -o jsonpath='{.data.token}'
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tokenBase64))
  1. In vScope, open Discovery > Credentials and add a Kubernetes credential.
  2. In Base URL, enter the API endpoint from step 4, for example https://api.example.com:6443.
  3. Leave Authorization Header Name set to Authorization.
  4. In Authorization Header Value, enter Bearer <token>, replacing <token> with the token from step 4. Include a space between Bearer and the token.
  5. If the connection should use a Discovery Proxy, select it in Proxy.
  6. Under Advanced Settings, enable Accept all server certificates.
  7. Click Test Credential and save the credential when the test succeeds.
  8. Run discovery and review the results in Discovery > Summary. If discovery fails, open Log History for details.

For the general discovery workflow, see Adding data sources.

SymptomCheck
Token wait times outConfirm the ServiceAccount exists and the Secret annotation matches its name. Ask the cluster administrator to check the ServiceAccount token controller and cluster policy.
401 UnauthorizedCheck that Authorization Header Name is Authorization, Authorization Header Value contains Bearer followed by the complete token, and the token Secret and ServiceAccount still exist. Replace expired short-lived tokens if used.
403 Forbidden for an allowed resourceCheck the ClusterRoleBinding’s subject and role name, the resource’s API group, and any optional extension rules.
Impersonation is forbiddenRun the auth can-i --as checks with an administrator allowed to impersonate ServiceAccounts.
Connection timeout or refusalCheck the endpoint, port, DNS, firewall, and routing from vScope or its Discovery Proxy.
Extension API is missingCheck installed APIs with kubectl api-resources. RBAC rules do not install KubeVirt, CDI, Longhorn, or CSI snapshot APIs.