@dooer/dooer-test-env 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/getting-access.md +52 -116
- package/docs/issuing-access.md +141 -50
- package/package.json +1 -1
package/docs/getting-access.md
CHANGED
|
@@ -6,7 +6,8 @@ fetch the BankID cert. That needs two things, in this order:
|
|
|
6
6
|
1. **VPN** — the cluster API is not reachable from the open internet.
|
|
7
7
|
2. **kubectl access** — a client certificate identifying you to the cluster.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Both come from an admin**: they create your VPN tunnel and your cluster credential, and send you two
|
|
10
|
+
files — a WireGuard `.conf` and a `kubeconfig`. You install them and check they work. See
|
|
10
11
|
[issuing-access.md](./issuing-access.md) for their side.
|
|
11
12
|
|
|
12
13
|
---
|
|
@@ -14,98 +15,50 @@ The VPN part is self-service. The kubectl part needs an admin to sign your certi
|
|
|
14
15
|
## 0. Prerequisites
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
|
-
brew install wireguard-tools
|
|
18
|
+
brew install wireguard-tools kubernetes-cli
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
`openssl` ships with macOS. Check everything is there:
|
|
21
22
|
|
|
22
23
|
```bash
|
|
23
|
-
wg --version &&
|
|
24
|
+
wg --version && kubectl version --client && openssl version
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
28
29
|
## 1. VPN
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
**Your admin creates the tunnel and sends you a `.conf` file.** You do not create it yourself and you do
|
|
32
|
+
not need a Robo10 token for this — ask an admin, who follows
|
|
33
|
+
[issuing-access.md § 1](./issuing-access.md#1-vpn--create-and-hand-over-a-tunnel).
|
|
31
34
|
|
|
32
|
-
The
|
|
33
|
-
|
|
35
|
+
The file they send you contains a **private key**. Treat it like a password: keep it out of chat, tickets
|
|
36
|
+
and git, install it, and delete the copy they sent you.
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
TOKEN="$(go run github.com/strehle/cmdline-openid-client/openid-client@latest \
|
|
37
|
-
-issuer https://oidc-provider-backend.api.staging.roboten-dev.com \
|
|
38
|
-
-client_id ro_authentication_cl_id \
|
|
39
|
-
-port 34466 | awk '/===/ { if (start) nextfile; start = 1; next } start { print }' | jq -r .access_token)"
|
|
40
|
-
|
|
41
|
-
[ -n "$TOKEN" ] && echo "token acquired" || echo "no token — did the browser login complete?"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Keep using **this same terminal** for the next steps: `$TOKEN` only exists in this shell.
|
|
45
|
-
|
|
46
|
-
<details>
|
|
47
|
-
<summary>Alternative if the Go client will not run</summary>
|
|
38
|
+
### 1a. Install the config
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
3. Copy the value of the `x-op-token` cookie.
|
|
52
|
-
4. In your terminal: `TOKEN=<the-cookie-value>`
|
|
53
|
-
</details>
|
|
54
|
-
|
|
55
|
-
### 1b. Create your tunnel
|
|
56
|
-
|
|
57
|
-
One tunnel per person per machine. Name it after yourself so admins can tell them apart:
|
|
40
|
+
`wg-quick` takes the interface name from the **filename**, so the name you pick here is the name you use in
|
|
41
|
+
every later command. Check first what is already there — **do not overwrite an existing tunnel**:
|
|
58
42
|
|
|
59
43
|
```bash
|
|
60
|
-
|
|
61
|
-
curl -sS -H "Authorization: Bearer $TOKEN" \
|
|
62
|
-
-H 'Content-Type: application/json' \
|
|
63
|
-
-d "{ \"name\": \"$NAME\" }" \
|
|
64
|
-
https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The response contains the tunnel's **id** — you need it next. To find it again later:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
curl -sS -H "Authorization: Bearer $TOKEN" https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 1c. Download the tunnel config
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
ID=<the id from the previous step>
|
|
77
|
-
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
|
|
78
|
-
https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels/$ID/actions/get-config \
|
|
79
|
-
> ~/roboten.conf
|
|
44
|
+
ls /opt/homebrew/etc/wireguard/
|
|
80
45
|
```
|
|
81
46
|
|
|
82
|
-
|
|
83
|
-
> chat or a ticket.
|
|
84
|
-
|
|
85
|
-
### 1d. Install it
|
|
86
|
-
|
|
87
|
-
`wg-quick` takes the interface name from the **filename**, so the name you choose here is the name you use
|
|
88
|
-
in every later command. Pick something that will not collide with a tunnel you already have:
|
|
47
|
+
Then install it (using `roboten` as the interface name):
|
|
89
48
|
|
|
90
49
|
```bash
|
|
91
50
|
sudo mkdir -p /opt/homebrew/etc/wireguard
|
|
92
|
-
sudo cp ~/
|
|
51
|
+
sudo cp ~/Downloads/<the-file-they-sent>.conf /opt/homebrew/etc/wireguard/roboten.conf
|
|
93
52
|
sudo chmod 600 /opt/homebrew/etc/wireguard/roboten.conf
|
|
94
|
-
rm ~/
|
|
53
|
+
rm ~/Downloads/<the-file-they-sent>.conf # the copy under /opt/homebrew is the one wg-quick reads
|
|
95
54
|
```
|
|
96
55
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
ls /opt/homebrew/etc/wireguard/
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 1e. Connect, disconnect, restart
|
|
56
|
+
### 1b. Connect, disconnect, restart
|
|
104
57
|
|
|
105
58
|
```bash
|
|
106
59
|
sudo wg-quick up roboten # connect
|
|
107
60
|
sudo wg-quick down roboten # disconnect
|
|
108
|
-
sudo wg-quick down roboten && sudo wg-quick up roboten # restart (after a config change or
|
|
61
|
+
sudo wg-quick down roboten && sudo wg-quick up roboten # restart (after a config change or sleep/wake)
|
|
109
62
|
|
|
110
63
|
sudo wg show # status: handshake time + transfer counters
|
|
111
64
|
```
|
|
@@ -122,75 +75,59 @@ sudo brew services start wireguard-tools # starts wg0 by default
|
|
|
122
75
|
|
|
123
76
|
That service is hard-wired to the `wg0` interface, so it only helps if you named your file `wg0.conf`.
|
|
124
77
|
|
|
125
|
-
###
|
|
78
|
+
### 1c. Verify the VPN works
|
|
126
79
|
|
|
127
80
|
```bash
|
|
128
81
|
sudo wg show # expect a recent "latest handshake"
|
|
129
82
|
nc -vz k8s.roboten-infra.com 64430 # expect: succeeded / open
|
|
130
83
|
```
|
|
131
84
|
|
|
132
|
-
If `nc` hangs or is refused, the tunnel is not carrying traffic — restart it (
|
|
85
|
+
If `nc` hangs or is refused, the tunnel is not carrying traffic — restart it (1b). Until this passes, no
|
|
133
86
|
amount of kubectl configuration will help.
|
|
134
87
|
|
|
88
|
+
> The tunnel routes the cluster network (including the API server) through WireGuard. If you already have
|
|
89
|
+
> another VPN claiming those ranges, they will fight over the routes — bring only one up at a time.
|
|
90
|
+
|
|
135
91
|
---
|
|
136
92
|
|
|
137
93
|
## 2. kubectl access
|
|
138
94
|
|
|
139
|
-
|
|
140
|
-
|
|
95
|
+
**Your admin sends you a ready-made `kubeconfig` file.** You do not generate keys or assemble anything —
|
|
96
|
+
they create the credential and hand it over
|
|
97
|
+
([issuing-access.md § 2](./issuing-access.md#2-kubectl--create-their-credential)).
|
|
141
98
|
|
|
142
|
-
|
|
99
|
+
Like the VPN config, that file contains a **private key**. Same handling: install it, then delete the copy
|
|
100
|
+
they sent you.
|
|
143
101
|
|
|
144
|
-
|
|
145
|
-
admin binds permissions to.
|
|
102
|
+
### 2a. Install it
|
|
146
103
|
|
|
147
|
-
|
|
148
|
-
USER_NAME=<firstname>-admin
|
|
149
|
-
mkdir -p ~/.kube/roboten && cd ~/.kube/roboten
|
|
104
|
+
**If you have no other clusters** — simplest, just put it in place:
|
|
150
105
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
106
|
+
```bash
|
|
107
|
+
mkdir -p ~/.kube
|
|
108
|
+
cp ~/Downloads/<yourname>.kubeconfig ~/.kube/config
|
|
109
|
+
chmod 600 ~/.kube/config
|
|
110
|
+
rm ~/Downloads/<yourname>.kubeconfig
|
|
154
111
|
```
|
|
155
112
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Send **only** `<firstname>-admin.csr`. It is not secret — but the `.key` next to it is, and must never
|
|
159
|
-
leave your machine.
|
|
160
|
-
|
|
161
|
-
Ask them for three things back:
|
|
162
|
-
|
|
163
|
-
| | |
|
|
164
|
-
| --- | --- |
|
|
165
|
-
| `<firstname>-admin.crt` | your signed certificate |
|
|
166
|
-
| `ca.crt` | the cluster CA certificate |
|
|
167
|
-
| the API server URL | currently `https://k8s.roboten-infra.com:64430` |
|
|
168
|
-
|
|
169
|
-
### 2c. Assemble your kubeconfig
|
|
170
|
-
|
|
171
|
-
Put the two `.crt` files next to your key in `~/.kube/roboten`, then:
|
|
113
|
+
**If you already use kubectl for something else**, merge instead of overwriting:
|
|
172
114
|
|
|
173
115
|
```bash
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
KUBECONFIG=~/.kube/config kubectl config set-credentials "$USER_NAME" \
|
|
182
|
-
--client-certificate="$USER_NAME.crt" --client-key="$USER_NAME.key" --embed-certs=true
|
|
116
|
+
cp ~/.kube/config ~/.kube/config.backup # always back up first
|
|
117
|
+
KUBECONFIG=~/.kube/config:~/Downloads/<yourname>.kubeconfig \
|
|
118
|
+
kubectl config view --flatten > /tmp/merged && mv /tmp/merged ~/.kube/config
|
|
119
|
+
chmod 600 ~/.kube/config
|
|
120
|
+
rm ~/Downloads/<yourname>.kubeconfig
|
|
121
|
+
```
|
|
183
122
|
|
|
184
|
-
|
|
185
|
-
--cluster=kubernetes --user="$USER_NAME"
|
|
123
|
+
Then select the context (the admin will tell you its name, `<firstname>-admin@kubernetes`):
|
|
186
124
|
|
|
187
|
-
|
|
125
|
+
```bash
|
|
126
|
+
kubectl config get-contexts
|
|
127
|
+
kubectl config use-context <firstname>-admin@kubernetes
|
|
188
128
|
```
|
|
189
129
|
|
|
190
|
-
|
|
191
|
-
only a backup after this point. Keep the `.key` anyway — you need it if you ever rebuild the config.
|
|
192
|
-
|
|
193
|
-
### 2d. Verify
|
|
130
|
+
### 2b. Verify
|
|
194
131
|
|
|
195
132
|
With the VPN up:
|
|
196
133
|
|
|
@@ -228,12 +165,11 @@ npx @dooer/dooer-test-env@latest setup
|
|
|
228
165
|
|
|
229
166
|
| Symptom | Cause |
|
|
230
167
|
| --- | --- |
|
|
231
|
-
| `dial tcp … i/o timeout` from kubectl | VPN is down — `sudo wg show`, then restart it (
|
|
168
|
+
| `dial tcp … i/o timeout` from kubectl | VPN is down — `sudo wg show`, then restart it (1b) |
|
|
232
169
|
| `nc` to the API server hangs | Tunnel is up but not carrying traffic; restart it |
|
|
233
|
-
| `error: You must be logged in to the server (Unauthorized)` |
|
|
234
|
-
| `Error from server (Forbidden)` | You *are* authenticated; your user has no permissions bound yet — ask the admin to complete their
|
|
235
|
-
| `no token — did the browser login complete?` | The OIDC flow did not finish; make sure you used your `@robo10.com` account |
|
|
170
|
+
| `error: You must be logged in to the server (Unauthorized)` | The certificate in your kubeconfig is expired or unknown to the cluster — ask for a re-issue |
|
|
171
|
+
| `Error from server (Forbidden)` | You *are* authenticated; your user has no permissions bound yet — ask the admin to complete their § 2e (bind permissions) |
|
|
236
172
|
| `wg-quick: 'roboten' already exists` | The interface is already up — `sudo wg-quick down roboten` first |
|
|
237
173
|
|
|
238
|
-
Certificates are issued for **one year**. When yours expires
|
|
239
|
-
|
|
174
|
+
Certificates are issued for **one year**. When yours expires, ask your admin for a new kubeconfig and
|
|
175
|
+
repeat section 2 — your permissions stay in place, so they only have to re-issue the credential.
|
package/docs/issuing-access.md
CHANGED
|
@@ -4,66 +4,133 @@ The other side of [getting-access.md](./getting-access.md). Two independent thin
|
|
|
4
4
|
|
|
5
5
|
| | Who does it | What you do |
|
|
6
6
|
| --- | --- | --- |
|
|
7
|
-
| **VPN tunnel** |
|
|
8
|
-
| **kubectl credential** |
|
|
7
|
+
| **VPN tunnel** | you | create it and send them the config |
|
|
8
|
+
| **kubectl credential** | you | create the certificate, bind permissions, send a kubeconfig |
|
|
9
9
|
|
|
10
|
-
You need `kubectl` with cluster-admin (`system:masters`) and
|
|
10
|
+
You need a Robo10 token (§1a), `kubectl` with cluster-admin (`system:masters`), and your own VPN up.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## 1. VPN —
|
|
14
|
+
## 1. VPN — create and hand over a tunnel
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
You create the tunnel and send the developer its config. They do not need a Robo10 token, which matters:
|
|
17
|
+
a brand-new joiner often has no working login yet, and the VPN API generates the keypair **server-side**
|
|
18
|
+
anyway — the config comes back with the private key in it either way, so there is no custody advantage to
|
|
19
|
+
making them do it.
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
### 1a. Get a Robo10 token (yours)
|
|
22
|
+
|
|
23
|
+
Opens a browser — log in with **your `@robo10.com` account**:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
TOKEN="$(go run github.com/strehle/cmdline-openid-client/openid-client@latest \
|
|
27
|
+
-issuer https://oidc-provider-backend.api.staging.roboten-dev.com \
|
|
28
|
+
-client_id ro_authentication_cl_id \
|
|
29
|
+
-port 34466 | awk '/===/ { if (start) nextfile; start = 1; next } start { print }' | jq -r .access_token)"
|
|
30
|
+
|
|
31
|
+
[ -n "$TOKEN" ] && echo "token acquired" || echo "no token — did the browser login complete?"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Needs `go` and `jq` (`brew install go jq`). Keep using **this same terminal**: `$TOKEN` lives only in this
|
|
35
|
+
shell, and everything below needs it.
|
|
36
|
+
|
|
37
|
+
<details>
|
|
38
|
+
<summary>Alternative if the Go client will not run</summary>
|
|
39
|
+
|
|
40
|
+
1. Open <https://chat.roboten.com> and log in.
|
|
41
|
+
2. DevTools → **Application** → **Storage → Cookies**.
|
|
42
|
+
3. Copy the value of the `x-op-token` cookie.
|
|
43
|
+
4. In your terminal: `TOKEN=<the-cookie-value>`
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
### 1b. Create their tunnel
|
|
47
|
+
|
|
48
|
+
Name it after the person (and their machine if they need more than one) so the list stays readable:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
NAME="Firstname Lastname"
|
|
52
|
+
curl -sS -H "Authorization: Bearer $TOKEN" \
|
|
53
|
+
-H 'Content-Type: application/json' \
|
|
54
|
+
-d "{ \"name\": \"$NAME\" }" \
|
|
55
|
+
https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The response contains the tunnel's **id**. To find it again later, list them (1d).
|
|
59
|
+
|
|
60
|
+
### 1c. Download the config and hand it over
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ID=<the id from the previous step>
|
|
64
|
+
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
|
|
65
|
+
https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels/$ID/actions/get-config \
|
|
66
|
+
> ~/Downloads/$NAME.conf
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> **This file contains a private key.** Send it over a channel you would send a password over — a shared
|
|
70
|
+
> vault entry or an expiring secure link — not email, Slack or a ticket. Tell them to install it and delete
|
|
71
|
+
> the copy you sent, and delete your own copy once they confirm.
|
|
22
72
|
|
|
23
73
|
Then point them at [getting-access.md § 1](./getting-access.md#1-vpn).
|
|
24
74
|
|
|
25
|
-
|
|
26
|
-
tunnel
|
|
75
|
+
One tunnel per person per machine: two devices sharing one config will fight over the session, so create a
|
|
76
|
+
second tunnel rather than re-sending the first.
|
|
27
77
|
|
|
28
|
-
|
|
78
|
+
### 1d. List tunnels
|
|
29
79
|
|
|
30
80
|
```bash
|
|
31
81
|
curl -sS -H "Authorization: Bearer $TOKEN" https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels
|
|
32
82
|
```
|
|
33
83
|
|
|
34
|
-
|
|
35
|
-
|
|
84
|
+
### 1e. Revoke a tunnel
|
|
85
|
+
|
|
86
|
+
`DELETE` is not in the VPN service's own docs, but it works (verified 2026-09-04 — returns `204` and the
|
|
87
|
+
tunnel disappears from the list):
|
|
36
88
|
|
|
37
89
|
```bash
|
|
38
90
|
curl -sS -X DELETE -H "Authorization: Bearer $TOKEN" \
|
|
39
91
|
https://vpn.api.staging.roboten-dev.com/v1/vpn-tunnels/$ID
|
|
40
92
|
```
|
|
41
93
|
|
|
42
|
-
That invalidates the tunnel
|
|
43
|
-
|
|
94
|
+
That invalidates the tunnel server-side, so it is the real off-switch for VPN access when someone leaves —
|
|
95
|
+
do it *and* revoke their kubectl permissions (§3).
|
|
44
96
|
|
|
45
97
|
---
|
|
46
98
|
|
|
47
|
-
## 2. kubectl —
|
|
99
|
+
## 2. kubectl — create their credential
|
|
48
100
|
|
|
49
101
|
The cluster authenticates users with **x509 client certificates** signed by the cluster CA. The username is
|
|
50
102
|
the certificate's **Common Name (CN)**; RBAC is bound to that string.
|
|
51
103
|
|
|
52
|
-
You
|
|
104
|
+
You do all of it and hand over a finished `kubeconfig`.
|
|
53
105
|
|
|
54
|
-
|
|
106
|
+
> Everything below was verified end to end against the live cluster (2026-09-04) with a throwaway
|
|
107
|
+
> `guidetest-admin`: the resulting kubeconfig read `dooer-production` and `dooer-staging`, including
|
|
108
|
+
> secrets. The test user was then removed.
|
|
55
109
|
|
|
56
110
|
```bash
|
|
57
|
-
USER_NAME=<firstname>-admin
|
|
58
|
-
|
|
111
|
+
USER_NAME=<firstname>-admin # becomes their cluster username
|
|
112
|
+
WORKDIR=$(mktemp -d) && cd "$WORKDIR"
|
|
113
|
+
```
|
|
59
114
|
|
|
115
|
+
Work in a temp directory — it holds their private key until you hand it over, and you delete it at the end.
|
|
116
|
+
|
|
117
|
+
### 2a. Generate their key and CSR
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
openssl genrsa -out "$USER_NAME.key" 2048
|
|
121
|
+
openssl req -new -key "$USER_NAME.key" -out "$USER_NAME.csr" -subj "/CN=$USER_NAME"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 2b. Submit it as a CertificateSigningRequest
|
|
125
|
+
|
|
126
|
+
```bash
|
|
60
127
|
cat <<EOF | kubectl apply -f -
|
|
61
128
|
apiVersion: certificates.k8s.io/v1
|
|
62
129
|
kind: CertificateSigningRequest
|
|
63
130
|
metadata:
|
|
64
131
|
name: $USER_NAME
|
|
65
132
|
spec:
|
|
66
|
-
request: $(base64 -i "$
|
|
133
|
+
request: $(base64 -i "$USER_NAME.csr" | tr -d '\n')
|
|
67
134
|
signerName: kubernetes.io/kube-apiserver-client
|
|
68
135
|
expirationSeconds: 31536000 # 1 year
|
|
69
136
|
usages:
|
|
@@ -71,15 +138,10 @@ spec:
|
|
|
71
138
|
EOF
|
|
72
139
|
```
|
|
73
140
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
openssl req -in "$CSR_FILE" -noout -subject
|
|
79
|
-
# subject=CN=<firstname>-admin
|
|
80
|
-
```
|
|
141
|
+
If the name already exists (a renewal), delete the old object first:
|
|
142
|
+
`kubectl delete csr "$USER_NAME" --ignore-not-found`.
|
|
81
143
|
|
|
82
|
-
###
|
|
144
|
+
### 2c. Approve and extract the certificate
|
|
83
145
|
|
|
84
146
|
```bash
|
|
85
147
|
kubectl certificate approve "$USER_NAME"
|
|
@@ -90,18 +152,16 @@ openssl x509 -in "$USER_NAME.crt" -noout -subject -issuer -dates
|
|
|
90
152
|
|
|
91
153
|
Expect `subject=CN=<firstname>-admin`, `issuer=CN=kubernetes`, and a one-year window.
|
|
92
154
|
|
|
93
|
-
If `.status.certificate` is empty
|
|
94
|
-
stays empty,
|
|
95
|
-
|
|
96
|
-
### 2c. Extract the cluster CA
|
|
155
|
+
If `.status.certificate` is empty the signer has not run yet — wait a couple of seconds and repeat. If it
|
|
156
|
+
stays empty, check `kubectl describe csr "$USER_NAME"`.
|
|
97
157
|
|
|
98
|
-
|
|
158
|
+
### 2d. Extract the cluster CA
|
|
99
159
|
|
|
100
160
|
```bash
|
|
101
161
|
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
|
|
102
162
|
```
|
|
103
163
|
|
|
104
|
-
###
|
|
164
|
+
### 2e. Bind permissions
|
|
105
165
|
|
|
106
166
|
This is what makes the certificate useful — without it they authenticate but can do nothing. These are the
|
|
107
167
|
same five ClusterRoles the existing admins (`jimmy-admin`, `sam-admin`, `trajko-admin`) hold:
|
|
@@ -125,25 +185,54 @@ The declarative equivalent lives in
|
|
|
125
185
|
is enough for the cluster, but that file is the record of who has access — **add the new user there and
|
|
126
186
|
commit it**, or the next person reading it will get a false picture.
|
|
127
187
|
|
|
128
|
-
|
|
188
|
+
On a renewal, skip this step: the bindings are attached to the username, not to the certificate.
|
|
189
|
+
|
|
190
|
+
### 2f. Assemble the kubeconfig
|
|
129
191
|
|
|
130
|
-
|
|
192
|
+
`--embed-certs=true` inlines the key and certificates, so the file you hand over is self-contained:
|
|
131
193
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
194
|
+
```bash
|
|
195
|
+
SERVER=https://k8s.roboten-infra.com:64430
|
|
196
|
+
KC="$WORKDIR/$USER_NAME.kubeconfig"
|
|
197
|
+
|
|
198
|
+
KUBECONFIG="$KC" kubectl config set-cluster kubernetes \
|
|
199
|
+
--server="$SERVER" --certificate-authority=ca.crt --embed-certs=true
|
|
135
200
|
|
|
136
|
-
|
|
201
|
+
KUBECONFIG="$KC" kubectl config set-credentials "$USER_NAME" \
|
|
202
|
+
--client-certificate="$USER_NAME.crt" --client-key="$USER_NAME.key" --embed-certs=true
|
|
137
203
|
|
|
138
|
-
|
|
204
|
+
KUBECONFIG="$KC" kubectl config set-context "$USER_NAME@kubernetes" \
|
|
205
|
+
--cluster=kubernetes --user="$USER_NAME"
|
|
206
|
+
|
|
207
|
+
KUBECONFIG="$KC" kubectl config use-context "$USER_NAME@kubernetes"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 2g. Verify it before handing it over
|
|
211
|
+
|
|
212
|
+
Test the actual file, not just the RBAC — with your own VPN up:
|
|
139
213
|
|
|
140
214
|
```bash
|
|
141
|
-
kubectl
|
|
142
|
-
kubectl
|
|
143
|
-
kubectl
|
|
215
|
+
KUBECONFIG="$KC" kubectl auth whoami # Username: <firstname>-admin
|
|
216
|
+
KUBECONFIG="$KC" kubectl get pods -n dooer-staging | head -3
|
|
217
|
+
KUBECONFIG="$KC" kubectl get pods -n dooer-production | head -3
|
|
218
|
+
KUBECONFIG="$KC" kubectl auth can-i get secrets -n dooer-staging # yes
|
|
144
219
|
```
|
|
145
220
|
|
|
146
|
-
|
|
221
|
+
If `whoami` works but the reads say `Forbidden`, step 2e did not take.
|
|
222
|
+
|
|
223
|
+
### 2h. Hand it over, then clean up
|
|
224
|
+
|
|
225
|
+
> **The kubeconfig contains their private key.** Send it the same way as the VPN config — a shared vault
|
|
226
|
+
> entry or an expiring secure link, never email, Slack or a ticket.
|
|
227
|
+
|
|
228
|
+
Tell them the context name (`<firstname>-admin@kubernetes`) and point them at
|
|
229
|
+
[getting-access.md § 2](./getting-access.md#2-kubectl-access).
|
|
230
|
+
|
|
231
|
+
Once they confirm it works, destroy your copy — you are holding their key until you do:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
rm -rf "$WORKDIR"
|
|
235
|
+
```
|
|
147
236
|
|
|
148
237
|
---
|
|
149
238
|
|
|
@@ -169,9 +258,11 @@ Also remove them from `clusterrole-admin.yaml` and commit.
|
|
|
169
258
|
|
|
170
259
|
## Renewals
|
|
171
260
|
|
|
172
|
-
A certificate expiring is routine
|
|
173
|
-
|
|
174
|
-
|
|
261
|
+
A certificate expiring is routine. Repeat **§2a–2d and 2f–2h** and send them a fresh kubeconfig; they
|
|
262
|
+
re-run [getting-access.md § 2](./getting-access.md#2-kubectl-access).
|
|
263
|
+
|
|
264
|
+
Skip **2e** — ClusterRoleBindings are bound to the username, not to the certificate, so they survive a
|
|
265
|
+
re-issue untouched.
|
|
175
266
|
|
|
176
267
|
Delete the old CSR object first, or the new one collides on the name:
|
|
177
268
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooer/dooer-test-env",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Run the whole Dooer backend locally (staging DB minus customers), copy/purge customers between environments, and shred — one CLI.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": "Dooer/cli-dooer-test-env",
|