Config-based KasmVNC
parent
d9cf46f83e
commit
36a1ffc5e4
@ -0,0 +1,15 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.python.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
mamba = "*"
|
||||||
|
expects = "*"
|
||||||
|
"path.py" = "*"
|
||||||
|
pexpect = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.8"
|
@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "6745d5e5d90e44a18d73a0e23bc3d3e68acb950af0b87df50b45272d25b9e615"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.8"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.python.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"args": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a785b8d837625e9b61c39108532d95b85274acd679693b71ebb5156848fcf814"
|
||||||
|
],
|
||||||
|
"version": "==0.1.0"
|
||||||
|
},
|
||||||
|
"clint": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:05224c32b1075563d0b16d0015faaf9da43aa214e4a2140e51f08789e7a4c5aa"
|
||||||
|
],
|
||||||
|
"version": "==0.5.1"
|
||||||
|
},
|
||||||
|
"coverage": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c",
|
||||||
|
"sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6",
|
||||||
|
"sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45",
|
||||||
|
"sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a",
|
||||||
|
"sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03",
|
||||||
|
"sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529",
|
||||||
|
"sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a",
|
||||||
|
"sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a",
|
||||||
|
"sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2",
|
||||||
|
"sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6",
|
||||||
|
"sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759",
|
||||||
|
"sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53",
|
||||||
|
"sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a",
|
||||||
|
"sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4",
|
||||||
|
"sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff",
|
||||||
|
"sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502",
|
||||||
|
"sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793",
|
||||||
|
"sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb",
|
||||||
|
"sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905",
|
||||||
|
"sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821",
|
||||||
|
"sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b",
|
||||||
|
"sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81",
|
||||||
|
"sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0",
|
||||||
|
"sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b",
|
||||||
|
"sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3",
|
||||||
|
"sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184",
|
||||||
|
"sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701",
|
||||||
|
"sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a",
|
||||||
|
"sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82",
|
||||||
|
"sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638",
|
||||||
|
"sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5",
|
||||||
|
"sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083",
|
||||||
|
"sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6",
|
||||||
|
"sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90",
|
||||||
|
"sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465",
|
||||||
|
"sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a",
|
||||||
|
"sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3",
|
||||||
|
"sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e",
|
||||||
|
"sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066",
|
||||||
|
"sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf",
|
||||||
|
"sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b",
|
||||||
|
"sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae",
|
||||||
|
"sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669",
|
||||||
|
"sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873",
|
||||||
|
"sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b",
|
||||||
|
"sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6",
|
||||||
|
"sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb",
|
||||||
|
"sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160",
|
||||||
|
"sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c",
|
||||||
|
"sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079",
|
||||||
|
"sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d",
|
||||||
|
"sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||||
|
"version": "==5.5"
|
||||||
|
},
|
||||||
|
"expects": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:419902ccafe81b7e9559eeb6b7a07ef9d5c5604eddb93000f0642b3b2d594f4c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.9.0"
|
||||||
|
},
|
||||||
|
"mamba": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:75cfc6dfd287dcccaf86dd753cf48e0a7337487c7c3fafda05a6a67ded6da496"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.11.2"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2de925e8d421f93bcea80d511b81accfb6a7e6b249afa4a5559557b0cf817097",
|
||||||
|
"sha256:340054c5bb459fc9fd40e7eb6768c5989f3e599d18224238465b5333bc8faa7d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==16.2.0"
|
||||||
|
},
|
||||||
|
"path.py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8d885e8b2497aed005703d94e0fd97943401f035e42a136810308bff034529a8",
|
||||||
|
"sha256:a43e82eb2c344c3fd0b9d6352f6b856f40b8b7d3d65cc05978b42c3715668496"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==12.5.0"
|
||||||
|
},
|
||||||
|
"pexpect": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937",
|
||||||
|
"sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.8.0"
|
||||||
|
},
|
||||||
|
"ptyprocess": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35",
|
||||||
|
"sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"
|
||||||
|
],
|
||||||
|
"version": "==0.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
VNC_PORT=8443
|
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
default_os=ubuntu
|
||||||
|
default_os_codename=focal
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
. ./builder/os_ver_cli.sh
|
||||||
|
. ./builder/common.sh
|
||||||
|
|
||||||
|
docker build --build-arg KASMVNC_PACKAGE_DIR="builder/build/${os_codename}" \
|
||||||
|
-t kasmvncdevenv_${os}:$os_codename \
|
||||||
|
-f builder/dockerfile.${os}_${os_codename}.vncserver_devenv.test .
|
||||||
|
docker run -it -v $(realpath ${PWD}):/src -p "443:$VNC_PORT" -p "$VNC_PORT:$VNC_PORT" \
|
||||||
|
-p 8444:8444 \
|
||||||
|
-p 8445:8445 --rm \
|
||||||
|
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
||||||
|
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
|
kasmvncdevenv_${os}:$os_codename
|
@ -1,18 +1,20 @@
|
|||||||
FROM centos:centos7
|
FROM centos:centos7
|
||||||
|
|
||||||
|
ENV STARTUPDIR=/dockerstartup
|
||||||
|
|
||||||
RUN yum install -y xterm
|
RUN yum install -y xterm
|
||||||
RUN yum install -y vim less
|
RUN yum install -y vim less
|
||||||
RUN yum install -y redhat-lsb-core
|
RUN yum install -y redhat-lsb-core
|
||||||
|
RUN yum install -y epel-release
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp/
|
||||||
RUN yum localinstall -y /tmp/*.rpm
|
RUN yum localinstall -y /tmp/*.rpm
|
||||||
|
|
||||||
RUN useradd -m foo
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY startup/vnc_startup_barebones.sh $STARTUPDIR
|
||||||
|
|
||||||
|
RUN useradd -m foo
|
||||||
USER foo:kasmvnc-cert
|
USER foo:kasmvnc-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
ENTRYPOINT "/$STARTUPDIR/vnc_startup_barebones.sh"
|
||||||
chmod +x ~/.vnc/xstartup
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
FROM debian:buster-slim
|
FROM debian:buster-slim
|
||||||
|
|
||||||
|
ENV STARTUPDIR=/dockerstartup
|
||||||
|
|
||||||
|
COPY ./builder/scripts/ /tmp/scripts/
|
||||||
|
COPY ./debian/changelog /tmp
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN /tmp/scripts/install_kasmvncserver_package
|
||||||
RUN apt-get update && apt-get -y install xterm
|
RUN apt-get update && apt-get -y install xterm
|
||||||
|
|
||||||
COPY startup/deb/kasmvncserver-easy-start /usr/local/bin
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY builder/startup/vnc_startup_barebones.sh $STARTUPDIR
|
||||||
|
|
||||||
RUN useradd -m foo
|
RUN useradd -m foo
|
||||||
|
|
||||||
USER foo:ssl-cert
|
USER foo:ssl-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
ENTRYPOINT "/$STARTUPDIR/vnc_startup_barebones.sh"
|
||||||
chmod +x ~/.vnc/xstartup
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | \
|
|
||||||
kasmvncpasswd -w -u $VNC_USER $HOME/.kasmpasswd && \
|
|
||||||
kasmvncserver-easy-start && tail -f $HOME/.vnc/*.log"
|
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
FROM fedora:33
|
FROM fedora:33
|
||||||
|
|
||||||
|
ENV STARTUPDIR=/dockerstartup
|
||||||
|
|
||||||
RUN dnf install -y xterm
|
RUN dnf install -y xterm
|
||||||
RUN dnf install -y vim less
|
RUN dnf install -y vim less
|
||||||
RUN yum install -y redhat-lsb-core
|
RUN yum install -y redhat-lsb-core
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp/
|
||||||
RUN dnf localinstall -y /tmp/*.rpm
|
RUN dnf localinstall -y /tmp/*.rpm
|
||||||
|
|
||||||
RUN useradd -m foo
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY startup/vnc_startup_barebones.sh $STARTUPDIR
|
||||||
|
|
||||||
|
RUN useradd -m foo
|
||||||
USER foo:kasmvnc-cert
|
USER foo:kasmvnc-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
ENTRYPOINT "/$STARTUPDIR/vnc_startup_barebones.sh"
|
||||||
chmod +x ~/.vnc/xstartup
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
FROM oraclelinux:8
|
FROM oraclelinux:8
|
||||||
|
|
||||||
|
ENV STARTUPDIR=/dockerstartup
|
||||||
|
|
||||||
RUN dnf install -y \
|
RUN dnf install -y \
|
||||||
less \
|
less \
|
||||||
redhat-lsb-core \
|
redhat-lsb-core \
|
||||||
vim \
|
vim \
|
||||||
xterm
|
xterm
|
||||||
|
RUN dnf config-manager --set-enabled ol8_codeready_builder
|
||||||
|
RUN dnf install -y oracle-epel-release-el8
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
||||||
RUN dnf localinstall -y /tmp/*.rpm
|
RUN dnf localinstall -y /tmp/*.rpm
|
||||||
|
|
||||||
RUN useradd -m foo
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY startup/vnc_startup_barebones.sh $STARTUPDIR
|
||||||
|
|
||||||
|
RUN useradd -m foo
|
||||||
USER foo:kasmvnc-cert
|
USER foo:kasmvnc-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
ENTRYPOINT "/$STARTUPDIR/vnc_startup_barebones.sh"
|
||||||
chmod +x ~/.vnc/xstartup
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
FROM ubuntu:focal
|
FROM ubuntu:focal
|
||||||
|
|
||||||
|
ENV STARTUPDIR=/dockerstartup
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
RUN apt-get update && apt-get -y install xterm lsb-release
|
RUN apt-get update && apt-get -y install xterm lsb-release
|
||||||
|
|
||||||
RUN useradd -m foo && addgroup foo ssl-cert
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY builder/startup/vnc_startup_barebones.sh $STARTUPDIR
|
||||||
|
|
||||||
|
RUN useradd -m foo && addgroup foo ssl-cert
|
||||||
USER foo
|
USER foo
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
ENTRYPOINT "/$STARTUPDIR/vnc_startup_barebones.sh"
|
||||||
chmod +x ~/.vnc/xstartup
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/ssl/certs/ssl-cert-snakeoil.pem -key /etc/ssl/private/ssl-cert-snakeoil.key -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
FROM ubuntu:focal
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y vim less
|
||||||
|
RUN apt-get update && apt-get install -y python3-pip
|
||||||
|
RUN apt-get update && apt-get install -y strace silversearcher-ag xfonts-base
|
||||||
|
RUN apt-get update && apt-get install -y cinnamon
|
||||||
|
RUN apt-get update && apt-get install -y mate
|
||||||
|
|
||||||
|
RUN useradd -m docker
|
||||||
|
|
||||||
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
|
RUN apt-get install -y /tmp/*$(dpkg --print-architecture).deb
|
||||||
|
|
||||||
|
ENV USER docker
|
||||||
|
ENV HOME /home/docker
|
||||||
|
|
||||||
|
RUN chown -R 1000:0 $HOME
|
||||||
|
USER 1000:ssl-cert
|
||||||
|
WORKDIR $HOME
|
||||||
|
|
||||||
|
RUN pip3 install --user pipenv
|
||||||
|
RUN echo 'PATH="~/.local/bin/:$PATH"' >> ~/.bashrc
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "-ic", "/src/builder/run-specs-inside-docker"]
|
@ -0,0 +1,67 @@
|
|||||||
|
FROM ubuntu:focal
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV VNC_PORT 8443
|
||||||
|
ENV VNC_PORT2 8444
|
||||||
|
ENV VNC_PORT3 8445
|
||||||
|
EXPOSE $VNC_PORT
|
||||||
|
EXPOSE $VNC_PORT2
|
||||||
|
EXPOSE $VNC_PORT3
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y supervisor xfce4 xfce4-terminal xterm libnss-wrapper gettext wget
|
||||||
|
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 -y install net-tools
|
||||||
|
|
||||||
|
# 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 ubuntu-mate-desktop
|
||||||
|
RUN apt-get update && apt-get install -y lxde
|
||||||
|
RUN apt-get update && apt-get install -y lxqt
|
||||||
|
RUN apt-get update && apt-get install -y kde-plasma-desktop
|
||||||
|
RUN apt-get update && apt-get install -y cinnamon
|
||||||
|
RUN apt-get update && apt-get install -y gnome-session
|
||||||
|
RUN apt-get purge -y pm-utils xscreensaver*
|
||||||
|
RUN apt-get purge -y clipit magnus kgpg
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y python3-pip
|
||||||
|
RUN apt-get update && apt-get install -y strace
|
||||||
|
RUN apt-get update && apt-get install -y silversearcher-ag
|
||||||
|
RUN apt-get update && apt-get install -y libdevel-stacktrace-perl
|
||||||
|
|
||||||
|
RUN useradd -m docker
|
||||||
|
ENV USER docker
|
||||||
|
|
||||||
|
COPY ./builder/scripts/ /tmp/scripts/
|
||||||
|
COPY ./debian/changelog /tmp
|
||||||
|
|
||||||
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
|
RUN /tmp/scripts/install_kasmvncserver_package
|
||||||
|
|
||||||
|
ENV KASMVNC_DEVELOPMENT 1
|
||||||
|
ENV HOME /home/docker
|
||||||
|
RUN chown -R 1000:0 $HOME
|
||||||
|
USER 1000:ssl-cert
|
||||||
|
WORKDIR $HOME
|
||||||
|
|
||||||
|
RUN pip3 install --user pipenv
|
||||||
|
RUN echo 'PATH="/src/unix:~/.local/bin/:$PATH"' >> ~/.bashrc
|
||||||
|
RUN echo 'export PERL5LIB="/src/unix"' >> ~/.bashrc
|
||||||
|
RUN echo 'ulimit -c 0' >> ~/.bashrc
|
||||||
|
RUN echo 'alias s="vncserver"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias k="vncserver -kill :1; pkill baloo_file; pkill gpg-agent; pkill ssh-agent; pkill xiccd"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias r="k; s"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias go="vncserver; vncserver -kill :1"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias ns="netstat -nltup"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias tv="./run-specs spec/vncserver_yaml_validation_spec.py"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias ty="./run-specs spec/vncserver_*spec.py"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias ta="./run-specs"' >> ~/.bashrc
|
||||||
|
RUN echo 'alias vd="vncserver -dry-run"' >> ~/.bashrc
|
||||||
|
ENV SET_PASSWORD_FUNC 'sp() { echo -e "$VNC_PW\\n$VNC_PW\\n" | kasmvncpasswd -w -u $USER $HOME/.kasmpasswd; }'
|
||||||
|
RUN echo $SET_PASSWORD_FUNC >> ~/.bashrc
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
|
||||||
|
ENTRYPOINT ["bash", "-ic", "cd /src && pipenv install; exec bash"]
|
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
logfile=/tmp/specs.log
|
||||||
|
red='\033[0;31m'
|
||||||
|
no_color='\033[0m'
|
||||||
|
|
||||||
|
fail_on_perl_warnings() {
|
||||||
|
if grep -qP 'line \d+\.$' "$logfile"; then
|
||||||
|
echo -e "${red}Failure: Perl warnings were found${no_color}"
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cd /src
|
||||||
|
pipenv install
|
||||||
|
./run-specs 2>&1 | tee "$logfile"
|
||||||
|
fail_on_perl_warnings
|
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
tag="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_debian() {
|
||||||
|
[[ -f /etc/debian_version ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
check_package_version_exists() {
|
||||||
|
if ! stat /tmp/kasmvncserver_"$package_version"*.deb; then
|
||||||
|
>&2 echo "No package found for version $package_version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_tagged_debs_unless_tag_used() {
|
||||||
|
if [[ -n "$tag" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f /tmp/kasmvncserver_*+*.deb
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_rpm_package_manager() {
|
||||||
|
if command -v dnf >/dev/null; then
|
||||||
|
rpm_package_manager=dnf
|
||||||
|
else
|
||||||
|
rpm_package_manager=yum
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_package_built_for_current_branch_package_version_deb() {
|
||||||
|
remove_tagged_debs_unless_tag_used
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y dpkg-dev
|
||||||
|
package_version=$(dpkg-parsechangelog --show-field Version \
|
||||||
|
--file /tmp/changelog)
|
||||||
|
|
||||||
|
check_package_version_exists
|
||||||
|
apt-get install -y /tmp/kasmvncserver_"$package_version"*"$tag"*.deb
|
||||||
|
}
|
||||||
|
|
||||||
|
install_package_built_for_current_branch_package_version_rpm() {
|
||||||
|
detect_rpm_package_manager
|
||||||
|
$rpm_package_manager install -y rpmdevtools
|
||||||
|
|
||||||
|
package_version=$(rpmspec -q --qf '%{version}\n' /tmp/kasmvncserver.spec 2>/dev/null)
|
||||||
|
if [[ $rpm_package_manager = "dnf" ]]; then
|
||||||
|
dnf localinstall -y --allowerasing /tmp/kasmvncserver-"$package_version"*.rpm
|
||||||
|
else
|
||||||
|
yum install -y /tmp/kasmvncserver-"$package_version"*.rpm
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_debian ; then
|
||||||
|
install_package_built_for_current_branch_package_version_deb
|
||||||
|
else
|
||||||
|
install_package_built_for_current_branch_package_version_rpm
|
||||||
|
fi
|
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
debug() {
|
||||||
|
if [ -z "$debug" ]; then return; fi
|
||||||
|
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
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|--debug)
|
||||||
|
enable_debug
|
||||||
|
;;
|
||||||
|
-k|--kill)
|
||||||
|
kill_vnc_server
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-s|--select-de)
|
||||||
|
action=select-de-and-start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo >&2 "Unsupported argument: $option"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
cat >&2 <<-USAGE
|
||||||
|
Usage: $(basename "$0") [options]
|
||||||
|
-d, --debug Debug output
|
||||||
|
-k, --kill Kill vncserver
|
||||||
|
-s, --select-de Select desktop environent to run
|
||||||
|
--help Show this help
|
||||||
|
USAGE
|
||||||
|
}
|
@ -0,0 +1,266 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
xstartup_script=~/.vnc/xstartup
|
||||||
|
de_was_selected_file="$HOME/.vnc/.de-was-selected"
|
||||||
|
|
||||||
|
debug() {
|
||||||
|
if [ -z "$debug" ]; then return; fi
|
||||||
|
|
||||||
|
echo "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_debug() {
|
||||||
|
debug=1
|
||||||
|
}
|
||||||
|
|
||||||
|
process_cli_options() {
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
local option="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$option" in
|
||||||
|
--help|-h)
|
||||||
|
show_help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-d|--debug)
|
||||||
|
enable_debug
|
||||||
|
;;
|
||||||
|
-y|--assume-yes)
|
||||||
|
assume_yes=1
|
||||||
|
;;
|
||||||
|
-s|--select-de)
|
||||||
|
action=select-de
|
||||||
|
if [[ -n "$1" && "${1:0:1}" != "-" ]]; then
|
||||||
|
selected_de="$1"
|
||||||
|
assume_yes_for_xstartup_overwrite=1
|
||||||
|
if [ "$selected_de" = "manual" ]; then
|
||||||
|
selected_de="$manual_xstartup_choice"
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo >&2 "Unsupported argument: $option"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
cat >&2 <<-USAGE
|
||||||
|
Usage: $(basename "$0") [options]
|
||||||
|
-d, --debug Debug output
|
||||||
|
-y, --assume-yes Automatic "yes" to prompts
|
||||||
|
-s, --select-de Select desktop environent to run
|
||||||
|
--help Show this help
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
add_uppercase_desktop_environment_keys() {
|
||||||
|
local de_cmd
|
||||||
|
|
||||||
|
for de in "${!all_desktop_environments[@]}"; do
|
||||||
|
de_cmd=${all_desktop_environments[$de]};
|
||||||
|
all_desktop_environments[${de^^}]="$de_cmd"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
manual_xstartup_choice="Manually edit xstartup"
|
||||||
|
|
||||||
|
process_cli_options "$@"
|
||||||
|
|
||||||
|
declare -A all_desktop_environments=(
|
||||||
|
[Cinnamon]="exec cinnamon-session"
|
||||||
|
[Mate]="XDG_CURRENT_DESKTOP=MATE exec dbus-launch --exit-with-session mate-session"
|
||||||
|
[LXDE]="exec lxsession"
|
||||||
|
[Lxqt]="exec startlxqt"
|
||||||
|
[KDE]="exec startkde"
|
||||||
|
[Gnome]="XDG_CURRENT_DESKTOP=GNOME exec dbus-launch --exit-with-session /usr/bin/gnome-session"
|
||||||
|
[XFCE]="exec 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")
|
||||||
|
add_uppercase_desktop_environment_keys
|
||||||
|
|
||||||
|
detected_desktop_environments=()
|
||||||
|
declare -A numbered_desktop_environments
|
||||||
|
|
||||||
|
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
|
||||||
|
if [[ -z "$de_number_to_run" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
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() {
|
||||||
|
if [[ -n "$assume_yes" || -n "$assume_yes_for_xstartup_overwrite" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$xstartup_script" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
$de_cmd
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$xstartup_script"
|
||||||
|
}
|
||||||
|
|
||||||
|
user_asked_to_select_de() {
|
||||||
|
[[ "$action" = "select-de" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
user_specified_de() {
|
||||||
|
[ -n "$selected_de" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
check_de_name_is_valid() {
|
||||||
|
local selected_de="$1"
|
||||||
|
if [[ "$selected_de" = "$manual_xstartup_choice" ]]; then
|
||||||
|
return 0;
|
||||||
|
fi
|
||||||
|
|
||||||
|
local de_cmd=${all_desktop_environments["${selected_de^^}"]:-}
|
||||||
|
if [ -z "$de_cmd" ]; then
|
||||||
|
echo >&2 "'$selected_de': not supported Desktop Environment"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
de_installed() {
|
||||||
|
local de_name="${1^^}"
|
||||||
|
|
||||||
|
for de in "${detected_desktop_environments[@]}"; do
|
||||||
|
if [ "${de^^}" = "$de_name" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_de_installed() {
|
||||||
|
local de_name="$1"
|
||||||
|
|
||||||
|
if ! de_installed "$de_name"; then
|
||||||
|
echo >&2 "'$de_name': Desktop Environment not installed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if user_asked_to_select_de || ! de_was_selected_on_previous_run; then
|
||||||
|
if user_specified_de; then
|
||||||
|
check_de_name_is_valid "$selected_de"
|
||||||
|
fi
|
||||||
|
|
||||||
|
detect_desktop_environments
|
||||||
|
if user_specified_de; then
|
||||||
|
check_de_installed "$selected_de"
|
||||||
|
de_name="$selected_de"
|
||||||
|
else
|
||||||
|
ask_user_to_choose_de
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug "You selected $de_name desktop environment"
|
||||||
|
if [[ "$de_name" != "$manual_xstartup_choice" ]]; then
|
||||||
|
setup_de_to_run_via_xstartup
|
||||||
|
fi
|
||||||
|
remember_de_choice
|
||||||
|
fi
|
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
set_xterm_to_run() {
|
||||||
|
mkdir "$config_dir"
|
||||||
|
echo '/usr/bin/xterm &' >> "$xstartup"
|
||||||
|
chmod +x "$xstartup"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_kasm_user() {
|
||||||
|
echo -e "$VNC_PW\n$VNC_PW\n" | kasmvncpasswd -w -u "$VNC_USER"
|
||||||
|
}
|
||||||
|
|
||||||
|
config_dir="$HOME/.vnc"
|
||||||
|
xstartup="$config_dir/xstartup"
|
||||||
|
|
||||||
|
set_xterm_to_run
|
||||||
|
create_kasm_user
|
||||||
|
|
||||||
|
vncserver -select-de manual -websocketPort "$VNC_PORT"
|
||||||
|
tail -f "$config_dir"/*.log
|
@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
default_os=ubuntu
|
||||||
|
default_os_codename=focal
|
||||||
|
|
||||||
|
. ./builder/os_ver_cli.sh
|
||||||
|
|
||||||
|
if [[ -n "$GITLAB_CI" ]]; then
|
||||||
|
package_dir="output/${os_codename}"
|
||||||
|
else
|
||||||
|
package_dir="builder/build/${os_codename}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build --build-arg KASMVNC_PACKAGE_DIR="$package_dir" \
|
||||||
|
-t kasmvnctester_${os}:$os_codename \
|
||||||
|
-f builder/dockerfile.${os}_${os_codename}.specs.test .
|
||||||
|
docker run -v $(realpath ${PWD}):/src \
|
||||||
|
--rm \
|
||||||
|
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
||||||
|
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
|
kasmvnctester_${os}:$os_codename
|
@ -0,0 +1,11 @@
|
|||||||
|
### logging.log_dest
|
||||||
|
|
||||||
|
Log to the instance's log file in `~/.vnc`, or syslog. You can find the right
|
||||||
|
log file by the display number. For example, in `9b1d402e204c:1.log`, `:1` is
|
||||||
|
the display number. When you start a new instance with `vncserver`, it prints
|
||||||
|
the display number it uses.
|
||||||
|
|
||||||
|
### logging.level
|
||||||
|
|
||||||
|
For the initial setup, it's a good idea to use 100. With 100, you're able to see
|
||||||
|
login attempts and reasons they fail.
|
@ -0,0 +1,763 @@
|
|||||||
|
### desktop.resolution.width, desktop.resolution.height
|
||||||
|
Set a fixed desktop resolution. Set `desktop.allow_resize` to `false` for
|
||||||
|
this to work.
|
||||||
|
|
||||||
|
Unit: pixels
|
||||||
|
<br>
|
||||||
|
Default: 1024x768
|
||||||
|
|
||||||
|
### desktop.allow_resize
|
||||||
|
If turned on, resizing browser window changes desktop resolution to fit the
|
||||||
|
window.
|
||||||
|
|
||||||
|
Set to `false` to have a fixed resolution (you need to set
|
||||||
|
[desktop.resolution.width and
|
||||||
|
desktop.resolution.heigh](#desktop.resolution.width%2C-desktop.resolution.height)).
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### desktop.pixel_depth
|
||||||
|
|
||||||
|
Pixel depth in bits. Possible values are 16, 24, 32. These values are fail-safe.
|
||||||
|
You can specify any bit value via command-line.
|
||||||
|
|
||||||
|
Unit: bits
|
||||||
|
<br>
|
||||||
|
Default: `24`
|
||||||
|
|
||||||
|
### network.protocol
|
||||||
|
With KasmVNC, you can use a web browser client (set value to `http`) or a
|
||||||
|
traditional VNC client (set value to `vnc`).
|
||||||
|
|
||||||
|
If you use `vnc` protocol, you're on your own. Passing `Xvnc` options via
|
||||||
|
command-line is required in this case. Most config settings are ignored.
|
||||||
|
|
||||||
|
Unit: `http`, `vnc`
|
||||||
|
<br>
|
||||||
|
Default: `http`
|
||||||
|
|
||||||
|
### network.interface
|
||||||
|
IP address or host name to listen on. Set to `0.0.0.0` to listen on all network
|
||||||
|
interfaces.
|
||||||
|
|
||||||
|
Unit: ip address or host name
|
||||||
|
<br>
|
||||||
|
Default: `0.0.0.0`
|
||||||
|
|
||||||
|
### network.websocket_port
|
||||||
|
|
||||||
|
Listen for websocket connections on this port. `auto` translates to 8443 + X
|
||||||
|
display number.
|
||||||
|
|
||||||
|
Unit: integer
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### network.use_ipv4
|
||||||
|
|
||||||
|
Use IPv4 for incoming and outgoing connections.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### network.use_ipv6
|
||||||
|
Use IPv6 for incoming and outgoing connections.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### network.udp.public_ip
|
||||||
|
|
||||||
|
KasmVNC server needs to know its IP from the perspective of the client. With `auto`,
|
||||||
|
it finds that out automatically (by using a public STUN server).
|
||||||
|
|
||||||
|
If the server and client are on the same private network, and not connected
|
||||||
|
to internet, you need to specify a private IP address of the server, accessible
|
||||||
|
to the client.
|
||||||
|
|
||||||
|
Unit: IP address, `auto`.
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### network.udp.port
|
||||||
|
|
||||||
|
Set UDP port to use. With `auto`,
|
||||||
|
value is inherited from [network.websocket_port](#network.websocket_port).
|
||||||
|
|
||||||
|
Unit: `auto`, integer
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### network.ssl.pem_certificate
|
||||||
|
|
||||||
|
SSL pem certificate to use for websocket connections.
|
||||||
|
|
||||||
|
Unit: path
|
||||||
|
<br>
|
||||||
|
Default: standard snake oil certificate on Debian-based distros. Auto-generated
|
||||||
|
on Fedora-based distros.
|
||||||
|
|
||||||
|
### network.ssl.pem_key
|
||||||
|
|
||||||
|
SSL pem key to use for websocket connections. If you have private and public
|
||||||
|
keys in one file, using
|
||||||
|
[network.ssl.pem_certificate](#network.ssl.pem_certificate) is enough to set
|
||||||
|
both.
|
||||||
|
|
||||||
|
Unit: path
|
||||||
|
<br>
|
||||||
|
Default: standard snake oil certificate on Debian-based distros. Auto-generated
|
||||||
|
on Fedora-based distros.
|
||||||
|
|
||||||
|
### network.ssl.require_ssl
|
||||||
|
|
||||||
|
Require SSL for websocket connections.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### user_session.session_type
|
||||||
|
|
||||||
|
* `shared` - allow multiple sessions for a single desktop.
|
||||||
|
* `exclusive` - only one user session can work with the desktop.
|
||||||
|
|
||||||
|
If set, overrides client settings.
|
||||||
|
|
||||||
|
When combined with
|
||||||
|
[concurrent_connections_prompt](#user_session.concurrent_connections_prompt) ,
|
||||||
|
the user is asked to let the incoming connection in.
|
||||||
|
|
||||||
|
Unit: `shared`, `exclusive`
|
||||||
|
<br>
|
||||||
|
Default: not set, client settings are used
|
||||||
|
|
||||||
|
## user_session.new_session_disconnects_existing_exclusive_session
|
||||||
|
|
||||||
|
Only applies if [session_type](#user_session.session_type) is set to
|
||||||
|
`exclusive`. The user working with the desktop is kicked out by a new session.
|
||||||
|
New session takes over the desktop.
|
||||||
|
|
||||||
|
When combined with
|
||||||
|
[concurrent_connections_prompt](#user_session.concurrent_connections_prompt),
|
||||||
|
the user is asked to confirm session takeover.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### user_session.concurrent_connections_prompt
|
||||||
|
|
||||||
|
Prompts the user of the desktop to explicitly accept or reject incoming
|
||||||
|
connections.
|
||||||
|
|
||||||
|
The vncconfig(1) program must be running on the desktop.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### user_session.concurrent_connections_prompt_timeout
|
||||||
|
|
||||||
|
Active only if
|
||||||
|
[concurrent_connections_prompt](#user_session.concurrent_connections_prompt),
|
||||||
|
is turned on.
|
||||||
|
|
||||||
|
Number of seconds to show the Accept Connection dialog before rejecting the
|
||||||
|
connection.
|
||||||
|
|
||||||
|
Unit: seconds
|
||||||
|
<br>
|
||||||
|
Default: `10`
|
||||||
|
|
||||||
|
### user_session.idle_timeout
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The number of seconds after which an idle session is dropped.
|
||||||
|
|
||||||
|
Unit: seconds
|
||||||
|
<br>
|
||||||
|
Default: `never`
|
||||||
|
|
||||||
|
### keyboard.remap_keys
|
||||||
|
|
||||||
|
Set up 1-to-1 character replacement. For example, to exchange the " and @
|
||||||
|
symbols you would specify the following: `0x22->0x40`. Similar to tr(1).
|
||||||
|
|
||||||
|
Unit: hex numbers in the format <from_character>-><to_character>
|
||||||
|
<br>
|
||||||
|
Default: not set
|
||||||
|
|
||||||
|
### keyboard.ignore_numlock
|
||||||
|
-- TODO
|
||||||
|
|
||||||
|
Key affected by NumLock often require a fake Shift to be inserted in order for
|
||||||
|
the correct symbol to be generated. Turning on this setting avoids these extra
|
||||||
|
fake Shift events but may result in a slightly different symbol (for example, a
|
||||||
|
Return instead of a keypad Enter).
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### keyboard.raw_keyboard
|
||||||
|
|
||||||
|
Send keyboard events straight through and avoid mapping them to the current
|
||||||
|
keyboard layout. This effectively makes the keyboard behave according to the
|
||||||
|
layout configured on the server instead of the layout configured on the client.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### pointer.enabled
|
||||||
|
|
||||||
|
Allows clicks from mice, trackpads, etc.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### runtime_configuration.allow_client_to_override_kasm_server_settings
|
||||||
|
|
||||||
|
KasmVNC exposes a few settings to the client the standard VNC does not. You can
|
||||||
|
let the client override these settings or forbid overriding.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### runtime_configuration.allow_override_standard_vnc_server_settings
|
||||||
|
|
||||||
|
If turned on, VNC settings defined in
|
||||||
|
[runtime_configuration.allow_override_list](#runtime_configuration.allow_override_list)
|
||||||
|
can be changed at runtime.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### runtime_configuration.allow_override_list
|
||||||
|
|
||||||
|
You can modify listed settings at runtime. Settings can be modified, for
|
||||||
|
example, using vncconfig(1) program from inside a running session.
|
||||||
|
|
||||||
|
The list must contain absolute config keys like
|
||||||
|
[pointer.enable](#pointer.enable). To actually change
|
||||||
|
[pointer.enable](#pointer.enable), you need to pass the corresponding
|
||||||
|
command-line option `AcceptPointerEvents` to vncconfig(1).
|
||||||
|
|
||||||
|
Unit: list of absolute config keys
|
||||||
|
<br>
|
||||||
|
Default:
|
||||||
|
```
|
||||||
|
pointer.enabled
|
||||||
|
data_loss_prevention.clipboard.server_to_client.enabled
|
||||||
|
data_loss_prevention.clipboard.client_to_server.enabled
|
||||||
|
data_loss_prevention.clipboard.server_to_client.primary_clipboard_enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### logging.log_writer_name
|
||||||
|
Log all subsystems of KasmVNC by setting to `all`. To log a specific subsystem,
|
||||||
|
consult source code for `LogWriter` instances. For example, `static LogWriter
|
||||||
|
vlog("STrayIcon");`. `STrayIcon` is the log writer name here.
|
||||||
|
|
||||||
|
Unit: `all`, \<log writer name found in source code\>
|
||||||
|
<br>
|
||||||
|
Default: `all`
|
||||||
|
|
||||||
|
### logging.log_dest
|
||||||
|
|
||||||
|
Log to the instance's log file in `~/.vnc`, or syslog.
|
||||||
|
|
||||||
|
Unit: `logfile`, `syslog`
|
||||||
|
<br>
|
||||||
|
Default: `logfile`
|
||||||
|
|
||||||
|
### logging.level
|
||||||
|
|
||||||
|
Logging verbosity level. Can be in the 0.\.100 range. 100 meaning most verbose
|
||||||
|
output, 0 meaning most concise.
|
||||||
|
|
||||||
|
Unit: integer in the 0.\.100 range
|
||||||
|
<br>
|
||||||
|
Default: `30`
|
||||||
|
|
||||||
|
### security.brute_force_protection.blacklist_threshold
|
||||||
|
|
||||||
|
The number of unauthenticated connection attempts allowed from any individual
|
||||||
|
host before that host is black-listed.
|
||||||
|
|
||||||
|
Unit: integer
|
||||||
|
<br>
|
||||||
|
Default: `5`
|
||||||
|
|
||||||
|
### security.brute_force_protection.blacklist_timeout
|
||||||
|
|
||||||
|
The initial timeout applied when a host is first black-listed by failing to
|
||||||
|
authenticate
|
||||||
|
[blacklist_threshold](#security.brute_force_protection.blacklist_threshold)
|
||||||
|
times. The host cannot re-attempt a connection until the timeout expires.
|
||||||
|
|
||||||
|
Unit: seconds
|
||||||
|
<br>
|
||||||
|
Default: `10`
|
||||||
|
|
||||||
|
### data_loss_prevention.visible_region.top, data_loss_prevention.visible_region.left, data_loss_prevention.visible_region.right, data_loss_prevention.visible_region.bottom
|
||||||
|
|
||||||
|
The regions feature allows you to select a region of the screen to render to the
|
||||||
|
user. Concealed portions of the screen are blacked out.
|
||||||
|
|
||||||
|
#### Absolute coordinates
|
||||||
|
|
||||||
|
Select a region using pixels:
|
||||||
|
```
|
||||||
|
top: 10
|
||||||
|
left: 10
|
||||||
|
right: 40
|
||||||
|
bottom: 40
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Offset coordinates
|
||||||
|
|
||||||
|
Use negative numbers to offset from the screen boundary. For `left` and `top`,
|
||||||
|
this means 0 plus the provided number. In the below example that would be 10.
|
||||||
|
For `right` and `bottom`, that means the maximum horizontal or vertical
|
||||||
|
resolution minus the provided number.
|
||||||
|
|
||||||
|
If the resolution was 1080x720 in the below example that would equate to a
|
||||||
|
`right` of 1070 and `bottom` of 710. Therefore, the example below would be
|
||||||
|
translated to 10, 10, 1070, 710. Using offset coordinates has an advantage of
|
||||||
|
scaling with screen size changes versus using absolute values.
|
||||||
|
|
||||||
|
Select a region using pixel offsets:
|
||||||
|
```
|
||||||
|
top: -10
|
||||||
|
left: -10
|
||||||
|
right: -10
|
||||||
|
bottom: -10
|
||||||
|
```
|
||||||
|
|
||||||
|
You can combine absolute values with offset values, such as the following
|
||||||
|
example:
|
||||||
|
|
||||||
|
```
|
||||||
|
top: 50
|
||||||
|
left: 10
|
||||||
|
right: -10
|
||||||
|
bottom: -10
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Percentages
|
||||||
|
|
||||||
|
Regions does support percent values, which are evaluated as a border that is a
|
||||||
|
percent of the total width and height respectively. Regions does not support
|
||||||
|
mixing percent values and absolute or offset values.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
top: 10%
|
||||||
|
left: 10%
|
||||||
|
right: 20%
|
||||||
|
bottom: 20%
|
||||||
|
```
|
||||||
|
|
||||||
|
Unit: pixels, offset pixels, percentages
|
||||||
|
<br>
|
||||||
|
Default: not set
|
||||||
|
|
||||||
|
### data_loss_prevention.visible_region.concealed_region.allow_click_down
|
||||||
|
Allow mouse button down events within the concealed regions, by default they are
|
||||||
|
blocked.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### data_loss_prevention.visible_region.concealed_region.allow_click_release
|
||||||
|
|
||||||
|
Allow mouse button releases within the concealed regions, by default they are
|
||||||
|
blocked until the cursor returns to the visible region.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.delay_between_operations
|
||||||
|
|
||||||
|
This many milliseconds must pass between clipboard actions.
|
||||||
|
|
||||||
|
Unit: milliseconds, `none`
|
||||||
|
<br>
|
||||||
|
Default: `none`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.allow_mimetypes
|
||||||
|
|
||||||
|
Allowed binary clipboard mimetypes.
|
||||||
|
|
||||||
|
Unit: mimetype
|
||||||
|
<br>
|
||||||
|
Default:
|
||||||
|
```
|
||||||
|
chromium/x-web-custom-data
|
||||||
|
text/html
|
||||||
|
image/png`
|
||||||
|
```
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.server_to_client.enabled
|
||||||
|
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
Whether to send desktop clipboard changes to clients.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.server_to_client.size
|
||||||
|
|
||||||
|
Limit clipboard bytes to send to clients in one transaction.
|
||||||
|
|
||||||
|
Unit: number of bytes, `unlimited`
|
||||||
|
<br>
|
||||||
|
Default: `unlimited`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.server_to_client.primary_clipboard_enabled
|
||||||
|
|
||||||
|
Send the primary selection to the client. Meaning, mouse-selected text is copied
|
||||||
|
to clipboard. Only works in Chromium-based browsers.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.client_to_server.enabled
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
Accept clipboard updates from clients.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
### data_loss_prevention.clipboard.client_to_server.size
|
||||||
|
|
||||||
|
Limit clipboard bytes to receive from clients in one transaction.
|
||||||
|
|
||||||
|
Unit: number of bytes, `unlimited`
|
||||||
|
<br>
|
||||||
|
Default: `unlimited`
|
||||||
|
|
||||||
|
### data_loss_prevention.keyboard.enabled
|
||||||
|
|
||||||
|
Accept key press and release events from clients.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### data_loss_prevention.keyboard.rate_limit
|
||||||
|
|
||||||
|
Reject keyboard presses over this many per second.
|
||||||
|
|
||||||
|
Unit: integer, `unlimited`
|
||||||
|
<br>
|
||||||
|
Default: `unlimited`
|
||||||
|
|
||||||
|
### data_loss_prevention.logging.level
|
||||||
|
|
||||||
|
Log clipboard and keyboard actions. `info` logs just clipboard direction and
|
||||||
|
size, `verbose` adds the contents for both.
|
||||||
|
|
||||||
|
Unit: `off`, `info`, `verbose`
|
||||||
|
<br>
|
||||||
|
Default: `off`
|
||||||
|
|
||||||
|
### encoding.max_frame_rate
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The maximum number of updates per second sent to clients. If the screen
|
||||||
|
updates any faster then those changes are aggregated and sent in a single
|
||||||
|
update to the client. Note that this only controls the maximum rate and a client
|
||||||
|
may get a lower rate when resources are limited.
|
||||||
|
|
||||||
|
Unit: integer
|
||||||
|
<br>
|
||||||
|
Default: `60`
|
||||||
|
|
||||||
|
### encoding.full_frame_updates
|
||||||
|
|
||||||
|
KasmVNC cuts the screen up into smaller rectangles and only sends sections of
|
||||||
|
the screen that change. When using UDP, some rectangles can be dropped, so this
|
||||||
|
option forces a full screen update every X frames.
|
||||||
|
|
||||||
|
Unit: `none`, positive integer
|
||||||
|
<br>
|
||||||
|
Default: `none`
|
||||||
|
|
||||||
|
### encoding.rect_encoding_mode.min_quality
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The minimum quality setting for JPEG/WEBP encoding. Rendering automatically
|
||||||
|
degrades JPEG quality when there is a lot of motion in a particular block. This
|
||||||
|
setting controls the minimum quality to use when there is a high degree of
|
||||||
|
change. The accepted values are 0.\.9 where 0 is low and 9 is high.
|
||||||
|
|
||||||
|
Unit: integer in the 0.\.9 range
|
||||||
|
<br>
|
||||||
|
Default: `7`
|
||||||
|
|
||||||
|
### encoding.rect_encoding_mode.max_quality
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The maximum quality setting for JPEG/WEBP encoding. Rendering automatically
|
||||||
|
degrades JPEG quality when there is a lot of motion in a particular block. This
|
||||||
|
setting controls the maximum quality to use when there is no or little motion.
|
||||||
|
The accepted values are 0.\.9 where 0 is low and 9 is high.
|
||||||
|
|
||||||
|
Unit: integer in the 0.\.9 range
|
||||||
|
<br>
|
||||||
|
Default: `8`
|
||||||
|
|
||||||
|
### encoding.rect_encoding_mode.consider_lossless_quality
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
KasmVNC dynamically adjusts the JPEG/WEBP quality based on how much change
|
||||||
|
there is in a particular section. Every x number of frames it sends a
|
||||||
|
'lossless' update. That way, if you are scrolling, the text blurs a little while
|
||||||
|
you scroll but as soon as you stop, it should clear up on the next lossless
|
||||||
|
update. `consider_lossless_quality` means "treat this quality as lossless."
|
||||||
|
Assuming the min quality of 3, the max of 7 and treat lossless of 5. KasmVNC
|
||||||
|
would constantly adjust the quality of images sent anywhere from 3 to 7
|
||||||
|
depending on the rate of change. If the last rectangle sent was at 5 then it
|
||||||
|
would not send a lossless update for that part of the screen.
|
||||||
|
|
||||||
|
Unit: integer in the 0.\.10 range
|
||||||
|
<br>
|
||||||
|
Default: `10`
|
||||||
|
|
||||||
|
### encoding.rect_encoding_mode.rectangle_compress_threads
|
||||||
|
|
||||||
|
Use this many threads to compress rectangles in parallel. `auto` sets threads to
|
||||||
|
match the core count.
|
||||||
|
|
||||||
|
Unit: integer, `auto`.
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.jpeg_quality
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The JPEG quality to use when in video mode. The accepted values are 0.\.9 where
|
||||||
|
0 is low and 9 is high. A value of -1 keeps the quality level used in normal
|
||||||
|
mode.
|
||||||
|
|
||||||
|
Unit: integer in the -1..9 range
|
||||||
|
<br>
|
||||||
|
Default: `-1`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.webp_quality
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The WEBP quality to use when in video mode. The accepted values are 0.\.9 where 0
|
||||||
|
is low and 9 is high. A value of -1 keeps the quality level used in normal mode.
|
||||||
|
|
||||||
|
Unit: integer in the -1..9 range
|
||||||
|
<br>
|
||||||
|
Default: `-1`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.max_resolution.width, encoding.video_encoding_mode.max_resolution.height
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
When in Video Mode, downscale the screen to this maximum size. Keeps aspect
|
||||||
|
ratio with client's actual resolution.
|
||||||
|
|
||||||
|
Unit: pixels
|
||||||
|
<br>
|
||||||
|
Default: 1920x1080
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.enter_video_encoding_mode.time_threshold
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
Number of seconds that a high rate of change most occur before switching to
|
||||||
|
video mode. Setting to 0 forces Video Mode at all times.
|
||||||
|
|
||||||
|
Unit: seconds
|
||||||
|
<br>
|
||||||
|
Default: `5`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.enter_video_encoding_mode.area_threshold
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The percent of the screen that must be seeing high rates of change to meet the
|
||||||
|
threshold of Video Mode. This percentage of the screen must see rapid changes
|
||||||
|
for the amount of time specified by [encoding.video_encoding_mode.enter_video_encoding_mode.time_threshold](#encoding.video_encoding_mode.enter_video_encoding_mode.time_threshold).
|
||||||
|
|
||||||
|
Unit: percentage of screen
|
||||||
|
<br>
|
||||||
|
Default: `45%`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.exit_video_encoding_mode.time_threshold
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
When in Video Mode, high rates of change must subside for this many seconds
|
||||||
|
before dropping out of video mode.
|
||||||
|
|
||||||
|
Unit: seconds
|
||||||
|
<br>
|
||||||
|
Default: `3`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.logging.level
|
||||||
|
|
||||||
|
Print the detected video area % value. This is useful when trying to tune your
|
||||||
|
settings for your particular use case.
|
||||||
|
|
||||||
|
Unit: `info`, `off`
|
||||||
|
<br>
|
||||||
|
Default: `off`
|
||||||
|
|
||||||
|
### encoding.video_encoding_mode.scaling_algorithm
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
The scaling method to use in video mode.
|
||||||
|
|
||||||
|
Unit: `nearest`, `bilinear`, `progressive_bilinear`
|
||||||
|
<br>
|
||||||
|
Default: `progressive_bilinear`
|
||||||
|
|
||||||
|
### encoding.compare_framebuffer
|
||||||
|
|
||||||
|
Perform pixel comparison on frame buffer to reduce unnecessary updates.
|
||||||
|
|
||||||
|
Unit: `off`, `always`, `auto`
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### encoding.zrle_zlib_level
|
||||||
|
⚠️ This setting only applies, when
|
||||||
|
[allow_client_to_override_kasm_server_settings](#runtime_configuration.allow_client_to_override_kasm_server_settings)
|
||||||
|
is turned off.
|
||||||
|
|
||||||
|
Zlib compression level for ZRLE encoding (it does not affect Tight encoding).
|
||||||
|
Acceptable values are between 0.\.9. Set to `auto` to use the standard
|
||||||
|
compression level provided by the zlib(3) compression library.
|
||||||
|
|
||||||
|
Unit: integer in the 0.\.9 range, `auto`
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### encoding.hextile_improved_compression
|
||||||
|
|
||||||
|
Use improved compression algorithm for Hextile encoding which achieves better
|
||||||
|
compression ratios by the cost of using slightly more CPU time.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
### server.advanced.x_font_path
|
||||||
|
|
||||||
|
Specify X font path.
|
||||||
|
|
||||||
|
Unit: path, `auto`
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### server.advanced.httpd_directory
|
||||||
|
|
||||||
|
Run a mini-HTTP server which serves files from the given directory. Normally the
|
||||||
|
directory contains the kasmweb client.
|
||||||
|
|
||||||
|
Unit: path
|
||||||
|
<br>
|
||||||
|
Default: `/usr/share/kasmvnc/www`
|
||||||
|
|
||||||
|
### server.advanced.kasm_password_file
|
||||||
|
|
||||||
|
Password file for Basic authentication, created with the `kasmvncpasswd(1)`
|
||||||
|
utility.
|
||||||
|
|
||||||
|
Unit: path
|
||||||
|
<br>
|
||||||
|
Default: `${HOME}/.kasmpasswd`
|
||||||
|
|
||||||
|
### server.advanced.x_authority_file
|
||||||
|
|
||||||
|
Set to X authority file. X authority file stores credentials in cookies used by
|
||||||
|
`xauth(1)` for authentication of X sessions.
|
||||||
|
|
||||||
|
`auto` means using file defined in `$XAUTHORITY` environment variable. If the
|
||||||
|
variable isn't defined, fallback to using `${HOME}/.Xauthority` file.
|
||||||
|
|
||||||
|
Unit: path
|
||||||
|
<br>
|
||||||
|
Default: `auto`
|
||||||
|
|
||||||
|
### server.auto_shutdown.no_user_session_timeout
|
||||||
|
|
||||||
|
Terminate KasmVNC when no client has been connected for this many
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
Unit: seconds, `never`
|
||||||
|
<br>
|
||||||
|
Default: `never`
|
||||||
|
|
||||||
|
### server.auto_shutdown.active_user_session_timeout
|
||||||
|
|
||||||
|
Terminate KasmVNC when a client has been connected for this many
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
Unit: seconds, `never`
|
||||||
|
<br>
|
||||||
|
Default: `never`
|
||||||
|
|
||||||
|
### server.auto_shutdown.inactive_user_session_timeout
|
||||||
|
|
||||||
|
Terminate KasmVNC after this many seconds of user inactivity.
|
||||||
|
|
||||||
|
Unit: seconds, `never`
|
||||||
|
<br>
|
||||||
|
Default: `never`
|
||||||
|
|
||||||
|
### command_line.prompt
|
||||||
|
|
||||||
|
Guide the user (by prompting), to ensure that KasmVNC is usable. For example,
|
||||||
|
prompt to create a KasmVNC user if no users exist or no users can control the
|
||||||
|
desktop.
|
||||||
|
|
||||||
|
Unit: boolean
|
||||||
|
<br>
|
||||||
|
Default: `true`
|
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
export PYTHONPATH=${PWD}/spec
|
||||||
|
|
||||||
|
RUN_CMD="pipenv run mamba"
|
||||||
|
|
||||||
|
specs=()
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" = "-d" ]]; then
|
||||||
|
export KASMVNC_SPEC_DEBUG_OUTPUT=1
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
if [[ "$arg" = "-v" ]]; then
|
||||||
|
verbose=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
specs+=("$arg")
|
||||||
|
done
|
||||||
|
set -- "${specs[@]}"
|
||||||
|
|
||||||
|
if [[ "$1" = "-h" || "$1" = "--help" ]]; then
|
||||||
|
echo >&2 "Usage: $(basename "$0") [-d] [-v] <spec...>"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$verbose" ]]; then
|
||||||
|
RUN_CMD="$RUN_CMD --format=documentation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
$RUN_CMD "$@"
|
||||||
|
else
|
||||||
|
$RUN_CMD spec/*_spec.py
|
||||||
|
fi
|
@ -0,0 +1,84 @@
|
|||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
allow_resize: true
|
||||||
|
pixel_depth: 24
|
||||||
|
|
||||||
|
network:
|
||||||
|
protocol: http
|
||||||
|
interface: 0.0.0.0
|
||||||
|
websocket_port: auto
|
||||||
|
use_ipv4: true
|
||||||
|
use_ipv6: true
|
||||||
|
ssl:
|
||||||
|
pem_certificate: /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||||
|
pem_key: /etc/ssl/private/ssl-cert-snakeoil.key
|
||||||
|
require_ssl: true
|
||||||
|
|
||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 5
|
||||||
|
blacklist_timeout: 10
|
||||||
|
|
||||||
|
data_loss_prevention:
|
||||||
|
visible_region:
|
||||||
|
# top: 10
|
||||||
|
# left: 10
|
||||||
|
# right: 40
|
||||||
|
# bottom: 40
|
||||||
|
concealed_region:
|
||||||
|
allow_click_down: false
|
||||||
|
allow_click_release: false
|
||||||
|
clipboard:
|
||||||
|
delay_between_operations: none
|
||||||
|
server_to_client:
|
||||||
|
enabled: true
|
||||||
|
size: 10000
|
||||||
|
primary_clipboard_enabled: false
|
||||||
|
client_to_server:
|
||||||
|
enabled: true
|
||||||
|
size: 10000
|
||||||
|
keyboard:
|
||||||
|
enabled: true
|
||||||
|
rate_limit: unlimited
|
||||||
|
logging:
|
||||||
|
level: off
|
||||||
|
|
||||||
|
encoding:
|
||||||
|
max_frame_rate: 60
|
||||||
|
rect_encoding_mode:
|
||||||
|
min_quality: 7
|
||||||
|
max_quality: 8
|
||||||
|
consider_lossless_quality: 10
|
||||||
|
rectangle_compress_threads: auto
|
||||||
|
|
||||||
|
video_encoding_mode:
|
||||||
|
jpeg_quality: -1
|
||||||
|
webp_quality: -1
|
||||||
|
max_resolution:
|
||||||
|
width: 1920
|
||||||
|
height: 1080
|
||||||
|
enter_video_encoding_mode:
|
||||||
|
time_threshold: 5
|
||||||
|
area_threshold: 45%
|
||||||
|
exit_video_encoding_mode:
|
||||||
|
time_threshold: 3
|
||||||
|
logging:
|
||||||
|
level: off
|
||||||
|
scaling_algorithm: progressive_bilinear
|
||||||
|
|
||||||
|
compare_framebuffer: auto
|
||||||
|
zrle_zlib_level: auto
|
||||||
|
hextile_improved_compression: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
x_font_path: auto
|
||||||
|
httpd_directory: /usr/share/kasmvnc/www
|
||||||
|
kasm_password_file: ~/.kasmpasswd
|
||||||
|
x_authority_file: auto
|
||||||
|
auto_shutdown:
|
||||||
|
no_user_session_timeout: never
|
||||||
|
active_user_session_timeout: never
|
||||||
|
inactive_user_session_timeout: never
|
@ -0,0 +1,3 @@
|
|||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 6
|
@ -0,0 +1,4 @@
|
|||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 7
|
||||||
|
blacklist_timeout: 12
|
@ -0,0 +1,93 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import pexpect
|
||||||
|
from path import Path
|
||||||
|
from expects import expect, equal
|
||||||
|
|
||||||
|
vncserver_cmd = 'vncserver :1'
|
||||||
|
running_xvnc = False
|
||||||
|
debug_output = False
|
||||||
|
config_dir = "spec/tmp"
|
||||||
|
config_filename = os.path.join(config_dir, "config.yaml")
|
||||||
|
|
||||||
|
if os.getenv('KASMVNC_SPEC_DEBUG_OUTPUT'):
|
||||||
|
debug_output = True
|
||||||
|
|
||||||
|
|
||||||
|
def write_config(config_text):
|
||||||
|
os.makedirs(config_dir, exist_ok=True)
|
||||||
|
|
||||||
|
with open(config_filename, "w") as f:
|
||||||
|
f.write(config_text)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_env():
|
||||||
|
clean_kasm_users()
|
||||||
|
|
||||||
|
home_dir = os.environ['HOME']
|
||||||
|
vnc_dir = os.path.join(home_dir, ".vnc")
|
||||||
|
Path(vnc_dir).rmtree(ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_kasm_users():
|
||||||
|
home_dir = os.environ['HOME']
|
||||||
|
password_file = os.path.join(home_dir, ".kasmpasswd")
|
||||||
|
Path(password_file).remove_p()
|
||||||
|
|
||||||
|
|
||||||
|
def start_xvnc_pexpect(extra_args="", **kwargs):
|
||||||
|
global running_xvnc
|
||||||
|
|
||||||
|
# ":;" is a hack. Without it, Xvnc doesn't run. No idea what happens, but
|
||||||
|
# when I run top, Xvnc just isn't there. I suspect a race.
|
||||||
|
child = pexpect.spawn('/bin/bash',
|
||||||
|
['-ic', f':;{vncserver_cmd} {extra_args}'],
|
||||||
|
timeout=5, encoding='utf-8', **kwargs)
|
||||||
|
if debug_output:
|
||||||
|
child.logfile_read = sys.stderr
|
||||||
|
|
||||||
|
running_xvnc = True
|
||||||
|
|
||||||
|
return child
|
||||||
|
|
||||||
|
|
||||||
|
def start_xvnc(extra_args="", **kwargs):
|
||||||
|
global running_xvnc
|
||||||
|
completed_process = run_cmd(f'{vncserver_cmd} {extra_args}',
|
||||||
|
print_stderr=False, **kwargs)
|
||||||
|
if completed_process.returncode == 0:
|
||||||
|
running_xvnc = True
|
||||||
|
|
||||||
|
return completed_process
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd(cmd, print_stderr=True, **kwargs):
|
||||||
|
completed_process = subprocess.run(cmd, shell=True, text=True,
|
||||||
|
capture_output=True,
|
||||||
|
executable='/bin/bash', **kwargs)
|
||||||
|
if debug_output:
|
||||||
|
if len(completed_process.stderr) > 0:
|
||||||
|
print(completed_process.stderr)
|
||||||
|
if len(completed_process.stdout) > 0:
|
||||||
|
print(completed_process.stdout)
|
||||||
|
elif print_stderr:
|
||||||
|
if len(completed_process.stderr) > 0:
|
||||||
|
print(completed_process.stderr)
|
||||||
|
|
||||||
|
return completed_process
|
||||||
|
|
||||||
|
|
||||||
|
def add_kasmvnc_user_docker():
|
||||||
|
completed_process = run_cmd('echo -e "password\\npassword\\n" | vncpasswd -u docker -w')
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
|
||||||
|
def kill_xvnc():
|
||||||
|
global running_xvnc
|
||||||
|
if not running_xvnc:
|
||||||
|
return
|
||||||
|
|
||||||
|
run_cmd('vncserver -kill :1', print_stderr=False)
|
||||||
|
running_xvnc = False
|
@ -0,0 +1,42 @@
|
|||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
allow_resize: true
|
||||||
|
pixel_depth: 24
|
||||||
|
|
||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 1
|
||||||
|
blacklist_timeout: 10
|
||||||
|
|
||||||
|
logging:
|
||||||
|
log_writer_name: all
|
||||||
|
log_dest: logfile
|
||||||
|
# 0 - silent(?), 100 - most verbose
|
||||||
|
level: 100
|
||||||
|
|
||||||
|
data_loss_prevention:
|
||||||
|
visible_region:
|
||||||
|
# top: 10
|
||||||
|
# left: 10
|
||||||
|
# right: 40
|
||||||
|
# bottom: 40
|
||||||
|
concealed_region:
|
||||||
|
allow_click_down: false
|
||||||
|
allow_click_release: false
|
||||||
|
clipboard:
|
||||||
|
delay_between_operations: none
|
||||||
|
# Cut buffers and CLIPBOARD selection.
|
||||||
|
server_to_client:
|
||||||
|
enabled: true
|
||||||
|
size: unlimited
|
||||||
|
primary_clipboard_enabled: false
|
||||||
|
|
||||||
|
keyboard:
|
||||||
|
remap_keys:
|
||||||
|
- 0x22->0x40
|
||||||
|
- 0x24->0x40
|
||||||
|
|
||||||
|
command_line:
|
||||||
|
prompt: true
|
@ -0,0 +1,10 @@
|
|||||||
|
from mamba import description, context, it, fit, before, after
|
||||||
|
from expects import expect, equal, contain, match
|
||||||
|
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
|
||||||
|
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect, \
|
||||||
|
write_config, config_filename
|
||||||
|
|
||||||
|
with description("Perl warnings"):
|
||||||
|
with it("treats Perl warning as error"):
|
||||||
|
completed_process = run_cmd("vncserver -dry-run")
|
||||||
|
expect(completed_process.stderr).not_to(match(r'line \d+\.$'))
|
@ -0,0 +1,155 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pexpect
|
||||||
|
from mamba import description, context, it, fit, before, after
|
||||||
|
from expects import expect, equal
|
||||||
|
|
||||||
|
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
|
||||||
|
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect
|
||||||
|
|
||||||
|
# WIP. Plan to move to start_xvnc_pexpect(), because pexpect provides a way to
|
||||||
|
# wait for vncserver output. start_xvnc() just blindly prints input to vncserver
|
||||||
|
# without knowing what it prints back.
|
||||||
|
|
||||||
|
|
||||||
|
def select_de(de_name):
|
||||||
|
try:
|
||||||
|
extra_args = f'-select-de {de_name}'
|
||||||
|
completed_process = start_xvnc(extra_args)
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
finally:
|
||||||
|
kill_xvnc()
|
||||||
|
|
||||||
|
|
||||||
|
def check_de_was_setup_to_run(de_name):
|
||||||
|
completed_process = run_cmd(f'grep -q {de_name} ~/.vnc/xstartup')
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
|
||||||
|
with description('vncserver') as self:
|
||||||
|
with before.each:
|
||||||
|
clean_env()
|
||||||
|
with after.each:
|
||||||
|
kill_xvnc()
|
||||||
|
|
||||||
|
with context('on the first run'):
|
||||||
|
with before.each:
|
||||||
|
add_kasmvnc_user_docker()
|
||||||
|
|
||||||
|
with it('asks user to select a DE'):
|
||||||
|
child = start_xvnc_pexpect()
|
||||||
|
child.expect(r'\[1\] Cinnamon.+Manually edit')
|
||||||
|
child.sendline("1\n")
|
||||||
|
child.wait()
|
||||||
|
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
check_de_was_setup_to_run('cinnamon')
|
||||||
|
|
||||||
|
with it("doesn't prompt user to select a DE if prompting is disabled"):
|
||||||
|
child = start_xvnc_pexpect("-prompt 0")
|
||||||
|
child.expect(r'Warning: the Desktop Environment.+wasn\'t selected')
|
||||||
|
child.wait()
|
||||||
|
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
|
||||||
|
with it('asks to select a DE, when ran with -select-de'):
|
||||||
|
child = start_xvnc_pexpect('-select-de')
|
||||||
|
child.expect(r'\[1\] Cinnamon.+Manually edit')
|
||||||
|
child.sendline("1\n")
|
||||||
|
child.wait()
|
||||||
|
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
check_de_was_setup_to_run('cinnamon')
|
||||||
|
|
||||||
|
with it("doesn't prompt user to select a DE if prompting is disabled"):
|
||||||
|
child = start_xvnc_pexpect("-select-de -prompt 0")
|
||||||
|
child.expect(r'Warning: the Desktop Environment.+wasn\'t selected')
|
||||||
|
child.wait()
|
||||||
|
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
|
||||||
|
with it('selects passed DE with -s'):
|
||||||
|
select_de('mate')
|
||||||
|
check_de_was_setup_to_run('mate')
|
||||||
|
|
||||||
|
with it('selects manual xstartup editing, not a DE'):
|
||||||
|
select_de('manual')
|
||||||
|
check_de_was_setup_to_run('twm')
|
||||||
|
|
||||||
|
with context('after DE was selected'):
|
||||||
|
with before.each:
|
||||||
|
add_kasmvnc_user_docker()
|
||||||
|
|
||||||
|
with it("don't ask to choose DE by default"):
|
||||||
|
select_de('mate')
|
||||||
|
|
||||||
|
completed_process = start_xvnc()
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
check_de_was_setup_to_run('mate')
|
||||||
|
|
||||||
|
with it('asks to select a DE, when ran with -select-de'):
|
||||||
|
select_de('mate')
|
||||||
|
|
||||||
|
choose_cinnamon_and_answer_yes = "1\ny\n"
|
||||||
|
completed_process = start_xvnc('-select-de',
|
||||||
|
input=choose_cinnamon_and_answer_yes)
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
check_de_was_setup_to_run('cinnamon')
|
||||||
|
|
||||||
|
with it("doesn't ask user to select a DE if prompting is disabled"):
|
||||||
|
child = start_xvnc_pexpect("-select-de -prompt 0")
|
||||||
|
child.expect(r'Warning: the Desktop Environment.+wasn\'t selected')
|
||||||
|
child.wait()
|
||||||
|
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
|
||||||
|
with it('selects passed DE with -s'):
|
||||||
|
select_de('mate')
|
||||||
|
|
||||||
|
completed_process = start_xvnc('-select-de cinnamon')
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
check_de_was_setup_to_run('cinnamon')
|
||||||
|
|
||||||
|
with context('guided user creation'):
|
||||||
|
with it('asks to create a user if none exist'):
|
||||||
|
clean_kasm_users()
|
||||||
|
|
||||||
|
child = start_xvnc_pexpect('-select-de cinnamon')
|
||||||
|
child.expect('Provide selection number:')
|
||||||
|
child.sendline('1')
|
||||||
|
child.expect('Enter username')
|
||||||
|
child.sendline()
|
||||||
|
child.expect('Password:')
|
||||||
|
child.sendline('password')
|
||||||
|
child.expect('Verify:')
|
||||||
|
child.sendline('password')
|
||||||
|
child.expect(pexpect.EOF)
|
||||||
|
child.close()
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
|
||||||
|
home_dir = os.environ['HOME']
|
||||||
|
user = os.environ['USER']
|
||||||
|
completed_process = run_cmd(f'grep -qw {user} {home_dir}/.kasmpasswd')
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
||||||
|
|
||||||
|
with it('specify custom username'):
|
||||||
|
custom_username = 'custom_username'
|
||||||
|
child = start_xvnc_pexpect('-select-de cinnamon')
|
||||||
|
child.expect('Provide selection number:')
|
||||||
|
child.sendline('1')
|
||||||
|
child.expect('Enter username')
|
||||||
|
child.sendline(custom_username)
|
||||||
|
child.expect('Password:')
|
||||||
|
child.sendline('password')
|
||||||
|
child.expect('Verify:')
|
||||||
|
child.sendline('password')
|
||||||
|
child.expect(pexpect.EOF)
|
||||||
|
child.wait()
|
||||||
|
expect(child.exitstatus).to(equal(0))
|
||||||
|
|
||||||
|
home_dir = os.environ['HOME']
|
||||||
|
completed_process = run_cmd(f'grep -qw {custom_username} {home_dir}/.kasmpasswd')
|
||||||
|
expect(completed_process.returncode).to(equal(0))
|
@ -0,0 +1,297 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from os.path import expanduser
|
||||||
|
from mamba import description, context, fcontext, it, fit, before, after
|
||||||
|
from expects import expect, equal, contain, match
|
||||||
|
|
||||||
|
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
|
||||||
|
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect, \
|
||||||
|
write_config, config_filename
|
||||||
|
|
||||||
|
home_dir = expanduser("~")
|
||||||
|
vnc_dir = f'{home_dir}/.vnc'
|
||||||
|
user_config = f'{vnc_dir}/kasmvnc.yaml'
|
||||||
|
|
||||||
|
|
||||||
|
def run_vncserver():
|
||||||
|
return run_cmd(f'vncserver -dry-run -config {config_filename}')
|
||||||
|
|
||||||
|
|
||||||
|
def pick_cli_option(cli_option, xvnc_cmd):
|
||||||
|
cli_option_regex = re.compile(f'\'?-{cli_option}\'?(?:\s+[^-][^\s]*|$)')
|
||||||
|
results = cli_option_regex.findall(xvnc_cmd)
|
||||||
|
if len(results) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ' '.join(results)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_env():
|
||||||
|
os.makedirs(vnc_dir, exist_ok=True)
|
||||||
|
shutil.copyfile('spec/kasmvnc.yaml', user_config)
|
||||||
|
|
||||||
|
|
||||||
|
with description('YAML to CLI') as self:
|
||||||
|
with before.all:
|
||||||
|
prepare_env()
|
||||||
|
|
||||||
|
with context("convert a boolean key"):
|
||||||
|
with it("convert true to 1"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
allow_resize: true
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('AcceptSetDesktopSize',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-AcceptSetDesktopSize '1'"))
|
||||||
|
|
||||||
|
with it("convert false to 0"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
allow_resize: false
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('AcceptSetDesktopSize',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-AcceptSetDesktopSize '0'"))
|
||||||
|
|
||||||
|
with it("converts a numeric key to a CLI arg"):
|
||||||
|
write_config('''
|
||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 2
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('BlacklistThreshold',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-BlacklistThreshold '2'"))
|
||||||
|
|
||||||
|
with it("converts an ANY key to a CLI arg"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
ssl:
|
||||||
|
pem_certificate: /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('cert',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(
|
||||||
|
equal("-cert '/etc/ssl/certs/ssl-cert-snakeoil.pem'"))
|
||||||
|
|
||||||
|
with it("converts an array key to a CLI arg"):
|
||||||
|
write_config('''
|
||||||
|
keyboard:
|
||||||
|
remap_keys:
|
||||||
|
- 0x22->0x40
|
||||||
|
- 0x24->0x40
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('RemapKeys',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(
|
||||||
|
equal("-RemapKeys '0x22->0x40,0x24->0x40'"))
|
||||||
|
|
||||||
|
with it("converts a constant value to the corresponding numeric value"):
|
||||||
|
write_config('''
|
||||||
|
data_loss_prevention:
|
||||||
|
clipboard:
|
||||||
|
server_to_client:
|
||||||
|
size: 20
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('DLP_ClipSendMax',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-DLP_ClipSendMax '20'"))
|
||||||
|
|
||||||
|
with context("websocketPort"):
|
||||||
|
with it("converts 'auto' value to calculated value"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
websocket_port: auto
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('websocketPort',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(["-websocketPort '8444'", "-websocketPort '8445'"]). \
|
||||||
|
to(contain(cli_option))
|
||||||
|
|
||||||
|
with it("passes numeric value to CLI option"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
websocket_port: 8555
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('websocketPort',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-websocketPort '8555'"))
|
||||||
|
|
||||||
|
with it("no key - no CLI option"):
|
||||||
|
write_config('''
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('websocketPort',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal(None))
|
||||||
|
|
||||||
|
with context("option that can yield nothing"):
|
||||||
|
with it("converts a config value that yields nothing"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
protocol: http
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('noWebsocket',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal(None))
|
||||||
|
|
||||||
|
with it("converts a config value that yields CLI option"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
protocol: vnc
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('noWebsocket',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-noWebsocket '1'"))
|
||||||
|
|
||||||
|
with it("interpolates env variable"):
|
||||||
|
write_config('''
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
kasm_password_file: ${HOME}/.kasmpasswd
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('KasmPasswordFile',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-KasmPasswordFile '/home/docker/.kasmpasswd'"))
|
||||||
|
|
||||||
|
with it("converts logging options into one -Log"):
|
||||||
|
write_config('''
|
||||||
|
logging:
|
||||||
|
log_writer_name: all
|
||||||
|
log_dest: logfile
|
||||||
|
level: 40
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('Log',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-Log '*:stdout:40'"))
|
||||||
|
|
||||||
|
with it("converts DLP region options into one -DLP_Region"):
|
||||||
|
write_config('''
|
||||||
|
data_loss_prevention:
|
||||||
|
visible_region:
|
||||||
|
top: -10
|
||||||
|
left: 10
|
||||||
|
right: 40%
|
||||||
|
bottom: 40
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('DLP_Region',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-DLP_Region '10,-10,40%,40'"))
|
||||||
|
|
||||||
|
with context("converts x_font_path"):
|
||||||
|
with it("auto"):
|
||||||
|
write_config('''
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
x_font_path: auto
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('fp',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(match(r'/usr/share/fonts'))
|
||||||
|
|
||||||
|
with it("none specified"):
|
||||||
|
write_config('''
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('fp',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(match(r'/usr/share/fonts'))
|
||||||
|
|
||||||
|
with it("path specified"):
|
||||||
|
write_config('''
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
x_font_path: /src
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('fp', completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-fp '/src'"))
|
||||||
|
|
||||||
|
with it("CLI override"):
|
||||||
|
write_config('''
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
x_font_path: /src
|
||||||
|
''')
|
||||||
|
completed_process = \
|
||||||
|
run_cmd(f'vncserver -dry-run -fp /override -config {config_filename}')
|
||||||
|
cli_option = pick_cli_option('fp', completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-fp '/override'"))
|
||||||
|
|
||||||
|
with it("converts network.interface to -interface"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
interface: 0.0.0.0
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('interface',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-interface '0.0.0.0'"))
|
||||||
|
|
||||||
|
with it("CLI option directly passed, overrides config"):
|
||||||
|
write_config('''
|
||||||
|
encoding:
|
||||||
|
video_encoding_mode:
|
||||||
|
jpeg_quality: -1
|
||||||
|
''')
|
||||||
|
completed_process = \
|
||||||
|
run_cmd(f'vncserver -dry-run -JpegVideoQuality 8 -config {config_filename}')
|
||||||
|
cli_option = pick_cli_option("JpegVideoQuality",
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("'-JpegVideoQuality' '8'"))
|
||||||
|
|
||||||
|
with it("converts 2 keys into a single CLI option"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
''')
|
||||||
|
completed_process = run_vncserver()
|
||||||
|
cli_option = pick_cli_option('geometry',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-geometry '1024x768'"))
|
||||||
|
|
||||||
|
with it("ignores empty section override"):
|
||||||
|
write_config('''
|
||||||
|
security:
|
||||||
|
''')
|
||||||
|
completed_process = \
|
||||||
|
run_cmd(f'vncserver -dry-run -config spec/fixtures/global_config.yaml,{config_filename}')
|
||||||
|
cli_option = pick_cli_option('BlacklistThreshold',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-BlacklistThreshold '6'"))
|
||||||
|
|
||||||
|
with it("overrides default config value with global config value"):
|
||||||
|
completed_process = run_cmd("vncserver -dry-run -config spec/fixtures/defaults_config.yaml,spec/fixtures/global_config.yaml")
|
||||||
|
cli_option = pick_cli_option('BlacklistThreshold',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-BlacklistThreshold '6'"))
|
||||||
|
|
||||||
|
with it("uses default config value even if section was overriden"):
|
||||||
|
completed_process = run_cmd("vncserver -dry-run -config spec/fixtures/defaults_config.yaml,spec/fixtures/global_config.yaml")
|
||||||
|
cli_option = pick_cli_option('BlacklistTimeout',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-BlacklistTimeout '10'"))
|
||||||
|
|
||||||
|
with it("overrides global config with user config value"):
|
||||||
|
completed_process = run_cmd("vncserver -dry-run -config spec/fixtures/defaults_config.yaml,spec/fixtures/global_config.yaml,spec/fixtures/user_config.yaml")
|
||||||
|
cli_option = pick_cli_option('BlacklistThreshold',
|
||||||
|
completed_process.stdout)
|
||||||
|
expect(cli_option).to(equal("-BlacklistThreshold '7'"))
|
@ -0,0 +1,111 @@
|
|||||||
|
from mamba import description, context, fit, it, before, after
|
||||||
|
from expects import expect, equal, contain
|
||||||
|
|
||||||
|
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
|
||||||
|
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect, \
|
||||||
|
config_filename, write_config
|
||||||
|
|
||||||
|
with description('YAML validation') as self:
|
||||||
|
with it("produces error message for an incomplete data clump"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
''')
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain('desktop.resolution.width, desktop.resolution.height: either all keys or none must be present'))
|
||||||
|
|
||||||
|
with it("produces error message if int key was set to a string"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
height: none
|
||||||
|
''')
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("desktop.resolution.height 'none': must be an integer"))
|
||||||
|
|
||||||
|
with it("produces no error for valid boolean values"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
use_ipv4: true
|
||||||
|
use_ipv6: false
|
||||||
|
''')
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}')
|
||||||
|
expect(completed_process.stderr).to(equal(''))
|
||||||
|
|
||||||
|
with it("produces an error for invalid boolean values"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
allow_resize: 10
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("desktop.allow_resize '10': must be true or false"))
|
||||||
|
|
||||||
|
with it("produces an error for invalid enum value"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
pixel_depth: none
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("desktop.pixel_depth 'none': must be one of [16, 24, 32]"))
|
||||||
|
|
||||||
|
with it("produces an error for invalid pattern enum value"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
pixel_depth: 16|24|32
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("desktop.pixel_depth '16|24|32': must be one of [16, 24, 32]"))
|
||||||
|
|
||||||
|
with it("produces an error fo partially present enum value"):
|
||||||
|
write_config('''
|
||||||
|
network:
|
||||||
|
protocol: vnc2
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("network.protocol 'vnc2': must be one of [http, vnc]"))
|
||||||
|
|
||||||
|
with it("is silent for a valid enum value"):
|
||||||
|
write_config('''
|
||||||
|
desktop:
|
||||||
|
pixel_depth: 16
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}')
|
||||||
|
expect(completed_process.stderr).to(equal(""))
|
||||||
|
|
||||||
|
with it("produces an error for an array value"):
|
||||||
|
write_config('''
|
||||||
|
keyboard:
|
||||||
|
remap_keys:
|
||||||
|
- 0xzz->0x40
|
||||||
|
- 0x24->0x40
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(contain("keyboard.remap_keys '0xzz->0x40, 0x24->0x40': must be in the format 0x<hex_number>->0x<hex_number>"))
|
||||||
|
|
||||||
|
with context("unsupported keys"):
|
||||||
|
with it("produces an error for an unsupported top-level key"):
|
||||||
|
write_config('''
|
||||||
|
foo: 1
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(
|
||||||
|
contain("Unsupported config keys found:\nfoo"))
|
||||||
|
|
||||||
|
with it("produces an error for an unsupported 2nd-level key"):
|
||||||
|
write_config('''
|
||||||
|
bar:
|
||||||
|
baz: 1
|
||||||
|
''')
|
||||||
|
|
||||||
|
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
|
||||||
|
expect(completed_process.stderr).to(
|
||||||
|
contain("Unsupported config keys found:\nbar.baz"))
|
@ -0,0 +1,199 @@
|
|||||||
|
package KasmVNC::CliOption;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use List::Util qw(first);
|
||||||
|
use List::MoreUtils qw(all);
|
||||||
|
use Switch;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
use KasmVNC::DataClumpValidator;
|
||||||
|
use KasmVNC::Utils;
|
||||||
|
|
||||||
|
our $fetchValueSub;
|
||||||
|
$KasmVNC::CliOption::dataClumpValidator = KasmVNC::DataClumpValidator->new();
|
||||||
|
@KasmVNC::CliOption::isActiveCallbacks = ();
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
name => $args->{name},
|
||||||
|
configKeys => $args->{configKeys},
|
||||||
|
deriveValueSub => $args->{deriveValueSub} || sub {
|
||||||
|
my $self = shift;
|
||||||
|
my @values = @{ listify($self->configValues()) };
|
||||||
|
|
||||||
|
@values = map { deriveBoolean($_) } @values;
|
||||||
|
|
||||||
|
join ",", @values;
|
||||||
|
},
|
||||||
|
isActiveSub => $args->{isActiveSub} || sub {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
scalar $self->configValues() > 0;
|
||||||
|
},
|
||||||
|
errors => []
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub activate {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->makeKeysWithValuesAccessible();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub beforeIsActive {
|
||||||
|
my $callback = shift;
|
||||||
|
|
||||||
|
push @KasmVNC::CliOption::isActiveCallbacks, $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isActiveByCallbacks {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
all { $_->($self) } @KasmVNC::CliOption::isActiveCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub makeKeysWithValuesAccessible {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
foreach my $name (@{ $self->configKeyNames() }) {
|
||||||
|
my $value = $self->fetchValue($name);
|
||||||
|
$self->{$name} = $value if defined($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isActive {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->isActiveByCallbacks() && $self->{isActiveSub}->($self);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub toString {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return unless $self->isActive();
|
||||||
|
|
||||||
|
my $derivedValue = $self->deriveValue();
|
||||||
|
if (defined($derivedValue)) {
|
||||||
|
return "-$self->{name} " . "'$derivedValue'";
|
||||||
|
}
|
||||||
|
|
||||||
|
"-$self->{name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub toValue {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return unless $self->isActive();
|
||||||
|
|
||||||
|
$self->deriveValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deriveValue {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $value = $self->{deriveValueSub}->($self);
|
||||||
|
$self->interpolateEnvVars($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub interpolateEnvVars {
|
||||||
|
my $self = shift;
|
||||||
|
my $value = shift;
|
||||||
|
|
||||||
|
return $value unless defined($value);
|
||||||
|
|
||||||
|
while ($value =~ /\$\{(\w+)\}/) {
|
||||||
|
my $envValue = $ENV{$1};
|
||||||
|
$value =~ s/\Q$&\E/$envValue/;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub errorMessages {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
join "\n", @{ $self->{errors} };
|
||||||
|
}
|
||||||
|
|
||||||
|
# private
|
||||||
|
|
||||||
|
sub isValid {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->validate() unless $self->{validated};
|
||||||
|
|
||||||
|
$self->isNoErrorsPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->validateDataClump();
|
||||||
|
$self->validateConfigValues();
|
||||||
|
|
||||||
|
$self->{validated} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isNoErrorsPresent {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
scalar @{ $self->{errors} } == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validateDataClump {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$KasmVNC::CliOption::dataClumpValidator->validate($self);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub configValues {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
map { $self->fetchValue($_->{name}) } @{ $self->{configKeys} };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub configValue {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
die "Multiple or no config keys defined for $self->{name}"
|
||||||
|
if (scalar @{ $self->{configKeys} } != 1);
|
||||||
|
|
||||||
|
@{ listify($self->configValues()) }[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub configKeyNames {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my @result = map { $_->{name} } @{ $self->{configKeys} };
|
||||||
|
\@result;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hasKey {
|
||||||
|
my $self = shift;
|
||||||
|
my $configKey = shift;
|
||||||
|
|
||||||
|
first { $_ eq $configKey } @{ $self->configKeyNames() };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addErrorMessage {
|
||||||
|
my ($self, $errorMessage) = @_;
|
||||||
|
|
||||||
|
push @{ $self->{errors} }, $errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validateConfigValues {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
map { $_->validate($self) } @{ $self->{configKeys} };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fetchValue {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
&$fetchValueSub(shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,86 @@
|
|||||||
|
package KasmVNC::Config;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use YAML::Tiny;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Hash::Merge::Simple;
|
||||||
|
use KasmVNC::Utils;
|
||||||
|
|
||||||
|
sub merge {
|
||||||
|
my @configsToMerge = map { $_->{data} } @_;
|
||||||
|
my $mergedConfig = Hash::Merge::Simple::merge(@configsToMerge) // {};
|
||||||
|
|
||||||
|
KasmVNC::Config->new({ data => $mergedConfig });
|
||||||
|
}
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
filename => $args->{filename},
|
||||||
|
data => $args->{data}
|
||||||
|
}, $class;
|
||||||
|
|
||||||
|
$self->load() if $self->{filename};
|
||||||
|
$self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
failIfConfigNotReadable($self->{filename});
|
||||||
|
|
||||||
|
$self->{data} = YAML::Tiny->read($self->{filename})->[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get {
|
||||||
|
my ($self, $absoluteKey) = @_;
|
||||||
|
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||||
|
my $config = $self->{data};
|
||||||
|
|
||||||
|
my $value = eval "\$config$path";
|
||||||
|
return unless defined($value);
|
||||||
|
|
||||||
|
$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub exists {
|
||||||
|
my ($self, $absoluteKey) = @_;
|
||||||
|
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||||
|
my $config = $self->{data};
|
||||||
|
|
||||||
|
eval "exists \$config$path";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delete {
|
||||||
|
my ($self, $absoluteKey) = @_;
|
||||||
|
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||||
|
my $config = $self->{data};
|
||||||
|
|
||||||
|
eval "delete \$config$path";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isEmpty {
|
||||||
|
my ($self, $absoluteKey) = @_;
|
||||||
|
my $path = absoluteKeyToHashPath($absoluteKey);
|
||||||
|
my $config = $self->{data};
|
||||||
|
|
||||||
|
$self->exists($absoluteKey) && isBlank($self->get($absoluteKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
sub absoluteKeyToHashPath {
|
||||||
|
my $absoluteKey = shift;
|
||||||
|
|
||||||
|
my @keyParts = split(/\./, $absoluteKey);
|
||||||
|
@keyParts = map { "->{\"$_\"}" } @keyParts;
|
||||||
|
join "", @keyParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub failIfConfigNotReadable {
|
||||||
|
my $config = shift;
|
||||||
|
|
||||||
|
-r $config || die "Couldn't load config: $config";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,123 @@
|
|||||||
|
package KasmVNC::ConfigKey;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Switch;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
use KasmVNC::Utils;
|
||||||
|
|
||||||
|
our $fetchValueSub;
|
||||||
|
|
||||||
|
use constant {
|
||||||
|
INT => 0,
|
||||||
|
STRING => 1,
|
||||||
|
BOOLEAN => 2,
|
||||||
|
ANY => 4
|
||||||
|
};
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
name => $args->{name},
|
||||||
|
type => $args->{type},
|
||||||
|
validator => $args->{validator}
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate {
|
||||||
|
my $self = shift;
|
||||||
|
$self->{cliOption} = shift;
|
||||||
|
|
||||||
|
return if $self->isValueBlank();
|
||||||
|
|
||||||
|
if ($self->{validator}) {
|
||||||
|
$self->resolveValidatorFromFunction() if (ref $self->{validator} eq "CODE");
|
||||||
|
|
||||||
|
$self->{validator}->validate($self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($self->{type}) {
|
||||||
|
case INT {
|
||||||
|
$self->validateInt();
|
||||||
|
}
|
||||||
|
case BOOLEAN {
|
||||||
|
$self->validateBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub resolveValidatorFromFunction {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{validator} = $self->{validator}();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addErrorMessage {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $errorMessage = $self->constructErrorMessage($_[0]);
|
||||||
|
$self->{cliOption}->addErrorMessage($errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
# private
|
||||||
|
|
||||||
|
sub validateBoolean {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return if $self->isValidBoolean();
|
||||||
|
$self->addErrorMessage("must be true or false");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validateInt {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return if $self->isValidInt();
|
||||||
|
|
||||||
|
$self->addErrorMessage("must be an integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isValueBlank {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $value = $self->value();
|
||||||
|
!defined($value) || $value eq "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fetchValue {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
&$fetchValueSub(shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub constructErrorMessage {
|
||||||
|
my $self = shift;
|
||||||
|
my $staticErrorMessage = shift;
|
||||||
|
|
||||||
|
my $name = $self->{name};
|
||||||
|
my $value = join ", ", @{ listify($self->fetchValue($name)) };
|
||||||
|
|
||||||
|
"$name '$value': $staticErrorMessage";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isValidInt {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->value() =~ /^(-)?\d+$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isValidBoolean {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->value() =~ /^true|false$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub value {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->fetchValue($self->{name});
|
||||||
|
}
|
||||||
|
|
||||||
|
our @EXPORT_OK = ('INT', 'STRING', 'BOOLEAN');
|
@ -0,0 +1,48 @@
|
|||||||
|
package KasmVNC::DataClumpValidator;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate {
|
||||||
|
my $self = shift;
|
||||||
|
$self->{cliOption} = shift;
|
||||||
|
|
||||||
|
if ($self->isDataClump() && !$self->isWhole()) {
|
||||||
|
$self->{cliOption}->addErrorMessage($self->errorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# private
|
||||||
|
|
||||||
|
sub isWhole {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $numberOfValues = scalar $self->{cliOption}->configValues();
|
||||||
|
return 1 if $numberOfValues == 0;
|
||||||
|
|
||||||
|
scalar @{ $self->{cliOption}->{configKeys} } == $numberOfValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isDataClump {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
scalar(@{ $self->{cliOption}->{configKeys} }) > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub errorMessage {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $configKeys = join ", ", @{ $self->{cliOption}->configKeyNames() };
|
||||||
|
|
||||||
|
"$configKeys: either all keys or none must be present";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,36 @@
|
|||||||
|
package KasmVNC::EnumValidator;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use List::MoreUtils qw(any);
|
||||||
|
use Data::Dumper;
|
||||||
|
use KasmVNC::Utils;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
allowedValues => $args->{allowedValues}
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate {
|
||||||
|
my $self = shift;
|
||||||
|
my $configKey = shift;
|
||||||
|
my @values = @{ listify($configKey->value()) };
|
||||||
|
|
||||||
|
foreach my $value (@values) {
|
||||||
|
unless (any { $_ eq $value } @{ $self->{allowedValues} }) {
|
||||||
|
$configKey->addErrorMessage($self->errorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub errorMessage {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $allowedValuesText = join ", ", @{ $self->{allowedValues} };
|
||||||
|
"must be one of [$allowedValuesText]"
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,20 @@
|
|||||||
|
package KasmVNC::Logger;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub warn {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
say { *STDERR } @_;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,37 @@
|
|||||||
|
package KasmVNC::PatternValidator;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
use KasmVNC::Utils;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
pattern => $args->{pattern},
|
||||||
|
errorMessage => $args->{errorMessage}
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validate {
|
||||||
|
my $self = shift;
|
||||||
|
$self->{configKey} = shift;
|
||||||
|
my @values = @{ listify($self->{configKey}->value()) };
|
||||||
|
|
||||||
|
foreach my $value (@values) {
|
||||||
|
$self->validateValue($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub validateValue {
|
||||||
|
my $self = shift;
|
||||||
|
my $value = shift;
|
||||||
|
|
||||||
|
unless ($value =~ $self->{pattern}) {
|
||||||
|
$self->{configKey}->addErrorMessage($self->{errorMessage});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,40 @@
|
|||||||
|
package KasmVNC::TextOption;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
description => $args->{description},
|
||||||
|
callback => $args->{callback} || sub {},
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
use overload fallback => 1, q("") => sub {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->stringify();
|
||||||
|
};
|
||||||
|
|
||||||
|
sub stringify {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{description};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub description {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{description};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub callback {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{callback};
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,55 @@
|
|||||||
|
package KasmVNC::TextUI;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
@KasmVNC::TextUI::ISA = qw(Exporter);
|
||||||
|
|
||||||
|
our @EXPORT = ('Prompt', 'askUserToChooseOption');
|
||||||
|
|
||||||
|
sub askUserToChooseOption {
|
||||||
|
my %args = @_;
|
||||||
|
my $banner = $args{banner};
|
||||||
|
my $prompt = $args{prompt};
|
||||||
|
my $options = $args{options};
|
||||||
|
|
||||||
|
my $userInput;
|
||||||
|
my $i = 1;
|
||||||
|
my %numberedOptions = map { $i++ => $_ } @$options;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
say $banner;
|
||||||
|
|
||||||
|
printOptions(\%numberedOptions);
|
||||||
|
|
||||||
|
$userInput = Prompt($prompt . ": ");
|
||||||
|
last if $numberedOptions{$userInput};
|
||||||
|
|
||||||
|
say "Invalid choice: '$userInput'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$numberedOptions{$userInput};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub printOptions {
|
||||||
|
my $choices = shift;
|
||||||
|
|
||||||
|
foreach my $choiceNumber (sort keys %$choices) {
|
||||||
|
say "[$choiceNumber] " . $choices->{$choiceNumber};
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Prompt {
|
||||||
|
my $prompt = shift;
|
||||||
|
|
||||||
|
print($prompt);
|
||||||
|
my $userInput = <STDIN>;
|
||||||
|
$userInput =~ s/^\s+|\s+$//g;
|
||||||
|
|
||||||
|
return $userInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,54 @@
|
|||||||
|
package KasmVNC::User;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
|
||||||
|
my $self = bless {
|
||||||
|
name => $args->{name},
|
||||||
|
permissions => $args->{permissions}
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub permissionsExplanation {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my %permissionExplanations = ("w" => "can use keyboard and mouse",
|
||||||
|
"o" => "can add/remove users",
|
||||||
|
"" => "can only view");
|
||||||
|
foreach (qw(ow wo)) {
|
||||||
|
$permissionExplanations{$_} = "can use keyboard and mouse, add/remove users";
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{permissions} =~ s/r//g;
|
||||||
|
$permissionExplanations{$self->{permissions}};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub name {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{name};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub permissions {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{permissions};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isOwner {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->permissions() =~ /o/;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub toString {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->name() . " (" . $self->permissionsExplanation() . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,169 @@
|
|||||||
|
package KasmVNC::Users;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
use List::MoreUtils qw(any);
|
||||||
|
use KasmVNC::User;
|
||||||
|
|
||||||
|
our $vncPasswdBin;
|
||||||
|
our $logger;
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
my $self = bless {
|
||||||
|
passwordFileName => $args->{passwordFileName},
|
||||||
|
}, $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub loadFrom {
|
||||||
|
my ($self, $passwordFileName) = @_;
|
||||||
|
|
||||||
|
my $users = KasmVNC::Users->new({
|
||||||
|
passwordFileName => $passwordFileName,
|
||||||
|
vncPasswdBin => $vncPasswdBin
|
||||||
|
});
|
||||||
|
$users->load();
|
||||||
|
|
||||||
|
$users;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub optionsToCliOptions {
|
||||||
|
my %options = @_;
|
||||||
|
my @cliOptons = ();
|
||||||
|
|
||||||
|
push(@cliOptons, "-u \"@{[$options{username}]}\"");
|
||||||
|
if ($options{permissions}) {
|
||||||
|
push(@cliOptons, "-" . $options{permissions});
|
||||||
|
}
|
||||||
|
if ($options{changePermissions}) {
|
||||||
|
push(@cliOptons, "-n");
|
||||||
|
}
|
||||||
|
|
||||||
|
join " ", @cliOptons;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub runKasmvncpasswd {
|
||||||
|
my ($self, $options) = @_;
|
||||||
|
my @cliOptions = optionsToCliOptions(%{ $options });
|
||||||
|
|
||||||
|
system("$vncPasswdBin " . join(" ", @cliOptions) . " " . $self->{passwordFileName});
|
||||||
|
$? ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub findByPermissions {
|
||||||
|
my ($self, $permissions) = @_;
|
||||||
|
|
||||||
|
any { $_->{permissions} =~ /$permissions/ }
|
||||||
|
(values %{ $self->{store} });
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fetchUser {
|
||||||
|
my ($self, $username) = @_;
|
||||||
|
|
||||||
|
$self->{store}->{$username};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub userExists {
|
||||||
|
fetchUser @_;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addUser {
|
||||||
|
my ($self, $username, $permissions) = @_;
|
||||||
|
|
||||||
|
if ($self->userExists($username)) {
|
||||||
|
$logger->warn("User $username already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->runKasmvncpasswd({ username => $username, permissions => $permissions });
|
||||||
|
}
|
||||||
|
|
||||||
|
sub checkUserExists {
|
||||||
|
my ($self, $username) = @_;
|
||||||
|
|
||||||
|
unless ($self->fetchUser($username)) {
|
||||||
|
die "User \"$username\" doesn't exist";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addPermissions {
|
||||||
|
my ($self, $username, $permissions) = @_;
|
||||||
|
|
||||||
|
$self->checkUserExists($username);
|
||||||
|
|
||||||
|
my $user = $self->fetchUser($username);
|
||||||
|
$permissions .= $user->{permissions};
|
||||||
|
|
||||||
|
$self->changePermissions($username, $permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub changePermissions {
|
||||||
|
my ($self, $username, $permissions) = @_;
|
||||||
|
|
||||||
|
$self->checkUserExists($username);
|
||||||
|
|
||||||
|
$self->runKasmvncpasswd({ username => $username, permissions => $permissions,
|
||||||
|
changePermissions => 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->{store} = $self->_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reload {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
sub count {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return scalar(keys %{ $self->{store} });
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_empty {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->count() eq 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _load {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $store = {};
|
||||||
|
|
||||||
|
open(FH, '<', $self->{passwordFileName}) or return $store;
|
||||||
|
|
||||||
|
while(<FH>){
|
||||||
|
chomp $_;
|
||||||
|
my ($name, $__, $permissions) = split(':', $_);
|
||||||
|
$store->{$name} = KasmVNC::User->new({
|
||||||
|
name => $name,
|
||||||
|
permissions => $permissions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
close(FH);
|
||||||
|
|
||||||
|
$store;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub users {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
values %{ $self->{store} }
|
||||||
|
}
|
||||||
|
|
||||||
|
sub toString {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my @userDescriptions = map { $_->toString() } $self->users();
|
||||||
|
join "\n", @userDescriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,66 @@
|
|||||||
|
package KasmVNC::Utils;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use v5.10;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Switch;
|
||||||
|
|
||||||
|
use Exporter;
|
||||||
|
|
||||||
|
@KasmVNC::Utils::ISA = qw(Exporter);
|
||||||
|
|
||||||
|
our @EXPORT = ('listify', 'flatten', 'isBlank', 'isPresent', 'deriveBoolean',
|
||||||
|
'printStackTrace');
|
||||||
|
|
||||||
|
sub listify {
|
||||||
|
# Implementation based on Hyper::Functions
|
||||||
|
if (scalar @_ > 1) {
|
||||||
|
return [ @_ ];
|
||||||
|
} elsif (defined $_[0]) {
|
||||||
|
my $ref_type = ref $_[0];
|
||||||
|
return ($ref_type && $ref_type eq 'ARRAY') ? $_[0] : [ $_[0] ];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub flatten {
|
||||||
|
map { ref $_ ? flatten(@{$_}) : $_ } @_;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isBlank {
|
||||||
|
!isPresent(shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub isPresent {
|
||||||
|
my $value = shift;
|
||||||
|
if (ref($value) eq "HASH") {
|
||||||
|
return scalar(keys %$value) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
defined($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deriveBoolean {
|
||||||
|
my $value = shift;
|
||||||
|
|
||||||
|
switch($value) {
|
||||||
|
case 'true' {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case 'false' {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub printStackTrace {
|
||||||
|
my $trace = Devel::StackTrace->new;
|
||||||
|
print { *STDERR } $trace->as_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,131 @@
|
|||||||
|
---
|
||||||
|
desktop:
|
||||||
|
resolution:
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
allow_resize: true
|
||||||
|
pixel_depth: 24
|
||||||
|
|
||||||
|
network:
|
||||||
|
protocol: http
|
||||||
|
interface: 0.0.0.0
|
||||||
|
websocket_port: auto
|
||||||
|
use_ipv4: true
|
||||||
|
use_ipv6: true
|
||||||
|
udp:
|
||||||
|
public_ip: auto
|
||||||
|
port: auto
|
||||||
|
ssl:
|
||||||
|
pem_certificate: /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||||
|
pem_key: /etc/ssl/private/ssl-cert-snakeoil.key
|
||||||
|
require_ssl: true
|
||||||
|
|
||||||
|
user_session:
|
||||||
|
# session_type: shared
|
||||||
|
new_session_disconnects_existing_exclusive_session: false
|
||||||
|
concurrent_connections_prompt: false
|
||||||
|
concurrent_connections_prompt_timeout: 10
|
||||||
|
idle_timeout: never
|
||||||
|
|
||||||
|
keyboard:
|
||||||
|
remap_keys:
|
||||||
|
# - 0x22->0x40
|
||||||
|
ignore_numlock: false
|
||||||
|
raw_keyboard: false
|
||||||
|
|
||||||
|
# Mouse, trackpad, etc.
|
||||||
|
pointer:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
runtime_configuration:
|
||||||
|
allow_client_to_override_kasm_server_settings: true
|
||||||
|
allow_override_standard_vnc_server_settings: true
|
||||||
|
allow_override_list:
|
||||||
|
- pointer.enabled
|
||||||
|
- data_loss_prevention.clipboard.server_to_client.enabled
|
||||||
|
- data_loss_prevention.clipboard.client_to_server.enabled
|
||||||
|
- data_loss_prevention.clipboard.server_to_client.primary_clipboard_enabled
|
||||||
|
|
||||||
|
logging:
|
||||||
|
log_writer_name: all
|
||||||
|
log_dest: logfile
|
||||||
|
# 0 - minimal verbosity, 100 - most verbose
|
||||||
|
level: 30
|
||||||
|
|
||||||
|
security:
|
||||||
|
brute_force_protection:
|
||||||
|
blacklist_threshold: 5
|
||||||
|
blacklist_timeout: 10
|
||||||
|
|
||||||
|
data_loss_prevention:
|
||||||
|
visible_region:
|
||||||
|
# top: 10
|
||||||
|
# left: 10
|
||||||
|
# right: 40
|
||||||
|
# bottom: 40
|
||||||
|
concealed_region:
|
||||||
|
allow_click_down: false
|
||||||
|
allow_click_release: false
|
||||||
|
clipboard:
|
||||||
|
delay_between_operations: none
|
||||||
|
allow_mimetypes:
|
||||||
|
- chromium/x-web-custom-data
|
||||||
|
- text/html
|
||||||
|
- image/png
|
||||||
|
# Add to docs: Cut buffers and CLIPBOARD selection.
|
||||||
|
server_to_client:
|
||||||
|
enabled: false
|
||||||
|
size: unlimited
|
||||||
|
primary_clipboard_enabled: false
|
||||||
|
client_to_server:
|
||||||
|
enabled: false
|
||||||
|
size: unlimited
|
||||||
|
keyboard:
|
||||||
|
enabled: true
|
||||||
|
rate_limit: unlimited
|
||||||
|
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard
|
||||||
|
# content.
|
||||||
|
logging:
|
||||||
|
level: off
|
||||||
|
|
||||||
|
encoding:
|
||||||
|
max_frame_rate: 60
|
||||||
|
full_frame_updates: none
|
||||||
|
rect_encoding_mode:
|
||||||
|
min_quality: 7
|
||||||
|
max_quality: 8
|
||||||
|
consider_lossless_quality: 10
|
||||||
|
rectangle_compress_threads: auto
|
||||||
|
|
||||||
|
video_encoding_mode:
|
||||||
|
jpeg_quality: -1
|
||||||
|
webp_quality: -1
|
||||||
|
max_resolution:
|
||||||
|
width: 1920
|
||||||
|
height: 1080
|
||||||
|
enter_video_encoding_mode:
|
||||||
|
time_threshold: 5
|
||||||
|
area_threshold: 45%
|
||||||
|
exit_video_encoding_mode:
|
||||||
|
time_threshold: 3
|
||||||
|
logging:
|
||||||
|
level: off
|
||||||
|
scaling_algorithm: progressive_bilinear
|
||||||
|
|
||||||
|
compare_framebuffer: auto
|
||||||
|
zrle_zlib_level: auto
|
||||||
|
hextile_improved_compression: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
advanced:
|
||||||
|
x_font_path: auto
|
||||||
|
httpd_directory: /usr/share/kasmvnc/www
|
||||||
|
kasm_password_file: ${HOME}/.kasmpasswd
|
||||||
|
x_authority_file: auto
|
||||||
|
auto_shutdown:
|
||||||
|
no_user_session_timeout: never
|
||||||
|
active_user_session_timeout: never
|
||||||
|
inactive_user_session_timeout: never
|
||||||
|
|
||||||
|
command_line:
|
||||||
|
prompt: true
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue