Compare commits

..

4 Commits

Author SHA1 Message Date
matt
551164b425 update noVNC submodule 2021-05-07 05:29:19 -04:00
matt
1518575982 Update NoVNC commit 2021-05-06 05:21:46 -04:00
matt
fda877dd72 WIP: update novnc commit 2021-05-04 15:46:26 -04:00
Lauri Kasanen
c0f107a83e Add server-side support for relative mouse motion, kasm-only hack 2021-05-03 13:52:58 +03:00
21 changed files with 129 additions and 245 deletions

2
.gitmodules vendored
View File

@@ -1,4 +1,4 @@
[submodule "kasmweb"]
path = kasmweb
url = https://github.com/kasmtech/noVNC.git
branch = master
branch = pointer_lock_api

View File

@@ -1,11 +1,11 @@
# KasmVNC - Linux Web Remote Desktop
<a href="https://kasmweb.com"><img src="https://kasm-static-content.s3.amazonaws.com/logo_kasm.png" width="300"><a/>
[Kasm Technologies](https://www.kasmweb.com) developed Kasm Workspaces, the Containerized Streaming Platform. Kasm has open-sourced the Workspace docker images, which include containerized [full desktops and apps](https://github.com/kasmtech/workspaces-images) and [base images](https://github.com/kasmtech/workspaces-core-images) intended for developers to create custimized streaming containers. These containers can be used standalone or within the [Kasm Workspaces Platform](https://www.kasmweb.com) which provides a full Enterprise feature set. KasmVNC is used as the streaming tech for our container images, however, you can use KasmVNC for individual servers. While the term VNC is in the name, KasmVNC is not intended to remain compliant with the RFB spec and has different goals than other VNC projects:
[![Kasm Technologies](https://kasm-static-content.s3.amazonaws.com/368_kasm_logo_.jpg "Kasm Logo")](https://kasmweb.com)
[Kasm Technologies LLC](https://www.kasmweb.com) developed Kasm Server, a Containerized Desktop Infrastructure (CDI) solution. Kasm started with TigerVNC and eventually forked it to create KasmVNC. KasmVNC is used within the overal Kasm CDI infrastructure, however, you can use KasmVNC for individual servers. KasmVNC has different goals than TigerVNC:
- Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default.
- Security - The RFB specification (VNC) limits the password field to 8 characters, so while the client may take a longer password, only the first 8 characters are sent. KasmVNC defaults to HTTPS with HTTP Basic Auth and disables the legacy VNC authentication method which is not sufficiently secure for internet accessible systems.
- Security - KasmVNC defaults to HTTPS and allows for HTTP Basic Auth. VNC Password authentication is limited by specification to 8 characters and is not suffecient for use on an internet accessible remote desktop. Our goal is to create a by default secure, web based experience.
- Simplicity - KasmVNC aims at being simple to deploy and configure.
# New Features!
@@ -91,13 +91,11 @@ The options for vncserver:
| interface | Which interface to bind the web server to. |
### Development
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com. We have investigated or are working on the following, if you have experience in these fields and would like to help please let us know.
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com
Real-time H264 encoding using NVIDIA and Intel encoding technology.
We need help, especially in packaging KasmVNC for various operating systems. We would love to have standard debian or RMP packages and host our own repo, however, that all requires a lot of experience, proper testing, and pipeline development for automated builds.
Windows version of KasmVNC. We have been able to get it to compile for Windows and increased the performance, but still not releasable. Experienced Windows developers with a background in cross compiling would help.
ARM version of KasmVNC, we have had requests for this and at one point we did have an ARM build of KasmVNC but it takes dev cycles to mainain and bring it back to life.
We also need help with Windows, which is not currently supported. While KasmVNC can technically be built for Windows 10, it is unusably slow, due to all the changes that occured in Windows since the original Windows support was added in the chain of VNC forked projects.
### Compiling From Source
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.

View File

@@ -34,14 +34,6 @@ RUN apt-get purge -y pm-utils xscreensaver*
RUN apt-get update && apt-get install -y vim less
RUN apt-get update && apt-get -y install lsb-release
RUN apt-get update && apt-get install -y task-cinnamon-desktop
RUN apt-get update && apt-get install -y task-gnome-desktop
RUN mkdir -p /usr/share/man/man1
RUN apt-get update && apt-get install -y apt-utils openjdk-11-jre
RUN apt-get update && apt-get install -y task-lxde-desktop
RUN apt-get update && apt-get install -y task-mate-desktop
RUN apt-get update && apt-get install -y task-kde-desktop
RUN echo 'source $STARTUPDIR/generate_container_user' >> $HOME/.bashrc
RUN mkdir -p $STARTUPDIR

View File

@@ -5,183 +5,25 @@ set -e
display=:10
interface=0.0.0.0
cert_group=ssl-cert
xstartup_script=~/.vnc/xstartup
de_was_selected_file="$HOME/.vnc/.kasmvncserver-easy-start-de-was-selected"
action=start
manual_xstartup_choice="Manually edit xstartup"
declare -A all_desktop_environments=(
[Cinnamon]=cinnamon-session
[Mate]="XDG_CURRENT_DESKTOP=MATE dbus-launch --exit-with-session mate-session"
[LXDE]=lxsession [Lxqt]=startlxqt
[KDE]=startkde
[Gnome]="XDG_CURRENT_DESKTOP=GNOME dbus-launch --exit-with-session /usr/bin/gnome-session"
[XFCE]=xfce4-session)
readarray -t sorted_desktop_environments < <(for de in "${!all_desktop_environments[@]}"; do echo "$de"; done | sort)
all_desktop_environments[$manual_xstartup_choice]=""
sorted_desktop_environments+=("$manual_xstartup_choice")
detected_desktop_environments=()
declare -A numbered_desktop_environments
debug() {
if [ -z "$debug" ]; then return; fi
echo "$@"
}
print_detected_desktop_environments() {
declare -i i=1
echo "Please choose Desktop Environment to run:"
for detected_de in "${detected_desktop_environments[@]}"; do
echo "[$i] $detected_de"
numbered_desktop_environments[$i]=$detected_de
i+=1
done
}
detect_desktop_environments() {
for de_name in "${sorted_desktop_environments[@]}"; do
if [[ "$de_name" = "$manual_xstartup_choice" ]]; then
detected_desktop_environments+=("$de_name")
continue;
fi
local executable=${all_desktop_environments[$de_name]}
executable=($executable)
executable=${executable[-1]}
if detect_desktop_environment "$de_name" "$executable"; then
detected_desktop_environments+=("$de_name")
fi
done
}
ask_user_to_choose_de() {
while : ; do
print_detected_desktop_environments
read -r de_number_to_run
de_name_from_number "$de_number_to_run"
if [[ -n "$de_name" ]]; then
break;
fi
echo "Incorrect number: $de_number_to_run"
echo
done
}
remember_de_choice() {
touch "$de_was_selected_file"
}
de_was_selected_on_previous_run() {
[[ -f "$de_was_selected_file" ]]
}
detect_desktop_environment() {
local de_name="$1"
local executable="$2"
if command -v "$executable" &>/dev/null; then
return 0
fi
return 1
}
did_user_forbid_replacing_xstartup() {
grep -q -v KasmVNC-safe-to-replace-this-file "$xstartup_script"
}
de_cmd_from_name() {
de_cmd=${all_desktop_environments[$de_name]}
}
de_name_from_number() {
local de_number_to_run="$1"
de_name=${numbered_desktop_environments[$de_number_to_run]}
}
warn_xstartup_will_be_overwriten() {
echo -n "WARNING: $xstartup_script will be overwritten y/N?"
read -r do_overwrite_xstartup
if [[ "$do_overwrite_xstartup" = "y" || "$do_overwrite_xstartup" = "Y" ]]; then
return 0
fi
return 1
}
setup_de_to_run_via_xstartup() {
warn_xstartup_will_be_overwriten
generate_xstartup "$de_name"
}
generate_xstartup() {
local de_name="$1"
de_cmd_from_name
cat <<-SCRIPT > "$xstartup_script"
#!/bin/sh
exec $de_cmd
SCRIPT
chmod +x "$xstartup_script"
}
enable_debug() {
debug=1
log_option="-log *:stderr:100"
}
kill_vnc_server() {
vncserver -kill $display
}
process_cli_options() {
for option in "$@"; do
case "$option" in
--help)
show_help
exit
;;
-d)
enable_debug
;;
-kill)
kill_vnc_server
exit
;;
-select-de)
action=select-de-and-start
;;
*)
echo >&2 "Unsupported argument: $option"
exit 1
esac
done
}
user_asked_to_select_de() {
[[ "$action" = "select-de-and-start" ]]
}
show_help() {
if [[ "$1" = "--help" ]]; then
cat >&2 <<-USAGE
Usage: `basename $0` [options]
-d Debug output
-kill Kill vncserver
-select-de Select desktop environent to run
--help show this help
-d Debug output
-kill Kill vncserver
--help show this help
USAGE
}
exit
fi
process_cli_options "$@"
if [[ "$1" = "-d" ]]; then
log_option="-log *:stderr:100"
fi
action=start
if [[ "$1" = "-kill" ]]; then
action=kill
fi
if groups | grep -qvw ssl-cert; then
cat <<-EOF
@@ -191,14 +33,9 @@ EOF
exit 1
fi
if user_asked_to_select_de || ! de_was_selected_on_previous_run; then
detect_desktop_environments
ask_user_to_choose_de
debug "You selected $de_name desktop environment"
if [[ "$de_name" != "$manual_xstartup_choice" ]]; then
setup_de_to_run_via_xstartup
fi
remember_de_choice
if [[ "$action" = "kill" ]]; then
vncserver -kill $display
exit
fi
vncserver $display -interface $interface

View File

@@ -61,8 +61,6 @@ kasmvncpasswd -d -u "$VNC_USER-to-delete" $HOME/.kasmpasswd
chmod 0600 $HOME/.kasmpasswd
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $HOME/.vnc/self.pem -out $HOME/.vnc/self.pem -subj "/C=US/ST=VA/L=None/O=None/OU=DoFu/CN=kasm/emailAddress=none@none.none"
exec /bin/bash
vncserver :1 -interface 0.0.0.0
vncserver -kill :1

View File

@@ -8,7 +8,7 @@ cd "$(dirname "$0")"
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
-t kasmvnctester_${os}:$os_codename \
-f dockerfile.${os}_${os_codename}.deb.test .
docker run -it -v $(realpath ${PWD}/..):/src -p 8443:8443 --rm \
docker run -it -p 443:8443 --rm \
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
kasmvnctester_${os}:$os_codename

View File

@@ -20,7 +20,6 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "websocket.h"
/*
@@ -228,13 +227,9 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
strcpy(addr.sun_path, ".KasmVNCSock");
addr.sun_path[0] = '\0';
struct timeval tv;
gettimeofday(&tv, NULL);
struct sockaddr_un myaddr;
myaddr.sun_family = AF_UNIX;
sprintf(myaddr.sun_path, ".%s@%s_%lu.%lu", ws_ctx->user, ws_ctx->ip,
tv.tv_sec, tv.tv_usec);
sprintf(myaddr.sun_path, ".%s@%s", ws_ctx->user, ws_ctx->ip);
myaddr.sun_path[0] = '\0';
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);

View File

@@ -50,7 +50,7 @@ namespace rfb {
int w, int h,
const ScreenSet& layout);
virtual void setCursor(int width, int height, const Point& hotspot,
const rdr::U8* data, const bool resizing = false) = 0;
const rdr::U8* data) = 0;
virtual void setPixelFormat(const PixelFormat& pf);
virtual void setName(const char* name);
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);

View File

@@ -36,6 +36,7 @@ namespace rfb {
rdr::U32 __unused_attr keycode,
bool __unused_attr down) { }
virtual void pointerEvent(const Point& __unused_attr pos,
const Point& __unused_attr abspos,
int __unused_attr buttonMask,
const bool __unused_attr skipClick,
const bool __unused_attr skipRelease) { }

View File

@@ -252,7 +252,4 @@ namespace rfb {
bool hasLocalClipboard;
};
}
#define WRITER_PERMS (AccessKeyEvents | AccessPtrEvents | AccessCutText | AccessSetDesktopSize)
#endif

View File

@@ -220,7 +220,7 @@ void SMsgReader::readPointerEvent()
int mask = is->readU8();
int x = is->readU16();
int y = is->readU16();
handler->pointerEvent(Point(x, y), mask, false, false);
handler->pointerEvent(Point(x, y), Point(0, 0), mask, false, false);
}

View File

@@ -86,9 +86,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
}
bool write, owner;
if (!getPerms(write, owner) || !write) {
accessRights &= ~WRITER_PERMS;
}
if (!getPerms(write, owner) || !write)
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
// Configure the socket
setSocketTimeouts();
@@ -682,14 +681,52 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
setCursor();
}
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease)
void VNCSConnectionST::pointerEvent(const Point& pos, const Point& abspos, int buttonMask, const bool skipClick, const bool skipRelease)
{
pointerEventTime = lastEventTime = time(0);
server->lastUserInputTime = lastEventTime;
if (!(accessRights & AccessPtrEvents)) return;
if (!rfb::Server::acceptPointerEvents) return;
if (!server->pointerClient || server->pointerClient == this) {
pointerEventPos = pos;
Point newpos = pos;
if (pos.x & 0x4000) {
newpos.x &= ~0x4000;
newpos.y &= ~0x4000;
if (newpos.x & 0x8000) {
newpos.x &= ~0x8000;
newpos.x = -newpos.x;
}
if (newpos.y & 0x8000) {
newpos.y &= ~0x8000;
newpos.y = -newpos.y;
}
if (newpos.x < 0) {
if (pointerEventPos.x + newpos.x >= 0)
pointerEventPos.x += newpos.x;
else
pointerEventPos.x = 0;
} else {
pointerEventPos.x += newpos.x;
if (pointerEventPos.x >= cp.width)
pointerEventPos.x = cp.width;
}
if (newpos.y < 0) {
if (pointerEventPos.y + newpos.y >= 0)
pointerEventPos.y += newpos.y;
else
pointerEventPos.y = 0;
} else {
pointerEventPos.y += newpos.y;
if (pointerEventPos.y >= cp.height)
pointerEventPos.y = cp.height;
}
} else {
pointerEventPos = pos;
}
if (buttonMask)
server->pointerClient = this;
else
@@ -710,7 +747,7 @@ void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool
}
}
server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease);
server->desktop->pointerEvent(newpos, pointerEventPos, buttonMask, skipclick, skiprelease);
}
}
@@ -1217,9 +1254,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
close("User was deleted");
return;
} else if (!write) {
accessRights &= ~WRITER_PERMS;
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
} else {
accessRights |= WRITER_PERMS;
accessRights |= AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize;
}
}

View File

@@ -175,7 +175,7 @@ namespace rfb {
virtual void queryConnection(const char* userName);
virtual void clientInit(bool shared);
virtual void setPixelFormat(const PixelFormat& pf);
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
virtual void pointerEvent(const Point& pos, const Point& abspos, int buttonMask, const bool skipClick, const bool skipRelease);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
virtual void setDesktopSize(int fb_width, int fb_height,
@@ -206,7 +206,7 @@ namespace rfb {
bool write, owner;
if (!getPerms(write, owner) || !write)
accessRights &= ~WRITER_PERMS;
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents));
needsPermCheck = false;
}

View File

@@ -79,7 +79,7 @@ namespace rfb {
// cursorData argument contains width*height rgba quadruplets with
// non-premultiplied alpha.
virtual void setCursor(int width, int height, const Point& hotspot,
const rdr::U8* cursorData, const bool resizing = false) = 0;
const rdr::U8* cursorData) = 0;
// setCursorPos() tells the server the current position of the cursor, and
// whether the server initiated that change (e.g. through another X11

View File

@@ -579,7 +579,7 @@ void VNCServerST::add_copied(const Region& dest, const Point& delta)
}
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
const rdr::U8* data, const bool resizing)
const rdr::U8* data)
{
delete cursor;
cursor = new Cursor(width, height, newHotspot, data);
@@ -587,13 +587,6 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
renderedCursorInvalid = true;
// If an app has an animated cursor on the resized edge, X internals
// will call for it to be rendered. Unlucky for us, the VNC screen
// is currently pointing to freed memory, and a cursor change
// would want to send a screen update. So, don't do that.
if (resizing)
return;
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;

View File

@@ -102,7 +102,7 @@ namespace rfb {
virtual void add_changed(const Region &region);
virtual void add_copied(const Region &dest, const Point &delta);
virtual void setCursor(int width, int height, const Point& hotspot,
const rdr::U8* data, const bool resizing = false);
const rdr::U8* data);
virtual void setCursorPos(const Point& p, bool warped);
virtual void setLEDState(unsigned state);

Submodule kasmweb updated: 67466077c0...9f9f4b3557

View File

@@ -234,6 +234,40 @@ void vncPointerMove(int x, int y)
cursorPosY = y;
}
void vncPointerMoveRelative(int x, int y, int absx, int absy)
{
int valuators[2];
#if XORG < 111
int n;
#endif
#if XORG >= 110
ValuatorMask mask;
#endif
// if (cursorPosX == absx && cursorPosY == absy)
// return;
valuators[0] = x;
valuators[1] = y;
#if XORG < 110
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
POINTER_RELATIVE, 0, 2, valuators);
enqueueEvents(vncPointerDev, n);
#elif XORG < 111
valuator_mask_set_range(&mask, 0, 2, valuators);
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
POINTER_RELATIVE, &mask);
enqueueEvents(vncPointerDev, n);
#else
valuator_mask_set_range(&mask, 0, 2, valuators);
QueuePointerEvents(vncPointerDev, MotionNotify, 0,
POINTER_RELATIVE, &mask);
#endif
cursorPosX = absx;
cursorPosY = absy;
}
void vncGetPointerPos(int *x, int *y)
{
if (vncPointerDev != NULL) {

View File

@@ -35,6 +35,7 @@ void vncInitInputDevice(void);
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
const unsigned char skiprelease);
void vncPointerMove(int x, int y);
void vncPointerMoveRelative(int x, int y, int absx, int absy);
void vncGetPointerPos(int *x, int *y);
void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down);

View File

@@ -74,7 +74,7 @@ XserverDesktop::XserverDesktop(int screenIndex_,
: screenIndex(screenIndex_),
server(0), listeners(listeners_),
directFbptr(true),
queryConnectId(0), queryConnectTimer(this), resizing(false)
queryConnectId(0), queryConnectTimer(this)
{
format = pf;
@@ -251,7 +251,7 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY,
}
try {
server->setCursor(width, height, Point(hotX, hotY), cursorData, resizing);
server->setCursor(width, height, Point(hotX, hotY), cursorData);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::setCursor: %s",e.str());
}
@@ -445,11 +445,17 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept,
// SDesktop callbacks
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask,
void XserverDesktop::pointerEvent(const Point& pos, const Point& abspos, int buttonMask,
const bool skipClick, const bool skipRelease)
{
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
pos.y + vncGetScreenY(screenIndex));
if (pos.equals(abspos)) {
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
pos.y + vncGetScreenY(screenIndex));
} else {
vncPointerMoveRelative(pos.x, pos.y,
abspos.x + vncGetScreenX(screenIndex),
abspos.y + vncGetScreenY(screenIndex));
}
vncPointerButtonAction(buttonMask, skipClick, skipRelease);
}
@@ -462,11 +468,8 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
layout.print(buffer, sizeof(buffer));
vlog.debug("%s", buffer);
resizing = true;
vncSetGlueContext(screenIndex);
const unsigned int ret = ::setScreenLayout(fb_width, fb_height, layout, &outputIdMap);
resizing = false;
return ret;
return ::setScreenLayout(fb_width, fb_height, layout, &outputIdMap);
}
void XserverDesktop::handleClipboardRequest()

View File

@@ -89,7 +89,7 @@ public:
const char* rejectMsg=0);
// rfb::SDesktop callbacks
virtual void pointerEvent(const rfb::Point& pos, int buttonMask,
virtual void pointerEvent(const rfb::Point& pos, const rfb::Point& abspos, int buttonMask,
const bool skipClick, const bool skipRelease);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
@@ -132,7 +132,5 @@ private:
OutputIdMap outputIdMap;
rfb::Point oldCursorPos;
bool resizing;
};
#endif