Skip to main content
Set up customer-approved kubectl access to troubleshoot your application in a customer’s cloud. This guide limits access to pod inspection and exec in app-system, with a 30-minute grant. Just-in-time access means you hold no standing credentials for the environment. You request access when you need it, your customer approves it, and the access ends on its own when the window closes. The example uses a support-debug permission set, a customer-prod environment, the app-system namespace, and an api installation. Substitute your own names.

Before you start

The requester and the approver must be different identities. The server rejects an approval by the identity that made the request. You also need:
  • customer-prod running components that support permission sets. See provisioning prerequisites.
  • kubectl v1.31 or newer. Ryvn bridges pod exec, attach, and port-forward over WebSocket, and older kubectl negotiates SPDY instead.
  • app-system present in that environment’s cluster.
A permission’s scope decides which namespaces its rules apply in: Every rule in a permission applies in every namespace its scope reaches. To grant different access in another namespace, such as read-only access in kube-system, add a separate permission with its own scope and rules. Read the installation’s namespace instead of guessing it:

Step 1: Create the permission set

Environment administrator. A permission set is a named set of Kubernetes RBAC rules paired with a Ryvn scope.
Pod permissions let you find a pod. The exec rule lets you run a command inside it — Kubernetes authorizes exec as create on pods/exec, so that verb is the one the set needs. Add pod logs only if your team needs them:
Do not add secrets, wildcards (*), or a cluster scope. Every rule you add applies for the full duration of every grant issued against the set. Read back what you published:
Requests and grants keep the revision approved for them, even if you later edit the permission set. Editing publishes a new revision when the rules change; renaming the description or saving unchanged rules does not.

Step 2: Require approval on the environment

Environment administrator. Set the environment’s Connect mode to by-request so no one reaches the cluster without an approved grant.
Go to Environments → customer-prod → Settings → Ryvn Connect, choose By request only, then click Save Connect access.
Two other modes exist. Default access (mode: default with a defaultPermissionSet) grants that one set without approval and follows its latest revision. Off (mode: off) blocks new access and revokes active grants. An environment that has never set connect keeps its previous role-derived behavior.

Step 3: Request access

Request 30 minutes of access and include the reason:
The command exits while approval is pending. --reason is required. --duration defaults to 1 hour and cannot exceed 8 hours. An undecided request expires after 24 hours, which is a deadline for the decision, not the length of the access. While an identical request is still pending, running the same command again returns that request instead of creating a second one. Change the reason or duration and you create a new request. Check on it without re-requesting:

Step 4: Approve

Your customer approves in the dashboard Inbox, or under Environment → Access requests on their stack. Pending only lists what needs a decision; All requests and View grants show decisions already made. Each request shows the requester, the environment, the permission set and its revision, the requested duration, and the reason. Expand it to read the rules before deciding. Approval accepts exactly what was requested; there is no field for trimming the namespace or dropping a verb. To narrow it, deny the request and ask for a different permission set.
Click Approve, or Deny with a note.
Approval creates a grant pinned to that revision, and the 30 minutes start at approval rather than at your first kubectl command. A new request emits an access_request.pending notification to any Slack destination subscribed to that event.

Step 5: Connect and debug

Run the resume command the CLI printed. In the dashboard, use Copy resume command on your own pending request, or Copy connect command once it is approved. Both commands pin the org, so prefer them over retyping:
That opens a subshell wired to the environment. Append -- <command> to run one command and exit instead.
An interactive shell works the same way:
Pass -n app-system explicitly. Every request is authorized against the namespace it names, so a command in another namespace is denied, and so is a cluster-wide request such as kubectl get pods -A. A command without -n uses your current context’s namespace, which is denied when that namespace is outside the grant.
Exec runs with the container’s privileges, including its mounted credentials. Leaving secrets out of the permission set stops Kubernetes Secrets API reads, not what a process in the pod can already reach.
Reconnect as often as you need while the grant lasts. A single exec, attach, port-forward, or watch stream ends when the credential that opened it expires; run the command again for a new stream. Add --kubeconfig to write a context into ~/.kube/config for your own tooling; it stays pinned to this grant and its revision, and stops working when the grant ends or is revoked rather than falling back to the environment’s default access. A context written under default access instead follows the environment’s default permission set: when a newer revision is published, the next token refresh picks it up. Nothing is ever moved onto a revision it was not authorized under: a stream that is already open is closed when the proxy next re-checks it, and a command that still presents kubectl’s cached token from the old revision is refused with 401 Unauthorized (revision_superseded), which makes kubectl discard the cached token and mint again; the refused command is not replayed, and the next one opens under the current revision. Grant contexts are never refreshed this way: their denials stay 403. kubectl attach and kubectl port-forward work the same way as exec. Ryvn forwards the request to the cluster exactly as kubectl sent it, so the cluster’s own RBAC decides: kubectl v1.31+ opens these WebSocket streams with an HTTP GET, which Kubernetes v1.31–v1.34 authorize as the get verb on pods/attach / pods/portforward, and Kubernetes v1.35+ additionally checks create on the same subresource. Grant both verbs on exactly the subresources you want to allow — the set in this guide has neither, so add a rule such as the one below before requesting. Do not widen the pods rule itself or the exec-only sets already in use. Port-forward over WebSocket also needs a cluster running Kubernetes v1.31 or newer; against an older cluster kubectl falls back to SPDY, which Ryvn refuses.

Step 6: Revoke access

Revoke your own grant when you are finished. Your customer can revoke any grant on their environment.
In the dashboard, go to Inbox → View grants → Revoke. Open streams close as soon as authorization stops holding. How quickly that happens is a trade-off the environment’s operator sets with the access.renewal server configuration. Ryvn accepts the rules an environment last verified for a bounded validity window, and each connection is re-checked on a check interval inside that window. By default the window is six seconds, so a revocation or rule change takes effect within seconds, but a short agent or network hiccup can also interrupt access. Raising the window (up to five minutes, with checks about every thirty seconds) keeps access steady through brief outages, at the cost that a revocation can take up to the remaining window to reach a stream whose agent has gone quiet; a reachable agent still applies it on the next check. A grant’s own expiry and the credential’s lifetime are enforced independently of the window and never extend it. The credential lifetime is the hub’s access.nativeCredentialTTL setting: 15 minutes unless configured, at most 8 hours, and never past the grant’s end for grant-backed access. A stream lives as long as the credential that opened it. Credentials are minted on demand by the kubeconfig’s exec plugin, one per kubectl refresh, so there is no single “session credential”: ryvn connect prints the grant deadline and notes how credentials are issued, labelling the expiry of its own preflight check credential (which kubectl never uses) as exactly that. Review what happened afterwards:
Ryvn records the request, the decision, the pinned rules, and the grant window. It does not record the commands run inside an exec session or their output. Expired and revoked grants are final. To continue, submit a new request with a reason, which can describe the same incident.

Troubleshooting

Nobody has decided yet, and re-running ryvn connect returns the same request. Ask a developer or operator on the environment other than yourself to decide. Undecided requests expire after 24 hours.
The grant is valid and the environment has not confirmed the revision on the cluster yet. The agent may be updating, disconnected, or running components that predate permission sets. Retry shortly; if it persists, ask the environment administrator to check the environment’s deployed components. Readiness can also cause the server to refuse an approval.
Expected. The API does not expose per-environment readiness yet, so describe permission-set prints unavailable instead of guessing. Neither the CLI nor the dashboard previews which namespaces a revision currently covers.
The environment has no access relay for Ryvn Connect, independent of permission sets. Ask the environment administrator to enable Connect for it.
The verb, resource, and namespace of that one request are not in the revision your grant pinned. Common causes are a different namespace, a cluster-wide request, a subresource the rules omit such as pods/log, or exec rules missing a verb. Check the pinned revision with ryvn describe permission-set support-debug --revision <revision-id>. Editing the set does not change a grant already approved.
The set’s pods/exec rule lacks create, the verb exec requests are authorized against. Publish a revision granting create on pods/exec and request again.
kubectl is negotiating SPDY. Upgrade to kubectl v1.31 or newer.
The proxy caps how many exec, attach, and port-forward streams it bridges at once, across all users. The response is a retryable 503 with a Retry-After; end an idle stream or wait a few seconds and run the command again. Long-running port-forwards count against the cap for as long as they stay open.
Confirm the name with ryvn describe installation api -e customer-prod. A namespaceNames scope grants nothing for a namespace that does not exist and is blocked for a namespace another Ryvn environment manages. allManagedNamespaces and namespaceSelector reach only namespaces Ryvn manages in the environment, so confirm the namespace is declared on the environment or used by an installation, and that namespaceFilter does not exclude it.
Rules that grant create, update, patch, delete, or deletecollection on configmaps are ordinary application authority and are issued as written, including in the environment’s Ryvn system namespace. Two ConfigMaps in that namespace are Ryvn Connect’s internal mailboxes (access-intent-<environment-id> and access-activation-<environment-id>) and are protected by a cluster admission policy the agent installs and reads back: only the Ryvn agent may mutate them. If a revision names one of them in resourceNames, the revision publishes but the environment never materializes it: connect stays materialization_pending and the projection is blocked with is a Ryvn Connect mailbox; publish a revision without the name. If a kubectl write against one of them is refused with a ValidatingAdmissionPolicy denial, that is expected and unrelated to your grant. Other ConfigMaps in the same namespace, and ConfigMaps with the same names in other namespaces, are writable under your rules.
Rules that grant create, update, patch, or delete on roles or rolebindings in rbac.authorization.k8s.io are issued as written for a namespace scope, and only after the environment’s agent has installed and read back the admission policies that protect Ryvn’s own RBAC objects. Until that readback succeeds the revision publishes but the environment does not materialize it (protection not yet observed); the agent retries on its own. Kubernetes’ escalation prevention still applies: you can only grant rights your grant already holds, so a Role adding secrets or a RoleBinding to cluster-admin is refused natively, and Ryvn-owned Roles and RoleBindings in the namespace are refused by admission. bind, escalate, impersonation, ServiceAccount tokens, and any write to clusterroles, clusterrolebindings, namespaces, CRDs, or admission policies are never issued; a revision that asks for them is blocked with UnsupportedDelegation. Reads (get, list, watch) on all four RBAC kinds stay available. Roles and RoleBindings you create or edit under such a grant are ordinary objects in the customer’s cluster: they remain after the grant expires or is revoked and are not rolled back by Ryvn. A RoleBinding you create or update under the grant may name only your own session subject (exactly one User, your session’s username): binding another user, a group, a ServiceAccount, or several subjects is refused by admission with a reason, so your rights cannot be handed to another holder that would keep them after your window closes. Bindings created by cluster administrators outside the grant are not restricted. Remove what you no longer need before the window closes.
The grant expired, was revoked, or the environment’s Connect mode changed, including a switch to Off. Request access again.
multiple_grants means more than one active grant applies; choose one with ryvn connect customer-prod --grant <grant-id>. If a permission set or environment appears not to exist, check ryvn auth status and pass --profile for the org that owns the environment.