diff --git a/jupyterhub_traefik_proxy/proxy.py b/jupyterhub_traefik_proxy/proxy.py index f8e566eb..f8580d99 100644 --- a/jupyterhub_traefik_proxy/proxy.py +++ b/jupyterhub_traefik_proxy/proxy.py @@ -26,6 +26,7 @@ from subprocess import Popen, TimeoutExpired from urllib.parse import urlparse, urlunparse +import bcrypt from jupyterhub.proxy import Proxy from jupyterhub.utils import exponential_backoff, new_token, url_path_join from tornado.httpclient import AsyncHTTPClient, HTTPClientError @@ -293,7 +294,21 @@ def _warn_empty_username(self): ) return "" - traefik_api_hashed_password = Unicode() + traefik_api_hashed_password = Unicode( + config=True, + help=""" + Set the hashed password to use for the API + + If unspecified, `traefik_api_password` will be hashed with bcrypt. + ref: https://doc.traefik.io/traefik/middlewares/http/basicauth/ + """, + ) + + @default("traefik_api_hashed_password") + def _generate_htpassword(self): + return bcrypt.hashpw( + self.traefik_api_password.encode("utf8"), bcrypt.gensalt() + ).decode("ascii") check_route_timeout = Integer( 30, @@ -301,11 +316,6 @@ def _warn_empty_username(self): help="""Timeout (in seconds) when waiting for traefik to register an updated route.""", ) - def _generate_htpassword(self): - from passlib.hash import apr_md5_crypt - - self.traefik_api_hashed_password = apr_md5_crypt.hash(self.traefik_api_password) - async def _check_for_traefik_service(self, routespec, kind): """Check for an expected router or service in the Traefik API. @@ -508,7 +518,6 @@ async def _setup_traefik_static_config(self): async def _setup_traefik_dynamic_config(self): self.log.debug("Setting up traefik's dynamic config...") - self._generate_htpassword() api_url = urlparse(self.traefik_api_url) api_path = api_url.path if api_url.path.strip("/") else '/api' api_credentials = ( diff --git a/requirements.txt b/requirements.txt index ccb6dc38..1b9dbac0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp +bcrypt escapism jupyterhub>=0.9 -passlib toml