mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 09:10:30 +08:00
vnc: using usc auth
This commit is contained in:
parent
c8cf06ee8c
commit
46ef5fd46b
@ -1,12 +1,9 @@
|
|||||||
# This file describes the credentials for VNCAuth. The left part before arrow is a passphrase
|
# This file contains passwords for the legacy VNCAuth, one per line.
|
||||||
# for VNCAuth. The right part is username and password with which the user can access to KVMD API.
|
# The passwords are NOT encrypted.
|
||||||
# The arrow is used as a separator and shows the relationship of user registrations on the system.
|
|
||||||
#
|
#
|
||||||
# Never use the same passwords for VNC and IPMI users. This default configuration is shown here
|
# WARNING! The VNCAuth method is NOT secure and should not be used at all.
|
||||||
# for example only.
|
# But we support it for compatibility with some clients.
|
||||||
#
|
#
|
||||||
# If this file does not contain any entries, VNCAuth will be disabled and you will only be able
|
# NEVER use the same passwords for KVMD, IPMI and VNCAuth users.
|
||||||
# to login in using your KVMD username and password using VeNCrypt methods.
|
|
||||||
|
|
||||||
# pa$$phr@se -> admin:password
|
admin
|
||||||
admin -> admin:admin
|
|
||||||
|
|||||||
@ -364,6 +364,7 @@ def _get_config_scheme() -> dict:
|
|||||||
"usc": {
|
"usc": {
|
||||||
"users": Option([
|
"users": Option([
|
||||||
"kvmd-ipmi",
|
"kvmd-ipmi",
|
||||||
|
"kvmd-vnc",
|
||||||
], type=valid_users_list), # PiKVM username has a same regex as a UNIX username
|
], type=valid_users_list), # PiKVM username has a same regex as a UNIX username
|
||||||
"groups": Option([], type=valid_users_list), # groupname has a same regex as a username
|
"groups": Option([], type=valid_users_list), # groupname has a same regex as a username
|
||||||
},
|
},
|
||||||
@ -798,8 +799,8 @@ def _get_config_scheme() -> dict:
|
|||||||
|
|
||||||
"auth": {
|
"auth": {
|
||||||
"vncauth": {
|
"vncauth": {
|
||||||
"enabled": Option(False, type=valid_bool),
|
"enabled": Option(False, type=valid_bool, unpack_as="vncpass_enabled"),
|
||||||
"file": Option("/etc/kvmd/vncpasswd", type=valid_abs_file, unpack_as="path"),
|
"file": Option("/etc/kvmd/vncpasswd", type=valid_abs_file, unpack_as="vncpass_path"),
|
||||||
},
|
},
|
||||||
"vencrypt": {
|
"vencrypt": {
|
||||||
"enabled": Option(True, type=valid_bool, unpack_as="vencrypt_enabled"),
|
"enabled": Option(True, type=valid_bool, unpack_as="vencrypt_enabled"),
|
||||||
|
|||||||
@ -30,7 +30,6 @@ from ... import htclient
|
|||||||
|
|
||||||
from .. import init
|
from .. import init
|
||||||
|
|
||||||
from .vncauth import VncAuthManager
|
|
||||||
from .server import VncServer
|
from .server import VncServer
|
||||||
|
|
||||||
|
|
||||||
@ -76,8 +75,8 @@ def main(argv: (list[str] | None)=None) -> None:
|
|||||||
|
|
||||||
kvmd=KvmdClient(user_agent=user_agent, **config.kvmd._unpack()),
|
kvmd=KvmdClient(user_agent=user_agent, **config.kvmd._unpack()),
|
||||||
streamers=streamers,
|
streamers=streamers,
|
||||||
vnc_auth_manager=VncAuthManager(**config.auth.vncauth._unpack()),
|
|
||||||
|
|
||||||
**config.server.keepalive._unpack(),
|
**config.server.keepalive._unpack(),
|
||||||
|
**config.auth.vncauth._unpack(),
|
||||||
**config.auth.vencrypt._unpack(),
|
**config.auth.vencrypt._unpack(),
|
||||||
).run()
|
).run()
|
||||||
|
|||||||
@ -67,7 +67,8 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
name: str,
|
name: str,
|
||||||
scroll_rate: int,
|
scroll_rate: int,
|
||||||
allow_cut_after: float,
|
allow_cut_after: float,
|
||||||
vnc_passwds: list[str],
|
|
||||||
|
vncpasses: set[str],
|
||||||
vencrypt: bool,
|
vencrypt: bool,
|
||||||
none_auth_only: bool,
|
none_auth_only: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -84,7 +85,8 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
self.__name = name
|
self.__name = name
|
||||||
self.__scroll_rate = scroll_rate
|
self.__scroll_rate = scroll_rate
|
||||||
self.__allow_cut_after = allow_cut_after
|
self.__allow_cut_after = allow_cut_after
|
||||||
self.__vnc_passwds = vnc_passwds
|
|
||||||
|
self.__vncpasses = vncpasses
|
||||||
self.__vencrypt = vencrypt
|
self.__vencrypt = vencrypt
|
||||||
self.__none_auth_only = none_auth_only
|
self.__none_auth_only = none_auth_only
|
||||||
|
|
||||||
@ -145,10 +147,10 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
async def _authorize_userpass(self, user: str, passwd: str) -> bool:
|
async def _authorize_userpass(self, user: str, passwd: str) -> bool:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def _on_authorized_vnc_passwd(self, passwd: str) -> str:
|
async def _on_authorized_vncpass(self) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def _on_authorized_none(self) -> bool:
|
async def _authorize_none(self) -> bool:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
@ -260,7 +262,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
sec_types[19] = ("VeNCrypt", self.__handshake_security_vencrypt)
|
sec_types[19] = ("VeNCrypt", self.__handshake_security_vencrypt)
|
||||||
if self.__none_auth_only:
|
if self.__none_auth_only:
|
||||||
sec_types[1] = ("None", self.__handshake_security_none)
|
sec_types[1] = ("None", self.__handshake_security_none)
|
||||||
elif self.__vnc_passwds:
|
elif self.__vncpasses:
|
||||||
sec_types[2] = ("VNCAuth", self.__handshake_security_vnc_auth)
|
sec_types[2] = ("VNCAuth", self.__handshake_security_vnc_auth)
|
||||||
|
|
||||||
if not sec_types:
|
if not sec_types:
|
||||||
@ -306,7 +308,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
if self.__x509_cert_path:
|
if self.__x509_cert_path:
|
||||||
auth_types[262] = ("VeNCrypt/X509Plain", 2, self.__handshake_security_vencrypt_userpass)
|
auth_types[262] = ("VeNCrypt/X509Plain", 2, self.__handshake_security_vencrypt_userpass)
|
||||||
auth_types[259] = ("VeNCrypt/TLSPlain", 1, self.__handshake_security_vencrypt_userpass)
|
auth_types[259] = ("VeNCrypt/TLSPlain", 1, self.__handshake_security_vencrypt_userpass)
|
||||||
if self.__vnc_passwds:
|
if self.__vncpasses:
|
||||||
# Некоторые клиенты не умеют работать с нешифрованными соединениями внутри VeNCrypt:
|
# Некоторые клиенты не умеют работать с нешифрованными соединениями внутри VeNCrypt:
|
||||||
# - https://github.com/LibVNC/libvncserver/issues/458
|
# - https://github.com/LibVNC/libvncserver/issues/458
|
||||||
# - https://bugzilla.redhat.com/show_bug.cgi?id=692048
|
# - https://bugzilla.redhat.com/show_bug.cgi?id=692048
|
||||||
@ -356,7 +358,7 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def __handshake_security_none(self) -> None:
|
async def __handshake_security_none(self) -> None:
|
||||||
allow = await self._on_authorized_none()
|
allow = await self._authorize_none()
|
||||||
await self.__handshake_security_send_result(
|
await self.__handshake_security_send_result(
|
||||||
allow=allow,
|
allow=allow,
|
||||||
allow_msg="NoneAuth access granted",
|
allow_msg="NoneAuth access granted",
|
||||||
@ -368,20 +370,19 @@ class RfbClient(RfbClientStream): # pylint: disable=too-many-instance-attribute
|
|||||||
challenge = rfb_make_challenge()
|
challenge = rfb_make_challenge()
|
||||||
await self._write_struct("VNCAuth challenge request", "", challenge)
|
await self._write_struct("VNCAuth challenge request", "", challenge)
|
||||||
|
|
||||||
user = ""
|
allow = False
|
||||||
response = (await self._read_struct("VNCAuth challenge response", "16s"))[0]
|
response = (await self._read_struct("VNCAuth challenge response", "16s"))[0]
|
||||||
for passwd in self.__vnc_passwds:
|
for passwd in self.__vncpasses:
|
||||||
passwd_bytes = passwd.encode("utf-8", errors="ignore")
|
passwd_bytes = passwd.encode("utf-8", errors="ignore")
|
||||||
if rfb_encrypt_challenge(challenge, passwd_bytes) == response:
|
if rfb_encrypt_challenge(challenge, passwd_bytes) == response:
|
||||||
user = await self._on_authorized_vnc_passwd(passwd)
|
await self._on_authorized_vncpass()
|
||||||
if user:
|
allow = True
|
||||||
assert user == user.strip()
|
|
||||||
break
|
break
|
||||||
|
|
||||||
await self.__handshake_security_send_result(
|
await self.__handshake_security_send_result(
|
||||||
allow=bool(user),
|
allow=allow,
|
||||||
allow_msg=f"VNCAuth access granted for user {user!r}",
|
allow_msg="VNCAuth access granted",
|
||||||
deny_msg="VNCAuth access denied (user not found)",
|
deny_msg="VNCAuth access denied (passwd not found)",
|
||||||
deny_reason="Invalid password",
|
deny_reason="Invalid password",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import dataclasses
|
|||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
import async_lru
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
@ -55,9 +56,6 @@ from .rfb import RfbClient
|
|||||||
from .rfb.stream import rfb_format_remote
|
from .rfb.stream import rfb_format_remote
|
||||||
from .rfb.errors import RfbError
|
from .rfb.errors import RfbError
|
||||||
|
|
||||||
from .vncauth import VncAuthKvmdCredentials
|
|
||||||
from .vncauth import VncAuthManager
|
|
||||||
|
|
||||||
from .render import make_text_jpeg
|
from .render import make_text_jpeg
|
||||||
|
|
||||||
|
|
||||||
@ -89,14 +87,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
kvmd: KvmdClient,
|
kvmd: KvmdClient,
|
||||||
streamers: list[BaseStreamerClient],
|
streamers: list[BaseStreamerClient],
|
||||||
|
|
||||||
vnc_credentials: dict[str, VncAuthKvmdCredentials],
|
vncpasses: set[str],
|
||||||
vencrypt: bool,
|
vencrypt: bool,
|
||||||
none_auth_only: bool,
|
none_auth_only: bool,
|
||||||
|
|
||||||
shared_params: _SharedParams,
|
shared_params: _SharedParams,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self.__vnc_credentials = vnc_credentials
|
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
reader=reader,
|
reader=reader,
|
||||||
writer=writer,
|
writer=writer,
|
||||||
@ -106,7 +103,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
x509_key_path=x509_key_path,
|
x509_key_path=x509_key_path,
|
||||||
scroll_rate=scroll_rate,
|
scroll_rate=scroll_rate,
|
||||||
allow_cut_after=allow_cut_after,
|
allow_cut_after=allow_cut_after,
|
||||||
vnc_passwds=list(vnc_credentials),
|
vncpasses=vncpasses,
|
||||||
vencrypt=vencrypt,
|
vencrypt=vencrypt,
|
||||||
none_auth_only=none_auth_only,
|
none_auth_only=none_auth_only,
|
||||||
**dataclasses.asdict(shared_params),
|
**dataclasses.asdict(shared_params),
|
||||||
@ -321,19 +318,17 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
# =====
|
# =====
|
||||||
|
|
||||||
async def _authorize_userpass(self, user: str, passwd: str) -> bool:
|
async def _authorize_userpass(self, user: str, passwd: str) -> bool:
|
||||||
self.__kvmd_session = self.__kvmd.make_session(user, passwd)
|
self.__kvmd_session = self.__kvmd.make_session()
|
||||||
if (await self.__kvmd_session.auth.check()):
|
if (await self.__kvmd_session.auth.check(user, passwd)):
|
||||||
self.__stage1_authorized.set_passed()
|
self.__stage1_authorized.set_passed()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def _on_authorized_vnc_passwd(self, passwd: str) -> str:
|
async def _on_authorized_vncpass(self) -> None:
|
||||||
kc = self.__vnc_credentials[passwd]
|
self.__kvmd_session = self.__kvmd.make_session()
|
||||||
if (await self._authorize_userpass(kc.user, kc.passwd)):
|
self.__stage1_authorized.set_passed()
|
||||||
return kc.user
|
|
||||||
return ""
|
|
||||||
|
|
||||||
async def _on_authorized_none(self) -> bool:
|
async def _authorize_none(self) -> bool:
|
||||||
return (await self._authorize_userpass("", ""))
|
return (await self._authorize_userpass("", ""))
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
@ -461,6 +456,8 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
x509_cert_path: str,
|
x509_cert_path: str,
|
||||||
x509_key_path: str,
|
x509_key_path: str,
|
||||||
|
|
||||||
|
vncpass_enabled: bool,
|
||||||
|
vncpass_path: str,
|
||||||
vencrypt_enabled: bool,
|
vencrypt_enabled: bool,
|
||||||
|
|
||||||
desired_fps: int,
|
desired_fps: int,
|
||||||
@ -471,7 +468,6 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
kvmd: KvmdClient,
|
kvmd: KvmdClient,
|
||||||
streamers: list[BaseStreamerClient],
|
streamers: list[BaseStreamerClient],
|
||||||
vnc_auth_manager: VncAuthManager,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
self.__host = network.get_listen_host(host)
|
self.__host = network.get_listen_host(host)
|
||||||
@ -481,7 +477,8 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
keymap_name = os.path.basename(keymap_path)
|
keymap_name = os.path.basename(keymap_path)
|
||||||
symmap = build_symmap(keymap_path)
|
symmap = build_symmap(keymap_path)
|
||||||
|
|
||||||
self.__vnc_auth_manager = vnc_auth_manager
|
self.__vncpass_enabled = vncpass_enabled
|
||||||
|
self.__vncpass_path = vncpass_path
|
||||||
|
|
||||||
shared_params = _SharedParams()
|
shared_params = _SharedParams()
|
||||||
|
|
||||||
@ -508,8 +505,8 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, timeout) # type: ignore
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, timeout) # type: ignore
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with kvmd.make_session("", "") as kvmd_session:
|
async with kvmd.make_session() as kvmd_session:
|
||||||
none_auth_only = await kvmd_session.auth.check()
|
none_auth_only = await kvmd_session.auth.check("", "")
|
||||||
except (aiohttp.ClientError, asyncio.TimeoutError) as ex:
|
except (aiohttp.ClientError, asyncio.TimeoutError) as ex:
|
||||||
logger.error("%s [entry]: Can't check KVMD auth mode: %s", remote, tools.efmt(ex))
|
logger.error("%s [entry]: Can't check KVMD auth mode: %s", remote, tools.efmt(ex))
|
||||||
return
|
return
|
||||||
@ -529,9 +526,9 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
allow_cut_after=allow_cut_after,
|
allow_cut_after=allow_cut_after,
|
||||||
kvmd=kvmd,
|
kvmd=kvmd,
|
||||||
streamers=streamers,
|
streamers=streamers,
|
||||||
vnc_credentials=(await self.__vnc_auth_manager.read_credentials())[0],
|
vncpasses=(await self.__read_vncpasses()),
|
||||||
none_auth_only=none_auth_only,
|
|
||||||
vencrypt=vencrypt_enabled,
|
vencrypt=vencrypt_enabled,
|
||||||
|
none_auth_only=none_auth_only,
|
||||||
shared_params=shared_params,
|
shared_params=shared_params,
|
||||||
).run()
|
).run()
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -542,9 +539,6 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
self.__handle_client = handle_client
|
self.__handle_client = handle_client
|
||||||
|
|
||||||
async def __inner_run(self) -> None:
|
async def __inner_run(self) -> None:
|
||||||
if not (await self.__vnc_auth_manager.read_credentials())[1]:
|
|
||||||
raise SystemExit(1)
|
|
||||||
|
|
||||||
get_logger(0).info("Listening VNC on TCP [%s]:%d ...", self.__host, self.__port)
|
get_logger(0).info("Listening VNC on TCP [%s]:%d ...", self.__host, self.__port)
|
||||||
(family, _, _, _, addr) = socket.getaddrinfo(self.__host, self.__port, type=socket.SOCK_STREAM)[0]
|
(family, _, _, _, addr) = socket.getaddrinfo(self.__host, self.__port, type=socket.SOCK_STREAM)[0]
|
||||||
with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as sock:
|
with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as sock:
|
||||||
@ -561,6 +555,21 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
async with server:
|
async with server:
|
||||||
await server.serve_forever()
|
await server.serve_forever()
|
||||||
|
|
||||||
|
@async_lru.alru_cache(maxsize=1, ttl=1)
|
||||||
|
async def __read_vncpasses(self) -> set[str]:
|
||||||
|
if self.__vncpass_enabled:
|
||||||
|
try:
|
||||||
|
vncpasses: set[str] = set()
|
||||||
|
for (_, line) in tools.passwds_splitted(await aiotools.read_file(self.__vncpass_path)):
|
||||||
|
if " -> " in line: # Compatibility with old ipmipasswd file format
|
||||||
|
line = line.split(" -> ", 1)[0]
|
||||||
|
if len(line.strip()) > 0:
|
||||||
|
vncpasses.add(line)
|
||||||
|
return vncpasses
|
||||||
|
except Exception:
|
||||||
|
get_logger(0).exception("Unhandled exception while reading VNCAuth passwd file")
|
||||||
|
return set()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
aiotools.run(self.__inner_run())
|
aiotools.run(self.__inner_run())
|
||||||
get_logger().info("Bye-bye")
|
get_logger().info("Bye-bye")
|
||||||
|
|||||||
@ -1,86 +0,0 @@
|
|||||||
# ========================================================================== #
|
|
||||||
# #
|
|
||||||
# KVMD - The main PiKVM daemon. #
|
|
||||||
# #
|
|
||||||
# Copyright (C) 2020 Maxim Devaev <mdevaev@gmail.com> #
|
|
||||||
# #
|
|
||||||
# This program is free software: you can redistribute it and/or modify #
|
|
||||||
# it under the terms of the GNU General Public License as published by #
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or #
|
|
||||||
# (at your option) any later version. #
|
|
||||||
# #
|
|
||||||
# This program is distributed in the hope that it will be useful, #
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
||||||
# GNU General Public License for more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License #
|
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
# #
|
|
||||||
# ========================================================================== #
|
|
||||||
|
|
||||||
|
|
||||||
import dataclasses
|
|
||||||
|
|
||||||
from ...logging import get_logger
|
|
||||||
|
|
||||||
from ... import aiotools
|
|
||||||
|
|
||||||
|
|
||||||
# =====
|
|
||||||
class VncAuthError(Exception):
|
|
||||||
def __init__(self, path: str, lineno: int, msg: str) -> None:
|
|
||||||
super().__init__(f"Syntax error at {path}:{lineno}: {msg}")
|
|
||||||
|
|
||||||
|
|
||||||
# =====
|
|
||||||
@dataclasses.dataclass(frozen=True)
|
|
||||||
class VncAuthKvmdCredentials:
|
|
||||||
user: str
|
|
||||||
passwd: str
|
|
||||||
|
|
||||||
|
|
||||||
class VncAuthManager:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
enabled: bool,
|
|
||||||
) -> None:
|
|
||||||
|
|
||||||
self.__path = path
|
|
||||||
self.__enabled = enabled
|
|
||||||
|
|
||||||
async def read_credentials(self) -> tuple[dict[str, VncAuthKvmdCredentials], bool]:
|
|
||||||
if self.__enabled:
|
|
||||||
try:
|
|
||||||
return (await self.__inner_read_credentials(), True)
|
|
||||||
except VncAuthError as ex:
|
|
||||||
get_logger(0).error(str(ex))
|
|
||||||
except Exception:
|
|
||||||
get_logger(0).exception("Unhandled exception while reading VNCAuth passwd file")
|
|
||||||
return ({}, (not self.__enabled))
|
|
||||||
|
|
||||||
async def __inner_read_credentials(self) -> dict[str, VncAuthKvmdCredentials]:
|
|
||||||
lines = (await aiotools.read_file(self.__path)).split("\n")
|
|
||||||
credentials: dict[str, VncAuthKvmdCredentials] = {}
|
|
||||||
for (lineno, line) in enumerate(lines):
|
|
||||||
if len(line.strip()) == 0 or line.lstrip().startswith("#"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if " -> " not in line:
|
|
||||||
raise VncAuthError(self.__path, lineno, "Missing ' -> ' operator")
|
|
||||||
|
|
||||||
(vnc_passwd, kvmd_userpass) = map(str.lstrip, line.split(" -> ", 1))
|
|
||||||
if ":" not in kvmd_userpass:
|
|
||||||
raise VncAuthError(self.__path, lineno, "Missing ':' operator in KVMD credentials (right part)")
|
|
||||||
|
|
||||||
(kvmd_user, kvmd_passwd) = kvmd_userpass.split(":")
|
|
||||||
kvmd_user = kvmd_user.strip()
|
|
||||||
if len(kvmd_user) == 0:
|
|
||||||
raise VncAuthError(self.__path, lineno, "Empty KVMD user (right part)")
|
|
||||||
|
|
||||||
if vnc_passwd in credentials:
|
|
||||||
raise VncAuthError(self.__path, lineno, "Duplicating VNC password (left part)")
|
|
||||||
|
|
||||||
credentials[vnc_passwd] = VncAuthKvmdCredentials(kvmd_user, kvmd_passwd)
|
|
||||||
return credentials
|
|
||||||
@ -56,16 +56,22 @@ class _BaseApiPart:
|
|||||||
|
|
||||||
|
|
||||||
class _AuthApiPart(_BaseApiPart):
|
class _AuthApiPart(_BaseApiPart):
|
||||||
async def check(self) -> bool:
|
async def check(self, user: str, passwd: str) -> bool:
|
||||||
session = self._ensure_http_session()
|
session = self._ensure_http_session()
|
||||||
try:
|
try:
|
||||||
async with session.get("/auth/check") as resp:
|
async with session.get("/auth/check", headers={
|
||||||
|
"X-KVMD-User": user,
|
||||||
|
"X-KVMD-Passwd": passwd,
|
||||||
|
}) as resp:
|
||||||
|
|
||||||
htclient.raise_not_200(resp)
|
htclient.raise_not_200(resp)
|
||||||
return True
|
return (resp.status == 200) # Just for my paranoia
|
||||||
|
|
||||||
except aiohttp.ClientResponseError as ex:
|
except aiohttp.ClientResponseError as ex:
|
||||||
if ex.status in [400, 401, 403]:
|
if ex.status in [400, 401, 403]:
|
||||||
return False
|
return False
|
||||||
raise
|
raise
|
||||||
|
raise RuntimeError("We should't be here")
|
||||||
|
|
||||||
|
|
||||||
class _StreamerApiPart(_BaseApiPart):
|
class _StreamerApiPart(_BaseApiPart):
|
||||||
@ -216,11 +222,5 @@ class KvmdClientSession(BaseHttpClientSession):
|
|||||||
|
|
||||||
|
|
||||||
class KvmdClient(BaseHttpClient):
|
class KvmdClient(BaseHttpClient):
|
||||||
def make_session(self, user: str="", passwd: str="") -> KvmdClientSession:
|
def make_session(self) -> KvmdClientSession:
|
||||||
headers: (dict[str, str] | None) = None
|
return KvmdClientSession(self._make_http_session)
|
||||||
if user:
|
|
||||||
headers = {
|
|
||||||
"X-KVMD-User": user,
|
|
||||||
"X-KVMD-Passwd": passwd,
|
|
||||||
}
|
|
||||||
return KvmdClientSession(lambda: self._make_http_session(headers))
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user