# Kubernetes tunnel

info

Available only with a [subscription](https://tuna.am/#pricing).

## Overview

A Kubernetes tunnel gives a colleague or a contractor temporary access to your cluster with plain `kubectl`: no VPN, no exposing the API server, no sharing your credentials.

The client reads your local kubeconfig, starts a reverse proxy to the API server and an HTTP tunnel to it. The guest gets a one-time link to a ready kubeconfig. It contains the tunnel address with a valid TLS certificate and a guest token. The proxy checks the token and replaces it with your real credentials, so they never leave your machine.

## Quick start

```shell
tuna kubernetes --context=staging --service-account=shop/debug --ttl=4h

```

The client opens a terminal UI: the header shows the service account, namespace, restrictions and expiry, below is the feed of guest requests. Instructions for the guest:

```shell
curl -sfO https://<tunnel address>/tuna/<secret>/kubeconfig.yaml
KUBECONFIG=./kubeconfig.yaml kubectl get pods

```

`get`, `list`, `watch`, `logs`, `exec` (including stdin), `port-forward`, `pods/proxy` and writes such as `scale`, `create`, `delete` work through the tunnel. Guest requests are shown in the [inspector](https://tuna.am/en/docs/tunnels/http/inspect.md) as regular HTTP requests.

The command has a short alias `tuna k8s`.

## Examples

note

All current flags, hints and examples are available in the help:

```shell
tuna kubernetes --help

```

Almost every flag has a matching [environment variable](https://tuna.am/en/docs/guides/environment-variables.md).

### Kubeconfig and context

The client uses a single kubeconfig file: from the `--kubeconfig` flag, otherwise from `$KUBECONFIG`, otherwise `~/.kube/config`. A `:`-separated list of files in `$KUBECONFIG` is not supported. The default context is `current-context`:

```shell
tuna kubernetes --kubeconfig=./staging.yaml --context=staging

```

Client certificates, `token`, `tokenFile` and exec plugins that return a token are supported. Exec plugins that return a certificate are not supported. With `auth-provider` the client exits with an error.

### Service account

Without `--service-account` the guest acts with **your** cluster permissions, and the client warns about it. Create a service account with minimal RBAC permissions for the guest and pass it in the flag:

```shell
tuna kubernetes --service-account=shop/debug

```

The format is `[namespace/]name`. Without a namespace the context namespace is used, or `default` if there is none. The client issues the service account token itself via the TokenRequest API and renews it every hour.

### Access restrictions

The restrictions below work on top of RBAC and only narrow permissions, never extend them.

| Flag                                 | What it does                                                                                                                                                    |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--namespace=shop`                   | A single namespace only. Cluster-scoped resources (`nodes`, the namespace list), `-A` and other namespaces are denied. The namespace object itself is read-only |
| `--read-only`                        | Reads only (`GET`, `HEAD`, `OPTIONS`). `exec`, `attach` and `port-forward` are denied; logs and `pods/proxy` are allowed                                        |
| `--no-delete`                        | Deleting is denied, other writes are allowed                                                                                                                    |
| `--deny-resource=secrets,configmaps` | The listed resources are denied, names are case-insensitive                                                                                                     |
| `--deny-subresource=exec,attach`     | Denied subresources: `exec`, `attach`, `portforward`, `log`, `proxy`                                                                                            |

With `--namespace` and `--read-only`, `kubectl auth whoami` and `kubectl auth can-i` do not work: they are POST requests to cluster-scoped resources.

The proxy strips `Impersonate-*` headers, so `kubectl --as` does not work through the tunnel.

### Presets

`--preset` enables a ready set of restrictions:

| Preset     | What is allowed                                                                  |
| ---------- | -------------------------------------------------------------------------------- |
| `view`     | Reads only, no `secrets`, no `exec`, `attach`, `portforward` or `proxy`          |
| `observer` | Reads of pods and their logs only, no `exec`, `attach`, `portforward` or `proxy` |
| `debug`    | Everything except deleting. `secrets` are not hidden: `exec` gets to them anyway |
| `open`     | No extra restrictions                                                            |

```shell
tuna kubernetes --preset=debug --namespace=shop

```

A preset and flags add up and never weaken each other: `--preset=debug --read-only` gives reads only.

The `observer` preset allows only `pods`, so `kubectl logs deploy/api` fails; point at a pod instead: `kubectl logs pod/api-7c9f8d6b5-x2k4q`.

### Lifetime and download limit

```shell
tuna kubernetes --ttl=4h --download-limit=1

```

`--ttl` sets how long the access stays valid, `0` (the default) means no limit. After it expires `kubectl` gets `this access has expired, ask for a new link`.

`--download-limit` limits kubeconfig downloads, `1` by default, `0` removes the limit. Beyond the limit the link responds with `410`, and you see a warning about the download attempt in the log. If the guest gets `410` on the very first download, someone else took the kubeconfig first: stop the tunnel and issue a new link.

### Permanent address

When the tunnel reconnects with a new address, the client issues a new token and a new link, and the lifetime and the download limit start over. To keep the address, use a reserved subdomain or your own domain:

```shell
tuna kubernetes --subdomain=k8s-staging
tuna kubernetes --domain=k8s.example.com

```

Guest tokens live only in the process memory: after the client restarts, old links and kubeconfigs stop working.

### Specifying a token

You can specify a particular token using the `--token` flag or the `TUNA_TOKEN` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy.

```shell
tuna kubernetes --token=tt_***

```

### Specifying a connection region

You can specify a particular [region](https://tuna.am/en/docs/tunnels/guides/locations.md) using the `--location`/`-l` flag or the `TUNA_LOCATION` environment variable. Overriding follows the [configuration ordering](https://tuna.am/en/docs/guides/config-ordering.md) policy.

```shell
tuna kubernetes --location=nl

```

### Running as a service

The tunnel can run as a [service](https://tuna.am/en/docs/tunnels/guides/service.md) or with [multiple tunnel startup](https://tuna.am/en/docs/tunnels/guides/multi-tunnels.md):

```shell
tuna service install --name=k8s-staging -- tuna kubernetes --context=staging --service-account=shop/debug --subdomain=k8s-staging

```
