Open RedBearAK opened 2 years ago
This does need addressing and is an active concern of mine, particularly before getting around and packaging Kinto.
I think at the time I was concerned with adding uinput to an additional group, but reading back over that thread it probably is a better alternative to running sudo and using the limitedadmin's method I used. By adding uinput to a group which is shared with the user it would prevent any other processes from somehow spawning with sudo level access.
I guess at the time I figured it was a temporary situation and less permanent than adding a new user group and permissions.. but really modifying sudoers.d was not any prettier.
@rbreaves
I'm going to try to verify the technique when I get a chance. If it really works we could finally have Kinto running on those distros like antiX and Gentoo that pitched a huge fit and broke with the current Kinto sudoers
setup. That would be huge.
@rbreaves
It's a bit rough around the edges (had to write a quick shell script to at least killall and start xkeysnail), but I can report that to the best of my knowledge I am now running Kinto without sudo. Everything is working normally, including all my special numpad fixes and tab navigation in different types of apps.
Reconstructing the launch()
commands without runuser
makes for a much less complicated setup, as you can see below. The current working directory now from the perspective of the config file is my user directory, so the path to the scripts just needs the "/.config/kinto/scripts/" added on.
Note: You have to be very careful with the launch()
syntax or you'll break xkeysnail and wind up in a very odd state with a strange task switcher dialog and no real keyboard input, including in a terminal, so you'll want to always have a mouse-usable way to restart xkeysnail if you mess up the syntax. This has actually been true all along as I've been messing with launch()
, it has nothing to do with running as user vs. running as sudo
. But I had to make a new desktop entry file to launch my xkeysnail starter script just to make sure I could easily restart xkeysnail from an icon on my Plank dock every time I messed up the launch()
syntax in some way.
Of course, until I remove it, the Kinto indicator in the top bar will still restart the service in the usual way, but neither that nor the Kinto GUI app will reflect that Kinto is running if I only use my user script to start xkeysnail. I kind of expected that.
I should remember to test what happens when I try to switch users without logging out. For some reason that option in the GNOME status menu isn't doing anything at the moment. I should also try to remember to test whether xkeysnail will still hang around and eat the first character of the password after logging out. I'm kind of expecting it to shut down when I log out of the user session.
# needs "import os"
_cwd = os.getcwd()
_path = _cwd + '/.config/kinto/scripts/'
# None referenced here originally
# - but remote clients and VM software ought to be set here
# These are the typical remaps for ALL GUI based apps
define_keymap(lambda wm_class: wm_class.casefold() not in remotes,{
####################################################################################################
############################ START OF SCREENSHOT LAUNCH SHORTCUTS ##############################
####################################################################################################
K("RC-Shift-M-b"): launch([_path + "beep.py", "error"]), # launch beepy error beep
K("RC-Shift-KEY_3"): launch([_path + "screenshot.sh", "desktop"]), # Cmd+Shift+3 # screenshot: entire desktop (all attached displays, macOS default)
K("M-RC-Shift-KEY_3"): launch([_path + "screenshot.sh", "screen"]), # Opt+Cmd+Shift+3 # screenshot: screen with focus
K("RC-Shift-KEY_4"): launch([_path + "screenshot.sh", "area"]), # Cmd+Shift+4 # screenshot: select area
K("M-RC-Shift-KEY_4"): launch([_path + "screenshot.sh", "window"]), # Opt+Cmd+Shift+4 # screenshot: window with focus
K("RC-Shift-KEY_5"): launch([_path + "screenshot.sh", "show-app"]), # Cmd+Shift+5 # screenshot: show screenshot tool (app)
####################################################################################################
# K("RC-Shift-M-b"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "beep.py"]), # launch beepy error beep
# K("RC-Shift-KEY_3"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "screenshot.sh desktop jpg"]), # Cmd+Shift+3 # screenshot: entire desktop (all attached displays, macOS default)
# K("M-RC-Shift-KEY_3"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "screenshot.sh screen"]), # Opt+Cmd+Shift+3 # screenshot: screen with focus
# K("RC-Shift-KEY_4"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "screenshot.sh area"]), # Cmd+Shift+4 # screenshot: select area
# K("M-RC-Shift-KEY_4"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "screenshot.sh window"]), # Opt+Cmd+Shift+4 # screenshot: window with focus
# K("RC-Shift-KEY_5"): launch(
# ["runuser", "-l", _user, "-c",
# _path + "screenshot.sh show-app"]), # Cmd+Shift+5 # screenshot: show screenshot tool (app)
####################################################################################################
############################ END OF SCREENSHOT LAUNCH SHORTCUTS ##############################
####################################################################################################
#
@rbreaves
Confirming that there seems to be no more "character eating" at the login screen. Seems running as user does away entirely with the issue of xkeysnail remaining in memory after user logout. As expected.
The "Switch User..." option is still inexplicably not doing anything, for some reason, so I can't test that scenario to see if it will still interfere with the keyboard at the login screen if my user is still logged in.
@rbreaves
This is just to confirm that "switching users" is still a problem when running xkeysnail
as user. I've tried two different methods: Running as a "user" systemd
service that activates when the user logs in and deactivates when the user logs out, and also just running a script activated with a .desktop
entry file at startup. Either way, if you try to "switch user" without logging out, xkeysnail
interferes with the keyboard at the login screen, so it's impossible to log back in without an onscreen keyboard, which fortunately Ubuntu provides.
Still haven't found a way around this, but it will only affect users who attempt to switch accounts without logging out. That's fairly rare even on macOS.
I was hoping a "user" systemd
service unit approach would solve this drawback, but no such luck. The service just keeps running because the user hasn't logged out, whether it's systemd
or just a normal user script running it.
Just a datapoint this is mostly handled on some distros out of the box. On Arch /dev/input/event*
are already added to the input
group... and because I happened to have Steam installed it (for some reason) adds a udev rule that gives ACL permissions to /dev/uinput
to the logged in user... so I merely needed to add myself to the input
group and then xkeysnail
works out of the box, as my user, with no need to be root.
I can't speak to using multiple local users as personally that isn't a need that I have.
@joshgoebel
Interesting.
I assume that when you typed "input group" each time you meant uinput
group?
No, on Arch the group name is input
.
No, on Arch the group name is
input
.
@joshgoebel
Ah, my mistake. So the ACL policy is actually for a group called input
. Got it.
No... ugh... I had a longer message but then I lost power and lost it... There are two udev rules, one for /dev/input/* and another for /dev/uinput... /dev/input is handled by normal group permissions while /dev/uinput is handled by the fancier systemd ACL stuff... (because I have Steam installed an it adds a rule for that)
But someone setting it up on a different distro could get by fine without the ACL stuff I'd imagine... just do it all with group permissions.
I found my original message, it got posted to the wrong thread, not lost in the power issue.
Only a single datapoint here, but half this work is done for you if you're on some distros (I'm on Arch Linux). All my /dev/input/event*
devices are already owned by input
, I merely had to add myself to the input
group. /dev/uinput
however is not, but for some reason I can magically access it just fine. That part still confuses me.
I assume the magic is from:
/usr/lib/udev/rules.d/50-udev-default.rules:
SUBSYSTEM=="input", GROUP="input"
But in any case widening the permissions on /dev/input
and /dev/uinput
is way better than running as root. Though of course anything that can log (or generate) keystrokes is always going to be a security concern for all sorts of other reasons...
Yep.. I do wish there was a better way of limiting input/uinput to the app itself and not via root or by widening the user permission. While we say it is better than running xkeysnail as root.. it would also then open up any 3rd party app running as the local user to having the ability to eavesdrop on the input/uinput would it not?
So in a way I feel that widening the local users ability to see into the input devices creates a greater security threat than running xkeysnail as an elevated root user. Now if we created a separate user level account just for xkeysnail that can bridge the gap that might be better - you're then not giving the local user full access to uinput, nor are you running xkeysnail as root. You have a specific local user created just for xkeysnail.
Also a multi-user terminal linux like environment isn't a real concern with xkeysnail because it depends on physical keyboards only. You have to roll back to my 1.0.7 build to find the xkbmap version of it that would even work inside of vnc server or xrdp server.
Just to note - I would love to figure out how to fix vnc server and xrdp to work - but I have no idea.. emulating a physical hid keyboard device would be required & to ignore input from xinput or redirect it at least. Feels like remote sessions on Linux are not designed to have physical input devices interacting with it by design.
any 3rd party app running as the local user to having the ability to eavesdrop on the input/uinput would it not?
Possibly, though I'm not sure if that would be possible after we've grabbed the input exclusively... but I take your point. Definitely food for thought as I was considering this approach locally...
So in a way I feel that widening the local users ability to see into the input devices creates a greater security threat than running xkeysnail as an elevated root user.
I guess you're arguing for "trust one app" vs "trust every app"... so in that way I'd have to agree. Just the damage that each could do still varies greatly.
Now if we created a separate user level account just for xkeysnail that can bridge the gap that might be better
Might as well go ahead and do that since you're going to all the trouble to run it as a separate systemd service anyways... the question is then how standardized is the udev stuff across distros, could you just drop a single file into a few folders and create the groups you need...
Just to note - I would love to figure out how to fix vnc server and xrdp to work
It wouldn't surprise me if that simply isn't possible... the keystrokes aren't going to the Linux IO layer, they are going over the network and into VNC or a RDP server which is interpreting them according to it's own idea of what keyboard input looks like...
So I've been pushing forward with running as a semi-privileged use account. I'm using the follow udev rules:
# group based
SUBSYSTEM=="input", GROUP="input"
KERNEL=="uinput", SUBSYSTEM=="misc", GROUP="input"
# or ACL based
KERNEL=="uinput", SUBSYSTEM=="misc", RUN+="/usr/bin/setfacl -m user:keymapper:rw /dev/uinput"
My keymapper
user is part of the input group, but that might still change... if I go ACLs all the way then the groups start to matter less, the permissions would just be added explicitly to the keymapper user and noone else. This might be slightly better since for some reason input
doesn't get access to /dev/uinput
by default - so going the input
group route for me (on Arch) would involve changing that (for better or worse)... so mater to matters at hand...
Now I'm confronted with:
Xlib.error.DisplayConnectionError: Can't connect to display ":0": b'Authorization required, but no authorization protocol specified\n'
Because keymapper
doesn't have permission to hack into my X session... this can of course be resolved by my local user with:
xhost +SI:localuser:keymapper
And after that it should all be gravy... I'm less concerned with launching things (I'm not sure we should be a launcher) - so for me that's a problem for another day... so the question is how and when does this xhost
command get run across the various environs that Kinto would be used it? If someone has systemd would it make sense to have a tiny user service just to handle that part of things, that once a user logs in it gives X permission to keymapper? Can that even be done, is that how the sequencing works?
Either way, if you try to "switch user" without logging out, xkeysnail interferes with the keyboard at the login screen,
How and why? Do you have the log? Is it just the matter of not having access to X? If so that's an easy fix... when the service doesn't have access to X it should just turn off all keymaps OR all conditional keymaps and pass all input thru unmolested... that's all it could do at a login screen anyways since there is no user, no active X session, etc...
Or if the problem is you were running it as YOUR user and your user had ACL permissions to input stuff (permissions that changed at logout) - that could be a little more problematic... probably fixable, but I'm not sure that's something we should support anyways because of the security issues already mentioned.
@joshgoebel
I have no understanding of how X or xhost
works, or why it works under one WM/DE and not another. The first time I ran into the issue was with trying to use a very old, lightweight desktop option called IceWM, installed on Linux Mint (XFCE variant). Everything would be fine in XFCE, then I'd log out and log into IceWM and Kinto wouldn't run, until I manually ran the xhost
command in a terminal after login.
Same thing happened to a user running xmonad
on Fedora. I tested it myself in a VM. The Kinto service simply wouldn't/couldn't load up in the xmonad environment without first running the xhost
command somehow from within xmonad's environment. I believe the user set it up as a startup command, and then Kinto would load up fine after that. In the GNOME desktop there was no such problem.
The errors in the Kinto log are not always the same, but it usually seems to hinge on the xhost
command. I believe there was a similar issue with OpenSUSE, where Kinto would run fine right after install, then fail after rebooting, until the xhost
command was run again after logging in. That was in GNOME, I think.
No idea what makes certain environments prone to this issue.
Either way, if you try to "switch user" without logging out, xkeysnail interferes with the keyboard at the login screen,
How and why? Do you have the log? Is it just the matter of not having access to X? If so that's an easy fix... when the service doesn't have access to X it should just turn off all keymaps OR all conditional keymaps and pass all input thru unmolested... that's all it could do at a login screen anyways since there is no user, no active X session, etc...
No idea. Just attempt to "switch users" yourself, if your DE has a method available for that. I think the catch when you don't log out first is that the keymapper still has access to X in the background, so it doesn't stop or crash the same way it does when you actually log out of your X session first.
But don't get confused into thinking I know anything technical about X or xhost
. I'm just reporting what I've experienced.
The Kinto service simply wouldn't/couldn't load up in the xmonad environment without first running the xhost command
Yes, this would be correct if it's running as some other user - it shouldn't have permission to know ANYTHING about your X session or which windows you are using, etc.... I have no idea why that would be different with Gnome. And that's exactly what I was asking about here, the best way for us to universally flip that permission bit given the wide variety of setups that Kinto users might be using...
As always specific logs/errors would be helpful... but right now I'm just handling Xlib.error.DisplayConnectionError
in a very naive way... and it seems to work fine... (keys come thru just fine - as if it had no idea which window I was using)...
Now I just need to figure out the best place to add "safe mode"... such that modmaps and keymaps are just disabled - ie, it seems like the keymapper has gone dormant when it's lost connection with X.... I think perhaps we could just attach the error state to the context object and handle it inside transform as a guard:
@RedBearAK https://github.com/joshgoebel/keyszer/pull/39
If you're not tired of testing yet you might try my new xorg_errors branch and see how it handles logouts, etc... right now it's working flawlessly for me without any X permissions at all - all it does is fall back to passing keys thru so input devices continue to function normally.
I don't think in an error case like that we should do any mapping at all since we're unsure of the environment and what's happening and couldn't even know something as simple as what modmap to use (terminal? non-terminal?).
@joshgoebel
new xorg_errors branch
I like that you're looking into this vexing issue. Have you been able to test this with not just a logout but a "switch user" scenario, where the user running keyszer
is still actually logged in in the background? Like Fast User Switching in macOS?
I'm not tired of testing but I just moved back to the main branch yesterday and was trying to keep up with the changes there and get some solid testing time in. I seemed to have the repeating keys issue a couple of times early on.
And I've been spending a lot of time in Windows finishing up the Option key special character entry scheme. Just about done with that and it will be a good stepping stone to bringing the same thing to Linux. Of course it will only be feasible if Kinto migrates to using keyszer
, since it needs nested shortcuts and Unicode.
I put up a repo for the documentation I've gathered on what all the special characters on the Apple keyboard actually are, and their Unicode addresses and Alt Codes.
https://github.com/RedBearAK/optspecialchars
I've got it working with a tray menu item and a keyboard shortcut that will enable/disable the whole thing. I'd really like to be able to do something similar when the time comes with keyszer
.
where the user running keyszer is still actually logged in in the background?
Your phrasing here confuses me. IMHO the "user running keyszer" should NEVER be logged in in the first place... keyzer (in a multi-user environ, and probably most environs) should be a system service running as a semi-privileged user. So once we clear up that confusion whoever is logging on and off should make very difference to keyszer.
Each active user would of course need to give the keymapper service access to it's X session... if they wanted keymapping services... in my experience all X failures have been VERY fast... the only issue we would have is if an X failure resulting in a long delay - which would freeze the keymapper waiting for X... if that was happening that would need to be explored further, but I haven't seen such a beast.
I suppose we could have a world in which keyszer was started and stopped every time a user logged in (and ran as that user)... but that's not a setup I'd recommend and would NOT be compatible with multiple users being logged in simultaneously.
Of course it will only be feasible if Kinto migrates to using keyszer
I think that's the long term plan if all goes well.
tray menu item and a keyboard shortcut that will enable/disable the whole thing.
Keyszer does keys not UI... but if someone wanted to do the work to allow user mode to speak to the keyzer service over a socket/pipe that could allow for these kinds of extensions.
@joshgoebel
Your phrasing here confuses me
I guess to be more accurate with the usual Kinto running xkeysnail
as root from a systemd
service unit, this would be stated as "the user whose X session triggered the graphical.target that loaded the background service". But the point was that there never seemed to be a reliable way to get that background process to stop or suspend when the user logged out, so it would "eat" the first key press on the login screen, which was often the first character of the user's password. Or the user hitting the Enter key to select their user on the login screen. Then xkeysnail
would crash in the background, and the user could type normally after that. So it wasn't a huge problem when the user had actually logged out.
But in the case of doing a "switch user" scenario, the user doesn't log out, but is sent to the login screen, just like with Fast User Switching on a Mac. That's when I would run into the complete lack of apparent keyboard output, including, if I remember right, the inability to even switch to a virtual terminal to kill the xkeysnail
process. I'd have to SSH in or power the machine off if SSH wasn't enabled.
I suppose we could have a world in which keyszer was started and stopped every time a user logged in (and ran as that user)... but that's not a setup I'd recommend and would NOT be compatible with multiple users being logged in simultaneously.
That is the current setup on my main machine, so that I could play with the much simpler launch
commands that result from running as my user. I have an autostart entry that launches xkeysnail
when I log in. This of course keeps running if I do "switch user" rather than logging out.
If you've managed to find a way to run as a different user, but not root, and have the process stop remapping keys when the X session is not on the screen, that's great.
Keyszer does keys not UI... but if someone wanted to do the work to allow user mode to speak to the keyzer service over a socket/pipe that could allow for these kinds of extensions.
Kinto has a GUI app and also a tray icon that shows the status of the xkeysnail
service and has functions in the context menu for stopping/(re)starting, opening the config file and other things related to "Kinto" as a whole. There would just need to be a way to toggle a variable inside the config file. The most correct way to do that is not something I'm familiar with. On the AHK side it creates the indicator tray menu itself, so I guess it's a little simpler to have the interaction with the variable.
But having a shortcut toggle things on and off should be something keyszer
could currently do, right?
AHK and xkeysnail/keyszer both have to be stopped and reloaded if shortcuts are changed or added, but in AHK the "#If" directive I used just seems to stop that whole section of the code from being "active" while the variable is not set to "1".
#If !WinActive("ahk_group remotes") && optspecialchars = 1
...
[ 550 lines of shortcuts, nested shortcuts, and comments ]
...
#If ; Unnecessary closing statement for AHK, but negates the previous "#If"
"the user whose X session triggered the graphical.target that loaded the background service".
But really it shouldn't be a user at all.. on most systems it should just start when the display manager was startup at boot-up... it really should have nothing to do with a user logging in or not.
Then xkeysnail would crash in the background,
I understand the genre of problems you're describing, but if the X connection going away is properly rescued (which it never has been in the past), I'm not sure why there should be any real problems at all... though I'm not really sure why fast user switching would have different behavior than logging out (maybe it suspends the processes?)... now if someone quit X entirely... that would be the real test... long-term I probably should setup a VM or two to test things...
toggle a variable inside the config file.
AH, that's one approach. It would (currently) require restarting the service though which has some downsides (messing up configured devices, keyboard repeat rates, mouse speed, etc)... longer-term live reload (of the config) might be doable, but it's not entirely trivial. Doing this over a pipe/socket would be the simple way I think (just to toggle a tiny piece of state) but would have to think about how to allow for arbitrary commands coming from the user-side...
But having a shortcut toggle things on and off should be something keyszer could currently do, right?
A keyboard shortcut? Sure, since they are allowed to run arbitrary code they can do just about anything you might imagine.
but in AHK the "#If" directive I used
Of course if you went that route in Python you could just use an if statement, since it's all python code... or if you did something really dynamic you might handle the on/off inside the conditionals themselves of the keymaps... (allowing you to turn it on and off via keystroke or IPC without restarting)
Not sure if we're also talking about multiple remote users using Kinto at the same time like via VNC or xrdp sessions, but I do want to state that that behavior will not likely be possible because of the way Xorg works and create Xinput mouse and keyboard devices. You will not have a physical raw uinput device to work with in a completely virtual xorg session. Those sessions are also incapable of simply handing off to a physical session and back again like RDP and the console user session does under Windows.
But yes if you are using multiple users on a single console/physical sessions then there shouldn't be a technical issue of grabbing the physical uinput keyboard device btwn multiple users.
That also means that my x11vnc fork only extends to VNC console sessions of Linux, whether that is VM or physical.
You will not have a physical raw uinput device to work with in a completely virtual xorg session.
You're losing me with your use of phrases like "physical raw" and "completely virtual" 😄 but otherwise I understand what you mean. :-) I don't think that's ever been what we're discussing here.
You could also do multiple local sessions (two monitors, two keyboards, etc) - two (or more) users on one system... Two X displays, two running instances of the keymapper, the X setups very carefully tuned to know which keyboards belong to who... but I'd call that an unsupported configuration. :-)
You're losing me with your use of phrases like "physical raw" and "completely virtual" 😄 but otherwise I understand what you mean. :-) I don't think that's ever been what we're discussing here.
Yea didn't seem like it, but was just trying to be extra clear about that aspect of multi-users since you were talking about multi-users even if not that specifically.
Ok, I did some very simple tests in my Debian VM with lightDM... I was running keyszer
as root (but it shouldn't matter who it's running as as long as it has permission tot he inputs) in a sep login session (on a virtual console)... to deal with no DISPLAY var at all I had to also rescue DisplayNameError
... after that I exported DISPLAY though and the keymapper was happy to do it's thing whether or not it could talk to X...
I gave it permission, I took permission away... I logged in and out... never had any issue with input... when permission was revoked an error would be logged every keystroke... but other than that...
So far this is about the behavior I expected.
@joshgoebel
Of course if you went that route in Python you could just use an if statement, since it's all python code... or if you did something really dynamic you might handle the on/off inside the conditionals themselves of the keymaps... (allowing you to turn it on and off via keystroke or IPC without restarting)
This would be the goal, getting the keymap to somehow become active or inactive depending on the toggling of the variable value, without reloading anything. Any hints as to how to do that would be helpful.
they are allowed to run arbitrary code they can do just about anything you might imagine
I see.
Just use a global variable in a conditional.
SPECIAL = False
keymap("special keys", {
# keymap
},
when = lambda _: SPECIAL is True)
Then you need a custom function to toggle it. See the set_mark
helper for an example... This doesn't give you any UI, but it does let you toggle on and off.
@rbreaves
According to this comment the same
xkeysnail
user responsible for the WM_NAME patch was able to getxkeysnail
to run at startup without usingsudo
back in 2020:https://github.com/mooz/xkeysnail/issues/64#issuecomment-600380800
Have you ever tested this setup? Is there some reason the Kinto installer can't be changed to set things up this way and thus entirely do away with the need to use
runuser
inlaunch
commands?