From b6c73aceb74cf6f3a376fc1223e9cc701a8f6ee5 Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Wed, 15 Jan 2025 00:46:29 +0200 Subject: pikvm/pikvm#1459: TOTP valid_window=5 --- kvmd/apps/__init__.py | 1 + kvmd/apps/kvmd/__init__.py | 1 + kvmd/apps/kvmd/auth.py | 6 ++++-- testenv/tests/apps/kvmd/test_auth.py | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index 091319ca..845da35a 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -370,6 +370,7 @@ def _get_config_scheme() -> dict: }, "totp": { + "valid_window": Option(1, type=functools.partial(valid_number, min=0, max=5)), "secret": { "file": Option("/etc/kvmd/totp.secret", type=valid_abs_path, if_empty=""), }, diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 088a62ef..800e5f5c 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -86,6 +86,7 @@ def main(argv: (list[str] | None)=None) -> None: external_kwargs=(config.auth.external._unpack(ignore=["type"]) if config.auth.external.type else {}), totp_secret_path=config.auth.totp.secret.file, + totp_valid_window=config.auth.totp.valid_window, ), info_manager=InfoManager(global_config), log_reader=(LogReader() if config.log_reader.enabled else None), diff --git a/kvmd/apps/kvmd/auth.py b/kvmd/apps/kvmd/auth.py index 008e8a4f..33beb5a6 100644 --- a/kvmd/apps/kvmd/auth.py +++ b/kvmd/apps/kvmd/auth.py @@ -34,7 +34,7 @@ from ...htserver import HttpExposed # ===== -class AuthManager: +class AuthManager: # pylint: disable=too-many-instance-attributes def __init__( self, enabled: bool, @@ -47,6 +47,7 @@ class AuthManager: external_type: str, external_kwargs: dict, + totp_valid_window: int, totp_secret_path: str, ) -> None: @@ -70,6 +71,7 @@ class AuthManager: self.__external_service = get_auth_service_class(external_type)(**external_kwargs) get_logger().info("Using external auth service %r", self.__external_service.get_plugin_name()) + self.__totp_valid_window = totp_valid_window self.__totp_secret_path = totp_secret_path self.__tokens: dict[str, str] = {} # {token: user} @@ -95,7 +97,7 @@ class AuthManager: secret = file.read().strip() if secret: code = passwd[-6:] - if not pyotp.TOTP(secret).verify(code): + if not pyotp.TOTP(secret).verify(code, valid_window=self.__totp_valid_window): get_logger().error("Got access denied for user %r by TOTP", user) return False passwd = passwd[:-6] diff --git a/testenv/tests/apps/kvmd/test_auth.py b/testenv/tests/apps/kvmd/test_auth.py index 4fa1c8ae..cef16399 100644 --- a/testenv/tests/apps/kvmd/test_auth.py +++ b/testenv/tests/apps/kvmd/test_auth.py @@ -69,6 +69,7 @@ async def _get_configured_manager( external_type=("htpasswd" if external_path else ""), external_kwargs=(_make_service_kwargs(external_path) if external_path else {}), + totp_valid_window=0, totp_secret_path="", ) @@ -200,6 +201,7 @@ async def test_ok__disabled() -> None: external_type="", external_kwargs={}, + totp_valid_window=0, totp_secret_path="", ) -- cgit v1.2.3