Skip to content

Commit 4051e2d

Browse files
Merge pull request #199 from minrk/simplify-tls
simplify ssl, passthrough configuration
2 parents d56e52d + c378729 commit 4051e2d

File tree

16 files changed

+395
-340
lines changed

16 files changed

+395
-340
lines changed

dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
black
2+
certipy
23
codecov
34
# etcd3 & python-consul2 are now soft dependencies
45
# Adding them here prevents CI from failing

jupyterhub_traefik_proxy/consul.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
import string
2323
from urllib.parse import urlparse
2424

25-
from traitlets import Any, Unicode, default
25+
from traitlets import Any, Dict, Unicode, default
2626

2727
from .kv_proxy import TKvProxy
28+
from .traefik_utils import deep_merge
2829

2930

3031
class TraefikConsulProxy(TKvProxy):
@@ -35,11 +36,9 @@ class TraefikConsulProxy(TKvProxy):
3536
# Consul doesn't accept keys containing // or starting with / so we have to escape them
3637
key_safe_chars = string.ascii_letters + string.digits + "!@#$%^&*();<>-.+?:"
3738

38-
consul_client_ca_cert = Unicode(
39+
consul_client_kwargs = Dict(
3940
config=True,
40-
allow_none=True,
41-
default_value=None,
42-
help="""Consul client root certificates""",
41+
help="Extra consul client constructor arguments",
4342
)
4443

4544
consul_url = Unicode(
@@ -85,10 +84,11 @@ def _default_client(self):
8584
kwargs = {
8685
"host": consul_service.hostname,
8786
"port": consul_service.port,
88-
"cert": self.consul_client_ca_cert,
8987
}
9088
if self.consul_password:
9189
kwargs.update({"token": self.consul_password})
90+
if self.consul_client_kwargs:
91+
kwargs.update(self.consul_client_kwargs)
9292
return consul.aio.Consul(**kwargs)
9393

9494
def __init__(self, **kwargs):
@@ -105,11 +105,9 @@ def _setup_traefik_static_config(self):
105105
}
106106
}
107107

108-
# FIXME: Same with the tls info
109-
if self.consul_client_ca_cert:
110-
provider_config["consul"]["tls"] = {"ca": self.consul_client_ca_cert}
111-
112-
self.static_config.update({"providers": provider_config})
108+
self.static_config = deep_merge(
109+
self.static_config, {"providers": provider_config}
110+
)
113111
return super()._setup_traefik_static_config()
114112

115113
def _start_traefik(self):

jupyterhub_traefik_proxy/etcd.py

Lines changed: 15 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
from urllib.parse import urlparse
2323

2424
from tornado.concurrent import run_on_executor
25-
from traitlets import Any, Bool, List, Unicode, default
25+
from traitlets import Any, Dict, Unicode, default
2626

2727
from .kv_proxy import TKvProxy
28+
from .traefik_utils import deep_merge
2829

2930

3031
class TraefikEtcdProxy(TKvProxy):
@@ -34,42 +35,9 @@ class TraefikEtcdProxy(TKvProxy):
3435

3536
provider_name = "etcd"
3637

37-
etcd_client_ca_cert = Unicode(
38+
etcd_client_kwargs = Dict(
3839
config=True,
39-
allow_none=True,
40-
default_value=None,
41-
help="""Etcd client root certificates""",
42-
)
43-
44-
etcd_client_cert_crt = Unicode(
45-
config=True,
46-
allow_none=True,
47-
default_value=None,
48-
help="""Etcd client certificate chain
49-
(etcd_client_cert_key must also be specified)""",
50-
)
51-
52-
etcd_client_cert_key = Unicode(
53-
config=True,
54-
allow_none=True,
55-
default_value=None,
56-
help="""Etcd client private key
57-
(etcd_client_cert_crt must also be specified)""",
58-
)
59-
60-
# The grpc client (used by the Python etcd library) doesn't allow untrusted
61-
# etcd certificates, although traefik does allow them.
62-
etcd_insecure_skip_verify = Bool(
63-
False,
64-
config=True,
65-
help="""Traefik will by default validate SSL certificate of etcd backend""",
66-
)
67-
68-
grpc_options = List(
69-
config=True,
70-
allow_none=True,
71-
default_value=None,
72-
help="""Any grpc options that need to be passed to the etcd client""",
40+
help="""Extra keyword arguments to pass to the etcd Python client constructor""",
7341
)
7442

7543
@default("executor")
@@ -120,10 +88,6 @@ def _default_client(self):
12088
kwargs = {
12189
'host': etcd_service.hostname,
12290
'port': etcd_service.port,
123-
'ca_cert': self.etcd_client_ca_cert,
124-
'cert_cert': self.etcd_client_cert_crt,
125-
'cert_key': self.etcd_client_cert_key,
126-
'grpc_options': self.grpc_options,
12791
}
12892
if self.etcd_password:
12993
kwargs.update(
@@ -132,6 +96,8 @@ def _default_client(self):
13296
"password": self.etcd_password,
13397
}
13498
)
99+
if self.etcd_client_kwargs:
100+
kwargs.update(self.etcd_client_kwargs)
135101
return etcd3.client(**kwargs)
136102

137103
def _cleanup(self):
@@ -194,29 +160,22 @@ async def _kv_atomic_delete(self, *keys):
194160
def _setup_traefik_static_config(self):
195161
self.log.debug("Setting up the etcd provider in the static config")
196162
url = urlparse(self.etcd_url)
197-
self.static_config.update(
198-
{
199-
"providers": {
200-
"etcd": {
201-
"endpoints": [url.netloc],
202-
"rootKey": self.kv_traefik_prefix,
203-
}
204-
}
205-
}
206-
)
163+
etcd_config = {
164+
"endpoints": [url.netloc],
165+
"rootKey": self.kv_traefik_prefix,
166+
}
207167
if url.scheme == "https":
208168
# If etcd is running over TLS, then traefik needs to know
209-
tls_conf = {}
210-
if self.etcd_client_ca_cert is not None:
211-
tls_conf["ca"] = self.etcd_client_ca_cert
212-
tls_conf["insecureSkipVerify"] = self.etcd_insecure_skip_verify
213-
self.static_config["providers"]["etcd"]["tls"] = tls_conf
169+
etcd_config["tls"] = {}
214170

215171
if self.etcd_username and self.etcd_password:
216-
self.static_config["providers"]["etcd"].update(
172+
etcd_config.update(
217173
{
218174
"username": self.etcd_username,
219175
"password": self.etcd_password,
220176
}
221177
)
178+
self.static_config = deep_merge(
179+
self.static_config, {"providers": {"etcd": etcd_config}}
180+
)
222181
return super()._setup_traefik_static_config()

jupyterhub_traefik_proxy/fileprovider.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def _persist_dynamic_config(self):
9292
http.pop(key)
9393
if not http:
9494
dynamic_config.pop("http")
95+
self.log.debug("Writing dynamic config %s", dynamic_config)
9596
self.dynamic_config_handler.atomic_dump(dynamic_config)
9697

9798
async def _setup_traefik_dynamic_config(self):

jupyterhub_traefik_proxy/kv_proxy.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,22 @@ def by_depth(item):
319319
key_path = key.split(sep)
320320
d = tree
321321
for parent_key, key in zip(key_path[:-1], key_path[1:]):
322-
if parent_key not in d:
322+
if parent_key.isdigit():
323+
parent_key = int(parent_key)
324+
if isinstance(d, dict) and parent_key not in d:
323325
# create container
324326
if key.isdigit():
325327
# integer keys mean it's a list
326328
d[parent_key] = []
327329
else:
328330
d[parent_key] = {}
331+
elif isinstance(d, list):
332+
if key.isdigit():
333+
# integer keys mean it's a list
334+
next_d = []
335+
else:
336+
next_d = {}
337+
d.append(next_d)
329338
# walk down to the next level
330339
d = d[parent_key]
331340
if isinstance(d, list):

0 commit comments

Comments
 (0)