Skip to content

Commit 9097f0d

Browse files
authored
Merge pull request #462 from jumpstarter-dev/alternate-insecure-tls-0.6
Add insecure_tls_config flag
2 parents 9801877 + f36a440 commit 9097f0d

File tree

8 files changed

+213
-9
lines changed

8 files changed

+213
-9
lines changed

docs/source/getting-started/configuration/authentication.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ Note, the HTTPS URL is mandatory, and you only need to include certificateAuthor
4646
prefix usernames with `keycloak:` as configured in the claim mappings:
4747

4848
```shell
49-
$ jmp admin create client test-client --oidc-username keycloak:developer-1
49+
$ jmp admin create client test-client --insecure-tls-config --oidc-username keycloak:developer-1
5050
```
5151

5252
4. Instruct users to log in with:
5353

5454
```shell
5555
$ jmp login --client <client alias> \
56+
--insecure-tls-config \
5657
--endpoint <jumpstarter controller endpoint> \
5758
--namespace <namespace> --name <client name> \
5859
--issuer https://<keycloak domain>/realms/<realm name>
@@ -62,6 +63,7 @@ For non-interactive login, add username and password:
6263

6364
```shell
6465
$ jmp login --client <client alias> [other parameters] \
66+
--insecure-tls-config \
6567
--username <username> \
6668
--password <password>
6769
```
@@ -76,6 +78,7 @@ For exporters, use similar login command but with the `--exporter` flag:
7678

7779
```shell
7880
$ jmp login --exporter <exporter alias> \
81+
--insecure-tls-config \
7982
--endpoint <jumpstarter controller endpoint> \
8083
--namespace <namespace> --name <exporter name> \
8184
--issuer https://<keycloak domain>/realms/<realm name>
@@ -188,6 +191,7 @@ jwt:
188191

189192
```shell
190193
$ jmp admin create exporter test-exporter \
194+
--insecure-tls-config \
191195
--oidc-username dex:system:serviceaccount:default:test-service-account
192196
```
193197

@@ -197,6 +201,7 @@ For clients:
197201

198202
```shell
199203
$ jmp login --client <client alias> \
204+
--insecure-tls-config \
200205
--endpoint <jumpstarter controller endpoint> \
201206
--namespace <namespace> --name <client name> \
202207
--issuer https://dex.dex.svc.cluster.local:5556 \
@@ -208,6 +213,7 @@ For exporters:
208213

209214
```shell
210215
$ jmp login --exporter <exporter alias> \
216+
--insecure-tls-config \
211217
--endpoint <jumpstarter controller endpoint> \
212218
--namespace <namespace> --name <exporter name> \
213219
--issuer https://dex.dex.svc.cluster.local:5556 \

docs/source/getting-started/usage/setup-distributed-mode.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
This guide walks you through the process of creating an exporter using the
44
controller service, configuring drivers, and running the exporter.
55

6+
```{warning}
7+
The jumpstarter-controller endpoints are secured by TLS. However, in release 0.6.x,
8+
the certificates are self-signed and rotated on every restart. This means the client
9+
will not be able to verify the server certificate. To bypass this, you should use the
10+
`--insecure-tls-config` flag when creating clients and exporters. This issue will be
11+
resolved in the next release. See [issue #455](https://github.com/jumpstarter-dev/jumpstarter/issues/455)
12+
for more details.
13+
Alternatively, you can configure the ingress/route in reencrypt mode with your own key and certificate.
14+
```
15+
616
## Prerequisites
717

818
Install [the following packages](../installation/packages.md) in your Python
@@ -30,7 +40,7 @@ Run this command to create an exporter named `example-distributed` and save the
3040
configuration locally:
3141

3242
```shell
33-
$ jmp admin create exporter example-distributed --save
43+
$ jmp admin create exporter example-distributed --save --insecure-tls-config
3444
```
3545

3646
After creating the exporter, find the new configuration file at
@@ -78,7 +88,7 @@ development purposes, and saves the configuration locally in
7888
`${HOME}/.config/jumpstarter/clients/`:
7989

8090
```shell
81-
$ jmp admin create client hello --save --unsafe
91+
$ jmp admin create client hello --save --unsafe --insecure-tls-config
8292
```
8393

8494
### Spawn an Exporter Shell

packages/jumpstarter-cli-admin/jumpstarter_cli_admin/create.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from jumpstarter_cli_common.opt import (
77
OutputMode,
88
OutputType,
9+
confirm_insecure_tls,
910
opt_context,
11+
opt_insecure_tls_config,
1012
opt_kubeconfig,
1113
opt_labels,
1214
opt_namespace,
@@ -69,6 +71,7 @@ def print_created_client(client: V1Alpha1Client, output: OutputType):
6971
@opt_labels
7072
@opt_kubeconfig
7173
@opt_context
74+
@opt_insecure_tls_config
7275
@opt_oidc_username
7376
@opt_nointeractive
7477
@opt_output_all
@@ -77,6 +80,7 @@ async def create_client(
7780
name: Optional[str],
7881
kubeconfig: Optional[str],
7982
context: Optional[str],
83+
insecure_tls_config: bool,
8084
namespace: str,
8185
labels: list[(str, str)],
8286
save: bool,
@@ -89,6 +93,7 @@ async def create_client(
8993
):
9094
"""Create a client object in the Kubernetes cluster"""
9195
try:
96+
confirm_insecure_tls(insecure_tls_config, nointeractive)
9297
async with ClientsV1Alpha1Api(namespace, kubeconfig, context) as api:
9398
if output is None:
9499
# Only print status if is not JSON/YAML
@@ -108,6 +113,7 @@ async def create_client(
108113
allow_drivers = allow.split(",") if allow is not None and len(allow) > 0 else []
109114
client_config.drivers.unsafe = unsafe
110115
client_config.drivers.allow = allow_drivers
116+
client_config.tls.insecure = insecure_tls_config
111117
ClientConfigV1Alpha1.save(client_config, out)
112118
# If this is the only client config, set it as default
113119
if out is None and len(ClientConfigV1Alpha1.list()) == 1:
@@ -151,6 +157,7 @@ def print_created_exporter(exporter: V1Alpha1Exporter, output: OutputType):
151157
@opt_labels
152158
@opt_kubeconfig
153159
@opt_context
160+
@opt_insecure_tls_config
154161
@opt_oidc_username
155162
@opt_nointeractive
156163
@opt_output_all
@@ -159,6 +166,7 @@ async def create_exporter(
159166
name: Optional[str],
160167
kubeconfig: Optional[str],
161168
context: Optional[str],
169+
insecure_tls_config: bool,
162170
namespace: str,
163171
labels: list[(str, str)],
164172
save: bool,
@@ -169,6 +177,7 @@ async def create_exporter(
169177
):
170178
"""Create an exporter object in the Kubernetes cluster"""
171179
try:
180+
confirm_insecure_tls(insecure_tls_config, nointeractive)
172181
async with ExportersV1Alpha1Api(namespace, kubeconfig, context) as api:
173182
if output is None:
174183
click.echo(f"Creating exporter '{name}' in namespace '{namespace}'")
@@ -178,6 +187,7 @@ async def create_exporter(
178187
if output is None:
179188
click.echo("Fetching exporter credentials from cluster")
180189
exporter_config = await api.get_exporter_config(name)
190+
exporter_config.tls.insecure = insecure_tls_config
181191
ExporterConfigV1Alpha1.save(exporter_config, out)
182192
if output is None:
183193
click.echo(f"Exporter configuration successfully saved to {exporter_config.path}")

packages/jumpstarter-cli-admin/jumpstarter_cli_admin/create_test.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import uuid
2+
from pathlib import Path
23
from unittest.mock import AsyncMock, Mock, patch
34

45
from click.testing import CliRunner
@@ -16,6 +17,7 @@
1617
from jumpstarter.config.client import ClientConfigV1Alpha1, ClientConfigV1Alpha1Drivers
1718
from jumpstarter.config.common import ObjectMeta
1819
from jumpstarter.config.exporter import ExporterConfigV1Alpha1
20+
from jumpstarter.config.tls import TLSConfigV1Alpha1
1921

2022
# Generate a random client name
2123
CLIENT_NAME = uuid.uuid4().hex
@@ -81,6 +83,15 @@
8183
drivers=ClientConfigV1Alpha1Drivers(allow=[], unsafe=True),
8284
)
8385

86+
INSECURE_TLS_CLIENT_CONFIG = ClientConfigV1Alpha1(
87+
alias=CLIENT_NAME,
88+
metadata=ObjectMeta(namespace="default", name=CLIENT_NAME),
89+
endpoint=CLIENT_ENDPOINT,
90+
token=CLIENT_TOKEN,
91+
tls=TLSConfigV1Alpha1(insecure=True),
92+
drivers=ClientConfigV1Alpha1Drivers(allow=[], unsafe=True),
93+
)
94+
8495

8596
@patch.object(ClientConfigV1Alpha1, "save")
8697
@patch.object(ClientsV1Alpha1Api, "get_client_config")
@@ -100,6 +111,32 @@ def test_create_client(
100111
mock_save_client.assert_not_called()
101112
mock_save_client.reset_mock()
102113

114+
# Insecure TLS config is returned
115+
mock_get_client_config.return_value = INSECURE_TLS_CLIENT_CONFIG
116+
117+
# Save with prompts accept insecure = Y, save = Y, unsafe = Y
118+
result = runner.invoke(create, ["client", "--insecure-tls-config", CLIENT_NAME], input="Y\nY\nY\n")
119+
assert result.exit_code == 0
120+
assert "Client configuration successfully saved" in result.output
121+
mock_save_client.assert_called_once_with(INSECURE_TLS_CLIENT_CONFIG, None)
122+
mock_save_client.reset_mock()
123+
124+
# Save no interactive and insecure tls
125+
result = runner.invoke(create, ["client", "--insecure-tls-config", "--unsafe",
126+
"--save", "--nointeractive", CLIENT_NAME])
127+
assert result.exit_code == 0
128+
assert "Client configuration successfully saved" in result.output
129+
mock_save_client.assert_called_once_with(INSECURE_TLS_CLIENT_CONFIG, None)
130+
mock_save_client.reset_mock()
131+
132+
# Insecure TLS config is returned
133+
mock_get_client_config.return_value = INSECURE_TLS_CLIENT_CONFIG
134+
135+
# Save with prompts accept insecure = N
136+
result = runner.invoke(create, ["client", "--insecure-tls-config", CLIENT_NAME], input="n\n")
137+
assert result.exit_code == 1
138+
assert "Aborted" in result.output
139+
103140
# Unsafe client config is returned
104141
mock_get_client_config.return_value = UNSAFE_CLIENT_CONFIG
105142

@@ -115,7 +152,7 @@ def test_create_client(
115152
result = runner.invoke(create, ["client", CLIENT_NAME, "--unsafe", "--out", out], input="\n\n")
116153
assert result.exit_code == 0
117154
assert "Client configuration successfully saved" in result.output
118-
mock_save_client.assert_called_once_with(UNSAFE_CLIENT_CONFIG, out)
155+
mock_save_client.assert_called_once_with(UNSAFE_CLIENT_CONFIG, str(Path(out).resolve()))
119156
mock_save_client.reset_mock()
120157

121158
# Regular client config is returned
@@ -219,6 +256,13 @@ def test_create_client(
219256
token=EXPORTER_TOKEN,
220257
)
221258

259+
INSECURE_TLS_EXPORTER_CONFIG = ExporterConfigV1Alpha1(
260+
alias=EXPORTER_NAME,
261+
metadata=ObjectMeta(namespace="default", name=EXPORTER_NAME),
262+
endpoint=EXPORTER_ENDPOINT,
263+
token=EXPORTER_TOKEN,
264+
tls=TLSConfigV1Alpha1(insecure=True),
265+
)
222266

223267
@patch.object(ExporterConfigV1Alpha1, "save")
224268
@patch.object(ExportersV1Alpha1Api, "_load_kube_config")
@@ -238,6 +282,31 @@ def test_create_exporter(
238282
save_exporter_mock.assert_not_called()
239283
save_exporter_mock.reset_mock()
240284

285+
# Insecure TLS config is returned
286+
_get_exporter_config_mock.return_value = INSECURE_TLS_EXPORTER_CONFIG
287+
# Save with prompts accept insecure = Y, save = Y
288+
result = runner.invoke(create, ["exporter", "--insecure-tls-config", EXPORTER_NAME], input="Y\nY\n")
289+
assert result.exit_code == 0
290+
assert "Exporter configuration successfully saved" in result.output
291+
save_exporter_mock.assert_called_once_with(INSECURE_TLS_EXPORTER_CONFIG, None)
292+
save_exporter_mock.reset_mock()
293+
294+
_get_exporter_config_mock.return_value = INSECURE_TLS_EXPORTER_CONFIG
295+
# Save with prompts accept no interactive
296+
result = runner.invoke(create, ["exporter", "--insecure-tls-config", "--nointeractive", "--save", EXPORTER_NAME])
297+
assert result.exit_code == 0
298+
assert "Exporter configuration successfully saved" in result.output
299+
save_exporter_mock.assert_called_once_with(INSECURE_TLS_EXPORTER_CONFIG, None)
300+
save_exporter_mock.reset_mock()
301+
302+
# Insecure TLS config is returned
303+
_get_exporter_config_mock.return_value = INSECURE_TLS_EXPORTER_CONFIG
304+
# Save with prompts accept insecure = N
305+
result = runner.invoke(create, ["exporter", "--insecure-tls-config", EXPORTER_NAME], input="n\n")
306+
assert result.exit_code == 1
307+
assert "Aborted" in result.output
308+
309+
241310
# Save with prompts
242311
result = runner.invoke(create, ["exporter", EXPORTER_NAME], input="Y\n")
243312
assert result.exit_code == 0
@@ -257,7 +326,7 @@ def test_create_exporter(
257326
result = runner.invoke(create, ["exporter", EXPORTER_NAME, "--out", out])
258327
assert result.exit_code == 0
259328
assert "Exporter configuration successfully saved" in result.output
260-
save_exporter_mock.assert_called_once_with(EXPORTER_CONFIG, out)
329+
save_exporter_mock.assert_called_once_with(EXPORTER_CONFIG, str(Path(out).resolve()))
261330
save_exporter_mock.reset_mock()
262331

263332
# Save with nointeractive

packages/jumpstarter-cli-admin/jumpstarter_cli_admin/import_res.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from jumpstarter_cli_common.blocking import blocking
55
from jumpstarter_cli_common.opt import (
66
PathOutputType,
7+
confirm_insecure_tls,
78
opt_context,
9+
opt_insecure_tls_config,
810
opt_kubeconfig,
911
opt_namespace,
1012
opt_nointeractive,
@@ -46,6 +48,7 @@ def import_res():
4648
@opt_namespace
4749
@opt_kubeconfig
4850
@opt_context
51+
@opt_insecure_tls_config
4952
@opt_output_path_only
5053
@opt_nointeractive
5154
@blocking
@@ -54,6 +57,7 @@ async def import_client(
5457
namespace: str,
5558
kubeconfig: Optional[str],
5659
context: Optional[str],
60+
insecure_tls_config: bool,
5761
allow: Optional[str],
5862
unsafe: bool,
5963
out: Optional[str],
@@ -65,6 +69,7 @@ async def import_client(
6569
if out is None and ClientConfigV1Alpha1.exists(name):
6670
raise click.ClickException(f"A client with the name '{name}' already exists")
6771
try:
72+
confirm_insecure_tls(insecure_tls_config, nointeractive)
6873
async with ClientsV1Alpha1Api(namespace, kubeconfig, context) as api:
6974
if unsafe is False and allow is None and nointeractive is False:
7075
unsafe = click.confirm("Allow unsafe driver client imports?")
@@ -76,6 +81,7 @@ async def import_client(
7681
click.echo("Fetching client credentials from cluster")
7782
allow_drivers = allow.split(",") if allow is not None and len(allow) > 0 else []
7883
client_config = await api.get_client_config(name, allow=allow_drivers, unsafe=unsafe)
84+
client_config.tls.insecure = insecure_tls_config
7985
config_path = ClientConfigV1Alpha1.save(client_config, out)
8086
# If this is the only client config, set it as default
8187
if out is None and len(ClientConfigV1Alpha1.list()) == 1:
@@ -102,6 +108,7 @@ async def import_client(
102108
@opt_namespace
103109
@opt_kubeconfig
104110
@opt_context
111+
@opt_insecure_tls_config
105112
@opt_output_path_only
106113
@opt_nointeractive
107114
@blocking
@@ -111,6 +118,7 @@ async def import_exporter(
111118
out: Optional[str],
112119
kubeconfig: Optional[str],
113120
context: Optional[str],
121+
insecure_tls_config: bool,
114122
output: PathOutputType,
115123
nointeractive: bool,
116124
):
@@ -122,10 +130,12 @@ async def import_exporter(
122130
else:
123131
raise click.ClickException(f'An exporter with the name "{name}" already exists')
124132
try:
133+
confirm_insecure_tls(insecure_tls_config, nointeractive)
125134
async with ExportersV1Alpha1Api(namespace, kubeconfig, context) as api:
126135
if output is None:
127136
click.echo("Fetching exporter credentials from cluster")
128137
exporter_config = await api.get_exporter_config(name)
138+
exporter_config.tls.insecure = insecure_tls_config
129139
config_path = ExporterConfigV1Alpha1.save(exporter_config, out)
130140
if output is None:
131141
click.echo(f"Exporter configuration successfully saved to {config_path}")

0 commit comments

Comments
 (0)