Skip to content

Commit c6cda54

Browse files
committed
feat(network): add egress tunneling
- Enabled `ssh -R` for tunneling from the cluster to the local host. This introduces a bunch of weirdness around "how does the cluster contact the server". - Moved `stream` to `tunnel` and `direct` to `ingress`. This is closer to what it is actually doing, especially because I didn't understand the difference between direct/forward/tcpip. - Added feature flags. Now, what features are enabled can be configured at runtime. This allows any incoming requests to be short-circuited if they'd never work or shouldn't be supported. - Made `Authenticate` return an identity (that has been authenticated) instead of a client. It is weird to have `Identity::authenticate` return an identity, but it makes more sense for `Key`. - Moved `Identity` into `State::Authenticated`. This was primarily to get the user's identity on the created `Egress` tunnel but likely makes more sense anyways as you end up creating a new client all the time as it is. Unfortunately, the `Controller` is still required to create that client - they can't be minted fresh from an Identity.
1 parent 42e7a9e commit c6cda54

21 files changed

+900
-334
lines changed

Cargo.lock

+39
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jsonwebtoken = "9.3.0"
4646
k8s-openapi = { version = "0.22.0", features = ["earliest"] }
4747
kube = { version = "0.93.1", features = ["derive", "runtime", "ws"] }
4848
lazy_static = "1.5.0"
49+
local-ip-address = "0.6.2"
4950
mio = "1.0.2"
5051
pkcs8 = "0.10.2"
5152
prometheus = "0.13.4"
@@ -67,6 +68,7 @@ serde_json = "1.0.127"
6768
serde_path_to_error = "0.1.16"
6869
serde_yaml = "0.9.34"
6970
ssh-key = "0.6.6"
71+
strum = { version = "0.26.3", features = ["derive"] }
7072
strum_macros = "0.26.4"
7173
syntect = "5.2.0"
7274
syntect-tui = "3.0.3"

DEVELOPMENT.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ be available to run inside the cluster.
4242

4343
[k3d]: https://k3d.io/v5.6.3/#releases
4444

45-
## Forwarding
45+
## Ingress Tunnel
4646

4747
If testing port forwarding and running the service locally (aka not on the
4848
cluster), you won't have access to any of the DNS or IP addresses that might be
@@ -60,3 +60,14 @@ ssh -L 9090:svc/default/localhost:9091 me@localhost -p 2222
6060

6161
Testing `pods` and `nodes` requires running on the cluster as the response from
6262
`addr` for those is IP addresses.
63+
64+
## Egress Tunnel
65+
66+
If you're not running on the cluster, you'll want to:
67+
68+
- Make sure you're running from the same network (doable with some games with
69+
VPNs and local clusters).
70+
- Set `HOSTNAME` to a pod in the cluster that is in your default namespace.
71+
- Set `POD_UID` to the `metadata.uid` of the pod from `HOSTNAME`.
72+
- Set `POD_IP` to the IP address of your host. You can get this by going into a
73+
pod and doing a `nslookup host.docker.internal`.

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ You can:
1212
- Access the logs for running and exited containers in a pod.
1313
- Forward a local port remotely, allowing access to services and pods in the
1414
cluster.
15-
- `scp` files from pods. sftp clients work as well.
15+
- Forward a remote service to your local system.
16+
- `scp` files from pods. sftp clients work as well.
1617

1718
![demo](./assets/demo.gif)
1819

@@ -77,7 +78,7 @@ ssh anything@my-remote-host-or-ip -p 2222
7778
The provided username is not used as your identity is authenticated via other
7879
mechanisms.
7980

80-
### Port Forward
81+
### Ingress Tunnel (`ssh -L`)
8182

8283
You can forward requests from a local port into a resource on the remote
8384
cluster. The supported resources are `nodes`, `pods` and `services`. See the
@@ -108,6 +109,19 @@ With that running in one terminal, you can run this in another:
108109
ssh my-node-username@localhost -p 3333
109110
```
110111

112+
### Egress Tunnel (`ssh -R`)
113+
114+
You can forward a remote service on your cluster to a port on your local host.
115+
116+
To forward port 8080 on service `default/kuberift` to port `9090` on your local
117+
system, you can run:
118+
119+
```bash
120+
ssh me@my-cluster -p 2222 -R default/kuberift:8080:localhost:9090
121+
```
122+
123+
The format for service definitions is `<namespace>/<service-name>`.
124+
111125
### SFTP
112126

113127
The cluster is represented by a file tree:

TODO.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# TODO
22

3+
## Documentation
4+
5+
- Get a full architecture explanation together.
6+
- Explain "how it works" for each piece of functionality.
7+
38
## Authorization
49

510
- Groups are probably what most users are going to want to use to configure all
@@ -49,6 +54,14 @@
4954
- Enable `ssh -L` for forwarding requests _into_ a pod.
5055
- Enable `ssh -R` for forwarding a remote service _into_ a localhost.
5156

52-
## TCP/IP Forwarding
57+
## Ingress Tunnel
58+
59+
## Egress Tunnel
60+
61+
- Display error for when the `tcpip_forward` address is `localhost`.
62+
- Need some way to do cleanup and lifecycle management of endpoints and
63+
services.
64+
65+
## Build
5366

54-
- Implement `-R` for proxying from the cluster to localhost.
67+
- Move client_id and config_url to a build-time concern.

docs/auth.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ kubectl create clusterrolebinding foo-bar-com --clusterrole=<my-role> --user=foo
4545
Note that you can use a `RoleBinding` instead, but only for specific
4646
functionality.
4747

48-
### SSH RBAC
48+
### SSH
4949

5050
The minimum permissions are:
5151

@@ -71,7 +71,7 @@ verbs: ['create']
7171
Note: without the full permissions it is possible that the dashboard has some
7272
issues rendering.
7373
74-
### Port Forwarding RBAC
74+
### Ingress Tunnel (`ssh -L`)
7575

7676
For each supported resource type (nodes, services, pods), you need:
7777

@@ -106,7 +106,16 @@ rules:
106106
- get
107107
```
108108

109-
### SFTP(SCP) RBAC
109+
### Egress Tunnel (`ssh -L`)
110+
111+
The minimum permissions are:
112+
113+
```yaml
114+
resources: ['services', 'endpointslices']
115+
verbs: ['patch']
116+
```
117+
118+
### SFTP(SCP)
110119

111120
To support `scp`, the minimum permissions are:
112121

docs/deployment.md

+59-44
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,55 @@
33
The `kuberift` server needs access to your cluster's API server and credentials
44
to connect to it. There are a couple ways to do this:
55

6-
- On cluster - you can run it on cluster. Check out the [helm chart][helm-chart]
7-
for an easy way to get started. By running it on cluster, you get access and
8-
credentials automatically. The server then needs to be exposed so that you can
9-
connect to it. This can be done by any TCP load balancer. If you're running in
10-
the cloud, setting the server's service to `type: LoadBalancer` is the
11-
easiest. Alternatives include using the [gateway api][gateway-api] or
12-
configuring your ingress controller to route TCP.
13-
- Off cluster - if you're already using jump hosts to get into your cluster,
14-
kuberift can run there. All you need to do is create a `kubeconfig` that uses
15-
the correct service account. There are [some plugins][sa-plugin] to make this
16-
easy. You'll still need a valid `ClusterRole` and `ClusterRoleBinding` setup.
17-
Take a look at the sample [rbac][helm-rbac] to see what do to there.
6+
- [On cluster](#on-cluster)
7+
- [Off cluster](#off-cluster)
188

199
[gateway-api]: https://gateway-api.sigs.k8s.io
2010
[helm-chart]: #helm
2111
[sa-plugin]:
2212
https://github.com/superbrothers/kubectl-view-serviceaccount-kubeconfig-plugin
2313
[helm-rbac]: helm/templates/rbac.yaml
2414

25-
## Helm
15+
## Features
16+
17+
All the functionality is controlled via feature flags in the server:
18+
19+
- `pty` - Dashboard when `ssh` happens.
20+
- `sftp` - Enables `scp` and `sftp`.
21+
- `ingress-tunnel` - Provides `ssh -L` forwarding from a local port to the
22+
cluster.
23+
- `egress-tunnel` - Provides `ssh -R` forwarding from the cluster to a local
24+
port.
25+
26+
## Bring Your Own Provider
27+
28+
By default, kuberift provides Github and Google authentication via.
29+
[auth0][auth0]. To get your own setup using auth0, check out their
30+
[instructions][auth0-setup].
31+
32+
You can, alternatively, use your own provider. It must support the [device
33+
code][device-code] flow and have a URL that has the openid configuration. Take a
34+
look at the configuration for `kuberift serve` for the required values.
35+
36+
[auth0]: https://auth0.com
37+
[auth0-setup]:
38+
https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow/call-your-api-using-the-device-authorization-flow#prerequisites
39+
[device-code]: https://www.oauth.com/oauth2-servers/device-flow/
40+
41+
## On-Cluster
42+
43+
Check out the [helm chart][helm-chart] for an easy way to get started. If not
44+
using helm, there are some things to be aware of:
45+
46+
- Credentials need to be mounted into the pod, see [Server RBAC](#server-rbac)
47+
for a minimal list of permissions.
48+
- You need the pod to be reachable from where you're running `ssh`. This can be
49+
done by any TCP load balancer. If you're running in the cloud, setting the
50+
server's service to `type: LoadBalancer` is the easiest. Alternatives include
51+
using the [gateway api][gateway-api] or configuring your ingress controller to
52+
route TCP.
53+
54+
### Helm
2655

2756
There is a provided `getting-started.yaml` set of values. To install this on
2857
your cluster, you can run:
@@ -45,20 +74,21 @@ For more detailed instructions, take a look at the [README][helm-readme].
4574

4675
[helm-readme]: helm/README.md
4776

48-
## Bring Your Own Provider
77+
## Off-Cluster
4978

50-
By default, kuberift provides Github and Google authentication via.
51-
[auth0][auth0]. To get your own setup using auth0, check out their
52-
[instructions][auth0-setup].
79+
If you're already using jump hosts to get into your cluster, kuberift can run
80+
there. Here are some things to be aware of:
5381

54-
You can, alternatively, use your own provider. It must support the [device
55-
code][device-code] flow and have a URL that has the openid configuration. Take a
56-
look at the configuration for `kuberift serve` for the required values.
57-
58-
[auth0]: https://auth0.com
59-
[auth0-setup]:
60-
https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow/call-your-api-using-the-device-authorization-flow#prerequisites
61-
[device-code]: https://www.oauth.com/oauth2-servers/device-flow/
82+
- Provide credentials by creating a `kubeconfig` that uses the correct service
83+
account. here are [some plugins][sa-plugin] to make this easy. You'll still
84+
need a valid `ClusterRole` and `ClusterRoleBinding` setup. Take a look at the
85+
sample [rbac][helm-rbac] to see what do to there.
86+
- For `ingress-tunnel` support, you'll need to have the server running on a
87+
network that can reach IP addresses in the cluster (nodes, pods) and can
88+
resolve cluster DNS.
89+
- For `egress-tunnel` support, you'll need to have the server itself reachable
90+
from any pod in the cluster. In addition, make sure to configure `--pod-name`,
91+
`--pod-uid` and `--pod-ip` to some real values in the `serve` command.
6292

6393
## Server RBAC
6494

@@ -68,38 +98,23 @@ The kuberift server needs to be able to:
6898
- Manage `keys`.
6999
- Optionally update the CRDs.
70100

71-
To do the minimum of this, you can use the following `ClusterRole` + `Role`. For
72-
a more in-depth example, take a look at the
73-
[helm config](helm/templates/rbac.yaml).
101+
To do the minimum of this, you can use the following `ClusterRole`. For a more
102+
in-depth example, take a look at the [helm config](helm/templates/rbac.yaml).
74103

75104
```yaml
76105
apiVersion: rbac.authorization.k8s.io/v1
77106
kind: ClusterRole
78107
metadata:
79108
name: impersonator
80109
rules:
81-
- apiGroups:
82-
- ''
110+
- apiGroups: ['']
83111
resources:
84112
- users
85113
- groups
86114
verbs:
87-
- 'impersonate'
115+
- impersonate
88116
# Restrict the groups/users that can be impersonated through kuberift.
89117
# resourceNames:
90118
# - foo@bar.com
91119
# - my_group
92-
---
93-
apiVersion: rbac.authorization.k8s.io/v1
94-
kind: Role
95-
metadata:
96-
name: kuberift
97-
rules:
98-
- apiGroups:
99-
- 'key.kuberift.com'
100-
resources:
101-
- keys
102-
verbs:
103-
- '*'
104-
---
105120
```

helm/templates/server.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ spec:
9696
value: {{ .clientID }}
9797
- name: KUBERIFT_OID_CONFIG_URL
9898
value: {{ .configURL }}
99+
- name: POD_UID
100+
valueFrom:
101+
fieldRef:
102+
fieldPath: metadata.uid
99103
{{- end }}
100104

101105
{{- if .resources}}

0 commit comments

Comments
 (0)