Allow restricting the remote control actions in specific windows
This commit is contained in:
parent
b81fb3c865
commit
572e920466
@ -462,12 +462,16 @@ class Boss:
|
|||||||
return response
|
return response
|
||||||
if not pcmd:
|
if not pcmd:
|
||||||
return response
|
return response
|
||||||
allowed_unconditionally = (
|
extra_data: Dict[str, Any] = {}
|
||||||
self.allow_remote_control == 'y' or (peer_id > 0 and self.allow_remote_control in ('socket-only', 'socket')) or
|
try:
|
||||||
(window and window.allow_remote_control))
|
allowed_unconditionally = (
|
||||||
|
self.allow_remote_control == 'y' or (peer_id > 0 and self.allow_remote_control in ('socket-only', 'socket')) or
|
||||||
|
(window and window.remote_control_allowed(pcmd, extra_data)))
|
||||||
|
except PermissionError:
|
||||||
|
return {'ok': False, 'error': 'Remote control disallowed by window specific password'}
|
||||||
if allowed_unconditionally:
|
if allowed_unconditionally:
|
||||||
return self._execute_remote_command(pcmd, window, peer_id)
|
return self._execute_remote_command(pcmd, window, peer_id)
|
||||||
q = is_cmd_allowed(pcmd, window, peer_id > 0, {})
|
q = is_cmd_allowed(pcmd, window, peer_id > 0, extra_data)
|
||||||
if q is True:
|
if q is True:
|
||||||
return self._execute_remote_command(pcmd, window, peer_id)
|
return self._execute_remote_command(pcmd, window, peer_id)
|
||||||
if q is None:
|
if q is None:
|
||||||
|
|||||||
@ -142,6 +142,27 @@ enabled in :file:`kitty.conf`). Note that any program with the right level of
|
|||||||
permissions can still write to the pipes of any other program on the same
|
permissions can still write to the pipes of any other program on the same
|
||||||
computer and therefore can control kitty. It can, however, be useful to block
|
computer and therefore can control kitty. It can, however, be useful to block
|
||||||
programs running on other computers (for example, over SSH) or as other users.
|
programs running on other computers (for example, over SSH) or as other users.
|
||||||
|
See :option:`--remote-control-password` for ways to restrict actions allowed by
|
||||||
|
remote control.
|
||||||
|
|
||||||
|
|
||||||
|
--remote-control-password
|
||||||
|
type=list
|
||||||
|
Restrict the actions remote control is allowed to take. This works like
|
||||||
|
:opt:`remote_control_password`. You can specify a password and list of actions
|
||||||
|
just as for :opt:`remote_control_password`. For example::
|
||||||
|
|
||||||
|
--remote-control-password '"my passphrase" get-* set-colors'
|
||||||
|
|
||||||
|
This password will be in effect for this window only.
|
||||||
|
Note that any passwords you have defined for :opt:`remote_control_password`
|
||||||
|
in :file:`kitty.conf` are also in effect. You can override them by using the same password here.
|
||||||
|
You can also disable all :opt:`remote_control_password` global passwords for this window, by using::
|
||||||
|
|
||||||
|
--remote-control-password '!'
|
||||||
|
|
||||||
|
This option only takes effect if :option:`--allow-remote-control`
|
||||||
|
is also specified. Can be specified multiple times to create multiple passwords.
|
||||||
|
|
||||||
|
|
||||||
--stdin-source
|
--stdin-source
|
||||||
@ -320,6 +341,7 @@ def load_watch_modules(watchers: Iterable[str]) -> Optional[Watchers]:
|
|||||||
class LaunchKwds(TypedDict):
|
class LaunchKwds(TypedDict):
|
||||||
|
|
||||||
allow_remote_control: bool
|
allow_remote_control: bool
|
||||||
|
remote_control_passwords: Optional[Dict[str, FrozenSet[str]]]
|
||||||
cwd_from: Optional[CwdRequest]
|
cwd_from: Optional[CwdRequest]
|
||||||
cwd: Optional[str]
|
cwd: Optional[str]
|
||||||
location: Optional[str]
|
location: Optional[str]
|
||||||
@ -384,8 +406,16 @@ def launch(
|
|||||||
tm = boss.active_tab_manager
|
tm = boss.active_tab_manager
|
||||||
opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None
|
opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None
|
||||||
env = get_env(opts, active_child)
|
env = get_env(opts, active_child)
|
||||||
|
remote_control_restrictions: Optional[Dict[str, FrozenSet[str]]] = None
|
||||||
|
if opts.allow_remote_control and opts.remote_control_password:
|
||||||
|
from kitty.options.utils import remote_control_password
|
||||||
|
remote_control_restrictions = {}
|
||||||
|
for rcp in opts.remote_control_password:
|
||||||
|
for pw, rcp_items in remote_control_password(rcp, {}):
|
||||||
|
remote_control_restrictions[pw] = rcp_items
|
||||||
kw: LaunchKwds = {
|
kw: LaunchKwds = {
|
||||||
'allow_remote_control': opts.allow_remote_control,
|
'allow_remote_control': opts.allow_remote_control,
|
||||||
|
'remote_control_passwords': remote_control_restrictions,
|
||||||
'cwd_from': None,
|
'cwd_from': None,
|
||||||
'cwd': None,
|
'cwd': None,
|
||||||
'location': None,
|
'location': None,
|
||||||
|
|||||||
@ -36,6 +36,7 @@ class Launch(RemoteCommand):
|
|||||||
hold/bool: Boolean indicating whether to keep window open after cmd exits
|
hold/bool: Boolean indicating whether to keep window open after cmd exits
|
||||||
location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window
|
location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window
|
||||||
allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window
|
allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window
|
||||||
|
remote_control_password/list/str: A list of remote control passwords
|
||||||
stdin_source/choices.none.@selection.@screen.@screen_scrollback.@alternate.@alternate_scrollback.\
|
stdin_source/choices.none.@selection.@screen.@screen_scrollback.@alternate.@alternate_scrollback.\
|
||||||
@first_cmd_output_on_screen.@last_cmd_output.@last_visited_cmd_output: Where to get stdin for the process from
|
@first_cmd_output_on_screen.@last_cmd_output.@last_visited_cmd_output: Where to get stdin for the process from
|
||||||
stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin
|
stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin
|
||||||
|
|||||||
@ -97,6 +97,7 @@ class PasswordAuthorizer:
|
|||||||
def __init__(self, auth_items: FrozenSet[str]) -> None:
|
def __init__(self, auth_items: FrozenSet[str]) -> None:
|
||||||
self.command_patterns = []
|
self.command_patterns = []
|
||||||
self.function_checkers = []
|
self.function_checkers = []
|
||||||
|
self.name = ''
|
||||||
for item in auth_items:
|
for item in auth_items:
|
||||||
if item.endswith('.py'):
|
if item.endswith('.py'):
|
||||||
path = os.path.abspath(resolve_custom_file(item))
|
path = os.path.abspath(resolve_custom_file(item))
|
||||||
|
|||||||
@ -10,8 +10,8 @@ from contextlib import suppress
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Deque, Dict, Generator, Iterable, Iterator, List, NamedTuple,
|
Any, Deque, Dict, FrozenSet, Generator, Iterable, Iterator, List,
|
||||||
Optional, Sequence, Set, Tuple, Union
|
NamedTuple, Optional, Sequence, Set, Tuple, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from .borders import Border, Borders
|
from .borders import Border, Borders
|
||||||
@ -441,6 +441,7 @@ class Tab: # {{{
|
|||||||
watchers: Optional[Watchers] = None,
|
watchers: Optional[Watchers] = None,
|
||||||
overlay_behind: bool = False,
|
overlay_behind: bool = False,
|
||||||
is_clone_launch: str = '',
|
is_clone_launch: str = '',
|
||||||
|
remote_control_passwords: Optional[Dict[str, FrozenSet[str]]] = None,
|
||||||
) -> Window:
|
) -> Window:
|
||||||
child = self.launch_child(
|
child = self.launch_child(
|
||||||
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
||||||
@ -449,8 +450,8 @@ class Tab: # {{{
|
|||||||
window = Window(
|
window = Window(
|
||||||
self, child, self.args, override_title=override_title,
|
self, child, self.args, override_title=override_title,
|
||||||
copy_colors_from=copy_colors_from, watchers=watchers,
|
copy_colors_from=copy_colors_from, watchers=watchers,
|
||||||
|
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
|
||||||
)
|
)
|
||||||
window.allow_remote_control = allow_remote_control
|
|
||||||
# Must add child before laying out so that resize_pty succeeds
|
# Must add child before laying out so that resize_pty succeeds
|
||||||
get_boss().add_child(window)
|
get_boss().add_child(window)
|
||||||
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind)
|
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind)
|
||||||
|
|||||||
@ -14,8 +14,8 @@ from gettext import gettext as _
|
|||||||
from itertools import chain
|
from itertools import chain
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING, Any, Callable, Deque, Dict, Iterable, List, NamedTuple,
|
TYPE_CHECKING, Any, Callable, Deque, Dict, FrozenSet, Iterable, List,
|
||||||
Optional, Pattern, Sequence, Tuple, Union
|
NamedTuple, Optional, Pattern, Sequence, Tuple, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from .child import ProcessDesc
|
from .child import ProcessDesc
|
||||||
@ -462,7 +462,6 @@ global_watchers = GlobalWatchers()
|
|||||||
class Window:
|
class Window:
|
||||||
|
|
||||||
window_custom_type: str = ''
|
window_custom_type: str = ''
|
||||||
allow_remote_control: bool = False
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -471,7 +470,9 @@ class Window:
|
|||||||
args: CLIOptions,
|
args: CLIOptions,
|
||||||
override_title: Optional[str] = None,
|
override_title: Optional[str] = None,
|
||||||
copy_colors_from: Optional['Window'] = None,
|
copy_colors_from: Optional['Window'] = None,
|
||||||
watchers: Optional[Watchers] = None
|
watchers: Optional[Watchers] = None,
|
||||||
|
allow_remote_control: bool = False,
|
||||||
|
remote_control_passwords: Optional[Dict[str, FrozenSet[str]]] = None,
|
||||||
):
|
):
|
||||||
if watchers:
|
if watchers:
|
||||||
self.watchers = watchers
|
self.watchers = watchers
|
||||||
@ -518,6 +519,25 @@ class Window:
|
|||||||
self.screen.copy_colors_from(copy_colors_from.screen)
|
self.screen.copy_colors_from(copy_colors_from.screen)
|
||||||
else:
|
else:
|
||||||
setup_colors(self.screen, opts)
|
setup_colors(self.screen, opts)
|
||||||
|
self.remote_control_passwords = remote_control_passwords
|
||||||
|
self.allow_remote_control = allow_remote_control
|
||||||
|
|
||||||
|
def remote_control_allowed(self, pcmd: Dict[str, Any], extra_data: Dict[str, Any]) -> bool:
|
||||||
|
if not self.allow_remote_control or not self.remote_control_passwords:
|
||||||
|
return False
|
||||||
|
pw = pcmd.get('password', '')
|
||||||
|
auth_items = self.remote_control_passwords.get(pw)
|
||||||
|
if pw == '!':
|
||||||
|
auth_items = None
|
||||||
|
if auth_items is None:
|
||||||
|
if '!' in self.remote_control_passwords:
|
||||||
|
raise PermissionError()
|
||||||
|
return False
|
||||||
|
from .remote_control import password_authorizer
|
||||||
|
pa = password_authorizer(auth_items)
|
||||||
|
if not pa.is_cmd_allowed(pcmd, self, False, extra_data):
|
||||||
|
raise PermissionError()
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def file_transmission_control(self) -> 'FileTransmission':
|
def file_transmission_control(self) -> 'FileTransmission':
|
||||||
@ -611,6 +631,7 @@ class Window:
|
|||||||
'default_title': self.default_title,
|
'default_title': self.default_title,
|
||||||
'title_stack': list(self.title_stack),
|
'title_stack': list(self.title_stack),
|
||||||
'allow_remote_control': self.allow_remote_control,
|
'allow_remote_control': self.allow_remote_control,
|
||||||
|
'remote_control_passwords': self.remote_control_passwords,
|
||||||
'cwd': self.child.current_cwd or self.child.cwd,
|
'cwd': self.child.current_cwd or self.child.cwd,
|
||||||
'env': self.child.environ,
|
'env': self.child.environ,
|
||||||
'cmdline': self.child.cmdline,
|
'cmdline': self.child.cmdline,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user