├── .dockerignore ├── .gitignore ├── tutorials ├── guacamole-tutorial │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── webapp │ │ │ ├── WEB-INF │ │ │ │ └── web.xml │ │ │ └── index.html │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── guacamole │ │ │ └── net │ │ │ └── example │ │ │ └── TutorialGuacamoleTunnelServlet.java │ └── pom.xml ├── guacamole-auth-tutorial │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── guac-manifest.json │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── guacamole │ │ │ └── auth │ │ │ ├── TutorialGuacamoleProperties.java │ │ │ └── TutorialAuthenticationProvider.java │ └── pom.xml └── libguac-client-ball │ ├── Makefile.am │ ├── src │ ├── ball.h │ └── ball.c │ ├── configure.ac │ └── .gitignore ├── src ├── images │ ├── touchpad.png │ ├── edit-group.png │ ├── edit-user.png │ ├── guac-arch.png │ ├── client-panel.png │ ├── client-tiled.png │ ├── file-browser.png │ ├── manage-button.png │ ├── manage-groups.png │ ├── manage-users.png │ ├── totp-enroll.png │ ├── touchscreen.png │ ├── edit-connection.png │ ├── edit-user-group.png │ ├── file-transfers.png │ ├── guac-menu-share.png │ ├── guacamole-drive.png │ ├── manage-history.png │ ├── manage-sessions.png │ ├── duo-add-guacamole.png │ ├── duo-auth-factor-1.png │ ├── duo-auth-factor-2.png │ ├── duo-copy-details.png │ ├── manage-connections.png │ ├── totp-auth-factor-1.png │ ├── totp-auth-factor-2.png │ ├── totp-enroll-detail.png │ ├── totp-group-config.png │ ├── totp-user-config.png │ ├── duo-rename-guacamole.png │ ├── edit-sharing-profile.png │ ├── edit-user-membership.png │ ├── guac-menu-disconnect.png │ ├── guac-menu-share-link.png │ ├── client-connection-menu.png │ ├── edit-group-memberships.png │ ├── guacamole-home-screen.png │ ├── guacamole-preferences.png │ ├── recording-player-in-use.png │ ├── client-tiled-multi-focus.png │ ├── guacamole-drive-download.png │ ├── session-filter-example-1.png │ ├── session-filter-example-2.png │ ├── vault-ksm-002-select-ksm.png │ ├── guacamole-client-interface.png │ ├── guacamole-settings-sections.png │ ├── vault-ksm-004-generate-token.png │ ├── history-table-with-recordings.png │ ├── client-connection-menu-multiple.png │ ├── vault-ksm-003a-create-application.png │ ├── vault-ksm-003b-create-application.png │ ├── vault-ksm-001a-create-shared-folder.png │ ├── vault-ksm-001b-create-shared-folder.png │ ├── recording-storage-connection-config-option2.png │ └── recording-storage-connection-config-option1-recommended.png ├── _templates │ └── footer.html ├── include │ ├── sso-download.md │ └── sso-login-behavior.md ├── index.md ├── _static │ └── gug.css ├── conf.py ├── header-auth.md ├── saml-auth.md ├── cas-auth.md ├── adhoc-connections.md ├── guacamole-common.md ├── guacamole-architecture.md ├── introduction.md ├── duo-auth.md ├── openid-auth.md ├── totp-auth.md ├── ext │ └── guac.py ├── recording-playback.md ├── json-auth.md ├── radius-auth.md ├── event-listeners.md └── guacamole-common-js.md ├── NOTICE ├── README.md ├── .github └── workflows │ └── pr-build.yml ├── Makefile ├── Dockerfile ├── CONTRIBUTING └── LICENSE /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | *~ 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /tutorials/guacamole-tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target/ 3 | META-INF/ 4 | -------------------------------------------------------------------------------- /tutorials/guacamole-auth-tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target/ 3 | META-INF/ 4 | -------------------------------------------------------------------------------- /src/images/touchpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/touchpad.png -------------------------------------------------------------------------------- /src/images/edit-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-group.png -------------------------------------------------------------------------------- /src/images/edit-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-user.png -------------------------------------------------------------------------------- /src/images/guac-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guac-arch.png -------------------------------------------------------------------------------- /src/images/client-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/client-panel.png -------------------------------------------------------------------------------- /src/images/client-tiled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/client-tiled.png -------------------------------------------------------------------------------- /src/images/file-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/file-browser.png -------------------------------------------------------------------------------- /src/images/manage-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-button.png -------------------------------------------------------------------------------- /src/images/manage-groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-groups.png -------------------------------------------------------------------------------- /src/images/manage-users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-users.png -------------------------------------------------------------------------------- /src/images/totp-enroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-enroll.png -------------------------------------------------------------------------------- /src/images/touchscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/touchscreen.png -------------------------------------------------------------------------------- /src/images/edit-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-connection.png -------------------------------------------------------------------------------- /src/images/edit-user-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-user-group.png -------------------------------------------------------------------------------- /src/images/file-transfers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/file-transfers.png -------------------------------------------------------------------------------- /src/images/guac-menu-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guac-menu-share.png -------------------------------------------------------------------------------- /src/images/guacamole-drive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-drive.png -------------------------------------------------------------------------------- /src/images/manage-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-history.png -------------------------------------------------------------------------------- /src/images/manage-sessions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-sessions.png -------------------------------------------------------------------------------- /src/images/duo-add-guacamole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/duo-add-guacamole.png -------------------------------------------------------------------------------- /src/images/duo-auth-factor-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/duo-auth-factor-1.png -------------------------------------------------------------------------------- /src/images/duo-auth-factor-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/duo-auth-factor-2.png -------------------------------------------------------------------------------- /src/images/duo-copy-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/duo-copy-details.png -------------------------------------------------------------------------------- /src/images/manage-connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/manage-connections.png -------------------------------------------------------------------------------- /src/images/totp-auth-factor-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-auth-factor-1.png -------------------------------------------------------------------------------- /src/images/totp-auth-factor-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-auth-factor-2.png -------------------------------------------------------------------------------- /src/images/totp-enroll-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-enroll-detail.png -------------------------------------------------------------------------------- /src/images/totp-group-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-group-config.png -------------------------------------------------------------------------------- /src/images/totp-user-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/totp-user-config.png -------------------------------------------------------------------------------- /src/images/duo-rename-guacamole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/duo-rename-guacamole.png -------------------------------------------------------------------------------- /src/images/edit-sharing-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-sharing-profile.png -------------------------------------------------------------------------------- /src/images/edit-user-membership.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-user-membership.png -------------------------------------------------------------------------------- /src/images/guac-menu-disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guac-menu-disconnect.png -------------------------------------------------------------------------------- /src/images/guac-menu-share-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guac-menu-share-link.png -------------------------------------------------------------------------------- /src/images/client-connection-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/client-connection-menu.png -------------------------------------------------------------------------------- /src/images/edit-group-memberships.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/edit-group-memberships.png -------------------------------------------------------------------------------- /src/images/guacamole-home-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-home-screen.png -------------------------------------------------------------------------------- /src/images/guacamole-preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-preferences.png -------------------------------------------------------------------------------- /src/images/recording-player-in-use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/recording-player-in-use.png -------------------------------------------------------------------------------- /src/images/client-tiled-multi-focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/client-tiled-multi-focus.png -------------------------------------------------------------------------------- /src/images/guacamole-drive-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-drive-download.png -------------------------------------------------------------------------------- /src/images/session-filter-example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/session-filter-example-1.png -------------------------------------------------------------------------------- /src/images/session-filter-example-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/session-filter-example-2.png -------------------------------------------------------------------------------- /src/images/vault-ksm-002-select-ksm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-002-select-ksm.png -------------------------------------------------------------------------------- /src/images/guacamole-client-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-client-interface.png -------------------------------------------------------------------------------- /src/images/guacamole-settings-sections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/guacamole-settings-sections.png -------------------------------------------------------------------------------- /src/images/vault-ksm-004-generate-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-004-generate-token.png -------------------------------------------------------------------------------- /src/images/history-table-with-recordings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/history-table-with-recordings.png -------------------------------------------------------------------------------- /src/images/client-connection-menu-multiple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/client-connection-menu-multiple.png -------------------------------------------------------------------------------- /src/images/vault-ksm-003a-create-application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-003a-create-application.png -------------------------------------------------------------------------------- /src/images/vault-ksm-003b-create-application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-003b-create-application.png -------------------------------------------------------------------------------- /src/images/vault-ksm-001a-create-shared-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-001a-create-shared-folder.png -------------------------------------------------------------------------------- /src/images/vault-ksm-001b-create-shared-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/vault-ksm-001b-create-shared-folder.png -------------------------------------------------------------------------------- /src/images/recording-storage-connection-config-option2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/recording-storage-connection-config-option2.png -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache Guacamole 2 | Copyright 2020 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /src/images/recording-storage-connection-config-option1-recommended.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glyptodon/guacamole-manual/HEAD/src/images/recording-storage-connection-config-option1-recommended.png -------------------------------------------------------------------------------- /tutorials/guacamole-auth-tutorial/src/main/resources/guac-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "guacamoleVersion" : "1.5.3", 4 | 5 | "name" : "Tutorial Authentication Extension", 6 | "namespace" : "guac-auth-tutorial", 7 | 8 | "authProviders" : [ 9 | "org.apache.guacamole.auth.TutorialAuthenticationProvider" 10 | ] 11 | 12 | } 13 | -------------------------------------------------------------------------------- /tutorials/libguac-client-ball/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | 3 | ACLOCAL_AMFLAGS = -I m4 4 | AM_CFLAGS = -Werror -Wall -pedantic 5 | 6 | lib_LTLIBRARIES = libguac-client-ball.la 7 | 8 | # All source files of libguac-client-ball 9 | noinst_HEADERS = src/ball.h 10 | libguac_client_ball_la_SOURCES = src/ball.c 11 | 12 | # libtool versioning information 13 | libguac_client_ball_la_LDFLAGS = -version-info 0:0:0 14 | -------------------------------------------------------------------------------- /tutorials/libguac-client-ball/src/ball.h: -------------------------------------------------------------------------------- 1 | #ifndef BALL_CLIENT_H 2 | #define BALL_CLIENT_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | typedef struct ball_client_data { 9 | 10 | guac_layer* ball; 11 | 12 | int ball_x; 13 | int ball_y; 14 | 15 | int ball_velocity_x; 16 | int ball_velocity_y; 17 | 18 | pthread_t render_thread; 19 | 20 | } ball_client_data; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Guacamole was donated to the [Apache Software Foundation](http://apache.org) 2 | in 2016 and is now [Apache Guacamole](https://guacamole.apache.org)!** 3 | 4 | This repository is now archived. The proper repository for all future 5 | contributions and changes is [the upstream `guacamole-manual` 6 | repository](https://github.com/apache/guacamole-manual/). 7 | 8 | If looking to contribute, please do so by contributing to the upstream project. 9 | For more information, please see [the Apache Guacamole website](https://guacamole.apache.org). 10 | -------------------------------------------------------------------------------- /src/_templates/footer.html: -------------------------------------------------------------------------------- 1 | {% extends '!footer.html' %} 2 | 3 | {% block contentinfo %} 4 | {%- if show_copyright %} 5 |

Copyright © {{ copyright_year }} The Apache Software Foundation, 6 | Licensed under the Apache License, Version 2.0. 7 | Apache Guacamole, Guacamole, Apache, the Apache feather logo, and the Apache Guacamole project logo are 8 | trademarks of The Apache Software Foundation.

9 | {%- endif %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /tutorials/libguac-client-ball/configure.ac: -------------------------------------------------------------------------------- 1 | # Project information 2 | AC_PREREQ([2.61]) 3 | AC_INIT([libguac-client-ball], [0.1.0]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) 5 | AM_SILENT_RULES([yes]) 6 | 7 | AC_CONFIG_MACRO_DIRS([m4]) 8 | 9 | # Check for required build tools 10 | AC_PROG_CC 11 | AC_PROG_CC_C99 12 | AC_PROG_LIBTOOL 13 | 14 | # Check for libguac 15 | AC_CHECK_LIB([guac], [guac_client_stream_png],, 16 | AC_MSG_ERROR("libguac is required for communication via " 17 | "the Guacamole protocol")) 18 | 19 | AC_CONFIG_FILES([Makefile]) 20 | AC_OUTPUT 21 | -------------------------------------------------------------------------------- /tutorials/libguac-client-ball/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Object code 3 | *.o 4 | *.so 5 | *.lo 6 | *.la 7 | 8 | # gcov files 9 | *.gcda 10 | *.gcov 11 | *.gcno 12 | 13 | # Backup files 14 | *~ 15 | 16 | # Release files 17 | *.tar.gz 18 | 19 | # Files currently being edited by vim or vi 20 | *.swp 21 | 22 | # automake/autoconf 23 | .deps/ 24 | .dirstamp 25 | .libs/ 26 | Makefile 27 | Makefile.in 28 | aclocal.m4 29 | autom4te.cache/ 30 | m4/* 31 | !README 32 | compile 33 | config.guess 34 | config.h 35 | config.h.in 36 | config.log 37 | config.status 38 | config.sub 39 | configure 40 | depcomp 41 | install-sh 42 | libtool 43 | ltmain.sh 44 | missing 45 | stamp-h1 46 | test-driver 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/pr-build.yml: -------------------------------------------------------------------------------- 1 | name: Pull request CI build 2 | 3 | # Run build for all pull requests 4 | on: 5 | pull_request: 6 | 7 | # Limit to only one build for a given PR source branch at a time, 8 | # cancelling any in-progress builds 9 | concurrency: 10 | group: guacamole-manual-pr-${{ github.head_ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | 15 | docker_build: 16 | name: Run docker build 17 | runs-on: ubuntu-latest 18 | steps: 19 | 20 | - name: Check out code 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | persist-credentials: false 25 | 26 | - name: Build Docker container 27 | shell: sh 28 | run: | 29 | docker build --pull --no-cache --force-rm . 30 | -------------------------------------------------------------------------------- /src/include/sso-download.md: -------------------------------------------------------------------------------- 1 | Guacamole's SSO extensions are available separately from the main 2 | `guacamole.war`. The link for this and all other officially-supported and 3 | compatible extensions for a particular version of Guacamole are provided on the 4 | release notes for that version. You can find the release notes for current 5 | versions of Guacamole here: . 6 | 7 | The SSO extensions are packaged together in a `.tar.gz` file containing one 8 | extension for each supported SSO method: 9 | 10 | | SSO Method | Extension | 11 | | ----------------------------- | -------------------------------------------- | 12 | | [CAS](cas-auth) | `cas/guacamole-auth-sso-cas-1.5.3.jar` | 13 | | [OpenID Connect](openid-auth) | `openid/guacamole-auth-sso-openid-1.5.3.jar` | 14 | | [SAML](saml-auth) | `saml/guacamole-auth-sso-saml-1.5.3.jar` | 15 | 16 | -------------------------------------------------------------------------------- /tutorials/guacamole-tutorial/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | index.html 12 | 13 | 14 | 15 | 16 | Tunnel servlet. 17 | Tunnel 18 | 19 | org.apache.guacamole.net.example.TutorialGuacamoleTunnelServlet 20 | 21 | 22 | 23 | 24 | Tunnel 25 | /tunnel 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/include/sso-login-behavior.md: -------------------------------------------------------------------------------- 1 | Guacamole loads authentication extensions in order of priority, and evaluates 2 | authentication attempts in this same order. This has implications for how the 3 | Guacamole login process behaves when an SSO extension is present: 4 | 5 | If the SSO extension has priority: 6 | : Users that are not yet authenticated 7 | will be immediately redirected to the configured identity provider. They will 8 | not see a Guacamole login screen. 9 | 10 | If a non-SSO extension has priority: 11 | : Users that are not yet authenticated 12 | will be presented with a Guacamole login screen. Additionally, links to the 13 | configured identity provider(s) will be available for users that wish to log 14 | in using SSO. 15 | 16 | The default priority of extensions is dictated by their filenames, with 17 | extensions that sort earlier alphabetically having higher priority than others. 18 | This can be overridden [by setting the `extension-priority` property within 19 | `guacamole.properties`](initial-setup). 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | .PHONY: all clean html 21 | 22 | # 23 | # Build entire manual 24 | # 25 | 26 | all: html 27 | 28 | # 29 | # Clean build artifacts 30 | # 31 | 32 | clean: 33 | $(RM) -R build/ 34 | 35 | # All files which the build depends on 36 | MD_FILES=$(shell find "./src" -name "*.md") 37 | RST_FILES=$(shell find "./src" -name "*.rst") 38 | PNG_FILES=$(shell find "./src" -name "*.png") 39 | 40 | # 41 | # HTML manual build 42 | # 43 | 44 | html: build/html/index.html 45 | 46 | build/html/index.html: $(PNG_FILES) $(RST_FILES) $(MD_FILES) 47 | sphinx-build -b html -d build/doctrees src/ build/html 48 | 49 | -------------------------------------------------------------------------------- /tutorials/guacamole-auth-tutorial/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | org.apache.guacamole 8 | guacamole-auth-tutorial 9 | jar 10 | 1.5.3 11 | guacamole-auth-tutorial 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.3 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.guacamole 39 | guacamole-ext 40 | 1.5.3 41 | provided 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tutorials/guacamole-tutorial/src/main/java/org/apache/guacamole/net/example/TutorialGuacamoleTunnelServlet.java: -------------------------------------------------------------------------------- 1 | package org.apache.guacamole.net.example; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import org.apache.guacamole.GuacamoleException; 5 | import org.apache.guacamole.net.GuacamoleSocket; 6 | import org.apache.guacamole.net.GuacamoleTunnel; 7 | import org.apache.guacamole.net.InetGuacamoleSocket; 8 | import org.apache.guacamole.net.SimpleGuacamoleTunnel; 9 | import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; 10 | import org.apache.guacamole.protocol.GuacamoleConfiguration; 11 | import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet; 12 | 13 | public class TutorialGuacamoleTunnelServlet 14 | extends GuacamoleHTTPTunnelServlet { 15 | 16 | @Override 17 | protected GuacamoleTunnel doConnect(HttpServletRequest request) 18 | throws GuacamoleException { 19 | 20 | // Create our configuration 21 | GuacamoleConfiguration config = new GuacamoleConfiguration(); 22 | config.setProtocol("vnc"); 23 | config.setParameter("hostname", "localhost"); 24 | config.setParameter("port", "5901"); 25 | config.setParameter("password", "potato"); 26 | 27 | // Connect to guacd - everything is hard-coded here. 28 | GuacamoleSocket socket = new ConfiguredGuacamoleSocket( 29 | new InetGuacamoleSocket("localhost", 4822), 30 | config 31 | ); 32 | 33 | // Return a new tunnel which uses the connected socket 34 | return new SimpleGuacamoleTunnel(socket); 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | # 21 | # Dockerfile for guacamole-manual 22 | # 23 | # See the README for more information on how to use this file. 24 | 25 | # Set this build arg to any of the available version labels for the httpd image 26 | ARG HTTPD_VERSION=2.4 27 | 28 | # Perform the build itself using Python+Sphinx 29 | FROM sphinxdoc/sphinx AS builder 30 | RUN pip3 install sphinx-rtd-theme sphinx-inline-tabs myst-parser 31 | 32 | # Set the working directory for the remainder of the build process 33 | WORKDIR /manual 34 | 35 | # Copy the manual source into the working directory and build it 36 | COPY ./ ./ 37 | RUN make 38 | 39 | # For the runtime image, use the official Apache httpd image 40 | FROM httpd:${HTTPD_VERSION} 41 | 42 | # Copy any HTML generated by the build into httpd's document root 43 | COPY --from=builder /manual/build/html/ /usr/local/apache2/htdocs/ 44 | 45 | -------------------------------------------------------------------------------- /tutorials/guacamole-auth-tutorial/src/main/java/org/apache/guacamole/auth/TutorialGuacamoleProperties.java: -------------------------------------------------------------------------------- 1 | package org.apache.guacamole.auth; 2 | 3 | import org.apache.guacamole.properties.StringGuacamoleProperty; 4 | 5 | /** 6 | * Utility class containing all properties used by the custom authentication 7 | * tutorial. The properties defined here must be specified within 8 | * guacamole.properties to configure the tutorial authentication provider. 9 | */ 10 | public class TutorialGuacamoleProperties { 11 | 12 | /** 13 | * This class should not be instantiated. 14 | */ 15 | private TutorialGuacamoleProperties() {} 16 | 17 | /** 18 | * The only user to allow. 19 | */ 20 | public static final StringGuacamoleProperty TUTORIAL_USER = 21 | new StringGuacamoleProperty() { 22 | 23 | @Override 24 | public String getName() { return "tutorial-user"; } 25 | 26 | }; 27 | 28 | /** 29 | * The password required for the specified user. 30 | */ 31 | public static final StringGuacamoleProperty TUTORIAL_PASSWORD = 32 | new StringGuacamoleProperty() { 33 | 34 | @Override 35 | public String getName() { return "tutorial-password"; } 36 | 37 | }; 38 | 39 | 40 | /** 41 | * The protocol to use when connecting. 42 | */ 43 | public static final StringGuacamoleProperty TUTORIAL_PROTOCOL = 44 | new StringGuacamoleProperty() { 45 | 46 | @Override 47 | public String getName() { return "tutorial-protocol"; } 48 | 49 | }; 50 | 51 | 52 | /** 53 | * All parameters associated with the connection, as a comma-delimited 54 | * list of name="value" 55 | */ 56 | public static final StringGuacamoleProperty TUTORIAL_PARAMETERS = 57 | new StringGuacamoleProperty() { 58 | 59 | @Override 60 | public String getName() { return "tutorial-parameters"; } 61 | 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/index.md: -------------------------------------------------------------------------------- 1 | Apache Guacamole Manual 2 | ======================= 3 | 4 | :::{note} 5 | Licensed to the Apache Software Foundation (ASF) under one or more contributor 6 | license agreements. See the [NOTICE] file distributed with this work for 7 | additional information regarding copyright ownership. The ASF licenses this 8 | file to you under the Apache License, Version 2.0 (the "License"); you may not 9 | use this file except in compliance with the License. You may obtain a copy of 10 | the License at: 11 | 12 | 13 | 14 | Unless required by applicable law or agreed to in writing, software distributed 15 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 16 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 17 | specific language governing permissions and limitations under the License. 18 | 19 | [NOTICE]: https://raw.githubusercontent.com/apache/guacamole-manual/master/NOTICE 20 | ::: 21 | 22 | :::{toctree} 23 | :caption: Overview 24 | :name: overview 25 | :maxdepth: 1 26 | 27 | introduction 28 | ::: 29 | 30 | :::{toctree} 31 | :caption: User's Guide 32 | :name: users-guide 33 | :maxdepth: 1 34 | 35 | guacamole-architecture 36 | installing-guacamole 37 | guacamole-docker 38 | reverse-proxy 39 | configuring-guacamole 40 | jdbc-auth 41 | ldap-auth 42 | vault 43 | duo-auth 44 | totp-auth 45 | header-auth 46 | json-auth 47 | cas-auth 48 | openid-auth 49 | saml-auth 50 | radius-auth 51 | adhoc-connections 52 | using-guacamole 53 | recording-playback 54 | administration 55 | troubleshooting 56 | ::: 57 | 58 | :::{toctree} 59 | :caption: Developer's Guide 60 | :name: developers-guide 61 | :maxdepth: 1 62 | 63 | guacamole-protocol 64 | libguac 65 | guacamole-common 66 | guacamole-common-js 67 | guacamole-ext 68 | custom-protocols 69 | custom-auth 70 | event-listeners 71 | writing-you-own-guacamole-app 72 | ::: 73 | 74 | :::{toctree} 75 | :caption: Appendices 76 | :name: appendices 77 | :maxdepth: 1 78 | 79 | protocol-reference 80 | ::: 81 | 82 | -------------------------------------------------------------------------------- /tutorials/guacamole-tutorial/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Guacamole Tutorial 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 |
16 | 17 | 18 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | 2 | ------------------------------------------------------------ 3 | Contributing to Apache Guacamole 4 | ------------------------------------------------------------ 5 | 6 | Thank you for contributing to the Apache Guacamole project! 7 | 8 | There are certain procedures that must be followed for all contributions. These 9 | procedures are necessary to allow us to allocate resources for reviewing and 10 | testing your contribution, as well as communicate effectively with you during 11 | the review process. 12 | 13 | 1) Create an issue in our JIRA 14 | 15 | All changes to Guacamole must have corresponding issues in JIRA so the 16 | change can be properly tracked: 17 | 18 | https://issues.apache.org/jira/browse/GUACAMOLE/ 19 | 20 | If you do not already have an account on the Apache Software Foundation's 21 | JIRA, you will need to create one before creating your new issue. 22 | 23 | 2) Make and test your changes locally 24 | 25 | The Guacamole source is maintained in git repositories hosted on GitHub: 26 | 27 | https://github.com/apache/guacamole-client 28 | https://github.com/apache/guacamole-manual 29 | https://github.com/apache/guacamole-server 30 | https://github.com/apache/guacamole-website 31 | 32 | To make your changes, fork the applicable repositories and make commits 33 | to a topic branch in your fork. Commits should be made in logical units 34 | and must reference the JIRA issue number: 35 | 36 | $ git commit -m "GUACAMOLE-123: High-level message describing the changes." 37 | 38 | Avoid commits which cover multiple, distinct goals that could (and should) 39 | be handled separately. 40 | 41 | If you do not already have an account on GitHub, you will need to create 42 | one before making your changes. 43 | 44 | 3) Submit your changes via a pull request on GitHub 45 | 46 | Once your changes are ready, submit them by creating a pull request for 47 | the corresponding topic branch you created when you began working on your 48 | changes. 49 | 50 | The Guacamole team will then review your changes and, if they pass review, 51 | your changes will be merged. 52 | 53 | -------------------------------------------------------------------------------- /src/_static/gug.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /* Do not include border/background around code literals */ 21 | .rst-content code { 22 | border: none; 23 | background: transparent; 24 | padding: 0; 25 | } 26 | 27 | /* Use header font size for code literals in headers */ 28 | .rst-content h1 code.literal, 29 | .rst-content h2 code.literal, 30 | .rst-content h3 code.literal, 31 | .rst-content h4 code.literal, 32 | .rst-content h5 code.literal, 33 | .rst-content h6 code.literal { 34 | font-size: 100%; 35 | } 36 | 37 | /* Add border/background around replacable parts of code literals */ 38 | .rst-content code.samp em { 39 | background: #fff; 40 | border: 1px solid #e1e4e5; 41 | padding: 2px; 42 | font-style: normal; 43 | font-weight: bold; 44 | } 45 | 46 | /* Allow links containing code to be differentiated from normal text containing 47 | * code */ 48 | .rst-content a[href] code.literal { 49 | color: inherit; 50 | } 51 | 52 | /* Allow text within tables to wrap across multiple lines, rather than 53 | * potentially require horizontal scrolling */ 54 | .wy-table-responsive table td { 55 | white-space: normal; 56 | } 57 | 58 | /* Align table content with top of cell, rather than middle, so that it's 59 | * easier to see the text of adjacent table cells when one or more cells are 60 | * taller than the rest (due to word wrapping) */ 61 | .rst-content table.docutils td, 62 | .rst-content table.field-list td, 63 | .wy-table td { 64 | vertical-align:top 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | from datetime import date 20 | import os, sys 21 | 22 | # 23 | # Project, version, and author information 24 | # 25 | 26 | project = u'Apache Guacamole' 27 | version = u'1.5.3' 28 | 29 | year = date.today().year 30 | author = u'The Apache Software Foundation' 31 | copyright = u'%s %s' % (year, author) 32 | 33 | # Include "ext" directory in search path for custom Sphinx extensions 34 | sys.path.insert(0, os.path.abspath('ext')) 35 | 36 | # 37 | # Global options 38 | # 39 | 40 | extensions = [ 41 | 'guac', 42 | 'myst_parser', 43 | 'sphinx.ext.ifconfig', 44 | 'sphinx.ext.extlinks', 45 | 'sphinx_inline_tabs' 46 | ] 47 | 48 | # Allow shorthand notation for JIRA issue links 49 | extlinks = { 50 | 'jira': ( 'https://issues.apache.org/jira/browse/%s', '%s') 51 | } 52 | 53 | templates_path = [ '_templates' ] 54 | 55 | # Do not parse files within include/ unless they are explicitly included with 56 | # the "include" directive 57 | exclude_patterns = [ 'include/**' ] 58 | 59 | # Do not highlight source unless a Pygments lexer name is explicitly provided 60 | highlight_language = 'none' 61 | 62 | myst_enable_extensions = [ 63 | "colon_fence", 64 | "deflist", 65 | "replacements", 66 | "smartquotes", 67 | "substitution" 68 | ] 69 | 70 | myst_substitutions = { 71 | "version" : version 72 | } 73 | 74 | # 75 | # HTML output options 76 | # 77 | 78 | html_theme = 'sphinx_rtd_theme' 79 | html_title = u'Apache Guacamole Manual v%s' % version 80 | 81 | html_static_path = [ '_static' ] 82 | html_css_files = [ 'gug.css' ] 83 | 84 | html_context = { 85 | 'copyright_year' : year 86 | } 87 | 88 | -------------------------------------------------------------------------------- /tutorials/guacamole-tutorial/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | org.apache.guacamole 8 | guacamole-tutorial 9 | war 10 | 1.5.3 11 | guacamole-tutorial 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.3 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-war-plugin 35 | 2.6 36 | 37 | 38 | 39 | org.apache.guacamole 40 | guacamole-common-js 41 | zip 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | javax.servlet 56 | servlet-api 57 | 2.5 58 | provided 59 | 60 | 61 | 62 | 63 | org.apache.guacamole 64 | guacamole-common 65 | 1.5.3 66 | compile 67 | 68 | 69 | 70 | 71 | org.apache.guacamole 72 | guacamole-common-js 73 | 1.5.3 74 | zip 75 | runtime 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /tutorials/guacamole-auth-tutorial/src/main/java/org/apache/guacamole/auth/TutorialAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package org.apache.guacamole.auth; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.apache.guacamole.GuacamoleException; 6 | import org.apache.guacamole.GuacamoleServerException; 7 | import org.apache.guacamole.environment.Environment; 8 | import org.apache.guacamole.environment.LocalEnvironment; 9 | import org.apache.guacamole.net.auth.simple.SimpleAuthenticationProvider; 10 | import org.apache.guacamole.net.auth.Credentials; 11 | import org.apache.guacamole.protocol.GuacamoleConfiguration; 12 | 13 | /** 14 | * Authentication provider implementation intended to demonstrate basic use 15 | * of Guacamole's extension API. The credentials and connection information for 16 | * a single user are stored directly in guacamole.properties. 17 | */ 18 | public class TutorialAuthenticationProvider extends SimpleAuthenticationProvider { 19 | 20 | @Override 21 | public String getIdentifier() { 22 | return "tutorial"; 23 | } 24 | 25 | @Override 26 | public Map 27 | getAuthorizedConfigurations(Credentials credentials) 28 | throws GuacamoleException { 29 | 30 | // Get the Guacamole server environment 31 | Environment environment = LocalEnvironment.getInstance(); 32 | 33 | // Get username from guacamole.properties 34 | String username = environment.getRequiredProperty( 35 | TutorialGuacamoleProperties.TUTORIAL_USER 36 | ); 37 | 38 | // If wrong username, fail 39 | if (!username.equals(credentials.getUsername())) 40 | return null; 41 | 42 | // Get password from guacamole.properties 43 | String password = environment.getRequiredProperty( 44 | TutorialGuacamoleProperties.TUTORIAL_PASSWORD 45 | ); 46 | 47 | // If wrong password, fail 48 | if (!password.equals(credentials.getPassword())) 49 | return null; 50 | 51 | // Successful login. Return configurations. 52 | Map configs = 53 | new HashMap(); 54 | 55 | // Create new configuration 56 | GuacamoleConfiguration config = new GuacamoleConfiguration(); 57 | 58 | // Set protocol specified in properties 59 | config.setProtocol(environment.getRequiredProperty( 60 | TutorialGuacamoleProperties.TUTORIAL_PROTOCOL 61 | )); 62 | 63 | // Set all parameters, splitting at commas 64 | for (String parameterValue : environment.getRequiredProperty( 65 | TutorialGuacamoleProperties.TUTORIAL_PARAMETERS 66 | ).split(",\\s*")) { 67 | 68 | // Find the equals sign 69 | int equals = parameterValue.indexOf('='); 70 | if (equals == -1) 71 | throw new GuacamoleServerException("Required equals sign missing"); 72 | 73 | // Get name and value from parameter string 74 | String name = parameterValue.substring(0, equals); 75 | String value = parameterValue.substring(equals+1); 76 | 77 | // Set parameter as specified 78 | config.setParameter(name, value); 79 | 80 | } 81 | 82 | configs.put("Tutorial Connection", config); 83 | return configs; 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/header-auth.md: -------------------------------------------------------------------------------- 1 | HTTP header authentication 2 | ========================== 3 | 4 | Guacamole supports delegating authentication to an arbitrary external service, 5 | relying on the presence of an HTTP header which contains the username of the 6 | authenticated user. This authentication method must be layered on top of some 7 | other authentication extension, such as those available from the main project 8 | website, in order to provide access to actual connections. 9 | 10 | :::{important} 11 | All external requests must be properly sanitized if this extension is used. The 12 | chosen HTTP header must be stripped from untrusted requests, such that the 13 | authentication service is the only possible source of that header. *If such 14 | sanitization is not performed, it will be trivial for malicious users to add 15 | this header manually, and thus gain unrestricted access.* 16 | ::: 17 | 18 | (header-downloading)= 19 | 20 | Downloading the HTTP header authentication extension 21 | ---------------------------------------------------- 22 | 23 | The HTTP header authentication extension is available separately from the main 24 | `guacamole.war`. The link for this and all other officially-supported and 25 | compatible extensions for a particular version of Guacamole are provided on the 26 | release notes for that version. You can find the release notes for current 27 | versions of Guacamole here: . 28 | 29 | The HTTP header authentication extension is packaged as a `.tar.gz` file 30 | containing only the extension itself, `guacamole-auth-header-1.5.3.jar`, which 31 | must ultimately be placed in `GUACAMOLE_HOME/extensions`. 32 | 33 | (installing-header-auth)= 34 | 35 | Installing HTTP header authentication 36 | ------------------------------------- 37 | 38 | Guacamole extensions are self-contained `.jar` files which are located within 39 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 40 | `GUACAMOLE_HOME` is located on your system, please consult 41 | [](configuring-guacamole) before proceeding.* 42 | 43 | To install the HTTP header authentication extension, you must: 44 | 45 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 46 | exist. 47 | 48 | 2. Copy `guacamole-auth-header-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 49 | 50 | 3. Configure Guacamole to use HTTP header authentication, as described below. 51 | 52 | (guac-header-config)= 53 | 54 | ### Configuring Guacamole for HTTP header authentication 55 | 56 | The HTTP header authentication extension provides only one configuration 57 | property, and it is optional. By default, the extension will pull the username 58 | of the authenticated user from the `REMOTE_USER` header, if present. If your 59 | authentication system uses a different HTTP header, you will need to override 60 | this by specifying the `http-auth-header` property within 61 | [`guacamole.properties`](initial-setup): 62 | 63 | `http-auth-header` 64 | : The HTTP header containing the username of the authenticated user. This 65 | property is optional. If not specified, `REMOTE_USER` will be used by 66 | default. 67 | 68 | (completing-header-install)= 69 | 70 | ### Completing the installation 71 | 72 | Guacamole will only reread `guacamole.properties` and load newly-installed 73 | extensions during startup, so your servlet container will need to be restarted 74 | before HTTP header authentication can be used. *Doing this will disconnect all 75 | active users, so be sure that it is safe to do so prior to attempting 76 | installation.* When ready, restart your servlet container and give the new 77 | authentication a try. 78 | 79 | -------------------------------------------------------------------------------- /src/saml-auth.md: -------------------------------------------------------------------------------- 1 | SAML Authentication 2 | =================== 3 | 4 | SAML is a widely implemented and used Single Sign On (SSO) provider that allows 5 | applications and services to authenticate in a standard way, and brokers those 6 | authentication requests to one or more back-end authentication providers. The 7 | SAML authentication extension allows Guacamole to redirect to a SAML Identity 8 | Provider (IdP) for authentication and user services. This module does not 9 | provide any capability for storing or retrieving connections, and must be 10 | layered with other authentication extensions that provide connection 11 | management. 12 | 13 | (saml-downloading)= 14 | 15 | Downloading the SAML authentication extension 16 | --------------------------------------------- 17 | 18 | ```{include} include/sso-download.md 19 | ``` 20 | 21 | The extension for the desired SSO method, in this case 22 | `guacamole-auth-sso-saml-1.5.3.jar` from within the `saml/` subdirectory, 23 | must ultimately be placed in `GUACAMOLE_HOME/extensions`. 24 | 25 | (installing-saml-auth)= 26 | 27 | Installing SAML authentication 28 | ------------------------------ 29 | 30 | Guacamole extensions are self-contained `.jar` files which are located within 31 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 32 | `GUACAMOLE_HOME` is located on your system, please consult 33 | [](configuring-guacamole) before proceeding.* 34 | 35 | To install the SAML authentication extension, you must: 36 | 37 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 38 | exist. 39 | 40 | 2. Copy `guacamole-auth-sso-saml-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 41 | 42 | 3. Configure Guacamole to use SAML authentication, as described below. 43 | 44 | (guac-saml-config)= 45 | 46 | ### Configuring Guacamole for SAML Authentication 47 | 48 | The SAML authentication extension provides several configuration properties to 49 | set it up to talk to the IdP. The SAML IdP also must be configured with 50 | Guacamole as a Service Provider (SP). Configuration of the SAML IdP is beyond 51 | the scope of this document, and will vary widely based on the IdP in use. 52 | 53 | `saml-idp-metadata-url` 54 | : The URI of the XML metadata file that from the SAML Identity Provider that 55 | contains all of the information the SAML extension needs in order to know how 56 | to authenticate with the IdP. This URI can either be a remote server (e.g. 57 | `https://`) or a local file on the filesystem (e.g. `file://`). Often the 58 | metadata file contains most of the required properties for SAML 59 | authentication and the other parameters are not required. 60 | 61 | `saml-idp-url` 62 | : The base URL of the SAML IdP. This is the URL that the SAML authentication 63 | extension will use to redirect when requesting SAML authentication. If the 64 | `saml-idp-metadata-url` property is provided, this parameter will be ignored. 65 | If the metadata file is not provided this property is required. 66 | 67 | `saml-entity-id` 68 | : The entity ID of the Guacamole SAML client, which is generally the URL of the 69 | Guacamole server, but is not required to be so. This property is required if 70 | either the `saml-idp-metadata-url` property is not specified, or if the 71 | provided metadata file does not contain the SAML SP Entity ID for Guacamole 72 | Client. 73 | 74 | `saml-callback-url` 75 | : The URL that the IdP will use once authentication has succeeded to return to 76 | the Guacamole web application and provide the authentication details to the 77 | SAML extension. The SAML extension currently only supports callback as a POST 78 | operation to this callback URL. This property is required. 79 | 80 | `saml-strict` 81 | : Require strict security checks during SAML logins. This will insure that 82 | valid certificates are present for all interactions with SAML servers and 83 | fail SAML authentication if security restrictions are violated. This property 84 | is optional, and will default to true, requiring strict security checks. This 85 | property should only be set to false in non-production environments during 86 | testing of SAML authentication. 87 | 88 | `saml-debug` 89 | : Enable additional logging within the supporting SAML library that can assist 90 | in tracking down issues during SAML logins. This property is optional, and 91 | will default to false (no debugging). 92 | 93 | `saml-compress-request` 94 | : Enable compression of the HTTP requests sent to the SAML IdP. This property 95 | is optional and will default to true (compression enabled). 96 | 97 | `saml-compress-response` 98 | : Request that the SAML response returned by the IdP be compressed. This 99 | property is optional and will default to true (compression will be 100 | requested). 101 | 102 | `saml-group-attribute` 103 | : The name of the attribute provided by the SAML IdP that contains group 104 | membership of the user. These groups will be parsed and used to map group 105 | membership of the user logging in, which can be used for permissions 106 | management within Guacamole Client, particularly when layered with other 107 | authentication modules. This property is optional, and defaults to "groups". 108 | 109 | ### Controlling login behavior 110 | 111 | ```{include} include/sso-login-behavior.md 112 | ``` 113 | 114 | #### Automatically redirecting all unauthenticated users 115 | 116 | To ensure users are redirected to the SAML identity provider immediately 117 | (without a Guacamole login screen), ensure the SAML extension has priority over 118 | all others: 119 | 120 | ``` 121 | extension-priority: saml 122 | ``` 123 | 124 | #### Presenting unauthenticated users with a login screen 125 | 126 | To ensure users are given a normal Guacamole login screen and have the option 127 | to log in with traditional credentials _or_ with SAML, ensure the SAML 128 | extension does not have priority: 129 | 130 | ``` 131 | extension-priority: *, saml 132 | ``` 133 | 134 | (completing-saml-install)= 135 | 136 | ### Completing the installation 137 | 138 | Guacamole will only reread `guacamole.properties` and load newly-installed 139 | extensions during startup, so your servlet container will need to be restarted 140 | before SAML authentication can be used. *Doing this will disconnect all active 141 | users, so be sure that it is safe to do so prior to attempting installation.* 142 | When ready, restart your servlet container and give the new authentication a 143 | try. 144 | 145 | -------------------------------------------------------------------------------- /src/cas-auth.md: -------------------------------------------------------------------------------- 1 | CAS Authentication 2 | ================== 3 | 4 | CAS is an open-source Single Sign On (SSO) provider that allows multiple 5 | applications and services to authenticate against it and brokers those 6 | authentication requests to a back-end authentication provider. This module 7 | allows Guacamole to redirect to CAS for authentication and user services. This 8 | module must be layered on top of other authentication extensions that provide 9 | connection information, as it only provides user authentication. 10 | 11 | (cas-downloading)= 12 | 13 | Downloading the CAS authentication extension 14 | -------------------------------------------- 15 | 16 | ```{include} include/sso-download.md 17 | ``` 18 | 19 | The extension for the desired SSO method, in this case 20 | `guacamole-auth-sso-cas-1.5.3.jar` from within the `cas/` subdirectory, must 21 | ultimately be placed in `GUACAMOLE_HOME/extensions`. 22 | 23 | (installing-cas-auth)= 24 | 25 | Installing CAS authentication 26 | ----------------------------- 27 | 28 | Guacamole extensions are self-contained `.jar` files which are located within 29 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 30 | `GUACAMOLE_HOME` is located on your system, please consult 31 | [](configuring-guacamole) before proceeding.* 32 | 33 | To install the CAS authentication extension, you must: 34 | 35 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 36 | exist. 37 | 38 | 2. Copy `guacamole-auth-sso-cas-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 39 | 40 | 3. Configure Guacamole to use CAS authentication, as described below. 41 | 42 | (guac-cas-config)= 43 | 44 | ### Configuring Guacamole for CAS Authentication 45 | 46 | Guacamole's CAS support requires specifying two properties that describe the 47 | CAS authentication server and the Guacamole deployment. These properties are 48 | *absolutely required in all cases*, as they dictate how Guacamole should 49 | connect to the CAS and how CAS should redirect users back to Guacamole once 50 | their identity has been confirmed: 51 | 52 | `cas-authorization-endpoint` 53 | : The URL of the CAS authentication server. This should be the full path to the 54 | base of the CAS installation. 55 | 56 | `cas-redirect-uri` 57 | : The URI to redirect back to upon successful authentication. Normally this 58 | will be the full URL of your Guacamole installation. 59 | 60 | Additional optional properties are available to control how CAS tokens are 61 | processed, including whether [CAS ClearPass](cas-clearpass) should be used and 62 | how user group memberships should be derived: 63 | 64 | `cas-clearpass-key` 65 | : If using CAS ClearPass to pass the SSO password to Guacamole, this parameter 66 | specifies the private key file to use to decrypt the password. See [the section 67 | on ClearPass](cas-clearpass) below. 68 | 69 | `cas-group-attribute` 70 | : The CAS attribute that determines group membership, typically "memberOf". 71 | This parameter is only required if using CAS to define user group memberships. 72 | If omitted, groups aren't retrieved from CAS, and all other group-related 73 | properties for CAS are ignored. 74 | 75 | `cas-group-format` 76 | : The format that CAS will use for its group names. Possible values are 77 | `plain`, for groups that are simple text names, or `ldap`, for groups that are 78 | represented as LDAP DNs. If set to `ldap`, group names are always determined 79 | from the last (leftmost) attribute of the DN. If omitted, `plain` is used by 80 | default. 81 | 82 | This property has no effect if cas-group-attribute is not set. 83 | 84 | `cas-group-ldap-base-dn` 85 | : The base DN to require for LDAP-formatted CAS groups. If specified, only CAS 86 | groups beneath this DN will be included, and all other CAS groups will be 87 | ignored. 88 | 89 | This property has no effect if cas-group-format is not `ldap`. 90 | 91 | `cas-group-ldap-attribute` 92 | : The LDAP attribute to require for LDAP-formatted CAS groups. If specified, 93 | only CAS groups that use this attribute for the name of the group will be 94 | included. Note that LDAP group names are *always determined from the last 95 | (leftmost) attribute of the DN*. Specifying this property will only have the 96 | effect of ignoring any groups that do not use the specified attribute to 97 | represent the group name. 98 | 99 | This property has no effect if cas-group-format is not `ldap`. 100 | 101 | (cas-login)= 102 | 103 | ### Controlling login behavior 104 | 105 | ```{include} include/sso-login-behavior.md 106 | ``` 107 | 108 | #### Automatically redirecting all unauthenticated users 109 | 110 | To ensure users are redirected to the CAS identity provider immediately 111 | (without a Guacamole login screen), ensure the CAS extension has priority over 112 | all others: 113 | 114 | ``` 115 | extension-priority: cas 116 | ``` 117 | 118 | #### Presenting unauthenticated users with a login screen 119 | 120 | To ensure users are given a normal Guacamole login screen and have the option 121 | to log in with traditional credentials _or_ with CAS, ensure the CAS extension 122 | does not have priority: 123 | 124 | ``` 125 | extension-priority: *, cas 126 | ``` 127 | 128 | (completing-cas-install)= 129 | 130 | ### Completing the installation 131 | 132 | Guacamole will only reread `guacamole.properties` and load newly-installed 133 | extensions during startup, so your servlet container will need to be restarted 134 | before CAS authentication can be used. *Doing this will disconnect all active 135 | users, so be sure that it is safe to do so prior to attempting installation.* 136 | When ready, restart your servlet container and give the new authentication a 137 | try. 138 | 139 | (cas-clearpass)= 140 | 141 | ### Using CAS ClearPass 142 | 143 | CAS has a function called ClearPass that can be used to cache the password used 144 | for SSO authentication and make that available to services at a later time. 145 | Configuring the CAS server for ClearPass is beyond the scope of this article - 146 | more information can be found on the Apereo CAS wiki at the following URL: 147 | . 148 | 149 | Once you have CAS configured for credential caching, you need to configure the 150 | service with a keypair for passing the credential securely. The public key gets 151 | installed on the CAS server, while the private key gets configured with the 152 | cas-clearpass-key property. The private key file needs to be in RSA PKCS8 153 | format. 154 | 155 | -------------------------------------------------------------------------------- /src/adhoc-connections.md: -------------------------------------------------------------------------------- 1 | Ad-hoc Connections 2 | ================== 3 | 4 | The quickconnect extension provides a connection bar on the Guacamole Client 5 | home page that allows users to type in the URI of a server to which they want 6 | to connect and the client will parse the URI and immediately establish the 7 | connection. The purpose of the extension is to allow situations where 8 | administrators want to allow users the flexibility of establishing their own 9 | connections without having to grant them access to edit connections or even to 10 | have to create the connections at all, aside from typing the URI. 11 | 12 | :::{important} 13 | There are several implications of using this extension that should be 14 | well-understood by administrators prior to implementing it: 15 | 16 | * Connections established with this extension are created in-memory and only 17 | persist until the Guacamole session ends. 18 | 19 | * Connections created with this extension are not accessible to other users, 20 | and cannot be shared with other users. 21 | 22 | * This extension provides no functionality for authenticating users - it does 23 | not allow anonymous logins, and requires that users are successfully 24 | authenticated by another authentication module before it can be used. 25 | 26 | * The extension provides users the ability not only to establish connections, 27 | but also to set any of the parameters for a connection. There are security 28 | implications for this - for example, RDP file sharing can be used to pass 29 | through any directory available on the server running guacd to the remote 30 | desktop. This should be taken into consideration when enabling this extension 31 | and making sure that guacd is configured in a way that does not compromise 32 | sensitive system files by allowing access to them. 33 | ::: 34 | 35 | (quickconnect-downloading)= 36 | 37 | Downloading the quickconnect extension 38 | -------------------------------------- 39 | 40 | The quickconnect extension is available separately from the main 41 | `guacamole.war`. The link for this and all other officially-supported and 42 | compatible extensions for a particular version of Guacamole are provided in the 43 | release notes for that version. You can find the release notes for current 44 | versions of Guacamole here: . 45 | 46 | The quickconnect extension is packaged as a `.tar.gz` file containing only the 47 | extension itself, `guacamole-auth-quickconnect-1.5.3.jar`, which must 48 | ultimately be placed in `GUACAMOLE_HOME/extensions`. 49 | 50 | (installing-quickconnect)= 51 | 52 | Installing the quickconnect extension 53 | ------------------------------------- 54 | 55 | Guacamole extensions are self-contained `.jar` files which are located within 56 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 57 | `GUACAMOLE_HOME` is located on your system, please consult 58 | [](configuring-guacamole) before proceeding.* 59 | 60 | To install the extension, you must: 61 | 62 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 63 | exist. 64 | 65 | 2. Place the `guacamole-auth-quickconnect-1.5.3.jar` file in the 66 | `GUACAMOLE_HOME/extensions` directory. 67 | 68 | (guac-quickconnect-config)= 69 | 70 | ### Configuring Guacamole for the quickconnect extension 71 | 72 | The quickconnect extension has two configuration properties that allow for 73 | controlling what connection parameters can be used in the URIs that are opened 74 | with the quickconnect extension: 75 | 76 | `quickconnect-allowed-parameters` 77 | : An optional list of parameters that are allowed to be used by connections 78 | that are created and accessed via the quickconnect extension. If this 79 | property is present, only parameters in this list will be allowed. If this 80 | property is absent, any/all parameters will be allowed unless explicitly 81 | denied using the `quickconnect-denied-parameters` property. 82 | 83 | `quickconnect-denied-parameters` 84 | : An optional list of parameters that are explicitly denied from being used by 85 | connections created and accessed via the quickconnect extension. If this 86 | property is present, any parameters in this list will be removed from the 87 | connection configuration when it is created, *even if those parameter are 88 | listed above in the `quickconnect-allowed-parameters` property.* If this 89 | property is not present, no connection parameters will be explicitly denied. 90 | 91 | (completing-quickconnect-install)= 92 | 93 | ### Completing the installation 94 | 95 | Guacamole will only load newly-installed extensions during startup, so your 96 | servlet container will need to be restarted before the quickconnect extension 97 | can be used. *Doing this will disconnect all active users, so be sure that it 98 | is safe to do so prior to attempting installation.* When ready, restart your 99 | servlet container and give the extension a try. 100 | 101 | (using-quickconnect)= 102 | 103 | Using the quickconnect extension 104 | -------------------------------- 105 | 106 | The quickconnect extension provides a field on the home page that allows you to 107 | enter a Uniform Resource Identifier (URI) to create a connection. A URI is in 108 | the form: 109 | 110 | {samp}`{protocol}://{username}:{password}@{host}:{port}/?{parameters}` 111 | 112 | The `protocol` field can have any of the protocols supported by Guacamole, as 113 | documented in [](configuring-guacamole). Many of the protocols define a default 114 | `port` value, with the exception of VNC. The `parameters` field can specify any 115 | of the protocol-specific parameters as documented on the configuration page. 116 | 117 | To establish a connection, simply type in a valid URI and either press "Enter" 118 | or click the connect button. This extension will parse the URI and create a new 119 | connection, and immediately start that connection in the current browser. 120 | 121 | Here are a few examples of URIs: 122 | 123 | `ssh://linux1.example.com/` 124 | : Connect to the server linux1.example.com using the SSH protocol on the 125 | default SSH port (22). This will result in prompting for both username and 126 | password. 127 | 128 | `vnc://linux1.example.com:5900/` 129 | : Connect to the server linux1.example.com using the VNC protocol and 130 | specifying the port as 5900. 131 | 132 | `rdp://localuser@windows1.example.com/?security=rdp&ignore-cert=true&disable-audio=true&enable-drive=true&drive-path=/mnt/usb` 133 | : Connect to the server windows1.example.com using the RDP protocol and the 134 | user "localuser". This URI also specifies several RDP-specific parameters on 135 | the connection, including forcing security mode to RDP (security=rdp), ignoring 136 | any certificate errors (ignore-cert=true), disabling audio pass-through 137 | (disable-audio=true), and enabling filesystem redirection (enable-drive=true) 138 | to the /mnt/usb folder on the system running guacd (drive-path=/mnt/usb). 139 | 140 | -------------------------------------------------------------------------------- /tutorials/libguac-client-ball/src/ball.c: -------------------------------------------------------------------------------- 1 | #include "ball.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | /* Client plugin arguments (empty) */ 14 | const char* TUTORIAL_ARGS[] = { NULL }; 15 | 16 | void* ball_render_thread(void* arg) { 17 | 18 | /* Get data */ 19 | guac_client* client = (guac_client*) arg; 20 | ball_client_data* data = (ball_client_data*) client->data; 21 | 22 | /* Init time of last frame to current time */ 23 | guac_timestamp last_frame = guac_timestamp_current(); 24 | 25 | /* Update ball position as long as client is running */ 26 | while (client->state == GUAC_CLIENT_RUNNING) { 27 | 28 | /* Default to 30ms frames */ 29 | int frame_duration = 30; 30 | 31 | /* Lengthen frame duration if client is lagging */ 32 | int processing_lag = guac_client_get_processing_lag(client); 33 | if (processing_lag > frame_duration) 34 | frame_duration = processing_lag; 35 | 36 | /* Sleep for duration of frame, then get timestamp */ 37 | usleep(frame_duration * 1000); 38 | guac_timestamp current = guac_timestamp_current(); 39 | 40 | /* Calculate change in time */ 41 | int delta_t = current - last_frame; 42 | 43 | /* Update position */ 44 | data->ball_x += data->ball_velocity_x * delta_t / 1000; 45 | data->ball_y += data->ball_velocity_y * delta_t / 1000; 46 | 47 | /* Bounce if necessary */ 48 | if (data->ball_x < 0) { 49 | data->ball_x = -data->ball_x; 50 | data->ball_velocity_x = -data->ball_velocity_x; 51 | } 52 | else if (data->ball_x >= 1024 - 128) { 53 | data->ball_x = (2 * (1024 - 128)) - data->ball_x; 54 | data->ball_velocity_x = -data->ball_velocity_x; 55 | } 56 | 57 | if (data->ball_y < 0) { 58 | data->ball_y = -data->ball_y; 59 | data->ball_velocity_y = -data->ball_velocity_y; 60 | } 61 | else if (data->ball_y >= 768 - 128) { 62 | data->ball_y = (2 * (768 - 128)) - data->ball_y; 63 | data->ball_velocity_y = -data->ball_velocity_y; 64 | } 65 | 66 | guac_protocol_send_move(client->socket, data->ball, 67 | GUAC_DEFAULT_LAYER, data->ball_x, data->ball_y, 0); 68 | 69 | /* End frame and flush socket */ 70 | guac_client_end_frame(client); 71 | guac_socket_flush(client->socket); 72 | 73 | /* Update timestamp */ 74 | last_frame = current; 75 | 76 | } 77 | 78 | return NULL; 79 | 80 | } 81 | 82 | int ball_join_handler(guac_user* user, int argc, char** argv) { 83 | 84 | /* Get client associated with user */ 85 | guac_client* client = user->client; 86 | 87 | /* Get ball layer from client data */ 88 | ball_client_data* data = (ball_client_data*) client->data; 89 | guac_layer* ball = data->ball; 90 | 91 | /* Get user-specific socket */ 92 | guac_socket* socket = user->socket; 93 | 94 | /* Send the display size */ 95 | guac_protocol_send_size(socket, GUAC_DEFAULT_LAYER, 1024, 768); 96 | 97 | /* Create background tile */ 98 | guac_layer* texture = guac_client_alloc_buffer(client); 99 | 100 | guac_protocol_send_rect(socket, texture, 0, 0, 64, 64); 101 | guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture, 102 | 0x88, 0x88, 0x88, 0xFF); 103 | 104 | guac_protocol_send_rect(socket, texture, 0, 0, 32, 32); 105 | guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture, 106 | 0xDD, 0xDD, 0xDD, 0xFF); 107 | 108 | guac_protocol_send_rect(socket, texture, 32, 32, 32, 32); 109 | guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture, 110 | 0xDD, 0xDD, 0xDD, 0xFF); 111 | 112 | /* Prepare a curve which covers the entire layer */ 113 | guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER, 114 | 0, 0, 1024, 768); 115 | 116 | /* Fill curve with texture */ 117 | guac_protocol_send_lfill(socket, 118 | GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, 119 | texture); 120 | 121 | /* Set up ball layer */ 122 | guac_protocol_send_size(socket, ball, 128, 128); 123 | 124 | /* Prepare a circular curve */ 125 | guac_protocol_send_arc(socket, data->ball, 126 | 64, 64, 62, 0, 6.28, 0); 127 | 128 | guac_protocol_send_close(socket, data->ball); 129 | 130 | /* Draw a 4-pixel black border */ 131 | guac_protocol_send_cstroke(socket, 132 | GUAC_COMP_OVER, data->ball, 133 | GUAC_LINE_CAP_ROUND, GUAC_LINE_JOIN_ROUND, 4, 134 | 0x00, 0x00, 0x00, 0xFF); 135 | 136 | /* Fill the circle with color */ 137 | guac_protocol_send_cfill(socket, 138 | GUAC_COMP_OVER, data->ball, 139 | 0x00, 0x80, 0x80, 0x80); 140 | 141 | /* Free texture (no longer needed) */ 142 | guac_client_free_buffer(client, texture); 143 | 144 | /* Mark end-of-frame */ 145 | guac_protocol_send_sync(socket, client->last_sent_timestamp); 146 | 147 | /* Flush buffer */ 148 | guac_socket_flush(socket); 149 | 150 | /* User successfully initialized */ 151 | return 0; 152 | 153 | } 154 | 155 | int ball_free_handler(guac_client* client) { 156 | 157 | ball_client_data* data = (ball_client_data*) client->data; 158 | 159 | /* Wait for render thread to terminate */ 160 | pthread_join(data->render_thread, NULL); 161 | 162 | /* Free client-level ball layer */ 163 | guac_client_free_layer(client, data->ball); 164 | 165 | /* Free client-specific data */ 166 | free(data); 167 | 168 | /* Data successfully freed */ 169 | return 0; 170 | 171 | } 172 | 173 | int guac_client_init(guac_client* client) { 174 | 175 | /* Allocate storage for client-specific data */ 176 | ball_client_data* data = malloc(sizeof(ball_client_data)); 177 | 178 | /* Set up client data and handlers */ 179 | client->data = data; 180 | 181 | /* Allocate layer at the client level */ 182 | data->ball = guac_client_alloc_layer(client); 183 | 184 | /* Start ball at upper left */ 185 | data->ball_x = 0; 186 | data->ball_y = 0; 187 | 188 | /* Move at a reasonable pace to the lower right */ 189 | data->ball_velocity_x = 200; /* pixels per second */ 190 | data->ball_velocity_y = 200; /* pixels per second */ 191 | 192 | /* Start render thread */ 193 | pthread_create(&data->render_thread, NULL, ball_render_thread, client); 194 | 195 | /* This example does not implement any arguments */ 196 | client->args = TUTORIAL_ARGS; 197 | 198 | /* Client-level handlers */ 199 | client->join_handler = ball_join_handler; 200 | client->free_handler = ball_free_handler; 201 | 202 | return 0; 203 | 204 | } 205 | -------------------------------------------------------------------------------- /src/guacamole-common.md: -------------------------------------------------------------------------------- 1 | guacamole-common 2 | ================ 3 | 4 | The Java API provided by the Guacamole project is called guacamole-common. It 5 | provides a basic means of tunneling data between the JavaScript client provided 6 | by guacamole-common-js and the native proxy daemon, guacd, and for dealing with 7 | the Guacamole protocol. The purpose of this library is to facilitate the 8 | creation of custom tunnels between the JavaScript client and guacd, allowing 9 | your Guacamole-driven web application to enforce its own security model, if 10 | any, and dictate exactly what connections are established. 11 | 12 | (java-http-tunnel)= 13 | 14 | HTTP tunnel 15 | ----------- 16 | 17 | The Guacamole Java API implements the HTTP tunnel using a servlet called 18 | `GuacamoleHTTPTunnelServlet`. This servlet handles all requests coming to it 19 | over HTTP from the JavaScript client, and translates them into connect, read, 20 | or write requests, which each get dispatched to the `doConnect()`, `doRead()`, 21 | and `doWrite()` functions accordingly. 22 | 23 | Normally, you wouldn't touch the `doRead()` and `doWrite()` functions, as these 24 | have already been written to properly handle the requests of the JavaScript 25 | tunnel, and if you feel the need to touch these functions, you are probably 26 | better off writing your own tunnel implementation, although such a thing is 27 | difficult to do in a performant way. 28 | 29 | When developing an application based on the Guacamole API, you should use 30 | `GuacamoleHTTPTunnelServlet` by extending it, implementing your own version of 31 | `doConnect()`, which is the only abstract function it defines. The tutorial 32 | later in this book demonstrating how to write a Guacamole-based web application 33 | shows the basics of doing this, but generally, `doConnect()` is an excellent 34 | place for authentication or other validation, as it is the responsibility of 35 | `doConnect()` to create (or not create) the actual tunnel. If `doConnect()` 36 | does not create the tunnel, communication between the JavaScript client and 37 | guacd cannot take place, which is an ideal power to have as an authenticator. 38 | 39 | The `doConnect()` function is expected to return a new `GuacamoleTunnel`, but 40 | it is completely up to the implementation to decide how that tunnel is to be 41 | created. The already-implemented parts of `GuacamoleHTTPTunnelServlet` then 42 | return the unique identifier of this tunnel to the JavaScript client, allowing 43 | its own tunnel implementation to continue to communicate with the tunnel 44 | existing on the Java side. 45 | 46 | Instances of `GuacamoleTunnel` are associated with a `GuacamoleSocket`, which 47 | is the abstract interface surrounding the low-level connection to guacd. 48 | Overall, there is a socket (`GuacamoleSocket`) which provides a TCP connection 49 | to guacd. This socket is exposed to `GuacamoleTunnel`, which provides abstract 50 | protocol access around what is actually (but secretly, through the abstraction 51 | of the API) a TCP socket. 52 | 53 | The Guacamole web application extends this tunnel servlet in order to implement 54 | authentication at the lowest possible level, effectively prohibiting 55 | communication between the client and any remote desktops unless they have 56 | properly authenticated. Your own implementation can be considerably simpler, 57 | especially if you don't need authentication: 58 | 59 | ```java 60 | public class MyGuacamoleTunnelServlet 61 | extends GuacamoleHTTPTunnelServlet { 62 | 63 | @Override 64 | protected GuacamoleTunnel doConnect(HttpServletRequest request) 65 | throws GuacamoleException { 66 | 67 | // Connect to guacd here (this is a STUB) 68 | GuacamoleSocket socket; 69 | 70 | // Return a new tunnel which uses the connected socket 71 | return new SimpleGuacamoleTunnel(socket); 72 | 73 | } 74 | 75 | } 76 | ``` 77 | 78 | (java-protocol-usage)= 79 | 80 | Using the Guacamole protocol 81 | ---------------------------- 82 | 83 | guacamole-common provides basic low-level support for the Guacamole protocol. 84 | This low-level support is leveraged by the HTTP tunnel implementation to 85 | satisfy the requirements of the JavaScript client implementation, as the 86 | JavaScript client expects the handshake procedure to have already taken place. 87 | This support exists through the `GuacamoleReader` and `GuacamoleWriter` 88 | classes, which are similar to Java's `Reader` and `Writer` classes, except that 89 | they deal with the Guacamole protocol specifically, and thus have slightly 90 | different contracts. 91 | 92 | (java-reading-protocol)= 93 | 94 | ### `GuacamoleReader` 95 | 96 | `GuacamoleReader` provides a very basic `read()` function which is required to 97 | return one or more complete instructions in a `char` array. It also provides 98 | the typical `available()` function, which informs you whether `read()` is 99 | likely to block the next time it is called, and an even more abstract version 100 | of `read()` called `readInstruction()` which returns one instruction at a time, 101 | wrapped within a `GuacamoleInstruction` instance. 102 | 103 | Normally, you would not need to use this class yourself. It is used by 104 | `ConfiguredGuacamoleSocket` to complete the Guacamole protocol handshake 105 | procedure, and it is used by `GuacamoleHTTPTunnelServlet` within `doRead()` to 106 | implement the reading half of the tunnel. 107 | 108 | The only concrete implementation of `GuacamoleReader` is 109 | `ReaderGuacamoleReader`, which wraps a Java `Reader`, using that as the source 110 | for data to parse into Guacamole instructions. Again, you would not normally 111 | directly use this class, nor instantiate it yourself. A working, concrete 112 | instance of `GuacamoleReader` can be retrieved from any `GuacamoleSocket` or 113 | `GuacamoleTunnel`. 114 | 115 | (java-writing-protocol)= 116 | 117 | ### `GuacamoleWriter` 118 | 119 | `GuacamoleWriter` provides a very basic `write()` function and a more abstract 120 | version called `writeInstruction()` which writes instances of 121 | `GuacamoleInstruction`. These functions are analogous to the `read()` and 122 | `readInstruction()` functions provided by `GuacamoleReader`, and have similar 123 | restrictions: the contract imposed by `write()` requires that written 124 | instructions be complete. 125 | 126 | The only concrete implementation of `GuacamoleWriter` is 127 | `WriterGuacamoleWriter`, which wraps a Java `Writer`, using that as the 128 | destination for Guacamole instruction data, but you would not normally directly 129 | use this class, nor instantiate it yourself. It is used by 130 | `ConfiguredGuacamoleSocket` to complete the Guacamole protocol handshake 131 | procedure, and it is used by `GuacamoleHTTPTunnelServlet` within `doWrite()` to 132 | implement the writing half of the tunnel. 133 | 134 | If necessary, a `GuacamoleWriter` can be retrieved from any `GuacamoleSocket` 135 | or `GuacamoleTunnel`, but in most cases, the classes provided by the Guacamole 136 | Java API which already use `GuacamoleWriter` will be sufficient. 137 | 138 | -------------------------------------------------------------------------------- /src/guacamole-architecture.md: -------------------------------------------------------------------------------- 1 | Implementation and architecture 2 | =============================== 3 | 4 | Guacamole is not a self-contained web application and is made up of many parts. 5 | The web application is actually intended to be simple and minimal, with the 6 | majority of the gruntwork performed by lower-level components. 7 | 8 | :::{image} images/guac-arch.png 9 | :width: 2.5in 10 | ::: 11 | 12 | Users connect to a Guacamole server with their web browser. The Guacamole 13 | client, written in JavaScript, is served to users by a webserver within the 14 | Guacamole server. Once loaded, this client connects back to the server over 15 | HTTP using the Guacamole protocol. 16 | 17 | The web application deployed to the Guacamole server reads the Guacamole 18 | protocol and forwards it to guacd, the native Guacamole proxy. This proxy 19 | actually interprets the contents of the Guacamole protocol, connecting to any 20 | number of remote desktop servers on behalf of the user. 21 | 22 | The Guacamole protocol combined with guacd provide protocol agnosticism: 23 | neither the Guacamole client nor the web application need to be aware of what 24 | remote desktop protocol is actually being used. 25 | 26 | (guacamole-protocol-architecture)= 27 | 28 | The Guacamole protocol 29 | ---------------------- 30 | 31 | The web application does not understand any remote desktop protocol at all. It 32 | does not contain support for VNC or RDP or any other protocol supported by the 33 | Guacamole stack. It actually only understands the Guacamole protocol, which is 34 | a protocol for remote display rendering and event transport. While a protocol 35 | with those properties would naturally have the same abilities as a remote 36 | desktop protocol, the design principles behind a remote desktop protocol and 37 | the Guacamole protocol are different: the Guacamole protocol is not intended to 38 | implement the features of a specific desktop environment. 39 | 40 | As a remote display and interaction protocol, Guacamole implements a superset 41 | of existing remote desktop protocols. Adding support for a particular remote 42 | desktop protocol (like RDP) to Guacamole thus involves writing a middle layer 43 | which "translates" between the remote desktop protocol and the Guacamole 44 | protocol. Implementing such a translation is no different than implementing any 45 | native client, except that this particular implementation renders to a remote 46 | display rather than a local one. 47 | 48 | The middle layer that handles this translation is guacd. 49 | 50 | guacd 51 | ----- 52 | 53 | guacd is the heart of Guacamole which dynamically loads support for remote 54 | desktop protocols (called "client plugins") and connects them to remote 55 | desktops based on instructions received from the web application. 56 | 57 | guacd is a daemon process which is installed along with Guacamole and runs in 58 | the background, listening for TCP connections from the web application. guacd 59 | also does not understand any specific remote desktop protocol, but rather 60 | implements just enough of the Guacamole protocol to determine which protocol 61 | support needs to be loaded and what arguments must be passed to it. Once a 62 | client plugin is loaded, it runs independently of guacd and has full control of 63 | the communication between itself and the web application until the client 64 | plugin terminates. 65 | 66 | guacd and all client plugins depend on a common library, libguac, which makes 67 | communication via the Guacamole protocol easier and a bit more abstract. 68 | 69 | (web-application)= 70 | 71 | The web application 72 | ------------------- 73 | 74 | The part of Guacamole that a user actually interacts with is the web 75 | application. 76 | 77 | The web application, as mentioned before, does not implement any remote desktop 78 | protocol. It relies on guacd, and implements nothing more than a spiffy web 79 | interface and authentication layer. 80 | 81 | We chose to implement the server side of the web application in Java, but 82 | there's no reason that it can't be written in a different language. In fact, 83 | because Guacamole is intended be an API, we encourage this. 84 | 85 | RealMint 86 | -------- 87 | 88 | Guacamole is now a generalized remote desktop gateway, but this was not always 89 | the case. Guacamole began as a purely text-based Telnet client written in 90 | JavaScript called RealMint ("RealMint" is an anagram for "terminal"). It was 91 | written mainly as a demonstration and, while intended to be useful, its main 92 | claim to fame was only that it was pure JavaScript. 93 | 94 | The tunnel used by RealMint was written in PHP. In contrast to Guacamole's HTTP 95 | tunnel, RealMint's tunnel used only simple long-polling and was inefficient. 96 | RealMint had a decent keyboard implementation which lives on now in parts of 97 | Guacamole's keyboard code, but this was really the extent of RealMint's 98 | features and usability. 99 | 100 | Given that it was just an implementation of a legacy protocol, and that several 101 | other JavaScript terminal emulators exist, most of which well-established and 102 | stable, the project was dropped. 103 | 104 | VNC Client 105 | ---------- 106 | 107 | Once the developers learned of the HTML5 canvas tag, and saw that it was 108 | already implemented in Firefox and Chrome, work started instead on a 109 | proof-of-concept JavaScript VNC client. 110 | 111 | This client was purely JavaScript with a Java server component, and worked by 112 | translating VNC into an XML-based version of the same. Its development was 113 | naturally driven by VNC's features, and its scope was limited to forwarding a 114 | single connection to a set of users. Although relatively slow, the 115 | proof-of-concept worked well enough that the project needed an online place to 116 | live, and was registered with SourceForge as "Guacamole" - an HTML5 VNC client. 117 | 118 | As Guacamole grew and became more than a proof-of-concept, the need for speed 119 | increased, and the old RealMint-style long polling was dropped, as was the use 120 | of XML. 121 | 122 | As WebSocket could not be trusted to be supported at the time, and Java had no 123 | WebSocket standard for servlets, an equivalent HTTP-based tunnel was developed. 124 | This tunnel is still used today if WebSocket cannot be used for any reason. 125 | 126 | (gateway)= 127 | 128 | Remote Desktop Gateway 129 | ---------------------- 130 | 131 | A faster text-based protocol was developed which could present the features of 132 | multiple remote desktop protocols, not just VNC. The entire system was 133 | rearchitected into a standard daemon, guacd, and a common library, libguac, 134 | which drove both the daemon and protocol support, which became extendable. 135 | 136 | The scope of the project expanded from an adequate VNC client to a performant 137 | HTML5 remote desktop gateway and general API. In its current state, Guacamole 138 | can be used as a central gateway to access any number of machines running 139 | different remote desktop servers. It provides extendable authentication, and in 140 | the case you need something more specialized, a general API for HTML5-based 141 | remote access. 142 | 143 | -------------------------------------------------------------------------------- /src/introduction.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | This book is the official Apache Guacamole manual, written by the upstream 5 | developers of the Guacamole project. It is also the official general 6 | documentation, with an online version available at 7 | . It is a work in progress which will be 8 | continuously updated as Guacamole changes with each release. 9 | 10 | We decided to maintain the documentation for Guacamole as a book, as there is 11 | an awful lot that can be done with the Guacamole web application, and even more 12 | that can be done with the API. This book is intended to explore the 13 | possibilities of Guacamole as an application, and to provide documentation 14 | necessary to install, maintain, and use Guacamole. 15 | 16 | For the sake of users and administrators, we have provided a high-level 17 | overview of Guacamole's architecture and technical design, as well as basic 18 | usage instructions and installation instructions for common platforms. 19 | 20 | For the sake of developers, we have provided a protocol reference and tutorials 21 | for common tasks (implementing protocol support, integrating Guacamole into 22 | your own application, etc.) to give a good starting point beyond simply looking 23 | at the Guacamole codebase. 24 | 25 | This particular edition of the Guacamole Manual covers Guacamole version 26 | {{ version }}. New releases which create new features or break compatibility 27 | will result in new editions of the user's guide, as will any necessary 28 | corrections. As the official documentation for the project, this book will 29 | always be freely available in its entirety online. 30 | 31 | (what-is-guac)= 32 | 33 | What is Guacamole? 34 | ------------------ 35 | 36 | Guacamole is an HTML5 web application that provides access to desktop 37 | environments using remote desktop protocols (such as VNC or RDP). 38 | Guacamole is also the project that produces this web application, and 39 | provides an API that drives it. This API can be used to power other 40 | similar applications or services. 41 | 42 | "Guacamole" is most commonly used to refer to the web application 43 | produced by the Guacamole project using their API. This web application 44 | is part of a stack that provides a protocol-agnostic remote desktop 45 | gateway. Written in JavaScript and using only HTML5 and other standards, 46 | the client part of Guacamole requires nothing more than a modern web 47 | browser or web-enabled device when accessing any of the desktops served. 48 | 49 | Historically, Guacamole was an HTML5 VNC client, and before that, a 50 | JavaScript Telnet client called RealMint ("RealMint" is an anagram for 51 | "terminal"), but this is no longer the case. Guacamole's architecture 52 | has grown to encompass remote desktop in general, and can be used as a 53 | gateway for any number of computers. Originally a proof-of-concept, 54 | Guacamole is now performant enough for daily use, and all Guacamole 55 | development is done over Guacamole. 56 | 57 | As an API, Guacamole provides a common and efficient means of streaming 58 | text data over a JavaScript-based tunnel using either HTTP or WebSocket, 59 | and a client implementation which supports the Guacamole protocol and 60 | renders the remote display when combined with a Guacamole protocol 61 | stream from the tunnel. 62 | 63 | It provides cross-browser mouse and keyboard events, an XML-driven 64 | on-screen keyboard, and synchronized nestable layers with 65 | hardware-accelerated compositing. Projects that wish to provide remote 66 | desktop support over HTML5 can leverage the years of research and 67 | development that went into Guacamole by incorporating the API into their 68 | application or service. 69 | 70 | (access-from-anywhere)= 71 | 72 | Why use Guacamole? 73 | ------------------ 74 | 75 | The principle reason to use Guacamole is constant, world-wide, 76 | unfettered access to your computers. 77 | 78 | Guacamole allows access one or more desktops from anywhere remotely, 79 | without having to install a client, particularly when installing a 80 | client is not possible. By setting up a Guacamole server, you can 81 | provide access to any other computer on the network from virtually any 82 | other computer on the internet, anywhere in the world. Even mobile 83 | phones or tablets can be used, without having to install anything. 84 | 85 | As a true web application whose communication is over HTTP or HTTPS 86 | only, Guacamole allows you to access your machines from anywhere without 87 | violating the policy of your workplace, and without requiring the 88 | installation of special clients. The presence of a proxy or corporate 89 | firewall does not prevent Guacamole use. 90 | 91 | (access-from-anything)= 92 | 93 | Access your computers from any device 94 | ------------------------------------- 95 | 96 | As Guacamole requires only a reasonably-fast, standards-compliant 97 | browser, Guacamole will run on many devices, including mobile phones and 98 | tablets. 99 | 100 | Guacamole is specifically designed to not care whether you have a mouse, 101 | keyboard, touchscreen, or any combination of those. 102 | 103 | One of the major design philosophies behind Guacamole is that it should 104 | never assume you have a particular device (ie: a mobile phone) just 105 | because your browser has or is missing a specific feature (ie: touch 106 | events or a smallish screen). Guacamole's codebase provides support for 107 | both mouse and touch events simultaneously, without choosing one over 108 | the other, while the interface is intended to be usable regardless of 109 | screen size. 110 | 111 | Barring bugs, you should be able to use Guacamole on just about any 112 | modern device with a web browser. 113 | 114 | (non-physical-computer)= 115 | 116 | Keep a computer in the "cloud" 117 | ------------------------------ 118 | 119 | Ignoring the buzzword, it's often useful to have a computer that has no 120 | dedicated physical hardware, where its processing and storage power are 121 | handled transparently by redundant systems in some remote datacenter. 122 | 123 | Computers hosted on virtualized hardware are more resilient to failures, 124 | and with so many companies now offering on-demand computing resources, 125 | Guacamole is a perfect way to access several machines that are only 126 | accessible over the internet. 127 | 128 | In fact, all Guacamole development is done on computers like this. This 129 | is partly because we like the mobility, and partly because we want to 130 | ensure Guacamole is always performant enough for daily use. 131 | 132 | (group-access)= 133 | 134 | Provide easy access to a group 135 | ------------------------------ 136 | 137 | Guacamole allows you to centralize access to a large group of machines, 138 | and specify on a per-user basis which machines are accessible. Rather 139 | than remember a list of machines and credentials, users need only log 140 | into a central server and click on one of the connections listed. 141 | 142 | If you have multiple computers which you would like to access remotely, 143 | or you are part of a group where each person has a set of machines that 144 | they need remote access to, Guacamole is a good way to provide that 145 | access while also ensuring that access is available from anywhere. 146 | 147 | (adding-remote-access)= 148 | 149 | Adding HTML5 remote access to your existing infrastructure 150 | ---------------------------------------------------------- 151 | 152 | As Guacamole is an API, not just a web application, the core components 153 | and libraries provided by the Guacamole project can be used to add HTML5 154 | remote access features to an existing application. You need not use the 155 | main Guacamole web application; you can write (or integrate with) your 156 | own rather easily. 157 | 158 | If you host an on-demand computing service, adding HTML5-based remote 159 | access allows users of your service more broad access; users need 160 | nothing more than a web browser to see their computers' screens. 161 | 162 | -------------------------------------------------------------------------------- /src/duo-auth.md: -------------------------------------------------------------------------------- 1 | Duo two-factor authentication 2 | ============================= 3 | 4 | Guacamole supports Duo as a second authentication factor, layered on top 5 | of any other authentication extension, including those available from 6 | the main project website. The Duo authentication extension allows users 7 | to be additionally verified against the Duo service before the 8 | authentication process is allowed to succeed. 9 | 10 | :::{important} 11 | This chapter involves modifying the contents of `GUACAMOLE_HOME` - the 12 | Guacamole configuration directory. If you are unsure where `GUACAMOLE_HOME` is 13 | located on your system, please consult [](configuring-guacamole) before 14 | proceeding. 15 | ::: 16 | 17 | (duo-architecture)= 18 | 19 | How Duo works with Guacamole 20 | ---------------------------- 21 | 22 | Guacamole provides support for Duo as a second authentication factor. To 23 | make use of the Duo authentication extension, some other authentication 24 | mechanism will need be configured, as well. When a user attempts to log 25 | into Guacamole, other installed authentication methods will be queried 26 | first: 27 | 28 | ![](images/duo-auth-factor-1.png) 29 | 30 | Only after authentication has succeeded with one of those methods will 31 | Guacamole reach out to Duo to obtain additional verification of user 32 | identity: 33 | 34 | ![](images/duo-auth-factor-2.png) 35 | 36 | If both the initial authentication attempt and verification through Duo 37 | succeed, the user will be allowed in. If either mechanism fails, access 38 | to Guacamole is denied. 39 | 40 | (duo-downloading)= 41 | 42 | Downloading the Duo extension 43 | ----------------------------- 44 | 45 | The Duo authentication extension is available separately from the main 46 | `guacamole.war`. The link for this and all other officially-supported 47 | and compatible extensions for a particular version of Guacamole are 48 | provided on the release notes for that version. You can find the release 49 | notes for current versions of Guacamole here: 50 | http://guacamole.apache.org/releases/. 51 | 52 | The Duo authentication extension is packaged as a `.tar.gz` file 53 | containing only the extension itself, `guacamole-auth-duo-1.5.3.jar`, 54 | which must ultimately be placed in `GUACAMOLE_HOME/extensions`. 55 | 56 | (installing-duo-auth)= 57 | 58 | Installing Duo authentication 59 | ----------------------------- 60 | 61 | Guacamole extensions are self-contained `.jar` files which are located 62 | within the `GUACAMOLE_HOME/extensions` directory. To install the Duo 63 | authentication extension, you must: 64 | 65 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 66 | exist. 67 | 68 | 2. Copy `guacamole-auth-duo-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 69 | 70 | 3. Configure Guacamole to use Duo authentication, as described below. 71 | 72 | :::{important} 73 | You will need to restart Guacamole by restarting your servlet container in 74 | order to complete the installation. Doing this will disconnect all active 75 | users, so be sure that it is safe to do so prior to attempting installation. If 76 | you do not configure the Duo authentication properly, Guacamole will not start 77 | up again until the configuration is fixed. 78 | ::: 79 | 80 | ### Adding Guacamole to Duo 81 | 82 | Duo does not provide a specific integration option for Guacamole, but 83 | Guacamole's Duo extension uses Duo's generic authentication API which 84 | they refer to as the "Web SDK". To use Guacamole with Duo, you will need 85 | to add it as a new "Web SDK" application from within the "Applications" 86 | tab of the admin panel of your Duo account: 87 | 88 | ![](images/duo-add-guacamole.png) 89 | 90 | Within the settings of the newly-added application, rename the 91 | application to something more representative than "Web SDK". This 92 | application name is what will be presented to your users when they are 93 | prompted by Duo for additional authentication: 94 | 95 | ![](images/duo-rename-guacamole.png) 96 | 97 | Once you've finished adding Guacamole as an "Web SDK" application, the 98 | configuration information required to configure Guacamole is listed 99 | within the application's "Details" section. You will need to copy the 100 | integration key, secret key, and API hostname - they will later be 101 | specified within `guacamole.properties`: 102 | 103 | ![](images/duo-copy-details.png) 104 | 105 | (guac-duo-config)= 106 | 107 | ### Configuring Guacamole for Duo 108 | 109 | The application-specific configuration information retrieved from Duo 110 | must be added to `guacamole.properties` to describe how Guacamole 111 | should connect to the Duo service: 112 | 113 | duo-api-hostname 114 | : The hostname of the Duo API endpoint to be used to verify user identities. 115 | This will usually be in the form `api-XXXXXXXX.duosecurity.com`, where 116 | "XXXXXXXX" is some arbitrary alphanumeric value assigned by Duo. This 117 | value will have been generated by Duo when you added Guacamole as an "Web 118 | SDK" application, and can be found within the application details in the 119 | "API hostname" field. *This value is required.* 120 | 121 | duo-integration-key 122 | : The integration key provided for Guacamole by Duo. This value will 123 | have been generated by Duo when you added Guacamole as an "Web SDK" 124 | application, and can be found within the application details in the 125 | "Integration key" field. *This value is required and must be EXACTLY 126 | 20 characters.* 127 | 128 | duo-secret-key 129 | : The secret key provided for Guacamole by Duo. This value will have 130 | been generated by Duo when you added Guacamole as an "Web SDK" 131 | application, and can be found within the application details in the 132 | "Secret key" field. *This value is required and must be EXACTLY 40 133 | characters.* 134 | 135 | In addition to the above, *you must also manually generate an 136 | "application key"*. The application key is required by Duo's 137 | authentication API, but is not provided by Duo. It is an arbitrary value 138 | meant to be unique to each deployment of an application using their API. 139 | 140 | duo-application-key 141 | : An arbitrary, random key which you manually generated for Guacamole. 142 | *This value is required and must be AT LEAST 40 characters.* 143 | 144 | The application key can be generated with any method as long as it is 145 | sufficiently random. There exist utilities which will do this for you, like 146 | `pwgen`: 147 | 148 | ```console 149 | $ pwgen 40 1 150 | em1io4zievohneeseiwah0zie2raQuoo2ci5oBoo 151 | $ 152 | ``` 153 | 154 | Alternatively, one quick and fairly portable way to do this is to use the `dd` 155 | utility to copy random bytes from the secure random device `/dev/random`, 156 | sending the data through a cryptographic hash tool with a sufficiently-long 157 | result, like `sha256sum`: 158 | 159 | ```console 160 | $ dd if=/dev/random count=1 | sha256sum 161 | 5d16d6bb86da73e7d1abd3286b21dcf3b3e707532e64ceebc7a008350d0d485d - 162 | $ 163 | ``` 164 | 165 | (completing-duo-install)= 166 | 167 | ### Completing the installation 168 | 169 | Guacamole will only reread `guacamole.properties` and load newly-installed 170 | extensions during startup, so your servlet container will need to be restarted 171 | before Duo authentication will take effect. Restart your servlet container and 172 | give the new authentication a try. 173 | 174 | :::{important} 175 | You only need to restart your servlet container. *You do not need to restart 176 | guacd*. 177 | 178 | guacd is completely independent of the web application and does not deal with 179 | `guacamole.properties` or the authentication system in any way. Since you are 180 | already restarting the servlet container, restarting guacd as well technically 181 | won't hurt anything, but doing so is completely pointless. 182 | ::: 183 | 184 | If Guacamole does not come back online after restarting your servlet 185 | container, check the logs. Problems in the configuration of the Duo 186 | extension may prevent Guacamole from starting up, and any such errors 187 | will be recorded in the logs of your servlet container. 188 | 189 | -------------------------------------------------------------------------------- /src/openid-auth.md: -------------------------------------------------------------------------------- 1 | OpenID Connect Authentication 2 | ============================= 3 | 4 | [OpenID Connect](http://openid.net/connect/) is a widely-adopted open standard 5 | for implementing single sign-on (SSO). [Not to be confused with 6 | OAuth](https://oauth.net/articles/authentication/), which is *not* an 7 | authentication protocol, OpenID Connect defines an authentication protocol in 8 | the form of a simple identity layer on top of OAuth 2.0. 9 | 10 | Guacamole's OpenID Connect support implements the "[implicit 11 | flow](https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth)" 12 | of the OpenID Connect standard, and allows authentication of Guacamole users to 13 | be delegated to an identity provider which implements OpenID Connect, removing 14 | the need for users to log into Guacamole directly. This module must be layered 15 | on top of other authentication extensions that provide connection information, 16 | such as the [database authentication extension](jdbc-auth), as it only provides 17 | user authentication. 18 | 19 | (openid-downloading)= 20 | 21 | Downloading the OpenID Connect authentication extension 22 | ------------------------------------------------------- 23 | 24 | ```{include} include/sso-download.md 25 | ``` 26 | 27 | The extension for the desired SSO method, in this case 28 | `guacamole-auth-sso-openid-1.5.3.jar` from within the `openid/` subdirectory, 29 | must ultimately be placed in `GUACAMOLE_HOME/extensions`. 30 | 31 | (installing-openid-auth)= 32 | 33 | Installing support for OpenID Connect 34 | ------------------------------------- 35 | 36 | Guacamole extensions are self-contained `.jar` files which are located within 37 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 38 | `GUACAMOLE_HOME` is located on your system, please consult 39 | [](configuring-guacamole) before proceeding.* 40 | 41 | To install the OpenID Connect authentication extension, you must: 42 | 43 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 44 | exist. 45 | 46 | 2. Copy `guacamole-auth-sso-openid-1.5.3.jar` within 47 | `GUACAMOLE_HOME/extensions`. 48 | 49 | 3. Configure Guacamole to use OpenID Connect authentication, as described 50 | below. 51 | 52 | (guac-openid-config)= 53 | 54 | ### Configuring Guacamole for single sign-on with OpenID Connect 55 | 56 | Guacamole's OpenID connect support requires several properties which describe 57 | both the identity provider and the Guacamole deployment. These properties are 58 | *absolutely required in all cases*, as they dictate how Guacamole should 59 | connect to the identity provider, how it should verify the identity provider's 60 | response, and how the identity provider should redirect users back to Guacamole 61 | once their identity has been confirmed: 62 | 63 | `openid-authorization-endpoint` 64 | : The authorization endpoint (URI) of the OpenID service. 65 | 66 | This value should be provided to you by the identity provider. For identity 67 | providers that implement [OpenID Connect 68 | Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), this 69 | value can be retrieved from the `authorization_endpoint` property of the JSON 70 | file hosted at 71 | {samp}`{https://identity-provider}/.well-known/openid-configuration`, where 72 | `https://identity-provider` is the base URL of the identity provider. 73 | 74 | `openid-jwks-endpoint` 75 | : The endpoint (URI) of the JWKS service which defines how received ID tokens 76 | ([JSON Web Tokens](https://jwt.io/) or JWTs) shall be validated. 77 | 78 | This value should be provided to you by the identity provider. For 79 | identity providers that implement [OpenID Connect 80 | Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), 81 | this value can be retrieved from the `jwks_uri` property of the JSON 82 | file hosted at 83 | {samp}`{https://identity-provider}/.well-known/openid-configuration`, where 84 | `https://identity-provider` is the base URL of the identity provider. 85 | 86 | `openid-issuer` 87 | : The issuer to expect for all received ID tokens. 88 | 89 | This value should be provided to you by the identity provider. For 90 | identity providers that implement [OpenID Connect 91 | Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), 92 | this value can be retrieved from the `issuer` property of the JSON 93 | file hosted at 94 | {samp}`{https://identity-provider}/.well-known/openid-configuration`, where 95 | `https://identity-provider` is the base URL of the identity provider. 96 | 97 | `openid-client-id` 98 | : The OpenID client ID which should be submitted to the OpenID service when 99 | necessary. This value is typically provided to you by the OpenID service when 100 | OpenID credentials are generated for your application. 101 | 102 | `openid-redirect-uri` 103 | : The URI that should be submitted to the OpenID service such that they 104 | can redirect the authenticated user back to Guacamole after the 105 | authentication process is complete. This must be the full URL that a user 106 | would enter into their browser to access Guacamole. 107 | 108 | Additional optional properties are available to control how claims within 109 | received ID tokens are used to derive the user's Guacamole username, any 110 | associated groups, the OpenID scopes requested when user identities are 111 | confirmed, and to control the maximum amount of time allowed for various 112 | aspects of the conversation with the identity provider: 113 | 114 | `openid-username-claim-type` 115 | : The claim type within any valid JWT that contains the authenticated user's 116 | username. By default, the "`email`" claim type is used. 117 | 118 | `openid-groups-claim-type` 119 | : The claim type within any valid JWT that contains the list of groups of which 120 | the authenticated user is a member. By default, the "`groups`" claim type is 121 | used. 122 | 123 | `openid-scope` 124 | : The space-separated list of OpenID scopes to request. OpenID scopes determine 125 | the information returned within the OpenID token, and thus affect what values 126 | can be used as an authenticated user's username. To be compliant with OpenID, 127 | at least "`openid profile`" must be requested. By default, "`openid email 128 | profile`" is used. 129 | 130 | `openid-allowed-clock-skew` 131 | : The amount of clock skew tolerated for timestamp comparisons between the 132 | Guacamole server and OpenID service clocks, in seconds. By default, clock skew 133 | of up to 30 seconds is tolerated. 134 | 135 | `openid-max-token-validity` 136 | : The maximum amount of time that an OpenID token should remain valid, in 137 | minutes. By default, each OpenID token remains valid for 300 minutes (5 138 | hours). 139 | 140 | `openid-max-nonce-validity` 141 | : The maximum amount of time that a nonce generated by the Guacamole 142 | server should remain valid, in minutes. As each OpenID request has a unique 143 | nonce value, this imposes an upper limit on the amount of time any particular 144 | OpenID request can result in successful authentication within Guacamole. By 145 | default, each generated nonce expires after 10 minutes. 146 | 147 | (openid-login)= 148 | 149 | ### Controlling login behavior 150 | 151 | ```{include} include/sso-login-behavior.md 152 | ``` 153 | 154 | #### Automatically redirecting all unauthenticated users 155 | 156 | To ensure users are redirected to the OpenID identity provider immediately 157 | (without a Guacamole login screen), ensure the OpenID extension has priority 158 | over all others: 159 | 160 | ``` 161 | extension-priority: openid 162 | ``` 163 | 164 | #### Presenting unauthenticated users with a login screen 165 | 166 | To ensure users are given a normal Guacamole login screen and have the option 167 | to log in with traditional credentials _or_ with OpenID, ensure the OpenID 168 | extension does not have priority: 169 | 170 | ``` 171 | extension-priority: *, openid 172 | ``` 173 | 174 | (completing-openid-install)= 175 | 176 | ### Completing the installation 177 | 178 | Guacamole will only reread `guacamole.properties` and load newly-installed 179 | extensions during startup, so your servlet container will need to be restarted 180 | before OpenID Connect authentication can be used. *Doing this will disconnect 181 | all active users, so be sure that it is safe to do so prior to attempting 182 | installation.* When ready, restart your servlet container and give the new 183 | authentication a try. 184 | 185 | -------------------------------------------------------------------------------- /src/totp-auth.md: -------------------------------------------------------------------------------- 1 | TOTP two-factor authentication 2 | ============================== 3 | 4 | Guacamole supports TOTP as a second authentication factor, layered on top of 5 | any other authentication extension, including those available from the main 6 | project website, providing [base requirements for key storage and 7 | enrollment](totp-prerequisites) are met. The TOTP authentication extension 8 | allows users to be additionally verified against a user-specific and secret key 9 | generated during [enrollment of their authentication device](totp-enrollment). 10 | 11 | :::{important} 12 | This chapter involves modifying the contents of `GUACAMOLE_HOME` - the 13 | Guacamole configuration directory. If you are unsure where `GUACAMOLE_HOME` is 14 | located on your system, please consult [](configuring-guacamole) before 15 | proceeding. 16 | ::: 17 | 18 | (totp-prerequisites)= 19 | 20 | Prerequisites 21 | ------------- 22 | 23 | The enrollment process used by Guacamole's TOTP support needs to be able 24 | to store an automatically-generated key within the user's account. 25 | Another extension must be installed which supports storage of arbitrary 26 | data from other extensions. *Currently the only extensions provided with 27 | Guacamole which support this kind of storage are the [database 28 | authentication extensions](jdbc-auth).* 29 | 30 | It is thus recommended that authentication against a database be fully 31 | configured prior to setting up TOTP. Instructions walking through the setup of 32 | database authentication for Guacamole are provided in [](jdbc-auth). 33 | 34 | (totp-architecture)= 35 | 36 | How TOTP works with Guacamole 37 | ----------------------------- 38 | 39 | Guacamole provides support for TOTP as a second authentication factor. To make 40 | use of the TOTP authentication extension, some other authentication mechanism 41 | will need be configured, as well. When a user attempts to log into Guacamole, 42 | other installed authentication methods will be queried first: 43 | 44 | ![](images/totp-auth-factor-1.png) 45 | 46 | Only after authentication has succeeded with one of those methods will 47 | Guacamole prompt the user to further verify their identity with an 48 | authentication code: 49 | 50 | ![](images/totp-auth-factor-2.png) 51 | 52 | If both the initial authentication attempt and verification using TOTP succeed, 53 | the user will be allowed in. If either mechanism fails, access to Guacamole is 54 | denied. 55 | 56 | (totp-enrollment)= 57 | 58 | ### Enrollment 59 | 60 | If the user does not yet have a TOTP key associated with their account (they 61 | have not yet completed enrollment), they will be required to enroll an 62 | authentication device after passing the first authentication factor. A QR code 63 | containing an automatically-generated key will be presented to the user to be 64 | scanned by their authentication app or device: 65 | 66 | ![](images/totp-enroll.png) 67 | 68 | If the authentication device does not support scanning QR codes for enrollment, 69 | the details within the QR code can be revealed by clicking the "Show" link next 70 | to the "Details" header. These values can then be entered manually: 71 | 72 | ![](images/totp-enroll-detail.png) 73 | 74 | Enrollment is completed once the user enters a valid authentication code 75 | generated by their device using the provided key. 76 | 77 | (totp-reset-data)= 78 | 79 | ### Reseting TOTP Data 80 | 81 | It may become necessary for certain users to clear their TOTP key and/or force 82 | them to re-confirm enrollment, such as in situations where a user loses their 83 | phone and needs to reconfigure TOTP. The user's existing TOTP key can be cleared 84 | by checking the "Clear TOTP secret" box in the user interface and then saving the 85 | user configuration. The next time that the user logs in, they will be given a new 86 | key (QR code) and forced to re-enroll. 87 | 88 | If you simply want a user to be able to re-configure an existing key, without 89 | resetting the secret, you can un-check the box marked "TOTP key confirmed" and 90 | save the user configuration, and the user will be presented with the QR code 91 | at next login and asked to confirm it. 92 | 93 | (totp-disable-user-group)= 94 | 95 | ### Disabling TOTP for users or groups 96 | 97 | In versions of Guacamole prior to 1.6.0, installing and configuring the TOTP 98 | module meant that all Guacamole users would be required to enroll in and 99 | successfully authenticate via the TOTP factor. Starting with 1.6.0 the TOTP 100 | requirement can be disabled on a per-user or per-group basis, allowing 101 | administrators more flexibility in configuring the TOTP requirement. 102 | 103 | By default all users will still be required to authenticate with TOTP, 104 | however the requirement can be disabled by checking the "Disable TOTP" 105 | checkbox. This can be done for an individual user account, but it can 106 | also be disabled for a group resulting the TOTP requirement being 107 | disabled for any members of the group. 108 | 109 | ![](images/totp-user-config.png) 110 | 111 | ![](images/totp-group-config.png) 112 | 113 | (totp-downloading)= 114 | 115 | Downloading the TOTP extension 116 | ------------------------------ 117 | 118 | The TOTP authentication extension is available separately from the main 119 | `guacamole.war`. The link for this and all other officially-supported and 120 | compatible extensions for a particular version of Guacamole are provided on the 121 | release notes for that version. You can find the release notes for current 122 | versions of Guacamole here: . 123 | 124 | The TOTP authentication extension is packaged as a `.tar.gz` file containing 125 | only the extension itself, `guacamole-auth-totp-1.5.3.jar`, which must 126 | ultimately be placed in `GUACAMOLE_HOME/extensions`. 127 | 128 | (installing-totp-auth)= 129 | 130 | Installing TOTP authentication 131 | ------------------------------ 132 | 133 | Guacamole extensions are self-contained `.jar` files which are located within 134 | the `GUACAMOLE_HOME/extensions` directory. To install the TOTP authentication 135 | extension, you must: 136 | 137 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 138 | exist. 139 | 140 | 2. Copy `guacamole-auth-totp-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 141 | 142 | 3. Configure Guacamole to use TOTP authentication, as described below. 143 | 144 | :::{important} 145 | You will need to restart Guacamole by restarting your servlet container in 146 | order to complete the installation. Doing this will disconnect all active 147 | users, so be sure that it is safe to do so prior to attempting installation. If 148 | you do not configure the TOTP authentication properly, Guacamole will not start 149 | up again until the configuration is fixed. 150 | ::: 151 | 152 | (guac-totp-config)= 153 | 154 | ### Configuring Guacamole for TOTP 155 | 156 | With the exception of [the storage and permission requirements described 157 | above](totp-prerequisites), the TOTP extension should work out-of-the-box 158 | without any additional configuration. Defaults have been chosen for all 159 | configuration parameters such that the TOTP extension will be compatible with 160 | Google Authenticator and similar, popular TOTP implementations. 161 | 162 | If your intended authentication application or device has different 163 | requirements, or you wish to override the defaults, additional properties may 164 | be specified within `guacamole.properties`: 165 | 166 | `totp-issuer` 167 | : The human-readable name of the entity issuing user accounts. If not 168 | specified, "Apache Guacamole" will be used by default. 169 | 170 | `totp-digits` 171 | : The number of digits which should be included in each generated TOTP code. 172 | Legal values are 6, 7, or 8. By default, 6-digit codes are generated. 173 | 174 | `totp-period` 175 | : The duration that each generated code should remain valid, in seconds. By 176 | default, each code remains valid for 30 seconds. 177 | 178 | `totp-mode` 179 | : The hash algorithm that should be used to generate TOTP codes. Legal values 180 | are "sha1", "sha256", and "sha512". By default, "sha1" is used. 181 | 182 | (completing-totp-install)= 183 | 184 | ### Completing the installation 185 | 186 | Guacamole will only reread `guacamole.properties` and load newly-installed 187 | extensions during startup, so your servlet container will need to be restarted 188 | before TOTP authentication will take effect. Restart your servlet container 189 | and give the new authentication a try. 190 | 191 | :::{important} 192 | You only need to restart your servlet container. *You do not need to restart 193 | guacd*. 194 | 195 | guacd is completely independent of the web application and does not deal with 196 | `guacamole.properties` or the authentication system in any way. Since you are 197 | already restarting the servlet container, restarting guacd as well technically 198 | won't hurt anything, but doing so is completely pointless. 199 | ::: 200 | 201 | If Guacamole does not come back online after restarting your servlet container, 202 | check the logs. Problems in the configuration of the TOTP extension may prevent 203 | Guacamole from starting up, and any such errors will be recorded in the logs of 204 | your servlet container. 205 | -------------------------------------------------------------------------------- /src/ext/guac.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from docutils.nodes import Element 21 | from docutils.parsers.rst import directives 22 | from typing import List, Tuple 23 | 24 | from sphinx import addnodes 25 | from sphinx.addnodes import pending_xref 26 | from sphinx.application import Sphinx 27 | from sphinx.builders import Builder 28 | from sphinx.directives import ObjectDescription 29 | from sphinx.domains import Domain, ObjType 30 | from sphinx.environment import BuildEnvironment 31 | from sphinx.roles import XRefRole 32 | from sphinx.util.docfields import TypedField 33 | from sphinx.util.nodes import make_refnode 34 | 35 | def phase(option_value): 36 | """ 37 | Parses the given string, validating it against the set of valid Guacamole 38 | protocol session phase identifier strings ("handshake" or "interactive"). 39 | 40 | :param option_value: 41 | The string to parse, as may be received from Sphinx as the value of a 42 | directive option. 43 | 44 | :return string: 45 | The provided, valid string. 46 | 47 | :raises ValueError: 48 | If the provided phase is invalid. 49 | """ 50 | return directives.choice(option_value, ('handshake', 'interactive')) 51 | 52 | def endpoint(option_value): 53 | """ 54 | Parses the given string, validating it against the set of valid endpoint 55 | identifier strings ("client" or "server"). 56 | 57 | :param option_value: 58 | The string to parse, as may be received from Sphinx as the value of a 59 | directive option. 60 | 61 | :return string: 62 | The provided, valid string. 63 | 64 | :raises ValueError: 65 | If the provided endpoint is invalid. 66 | """ 67 | return directives.choice(option_value, ('client', 'server')) 68 | 69 | def phase_set(option_value): 70 | """ 71 | Parses the given string, converting it from a comma-separated list of 72 | values into a Python set of Guacamole protocol session phase identifier 73 | strings ("handshake" or "interactive"). 74 | 75 | :param option_value: 76 | The string to parse, as may be received from Sphinx as the value of a 77 | directive option. 78 | 79 | :return set: 80 | A Python set containing all phase identifier strings within the 81 | provided comma-separated list. 82 | 83 | :raises ValueError: 84 | If any provided phase is invalid. 85 | """ 86 | entries = directives.unchanged_required(option_value) 87 | return { phase(entry) for entry in entries.split(',') } 88 | 89 | def endpoint_set(option_value): 90 | """ 91 | Parses the given string, converting it from a comma-separated list of 92 | values into a Python set of endpoint identifier strings ("client" or 93 | "server"). 94 | 95 | :param option_value: 96 | The string to parse, as may be received from Sphinx as the value of a 97 | directive option. 98 | 99 | :return set: 100 | A Python set containing all endpoint identifier strings within the 101 | provided comma-separated list. 102 | 103 | :raises ValueError: 104 | If any provided endpoint is invalid. 105 | """ 106 | entries = directives.unchanged_required(option_value) 107 | return { endpoint(entry) for entry in entries.split(',') } 108 | 109 | class GuacInstruction(ObjectDescription[Tuple[str, str]]): 110 | """ 111 | Sphinx directive that represents a single Guacamole instruction. This 112 | directive allows the Guacamole manual to document Guacamole protocol 113 | instructions using minimal markup, just as the functions and structures of 114 | a library might be documented. 115 | """ 116 | 117 | # Each of the attributes below are defined and inherited from the 118 | # superclass (ObjectDescription). 119 | 120 | domain = 'guac' 121 | 122 | doc_field_types = [ 123 | TypedField('argument', 124 | label = "Arguments", 125 | names = ['arg'], 126 | can_collapse = True 127 | ) 128 | ] 129 | 130 | option_spec = { 131 | 'phase' : phase_set, 132 | 'sent-by' : endpoint_set 133 | } 134 | 135 | def get_phases(self): 136 | """ 137 | Returns the set of Guacamole protocol phases in which this instruction 138 | is valid, as declared by the "phase" directive option. An instruction 139 | may be valid in multiple phases. If no phase is declared, the 140 | "interactive" phase is assumed by default. 141 | 142 | :return set: 143 | The set of Guacamole protocol phases in which this instruction is 144 | valid. 145 | """ 146 | return self.options.get('phase', { 'interactive' }) 147 | 148 | def get_senders(self): 149 | """ 150 | Returns the set of Guacamole protocol endpoints ("client" or "server") 151 | that may send this instruction, as declared by the "sent-by" directive 152 | option. An instruction may be sent by client, server, or both. If no 153 | endpoint is declared, the "server" endpoint is assumed by default. 154 | 155 | :return set: 156 | The set of Guacamole protocol endpoints that may send this 157 | instruction. 158 | """ 159 | return self.options.get('sent-by', { 'server' }) 160 | 161 | def handle_signature(self, sig, signode): 162 | 163 | # This function is inherited from the superclass (ObjectDescription). 164 | # The implementation is expected to override this function to define 165 | # the unique signature and name for the object being documented. 166 | 167 | signode.clear() 168 | signode += addnodes.desc_name(sig, sig) 169 | 170 | # Generate an internal, unique name for referring to this instruction 171 | # based on how the instruction is actually used (there may be multiple 172 | # definitions having the same instruction opcode yet different meanings 173 | # if sent by client vs. server or during the handshake vs. interactive 174 | # phases) 175 | unique_name = sig 176 | 177 | # Add "client-" prefix for all client-specific instructions 178 | if self.get_senders() == { 'client' }: 179 | unique_name = 'client-' + unique_name 180 | 181 | # Add "-handshake" suffix for all handshake-specific instructions 182 | if self.get_phases() == { 'handshake' }: 183 | unique_name += '-handshake' 184 | 185 | # NOTE: This value will be passed as the name to add_target_and_index() 186 | return unique_name 187 | 188 | def add_target_and_index(self, name, sig, sig_node): 189 | 190 | # This function is inherited from the superclass (ObjectDescription). 191 | # The implementation is expected to override this function to define a 192 | # unique ID for the object being documented, assigning that ID to the 193 | # "sig_node" (signature node). The "name" received here is expected to 194 | # be unique and will be the value returned by a corresponding call to 195 | # handle_signature(). 196 | 197 | targetid = '%s-instruction' % (name) 198 | sig_node['ids'].append(targetid) 199 | self.state.document.note_explicit_target(sig_node) 200 | self.env.domaindata[self.domain]['instruction'][targetid] = self.env.docname, sig 201 | 202 | class GuacDomain(Domain): 203 | """ 204 | Sphinx domain specific to the Guacamole protocol, containing a single 205 | directive ("guac:instruction") that represents a single Guacamole 206 | instruction. 207 | """ 208 | 209 | # Each of the attributes and functions below are defined and inherited from 210 | # the superclass (Domain). 211 | 212 | name = 'guac' 213 | label = 'Guacamole Protocol' 214 | 215 | initial_data = { 216 | 217 | # Mapping of instruction name to (docname, title) tuples, where docname 218 | # is the name of the document containing the object and title is opcode 219 | # of the instruction being documented 220 | 'instruction' : {} 221 | 222 | } 223 | 224 | object_types = { 225 | 'instruction' : ObjType('instruction') 226 | } 227 | 228 | directives = { 229 | 'instruction' : GuacInstruction 230 | } 231 | 232 | roles = { 233 | 'instruction' : XRefRole() 234 | } 235 | 236 | dangling_warnings = { 237 | 'instruction' : "No documentation found for Guacamole instruction '%(target)s'" 238 | } 239 | 240 | def clear_doc(self, docname): 241 | 242 | # Clear all cached data associated with the given document 243 | instruction_list = self.data['instruction'] 244 | for inst, doc in list(instruction_list.items()): 245 | if doc == docname: 246 | del instruction_list[inst] 247 | 248 | def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): 249 | 250 | # Do not attempt to satisfy crossreferences for object types not 251 | # handled by this domain 252 | if typ not in self.data: 253 | return None 254 | 255 | # Do not attempt to satisfy crossreferences for unknown objects 256 | data = self.data[typ] 257 | if target not in data: 258 | return None 259 | 260 | # Retrieve target document and title from stored data 261 | (todocname, title) = data[target] 262 | 263 | return make_refnode(builder, fromdocname, todocname, target, contnode, title) 264 | 265 | def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): 266 | 267 | resolved = [] 268 | 269 | # Search ALL types to enumerate all possible resolutions 270 | for typ in self.object_types: 271 | 272 | data = self.data[typ] 273 | 274 | # If the target exists within the set of known objects of the 275 | # current type, generate the required ('domain:role', node) tuple 276 | if target in data: 277 | (todocname, title) = data[target] 278 | resolved.append(( 279 | self.name + ':' + typ, 280 | make_refnode(builder, fromdocname, todocname, target, contnode, title) 281 | )) 282 | 283 | return resolved 284 | 285 | def setup(app: Sphinx): 286 | app.add_domain(GuacDomain) 287 | 288 | -------------------------------------------------------------------------------- /src/recording-playback.md: -------------------------------------------------------------------------------- 1 | Viewing session recordings in-browser 2 | ===================================== 3 | 4 | Guacamole supports [recording activity within remote desktop sessions](graphical-recording) 5 | such that it can be played back and reviewed later. Graphical recordings can be 6 | converted to video [using the `guacenc` tool](graphical-recording) (part of 7 | [guacamole-server](building-guacamole-server)) or can be played back directly 8 | in the browser in their native format using Guacamole itself. This has several 9 | benefits: 10 | 11 | * Recordings can be played back while the session is underway. 12 | * Recordings need not be re-encoded as traditional video, an 13 | intensive process that often results in a larger file. 14 | * It is very easy to locate and play back the recording for a session when 15 | doing so only involves clicking a button in the connection history. 16 | 17 | This chapter of the documentation covers installing and using the extension 18 | that allows recordings stored on disk to be played back in the browser. 19 | 20 | :::{important} 21 | This chapter involves modifying the contents of `GUACAMOLE_HOME` - the 22 | Guacamole configuration directory. If you are unsure where `GUACAMOLE_HOME` is 23 | located on your system, please consult [](configuring-guacamole) before 24 | proceeding. 25 | ::: 26 | 27 | (playback-architecture)= 28 | 29 | How recording storage and playback works 30 | ---------------------------------------- 31 | 32 | The Guacamole web application includes its own support for playing back 33 | recordings from the history screen in the administration interface, but that 34 | support cannot automatically know where those recordings are stored nor how 35 | they are named. The extension documented here provides exactly that missing 36 | piece, allowing the web application to find recordings on disk so long as they 37 | are named appropriately and stored in a specific location. 38 | 39 | Each history entry has a deterministic, internal, unique identifier called its 40 | UUID, and all supported database backends make this UUID available ahead of 41 | time with the `${HISTORY_UUID}` parameter token. This provides a reliable way 42 | for data stored _outside_ the database to be associated with history entries 43 | that are otherwise stored purely _inside_ the database, and it is this UUID 44 | that the extension searches for when locating the recording for a history entry. 45 | 46 | When a user lists the history of a connection, the recording storage extension 47 | additionally [searches a predetermined location](recording-storage-config) for 48 | session recordings that match either of the following criteria: 49 | 50 | * The recording's filename is _identical_ to the history entry UUID and is 51 | directly within the search path. 52 | 53 | * The recording has any name at all and is within a directory whose filename is 54 | identical to the history entry UUID and is directly within the search path. 55 | 56 | If such a recording is found, it is made available to any user that can view 57 | the history entry. The availability of a recording is displayed as a "View" 58 | link in the "Logs" column of the history table: 59 | 60 | ![View link in the history UI](images/history-table-with-recordings.png) 61 | 62 | Clicking on that link navigates to a screen with a player that loads the 63 | recording and allows it to be played back: 64 | 65 | ![In-browser player interface](images/recording-player-in-use.png) 66 | 67 | (playback-downloading)= 68 | 69 | Downloading the recording storage extension 70 | ------------------------------------------- 71 | 72 | The recording storage extension is available separately from the main 73 | `guacamole.war`. The link for this and all other officially-supported and 74 | compatible extensions for a particular version of Guacamole are provided on the 75 | release notes for that version. You can find the release notes for current 76 | versions of Guacamole here: http://guacamole.apache.org/releases/. 77 | 78 | The recording storage extension is packaged as a `.tar.gz` file containing only 79 | the extension itself, `guacamole-history-recording-storage-1.5.3.jar`, which 80 | must ultimately be placed in `GUACAMOLE_HOME/extensions`. 81 | 82 | (installing-recording-storage)= 83 | 84 | Installing the recording storage extension 85 | ------------------------------------------ 86 | 87 | Guacamole extensions are self-contained `.jar` files which are located within 88 | the `GUACAMOLE_HOME/extensions` directory. To install the recording storage 89 | extension, you must: 90 | 91 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 92 | exist. 93 | 94 | 2. Copy `guacamole-history-recording-storage-1.5.3.jar` within 95 | `GUACAMOLE_HOME/extensions`. 96 | 97 | 3. [Configure your connections to store their recordings within the path 98 | searched by the recording storage extension, as described below.](recording-storage-connection-config) 99 | 100 | :::{important} 101 | You will need to restart Guacamole by restarting your servlet container in 102 | order to complete the installation. Doing this will disconnect all active 103 | users, so be sure that it is safe to do so prior to attempting installation. If 104 | you do not configure the recording storage extension properly, Guacamole will not 105 | start up again until the configuration is fixed. 106 | ::: 107 | 108 | (prepare-recording-storage)= 109 | 110 | ### Preparing a directory for recording storage 111 | 112 | By default, the recording storage extension will search within 113 | `/var/lib/guacamole/recordings` for the recordings associated with a 114 | connection. Unless you or a third-party installation tool have created this 115 | directory, this directory will not exist and you will need to create it 116 | manually: 117 | 118 | ```console 119 | $ mkdir -p /var/lib/guacamole/recordings 120 | ``` 121 | 122 | You can also use another directory of your own choosing if you 123 | [override the default location using the `recording-search-path` 124 | property](recording-storage-config). 125 | 126 | :::{important} 127 | The following steps will use `/var/lib/guacamole/recordings`, as it is a 128 | sensible location and the default search path. If you are using a different 129 | path, consider `/var/lib/guacamole/recordings` below to be a placeholder and 130 | use your own path instead. 131 | ::: 132 | 133 | Once the path has been created, its permissions and ownerships must be modified 134 | such that _both of the following are true_: 135 | 136 | * The guacd service can write to the directory. 137 | * The servlet container (typically Tomcat) can read from the directory, as well 138 | as read any files that are placed within the directory. 139 | 140 | The simplest way to do this is to ensure that: 141 | 142 | 1. The directory is owned by the user that runs the guacd service and the 143 | _group_ that runs the Tomcat service. 144 | 2. The directory has read/write/execute permissions for the user (so that guacd 145 | can write here), and read/execute/**setgid** permissions for the group (so 146 | that Tomcat can read here, and so that [any files placed here are automatically 147 | owned by the Tomcat user's group](https://en.wikipedia.org/wiki/Setuid#When_set_on_a_directory)). 148 | 149 | For example, if your guacd service runs as a dedicated `guacd` user, and your 150 | Tomcat service runs as a user within the `tomcat` group: 151 | 152 | ```console 153 | $ chown guacd:tomcat /var/lib/guacamole/recordings 154 | $ chmod 2750 /var/lib/guacamole/recordings 155 | ``` 156 | 157 | If set correctly, the ownerships and permissions should look like: 158 | 159 | ```console 160 | $ ls -ld /var/lib/guacamole/recordings 161 | drwxr-s---. 1 guacd tomcat 0 Feb 5 05:43 /var/lib/guacamole/recordings/ 162 | $ 163 | ``` 164 | 165 | (recording-storage-config)= 166 | 167 | ### Configuring Guacamole for recording storage 168 | 169 | The recording storage extension has no required properties and typically does 170 | not need to be configured beyond (1) creating the storage directory [as 171 | described above](prepare-recording-storage) and (2) configuring connections to 172 | use that storage directory [as described below](recording-storage-connection-config). 173 | Configuration is only necessary if you wish to use a storage location other 174 | than the default `/var/lib/guacamole/recordings`. 175 | 176 | If you wish to use a different storage location than 177 | `/var/lib/guacamole/recordings`, the following property must be added to 178 | `guacamole.properties`: 179 | 180 | `recording-search-path` 181 | : The directory to search for associated session recordings. This property is 182 | optional. By default, `/var/lib/guacamole/recordings` will be used. 183 | 184 | (completing-recording-storage-install)= 185 | 186 | ### Completing the installation 187 | 188 | Guacamole will only reread `guacamole.properties` and load newly-installed 189 | extensions during startup, so your servlet container will need to be restarted 190 | before installation of the recording storage extension will take effect. 191 | Restart your servlet container, configure a connection to use the new storage, 192 | and give in-browser playback a try. 193 | 194 | :::{important} 195 | You only need to restart your servlet container. *You do not need to restart 196 | guacd*. 197 | 198 | guacd is completely independent of the web application and does not deal with 199 | `guacamole.properties` or extensions in any way. Since you are already 200 | restarting the servlet container, restarting guacd as well technically won't 201 | hurt anything, but doing so is completely pointless. 202 | ::: 203 | 204 | If Guacamole does not come back online after restarting your servlet container, 205 | check the logs. Problems in the configuration of the recording storage 206 | extension may prevent Guacamole from starting up, and any such errors will be 207 | recorded in the logs of your servlet container. 208 | 209 | (recording-storage-connection-config)= 210 | 211 | Configuring connections to use recording storage 212 | ------------------------------------------------ 213 | 214 | Recordings of connections can be found by the recording storage extension as 215 | long as those connections are configured in either of two ways, each involving 216 | naming a file or directory with the history UUID (`${HISTORY_UUID}`). 217 | 218 | ### Option 1: Using a subdirectory named with the history UUID (RECOMMENDED) 219 | 220 | If the recording path of a connection is set to 221 | `${HISTORY_PATH}/${HISTORY_UUID}` and "automatically create path" is checked, 222 | then the recording storage extension will be able to locate the recording by 223 | recognizing that the directory is named with the UUID: 224 | 225 | ![Configuring session recording with the _path_ containing the history UUID](images/recording-storage-connection-config-option1-recommended.png) 226 | 227 | **This is the recommended method of storing recordings.** This method is the 228 | most flexible in that it allows other recordings like typescripts to be stored 229 | within the same directory, and it allows recordings to be given _any_ name, 230 | including names that are more human-readable, contain [`${GUAC_DATE}` or 231 | `${GUAC_TIME}` tokens](parameter-tokens), etc. 232 | 233 | Though the web application does not currently support in-browser playback of 234 | typescripts, server logs, or other files that might be of interest to the 235 | administrator looking at the history of a connection, it _does_ recognize these 236 | files. Following this method will allow any future support for playback of 237 | other types of recordings to work even for old recordings. 238 | 239 | ### Option 2: Naming the recording with the history UUID 240 | 241 | If the recording path of a connection is set to `${HISTORY_PATH}` and the 242 | recording name is set to `${HISTORY_UUID}`, the recording storage extension 243 | will be able to locate the recording by recognizing that its name is identical 244 | to the UUID: 245 | 246 | ![Configuring session recording with the _name_ containing the history UUID](images/recording-storage-connection-config-option2.png) 247 | 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/json-auth.md: -------------------------------------------------------------------------------- 1 | Encrypted JSON authentication 2 | ============================= 3 | 4 | Guacamole supports delegating authentication to an arbitrary external service, 5 | relying on receipt of JSON data which has been [signed using HMAC/SHA-256 and 6 | encrypted with 128-bit AES in CBC mode](#generating-encrypted-json). This JSON 7 | contains [all information describing the user being authenticated](#json-format), 8 | as well as any connections they have access to, and is accepted only if the 9 | configured secret key was used to sign and encrypt the data. 10 | 11 | (json-downloading)= 12 | 13 | Downloading the JSON authentication extension 14 | --------------------------------------------- 15 | 16 | The JSON authentication extension is available separately from the main 17 | `guacamole.war`. The link for this and all other officially-supported and 18 | compatible extensions for a particular version of Guacamole are provided on the 19 | release notes for that version. You can find the release notes for current 20 | versions of Guacamole here: . 21 | 22 | The JSON authentication extension is packaged as a `.tar.gz` file containing 23 | only the extension itself, `guacamole-auth-json-1.5.3.jar`, which must 24 | ultimately be placed in `GUACAMOLE_HOME/extensions`. 25 | 26 | (installing-json-auth)= 27 | 28 | Installing JSON authentication 29 | ------------------------------ 30 | 31 | Guacamole extensions are self-contained `.jar` files which are located within 32 | the `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 33 | `GUACAMOLE_HOME` is located on your system, please consult 34 | [](configuring-guacamole) before proceeding.* 35 | 36 | To install the JSON authentication extension, you must: 37 | 38 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 39 | exist. 40 | 41 | 2. Copy `guacamole-auth-json-1.5.3.jar` within `GUACAMOLE_HOME/extensions`. 42 | 43 | 3. Configure Guacamole to use JSON authentication, as described below. 44 | 45 | (json-config)= 46 | 47 | ### Configuring Guacamole to accept encrypted JSON 48 | 49 | To verify and decrypt the received signed and encrypted JSON, a secret key must 50 | be generated which will be shared by both the Guacamole server and systems that 51 | will generate the JSON data. As guacamole-auth-json uses 128-bit AES, this key 52 | must be 128 bits. 53 | 54 | An easy way of generating such a key is to echo a passphrase through the 55 | "md5sum" utility. This is the technique OpenSSL itself uses to generate 128-bit 56 | keys from passphrases. For example: 57 | 58 | $ echo -n "ThisIsATest" | md5sum 59 | 4c0b569e4c96df157eee1b65dd0e4d41 - 60 | 61 | The generated key must then be saved within [`guacamole.properties`](initial-setup) 62 | as the full 32-digit hex value using the `json-secret-key` property: 63 | 64 | json-secret-key: 4c0b569e4c96df157eee1b65dd0e4d41 65 | 66 | (completing-json-install)= 67 | 68 | ### Completing the installation 69 | 70 | Guacamole will only reread `guacamole.properties` and load newly-installed 71 | extensions during startup, so your servlet container will need to be restarted 72 | before JSON authentication can be used. *Doing this will disconnect all active 73 | users, so be sure that it is safe to do so prior to attempting installation.* 74 | When ready, restart your servlet container and give the new authentication a 75 | try. 76 | 77 | (json-format)= 78 | 79 | JSON format 80 | ----------- 81 | 82 | The general format of the JSON (prior to being encrypted, signed, and sent to 83 | Guacamole), is as follows: 84 | 85 | { 86 | 87 | "username" : "arbitraryUsername", 88 | "expires" : TIMESTAMP, 89 | "connections" : { 90 | 91 | "Connection Name" : { 92 | "protocol" : "PROTOCOL", 93 | "parameters" : { 94 | "name1" : "value1", 95 | "name2" : "value2", 96 | ... 97 | } 98 | }, 99 | 100 | ... 101 | 102 | } 103 | 104 | } 105 | 106 | where `TIMESTAMP` is a standard UNIX epoch timestamp with millisecond 107 | resolution (the number of milliseconds since midnight of January 1, 1970 UTC) 108 | and `PROTOCOL` is the internal name of any of Guacamole's supported protocols, 109 | such as `vnc`, `rdp`, or `ssh`. 110 | 111 | The JSON will cease to be accepted as valid after the server time passes the 112 | timestamp. If no timestamp is specified, the data will not expire. 113 | 114 | The top-level JSON object which must be submitted to Guacamole has the 115 | following properties: 116 | 117 | :::{list-table} 118 | :widths: auto 119 | :header-rows: 1 120 | 121 | * - Property name 122 | - Type 123 | - Description 124 | 125 | * - `username` 126 | - `string` 127 | - The unique username of the user authenticated by the JSON. If the user is 128 | anonymous, this should be the empty string (`""`). 129 | 130 | * - `expires` 131 | - `number` 132 | - The absolute time after which the JSON should no longer be accepted, even 133 | if the signature is valid, as a standard UNIX epoch timestamp with 134 | millisecond resolution (the number of milliseconds since midnight of 135 | January 1, 1970 UTC). 136 | 137 | * - `connections` 138 | - `object` 139 | - The set of connections which should be exposed to the user by their 140 | corresponding, unique names. If no connections will be exposed to the user, 141 | this can simply be an empty object (`{}`). 142 | ::: 143 | 144 | Each normal connection defined within each submitted JSON object has the 145 | following properties: 146 | 147 | :::{list-table} 148 | :widths: auto 149 | :header-rows: 1 150 | 151 | * - Property name 152 | - Type 153 | - Description 154 | 155 | * - `id` 156 | - `string` 157 | - An optional opaque value which uniquely identifies this connection across 158 | all other connections which may be active at any given time. This property 159 | is only required if you wish to allow the connection to be shared or 160 | shadowed. 161 | 162 | * - `protocol` 163 | - `string` 164 | - The internal name of a supported protocol, such as `vnc`, `rdp`, or `ssh`. 165 | 166 | * - `parameters` 167 | - `object` 168 | - An object representing the connection parameter name/value pairs to apply 169 | to the connection, as documented in [](connection-configuration). 170 | 171 | ::: 172 | 173 | Connections which share or shadow other connections use a `join` property 174 | instead of a `protocol` property, where `join` contains the value of the `id` 175 | property of the connection being joined: 176 | 177 | :::{list-table} 178 | :widths: auto 179 | :header-rows: 1 180 | 181 | * - Property name 182 | - Type 183 | - Description 184 | 185 | * - `id` 186 | - `string` 187 | - An optional opaque value which uniquely identifies this connection across 188 | all other connections which may be active at any given time. This property 189 | is only required if you wish to allow the connection to be shared or 190 | shadowed. (Yes, a connection which shadows another connection may itself be 191 | shadowed.) 192 | 193 | * - `join` 194 | - `string` 195 | - The opaque ID given within the `id` property of the connection being joined 196 | (shared / shadowed). 197 | 198 | * - `parameters` 199 | - `object` 200 | - An object representing the connection parameter name/value pairs to apply 201 | to the connection, as documented in [](connection-configuration). 202 | 203 | Most of the connection configuration is inherited from the connection being 204 | joined. In general, the only property relevant to joining connections is 205 | `read-only`. 206 | 207 | ::: 208 | 209 | If a connection is configured to join another connection, that connection will 210 | only be usable if the connection being joined is currently active. If two 211 | connections are established having the same `id` value, only the last 212 | connection will be joinable using the given `id`. 213 | 214 | (generating-encrypted-json)= 215 | 216 | Generating encrypted JSON 217 | ------------------------- 218 | 219 | To authenticate a user with the above JSON format, the JSON must be both signed 220 | and encrypted using the same 128-bit secret key specified with the 221 | `json-secret-key` within `guacamole.properties`: 222 | 223 | 1. Generate JSON in the format described above 224 | 2. Sign the JSON using the secret key (the same 128-bit key stored within 225 | `guacamole.properties` with the `json-secret-key` property) with 226 | **HMAC/SHA-256**. Prepend the binary result of the signing process to the 227 | plaintext JSON that was signed. 228 | 3. Encrypt the result of (2) above using **AES in CBC mode**, with the initial 229 | vector (IV) set to all zero bytes. 230 | 4. Encode the encrypted result using base64. 231 | 5. POST the encrypted result to the `/api/tokens` REST endpoint as the value of 232 | an HTTP parameter named `data` (or include it in the URL of any Guacamole 233 | page as a query parameter named `data`). 234 | 235 | For example, if Guacamole is running on localhost at `/guacamole`, and 236 | `BASE64_RESULT` is the result of the above process, the equivalent run of 237 | the "curl" utility would be: 238 | 239 | $ curl --data-urlencode "data=BASE64_RESULT" http://localhost:8080/guacamole/api/tokens 240 | 241 | **NOTE:** Be sure to URL-encode the base64-encoded result prior to POSTing 242 | it to `/api/tokens` or including it in the URL. Base64 can contain both "+" 243 | and "=" characters, which have special meaning within URLs. 244 | 245 | If the data is invalid in any way, if the signature does not match, if 246 | decryption or signature verification fails, or if the submitted data has 247 | expired, the REST service will return an invalid credentials error and fail 248 | without user-visible explanation. Details describing the error that occurred 249 | will be in the Tomcat logs, however. 250 | 251 | Reference implementation 252 | ------------------------ 253 | 254 | The source includes a shell script, [`doc/encrypt-json.sh`](https://raw.githubusercontent.com/apache/guacamole-client/master/extensions/guacamole-auth-json/doc/encrypt-json.sh), 255 | which uses the OpenSSL command-line utility to encrypt and sign JSON in the 256 | manner that guacamole-auth-json requires. It is thoroughly commented and should 257 | work well as a reference implementation, for testing, and as a point of 258 | comparison for development. The script is run as: 259 | 260 | $ ./encrypt-json.sh HEX_ENCRYPTION_KEY file-to-sign-and-encrypt.json 261 | 262 | For example, if you have a file called `auth.json` containing the following: 263 | 264 | { 265 | "username" : "test", 266 | "expires" : "1446323765000", 267 | "connections" : { 268 | "My Connection" : { 269 | "protocol" : "rdp", 270 | "parameters" : { 271 | "hostname" : "10.10.209.63", 272 | "port" : "3389", 273 | "ignore-cert": "true", 274 | "recording-path": "/recordings", 275 | "recording-name": "My-Connection-${GUAC_USERNAME}-${GUAC_DATE}-${GUAC_TIME}" 276 | } 277 | }, 278 | "My OTHER Connection" : { 279 | "protocol" : "rdp", 280 | "parameters" : { 281 | "hostname" : "10.10.209.64", 282 | "port" : "3389", 283 | "ignore-cert": "true", 284 | "recording-path": "/recordings", 285 | "recording-name": "My-OTHER-Connection-${GUAC_USERNAME}-${GUAC_DATE}-${GUAC_TIME}" 286 | } 287 | } 288 | } 289 | } 290 | 291 | and you run: 292 | 293 | $ ./encrypt-json.sh 4C0B569E4C96DF157EEE1B65DD0E4D41 auth.json 294 | 295 | You will receive the following output: 296 | 297 | A2Pf5Kpmm97I2DT1PifIrfU6q3yzoGcIbNXEd60WNangT8DAVjAl6luaqwhBJnCK 298 | uqcf9ZZlRo3uDxTHvUM3eq1YvdghL0GbosOn8Mn38j2ydOMk+Cd15a8ggb4/ddt/ 299 | yIBK4DxrN7MNbouZ091KYtXC6m20E6sGzLy676BlMSg1cmsENRIihOynsSLSCvo0 300 | diif6H7T+ZuIqF7B5SW+adGfMaHlfknlIvSpLGHhrIP4aMYE/ZU2vYNg8ez27sCS 301 | wDBWu5lERtfCYFyU4ysjRU5Hyov+yKa+O7jcRYpw3N+fHbCg7/dxVNW07qNOKssv 302 | pzUciGvDPUCPpa02WmPJNEBowwQireO1952/MNAI77cW2UepbljD/bwOiZl2THJz 303 | LrENo7K5acimBa+EjWEesgn7lx/WTCF3zxR6TH1CWrQM8Et1aUK1Nf8K11xEQbTy 304 | klyaNtCmTfyahRZ/fUPxDNrdJVpPOSELkf7RJO5tOdK/FFIFIbze3ZUyXgRq+pHY 305 | owpgOmudDBTBlxhXiONdutRI/RZbFM/7GBMdmI8AR/401OCV3nsI4jLhukjMXH3V 306 | f3pQg+xKMhi/QExHhDk8VTNYk7GurK4vgehn7HQ0oSGh8pGcmxB6W43cz+hyn6VQ 307 | On6i90cSnIhRO8SysZt332LwJCDm7I+lBLaI8NVHU6bnAY1Axx5oH3YTKc4qzHls 308 | HEAFYLkD6aHMvHkF3b798CMravjxiJV3m7hsXDbaFN6AFhn8GIkMRRrjuevfZ+q9 309 | enWN14s24vt5OVg69DljzALobUNKUXFx69SR8EpSBvUcKq8s/OgbDpFvKbwsDY57 310 | HGT4T0CuRIA0TGUI075uerKBNApVhuBA1BmWJIrI4JXw5MuX6pdBe+MYccO3vfo+ 311 | /frazj8rHdkDa/IbueMbvq+1ozV2+UuxrbaTrV2i4jSRgd74U0QzOh9e8Q0i7vOi 312 | l3hnIfOfg+v1oULmZmJSeiAYWxeGvPptp+n7rNFqHGM= 313 | 314 | The resulting base64 data above, if submitted using the `data` parameter to 315 | Guacamole, will authenticate a user and grant them access to the connections 316 | described in the JSON (at least until the expires timestamp is reached, at 317 | which point the JSON will no longer be accepted). 318 | 319 | 320 | -------------------------------------------------------------------------------- /src/radius-auth.md: -------------------------------------------------------------------------------- 1 | RADIUS Authentication 2 | ===================== 3 | 4 | Guacamole supports delegating authentication to a RADIUS service, such as 5 | FreeRADIUS, to validate username and password combinations, and to support 6 | multi-factor authentication. This authentication method must be layered on top 7 | of some other authentication extension, such as those available from the main 8 | project website, in order to provide access to actual connections. 9 | 10 | (radius-downloading)= 11 | 12 | Downloading the RADIUS authentication extension 13 | ----------------------------------------------- 14 | 15 | The RADIUS extension depends on software that is covered by a LGPL license, 16 | which is incompatible with the Apache 2.0 license under which Guacamole is 17 | licensed. Due to this dependency, the Guacamole project cannot distribute 18 | binary versions of the RADIUS extension. If you want to use this extension you 19 | will need to build the code - or at least the RADIUS extension yourself. Build 20 | instructions can be found in the section [](installing-guacamole). 21 | 22 | (installing-radius-auth)= 23 | 24 | Installing RADIUS authentication 25 | -------------------------------- 26 | 27 | The RADIUS extension must be explicitly enabled during build time in order to 28 | generate the binaries and resulting JAR file. This is done by adding the flag 29 | `-Plgpl-extensions` to the Maven command line during the build, and should 30 | result in the output below: 31 | 32 | :::{code-block} console 33 | :emphasize-lines: 35,71 34 | $ mvn clean package -Plgpl-extensions 35 | [INFO] Scanning for projects... 36 | [INFO] ------------------------------------------------------------------------ 37 | [INFO] Reactor Build Order: 38 | [INFO] 39 | [INFO] guacamole-client [pom] 40 | [INFO] guacamole-common [jar] 41 | [INFO] guacamole-ext [jar] 42 | [INFO] guacamole-common-js [pom] 43 | [INFO] guacamole [war] 44 | [INFO] extensions [pom] 45 | [INFO] guacamole-auth-duo [jar] 46 | [INFO] guacamole-auth-header [jar] 47 | [INFO] guacamole-auth-jdbc [pom] 48 | [INFO] guacamole-auth-jdbc-base [jar] 49 | [INFO] guacamole-auth-jdbc-mysql [jar] 50 | [INFO] guacamole-auth-jdbc-postgresql [jar] 51 | [INFO] guacamole-auth-jdbc-sqlserver [jar] 52 | [INFO] guacamole-auth-jdbc-dist [pom] 53 | [INFO] guacamole-auth-json [jar] 54 | [INFO] guacamole-auth-ldap [jar] 55 | [INFO] guacamole-auth-quickconnect [jar] 56 | [INFO] guacamole-auth-sso [pom] 57 | [INFO] guacamole-auth-sso-base [jar] 58 | [INFO] guacamole-auth-sso-cas [jar] 59 | [INFO] guacamole-auth-sso-openid [jar] 60 | [INFO] guacamole-auth-sso-saml [jar] 61 | [INFO] guacamole-auth-sso-dist [pom] 62 | [INFO] guacamole-auth-totp [jar] 63 | [INFO] guacamole-history-recording-storage [jar] 64 | [INFO] guacamole-vault [pom] 65 | [INFO] guacamole-vault-base [jar] 66 | [INFO] guacamole-vault-ksm [jar] 67 | [INFO] guacamole-vault-dist [pom] 68 | [INFO] guacamole-auth-radius [jar] 69 | [INFO] guacamole-example [war] 70 | [INFO] guacamole-playback-example [war] 71 | ... 72 | [INFO] ------------------------------------------------------------------------ 73 | [INFO] Reactor Summary for guacamole-client 1.5.3: 74 | [INFO] 75 | [INFO] guacamole-client ................................... SUCCESS [ 12.839 s] 76 | [INFO] guacamole-common ................................... SUCCESS [ 15.446 s] 77 | [INFO] guacamole-ext ...................................... SUCCESS [ 19.988 s] 78 | [INFO] guacamole-common-js ................................ SUCCESS [ 22.000 s] 79 | [INFO] guacamole .......................................... SUCCESS [01:08 min] 80 | [INFO] extensions ......................................... SUCCESS [ 0.451 s] 81 | [INFO] guacamole-auth-duo ................................. SUCCESS [ 7.043 s] 82 | [INFO] guacamole-auth-header .............................. SUCCESS [ 4.836 s] 83 | [INFO] guacamole-auth-jdbc ................................ SUCCESS [ 0.244 s] 84 | [INFO] guacamole-auth-jdbc-base ........................... SUCCESS [ 8.011 s] 85 | [INFO] guacamole-auth-jdbc-mysql .......................... SUCCESS [ 4.717 s] 86 | [INFO] guacamole-auth-jdbc-postgresql ..................... SUCCESS [ 5.098 s] 87 | [INFO] guacamole-auth-jdbc-sqlserver ...................... SUCCESS [ 5.620 s] 88 | [INFO] guacamole-auth-jdbc-dist ........................... SUCCESS [ 4.031 s] 89 | [INFO] guacamole-auth-json ................................ SUCCESS [ 6.319 s] 90 | [INFO] guacamole-auth-ldap ................................ SUCCESS [ 8.948 s] 91 | [INFO] guacamole-auth-quickconnect ........................ SUCCESS [ 9.128 s] 92 | [INFO] guacamole-auth-sso ................................. SUCCESS [ 0.270 s] 93 | [INFO] guacamole-auth-sso-base ............................ SUCCESS [ 3.665 s] 94 | [INFO] guacamole-auth-sso-cas ............................. SUCCESS [ 12.263 s] 95 | [INFO] guacamole-auth-sso-openid .......................... SUCCESS [ 5.667 s] 96 | [INFO] guacamole-auth-sso-saml ............................ SUCCESS [ 5.068 s] 97 | [INFO] guacamole-auth-sso-dist ............................ SUCCESS [ 4.884 s] 98 | [INFO] guacamole-auth-totp ................................ SUCCESS [ 9.310 s] 99 | [INFO] guacamole-history-recording-storage ................ SUCCESS [ 3.131 s] 100 | [INFO] guacamole-vault .................................... SUCCESS [ 0.231 s] 101 | [INFO] guacamole-vault-base ............................... SUCCESS [ 4.671 s] 102 | [INFO] guacamole-vault-ksm ................................ SUCCESS [ 6.411 s] 103 | [INFO] guacamole-vault-dist ............................... SUCCESS [ 3.421 s] 104 | [INFO] guacamole-auth-radius .............................. SUCCESS [ 10.806 s] 105 | [INFO] guacamole-example .................................. SUCCESS [ 2.052 s] 106 | [INFO] guacamole-playback-example ......................... SUCCESS [ 0.938 s] 107 | [INFO] ------------------------------------------------------------------------ 108 | [INFO] BUILD SUCCESS 109 | [INFO] ------------------------------------------------------------------------ 110 | [INFO] Total time: 04:36 min 111 | [INFO] Finished at: 2023-01-10T17:27:11-08:00 112 | [INFO] ------------------------------------------------------------------------ 113 | $ 114 | ::: 115 | 116 | After the build completes successfully, the extension will be in the 117 | `extensions/guacamole-auth-radius/target/` directory, and will be called 118 | guacamole-auth-radius-1.5.3.jar. This extension file can be copied to the 119 | `GUACAMOLE_HOME/extensions` directory. *If you are unsure where 120 | `GUACAMOLE_HOME` is located on your system, please consult 121 | [](configuring-guacamole) before proceeding.* 122 | 123 | Extensions are loaded in alphabetical order, and authentication is performed in 124 | the order in which the extensions were loaded. If you are stacking the RADIUS 125 | extension with another extension, like the JDBC extension, in order to store 126 | connection information, you may need to change the name of the RADIUS extension 127 | such that it is evaluated prior to the JDBC extension - otherwise an 128 | authentication failure in one of the previous modules may block the RADIUS 129 | module from ever being evaluated. 130 | 131 | To install the RADIUS authentication extension, you must: 132 | 133 | 1. Create the `GUACAMOLE_HOME/extensions` directory, if it does not already 134 | exist. 135 | 136 | 2. Copy `guacamole-auth-radius-1.5.3.jar` into `GUACAMOLE_HOME/extensions`. 137 | 138 | 3. Configure Guacamole to use RADIUS authentication, as described below. 139 | 140 | (guac-radius-config)= 141 | 142 | Configuring Guacamole for RADIUS authentication 143 | ----------------------------------------------- 144 | 145 | This extension provides several configuration properties in order to 146 | communicate properly with the RADIUS server to which it needs to authenticate. 147 | It is important that you know several key pieces of information about the 148 | RADIUS server - at a minimum, the server name or IP, the authentication port, 149 | the authentication protocol in use by the server, and the shared secret for the 150 | RADIUS client. If you are responsible for the RADIUS server, you'll need to 151 | properly configure these items to get Guacamole to authenticate properly. If 152 | you're not responsible for the RADIUS server you will need to work with the 153 | administrator to get all of the necessary configuration items for the server. 154 | These items will need to be configured in the 155 | [`guacamole.properties`](initial-setup) file. 156 | 157 | `radius-hostname` 158 | : The RADIUS server to authenticate against. If not specified, localhost will 159 | be used. 160 | 161 | `radius-auth-port` 162 | : The RADIUS authentication port on which the RADIUS service is is listening. 163 | If not specified, the default of 1812 will be used. 164 | 165 | `radius-shared-secret` 166 | : The shared secret to use when talking to the RADIUS server. This parameter is 167 | required and the extension will not load if this is not specified. 168 | 169 | `radius-auth-protocol` 170 | : The authentication protocol to use when talking to the RADIUS server. This 171 | parameter is required for the extension to operate. Supported values are: 172 | pap, chap, mschapv1, mschapv2, eap-md5, eap-tls, and eap-ttls. Support for 173 | PEAP is implemented inside the extension, but, due to a regression in the 174 | JRadius implementation, it is currently broken. Also, if you specify eap-ttls 175 | you will also need to specify the `radius-eap-ttls-inner-protocol` parameter 176 | in order to properly configure the protocol used inside the EAP TTLS tunnel. 177 | 178 | `radius-key-file` 179 | : The combination certificate and private key pair to use for TLS-based RADIUS 180 | protocols that require a client-side certificate. This parameter should specify 181 | the absolute path to the file. By default the extension will look for a file 182 | called `radius.key` in the `GUACAMOLE_HOME` directory. 183 | 184 | `radius-key-type` 185 | : The file type of the keystore specified by the `radius-key-file` parameter. 186 | Valid keystore types are pem, jceks, jks, and pkcs12. If not specified, this 187 | defaults to pkcs12, the default used by the JRadius library. 188 | 189 | `radius-key-password` 190 | : The password of the private key specified in the `radius-key-file` parameter. 191 | By default the extension will not use any password when trying to open the 192 | key file. 193 | 194 | `radius-ca-file` 195 | : The absolute path to the file that stores the certificate authority 196 | certificates for encrypted connections to the RADIUS server. By default a 197 | file with the name ca.crt in the `GUACAMOLE_HOME` directory will be used. 198 | 199 | `radius-ca-type` 200 | : The file type of keystore used for the certificate authority. Valid formats 201 | are pem, jceks, jks, and pkcs12. If not specified this defaults to pem. 202 | 203 | `radius-ca-password` 204 | : The password used to protect the certificate authority store, if any. If 205 | unspecified the extension will attempt to read the CA store without any 206 | password. 207 | 208 | `radius-trust-all` 209 | : This parameter controls whether or not the RADIUS extension should trust all 210 | certificates or verify them against known good certificate authorities. Set 211 | to true to allow the RADIUS server to connect without validating 212 | certificates. The default is false, which causes certificates to be 213 | validated. 214 | 215 | `radius-retries` 216 | : The number of times the client will retry the connection to the RADIUS server 217 | and not receive a response before giving up. By default the client will try 218 | the connection at most 5 times. 219 | 220 | `radius-timeout` 221 | : The timeout for a RADIUS connection in seconds. By default the client will 222 | wait for a response from the server for at most 60 seconds. 223 | 224 | `radius-eap-ttls-inner-protocol` 225 | : When EAP-TTLS is used, this parameter specifies the inner (tunneled) protocol 226 | to use talking to the RADIUS server. It is required when the 227 | `radius-auth-protocol` parameter is set to eap-ttls. If the 228 | `radius-auth-protocol` value is set to something other than eap-ttls, this 229 | parameter has no effect and will be ignored. Valid options for this are any of 230 | the values for `radius-auth-protocol`, except for eap-ttls. 231 | 232 | `radius-nas-ip` 233 | : This property allows the server administrator to manually set an IP address 234 | that will be sent to the RADIUS server to identify this RADIUS client, known 235 | as the "Network Access Server" (NAS) IP address. When this property is not 236 | specified, the RADIUS extension attempts to automatically determine the IP 237 | address of the system on which Guacamole is running and uses that value. 238 | 239 | (completing-radius-install)= 240 | 241 | Completing the installation 242 | --------------------------- 243 | 244 | Guacamole will only reread `guacamole.properties` and load newly-installed 245 | extensions during startup, so your servlet container will need to be restarted 246 | before HTTP header authentication can be used. *Doing this will disconnect all 247 | active users, so be sure that it is safe to do so prior to attempting 248 | installation.* When ready, restart your servlet container and give the new 249 | authentication a try. 250 | 251 | -------------------------------------------------------------------------------- /src/event-listeners.md: -------------------------------------------------------------------------------- 1 | Event listeners 2 | =============== 3 | 4 | Guacamole supports the delivery of event notifications to custom extensions. 5 | Developers can use listener extensions to integrate custom handling of events 6 | such as successful and failed authentications, and requests to connect and 7 | disconnect tunnels to desktop environments. 8 | 9 | A listener extension could be used, for example, to record authentication 10 | attempts in an external database for security auditing or alerting. By 11 | listening to tunnel lifecycle events, a listener extension could be used to 12 | help coordinate startup and shutdown of machine resources; particularly useful 13 | in cloud environments where minimizing running-but-idle resources is an 14 | important cost savings measure. 15 | 16 | For certain *vetoable* events, an event listener can even influence Guacamole's 17 | behavior. For example, a listener can veto a successful authentication, 18 | effectively causing the authentication to be considered failed. Similarly, a 19 | listener can veto a tunnel connection, effectively preventing the tunnel from 20 | being connected to a virtual desktop resource. 21 | 22 | Custom event listeners are packaged using the same extension mechanism used for 23 | custom authentication providers. A single listener extension can include any 24 | number of classes that implement the listener interface. A single extension 25 | module can also include any combination of authentication providers and 26 | listeners, so developers can easily combine authentication providers with 27 | listeners designed to support them. 28 | 29 | To demonstrate the principles involved in receiving Guacamole event 30 | notifications, we will implement a simple listener extension that logs 31 | authentication events. While our approach simply writes event details to the 32 | same log used by the Guacamole web application, a listener could process these 33 | events in arbitrary ways, limited only by the imagination and ingenuity of the 34 | developer. 35 | 36 | (custom-event-listener-skeleton)= 37 | 38 | A Guacamole listener extension skeleton 39 | --------------------------------------- 40 | 41 | For simplicity's sake, and because this is how things are done upstream in the 42 | Guacamole project, we will use Maven to build our extension. 43 | 44 | The bare minimum required for a Guacamole listener extension is a `pom.xml` 45 | file listing guacamole-ext as a dependency, a single `.java` file implementing 46 | our stub of a listener, and a `guac-manifest.json` file describing the 47 | extension and pointing to our listener class. 48 | 49 | ```xml 50 | 54 | 55 | 4.0.0 56 | org.apache.guacamole 57 | guacamole-listener-tutorial 58 | jar 59 | 1.5.3 60 | guacamole-listener-tutorial 61 | 62 | 63 | UTF-8 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-compiler-plugin 73 | 3.3 74 | 75 | 1.8 76 | 1.8 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | org.apache.guacamole 88 | guacamole-ext 89 | 1.5.3 90 | provided 91 | 92 | 93 | 94 | 96 | 97 | org.slf4j 98 | slf4j-api 99 | 1.7.7 100 | provided 101 | 102 | 103 | 104 | 105 | 106 | ``` 107 | 108 | Naturally, we need the actual listener extension skeleton code. While you can 109 | put this in whatever file and package you want, for the sake of this tutorial, 110 | we will assume you are using `org.apache.guacamole.event.TutorialListener`. 111 | 112 | For now, we won't actually do anything other than log the fact that an event 113 | notification was received. At this point, we're just creating the skeleton for 114 | our listener extension. 115 | 116 | ```java 117 | package org.apache.guacamole.event; 118 | 119 | import org.apache.guacamole.GuacamoleException; 120 | import org.apache.guacamole.net.event.listener.Listener; 121 | import org.slf4j.Logger; 122 | import org.slf4j.LoggerFactory; 123 | 124 | /** 125 | * A Listener implementation intended to demonstrate basic use 126 | * of Guacamole's listener extension API. 127 | */ 128 | public class TutorialListener implements Listener { 129 | 130 | private static final Logger logger = 131 | LoggerFactory.getLogger(TutorialListener.class); 132 | 133 | @Override 134 | public void handleEvent(Object event) throws GuacamoleException { 135 | logger.info("received Guacamole event notification"); 136 | } 137 | 138 | } 139 | ``` 140 | 141 | To conform with Maven, this skeleton file must be placed within 142 | `src/main/java/org/apache/guacamole/event` as `TutorialListener.java`. 143 | 144 | As you can see, implementing a listener is quite simple. There is a single 145 | `Listener` interface to implement. All Guacamole event notifications will be 146 | delivered to your code by invoking the handleEvent method. We will see shortly 147 | how to use the passed event object to get the details of the event itself. 148 | 149 | The only remaining piece for the overall skeleton to be complete is a 150 | `guac-manifest.json` file. *This file is absolutely required for all Guacamole 151 | extensions.* The `guac-manifest.json` format is described in more detail in 152 | [](guacamole-ext). It provides for quite a few properties, but for our listener 153 | extension we are mainly interested in the Guacamole version sanity check (to 154 | make sure an extension built for the API of Guacamole version X is not 155 | accidentally used against version Y) and telling Guacamole where to find our 156 | listener class. 157 | 158 | The Guacamole extension format requires that `guac-manifest.json` be placed in 159 | the root directory of the extension `.jar` file. To accomplish this with Maven, 160 | we place it within the `src/main/resources` directory. Maven will automatically 161 | pick it up during the build and include it within the `.jar`. 162 | 163 | ```json 164 | { 165 | 166 | "guacamoleVersion" : "1.5.3", 167 | 168 | "name" : "Tutorial Listener Extension", 169 | "namespace" : "guac-listener-tutorial", 170 | 171 | "listeners" : [ 172 | "org.apache.guacamole.event.TutorialListener" 173 | ] 174 | 175 | } 176 | ``` 177 | 178 | (custom-listener-building)= 179 | 180 | Building the extension 181 | ---------------------- 182 | 183 | Once all three of the above files are in place, the extension should build 184 | successfully even though it is just a skeleton at this point. 185 | 186 | ```console 187 | $ mvn package 188 | [INFO] Scanning for projects... 189 | [INFO] --------------------------------------------------------------- 190 | [INFO] Building guacamole-listener-tutorial 1.5.3 191 | [INFO] --------------------------------------------------------------- 192 | ... 193 | [INFO] --------------------------------------------------------------- 194 | [INFO] BUILD SUCCESS 195 | [INFO] --------------------------------------------------------------- 196 | [INFO] Total time: 1.297 s 197 | [INFO] Finished at: 2017-10-08T13:12:39-04:00 198 | [INFO] Final Memory: 19M/306M 199 | [INFO] --------------------------------------------------------------- 200 | $ 201 | ``` 202 | 203 | Assuming you see the "`BUILD SUCCESS`" message when you build the extension, 204 | there will be a new file, `target/guacamole-listener-tutorial-1.5.3.jar`, which 205 | can be installed within Guacamole (see [](custom-listener-installing) at the 206 | end of this chapter). It should log event notifications that occur during, for 207 | example, authentication attempts. If you changed the name or version of the 208 | project in the `pom.xml` file, the name of this new `.jar` file will be 209 | different, but it can still be found within `target/`. 210 | 211 | (custom-listener-event-handling)= 212 | 213 | Handling events 214 | --------------- 215 | 216 | The Guacamole `Listener` interface represents a low-level event handling API. A 217 | listener is notified of every event generated by Guacamole. The listener must 218 | examine the event type to determine whether the event is of interest, and if so 219 | to dispatch the event to the appropriate entry point. 220 | 221 | The event types that can be produced by Guacamole are described in the 222 | `org.apache.guacamole.net.event` package of the guacamole-ext API. In this 223 | package you will find several concrete event types as well as interfaces that 224 | describe common characteristics of certain of event types. You can use any of 225 | these types to distinguish the events received by your listener, and to examine 226 | properties of an event of a given type. 227 | 228 | Suppose we wish to log authentication success and failure events, while 229 | ignoring all other event types. The `AuthenticationSuccessEvent` and 230 | `AuthenticationFailureEvent` types are used to notify a listener of 231 | authentication events. We can simply check whether a received event is of one 232 | of these types and, if so, log an appropriate message. 233 | 234 | ```java 235 | package org.apache.guacamole.event; 236 | 237 | import org.apache.guacamole.GuacamoleException; 238 | import org.apache.guacamole.net.event.AuthenticationFailureEvent; 239 | import org.apache.guacamole.net.event.AuthenticationSuccessEvent; 240 | import org.apache.guacamole.net.event.listener.Listener; 241 | import org.slf4j.Logger; 242 | import org.slf4j.LoggerFactory; 243 | 244 | /** 245 | * A Listener that logs authentication success and failure events. 246 | */ 247 | public class TutorialListener implements Listener { 248 | 249 | private static final Logger logger = 250 | LoggerFactory.getLogger(TutorialListener.class); 251 | 252 | @Override 253 | public void handleEvent(Object event) throws GuacamoleException { 254 | 255 | if (event instanceof AuthenticationSuccessEvent) { 256 | logger.info("successful authentication for user {}", 257 | ((AuthenticationSuccessEvent) event) 258 | .getCredentials().getUsername()); 259 | } 260 | else if (event instanceof AuthenticationFailureEvent) { 261 | logger.info("failed authentication for user {}", 262 | ((AuthenticationFailureEvent) event) 263 | .getCredentials().getUsername()); 264 | } 265 | } 266 | 267 | } 268 | ``` 269 | 270 | In our example, we use `instanceof` to check for the two event types of 271 | interest to our listener. Once we have identified an event of interest, we can 272 | safely cast the event type to access properties of the event. 273 | 274 | The extension is now complete and can be built as described earlier in 275 | [](custom-listener-building) and installed as described below in 276 | [](custom-listener-installing). 277 | 278 | (custom-listener-veto)= 279 | 280 | Influencing Guacamole by event veto 281 | ----------------------------------- 282 | 283 | An implementation of the handleEvent method is permitted to throw any 284 | `GuacamoleException`. For certain *vetoable* event types, throwing a 285 | `GuacamoleException` serves to effectively veto the action that resulted in the 286 | event notification. See the API documentation for guacamole-ext to learn more 287 | about vetoable event types. 288 | 289 | As an (admittedly contrived) example, suppose we want to prevent a user named 290 | "guacadmin" from accessing Guacamole. For whatever reason, we don't wish to 291 | remove or disable the auth database entry for this user. In this case we can 292 | use a listener to block this user, preventing access to Guacamole. In the 293 | listener, when we get an `AuthenticationSuccessEvent` we can check to see if 294 | the user is "guacadmin" and, if so, throw an exception to prevent this user 295 | from logging in to Guacamole. 296 | 297 | ```java 298 | package org.apache.guacamole.event; 299 | 300 | import org.apache.guacamole.GuacamoleException; 301 | import org.apache.guacamole.GuacamoleSecurityException; 302 | import org.apache.guacamole.net.event.AuthenticationFailureEvent; 303 | import org.apache.guacamole.net.event.AuthenticationSuccessEvent; 304 | import org.apache.guacamole.net.event.listener.Listener; 305 | import org.slf4j.Logger; 306 | import org.slf4j.LoggerFactory; 307 | 308 | /** 309 | * A Listener that logs authentication success and failure events 310 | * and prevents the "guacadmin" user from logging in by throwing 311 | * a GuacamoleSecurityException. 312 | */ 313 | public class TutorialListener implements Listener { 314 | 315 | private static final Logger logger = 316 | LoggerFactory.getLogger(TutorialListener.class); 317 | 318 | @Override 319 | public void handleEvent(Object event) throws GuacamoleException { 320 | 321 | if (event instanceof AuthenticationSuccessEvent) { 322 | final String username = ((AuthenticationSuccessEvent) event) 323 | .getCredentials().getUsername(); 324 | 325 | if ("guacadmin".equals(username)) { 326 | logger.warn("user {} has been blocked", username); 327 | throw new GuacamoleSecurityException( 328 | "User '" + username + "' is currently blocked"); 329 | } 330 | 331 | logger.info("successful authentication for user {}", username); 332 | } 333 | else if (event instanceof AuthenticationFailureEvent) { 334 | logger.info("failed authentication for user {}", 335 | ((AuthenticationFailureEvent) event) 336 | .getCredentials().getUsername()); 337 | } 338 | } 339 | 340 | } 341 | ``` 342 | 343 | If our Guacamole user database contains a user named "guacadmin", and we build 344 | and install this listener extension, we will find that an attempt to log in as 345 | this user now results in a message in the UI indicating that the user is 346 | blocked. If we examine the Guacamole log, we will see the message indicating 347 | that the user was blocked. Because the successful authentication was vetoed, 348 | Guacamole sends a subsequent authentication failure notification, which we see 349 | logged as well. 350 | 351 | (custom-listener-installing)= 352 | 353 | Installing the extension 354 | ------------------------ 355 | 356 | Guacamole extensions are self-contained `.jar` files which are installed by 357 | being placed within `GUACAMOLE_HOME/extensions`, and this extension is no 358 | different. As described in [](configuring-guacamole), `GUACAMOLE_HOME` is a 359 | placeholder used to refer to the directory that Guacamole uses to locate its 360 | configuration files and extensions. Typically, this will be the `.guacamole` 361 | directory within the home directory of the user running Tomcat. 362 | 363 | To install your extension, copy the 364 | `target/guacamole-listener-tutorial-1.5.3.jar` file into 365 | `GUACAMOLE_HOME/extensions` and restart Tomcat. Guacamole will automatically 366 | load your extension, logging an informative message that it has done so: 367 | 368 | ``` 369 | Extension "Tutorial Listener Extension" loaded. 370 | ``` 371 | 372 | -------------------------------------------------------------------------------- /src/guacamole-common-js.md: -------------------------------------------------------------------------------- 1 | guacamole-common-js 2 | =================== 3 | 4 | The Guacamole project provides a JavaScript API for interfacing with other 5 | components that conform to the design of Guacamole, such as projects using 6 | libguac or guacamole-common. This API is called guacamole-common-js. 7 | 8 | guacamole-common-js provides a JavaScript implementation of a Guacamole client, 9 | as well as tunneling mechanisms for getting protocol data out of JavaScript and 10 | into guacd or the server side of a web application. 11 | 12 | For convenience, it also provides mouse and keyboard abstraction objects that 13 | translate JavaScript mouse, touch, and keyboard events into consistent data 14 | that Guacamole can more easily digest. The extendable on-screen keyboard that 15 | was developed for the Guacamole web application is also included. 16 | 17 | Guacamole client 18 | ---------------- 19 | 20 | The main benefit to using the JavaScript API is the full Guacamole client 21 | implementation, which implements all Guacamole instructions, and makes use of 22 | the tunnel implementations provided by both the JavaScript and Java APIs. 23 | 24 | Using the Guacamole client is straightforward. The client, like all other 25 | objects within the JavaScript API, is within the `Guacamole` namespace. It is 26 | instantiated given an existing, unconnected tunnel: 27 | 28 | ```javascript 29 | var client = new Guacamole.Client(tunnel); 30 | ``` 31 | 32 | Once you have the client, it won't immediately appear within the DOM. You need 33 | to add its display element manually: 34 | 35 | ```javascript 36 | document.body.appendChild(client.getDisplay().getElement()); 37 | ``` 38 | 39 | At this point, the client will be visible, rendering all updates as soon as 40 | they are received through the tunnel. 41 | 42 | ```javascript 43 | client.connect(); 44 | ``` 45 | 46 | It is possible to pass arbitrary data to the tunnel during connection which can 47 | be used for authentication or for choosing a particular connection. When the 48 | `connect()` function of the Guacamole client is called, it in turn calls the 49 | `connect()` function of the tunnel originally given to the client, establishing 50 | a connection. 51 | 52 | :::{important} 53 | When creating the `Guacamole.Client`, the tunnel used must not already be 54 | connected. The `Guacamole.Client` will call the `connect()` function for you 55 | when its own `connect()` function is invoked. If the tunnel is already 56 | connected when it is given to the `Guacamole.Client`, connection may not work 57 | at all. 58 | ::: 59 | 60 | In general, all instructions available within the Guacamole protocol are 61 | automatically handled by the Guacamole client, including instructions related 62 | to audio and video. The only instructions which you must handle yourself are 63 | "name" (used to name the connection), "clipboard" (used to update clipboard 64 | data on the client side), and "error" (used when something goes wrong 65 | server-side). Each of these instructions has a corresponding event handler; you 66 | need only supply functions to handle these events. If any of these event 67 | handlers are left unset, the corresponding instructions are simply ignored. 68 | 69 | HTTP tunnel 70 | ----------- 71 | 72 | Both the Java and JavaScript API implement corresponding ends of an HTTP 73 | tunnel, based on `XMLHttpRequest`. 74 | 75 | The tunnel is a true stream - there is no polling. An initial request is made 76 | from the JavaScript side, and this request is handled on the Java side. While 77 | this request is open, data is streamed along the connection, and instructions 78 | within this stream are handled as soon as they are received by the client. 79 | 80 | While data is being streamed along this existing connection, a second 81 | connection attempt is made. Data continues to be streamed along the original 82 | connection until the server receives and handles the second request, at which 83 | point the original connection closes and the stream is transferred to the new 84 | connection. 85 | 86 | This process repeats, alternating between active streams, thus creating an 87 | unbroken sequence of instructions, while also allowing JavaScript to free any 88 | memory used by the previously active connection. 89 | 90 | The tunnel is created by supplying the relative URL to the server-side tunnel 91 | servlet: 92 | 93 | ```javascript 94 | var tunnel = new Guacamole.Tunnel("tunnel"); 95 | ``` 96 | 97 | Once created, the tunnel can be passed to a `Guacamole.Client` for use in a 98 | Guacamole connection. 99 | 100 | The tunnel actually takes care of the Guacamole protocol parsing on behalf of 101 | the client, triggering "oninstruction" events for every instruction received, 102 | splitting each element into elements of an array so that the client doesn't 103 | have to. 104 | 105 | Input abstraction 106 | ----------------- 107 | 108 | Browsers can be rather finicky when it comes to keyboard and mouse input, not 109 | to mention touch events. There is little agreement on which keyboard events get 110 | fired when, and what detail about the event is made available to JavaScript. 111 | Touch and mouse events can also cause confusion, as most browsers will generate 112 | *both* events when the user touches the screen (for compatibility with 113 | JavaScript code that only handles mouse events), making it more difficult for 114 | applications to support both mouse and touch independently. 115 | 116 | The Guacamole JavaScript API abstracts mouse, keyboard, and touch interaction, 117 | providing several helper objects which act as an abstract interface between you 118 | and the browser events. 119 | 120 | (guacamole-mouse)= 121 | 122 | ### Mouse 123 | 124 | Mouse event abstraction is provided by the `Guacamole.Mouse` object. Given an 125 | arbitrary DOM element, `Guacamole.Mouse` triggers onmousedown, onmousemove, and 126 | onmouseup events which are consistent across browsers. This object only 127 | responds to true mouse events. Mouse events which are actually the result of 128 | touch events are ignored. 129 | 130 | ```javascript 131 | var element = document.getElementById("some-arbitrary-id"); 132 | var mouse = new Guacamole.Mouse(element); 133 | 134 | mouse.onmousedown = 135 | mouse.onmousemove = 136 | mouse.onmouseup = function(state) { 137 | 138 | // Do something with the mouse state received ... 139 | 140 | }; 141 | ``` 142 | 143 | The handles of each event are given an instance of `Guacamole.Mouse.State` 144 | which represents the current state of the mouse, containing the state of each 145 | button (including the scroll wheel) as well as the X and Y coordinates of the 146 | pointer in pixels. 147 | 148 | (guacamole-touch)= 149 | 150 | ### Touch 151 | 152 | Touch event abstraction is provided by either `Guacamole.Touchpad` (emulates a 153 | touchpad to generate artificial mouse events) or `Guacamole.Touchscreen` 154 | (emulates a touchscreen, again generating artificial mouse events). Guacamole 155 | uses the touchpad emulation, as this provides the most flexibility and 156 | mouse-like features, including scrollwheel and clicking with different buttons, 157 | but your preferences may differ. 158 | 159 | ```javascript 160 | var element = document.getElementById("some-arbitrary-id"); 161 | var touch = new Guacamole.Touchpad(element); // or Guacamole.Touchscreen 162 | 163 | touch.onmousedown = 164 | touch.onmousemove = 165 | touch.onmouseup = function(state) { 166 | 167 | // Do something with the mouse state received ... 168 | 169 | }; 170 | ``` 171 | 172 | Note that even though these objects are touch-specific, they still provide 173 | mouse events. The state object given to the event handlers of each event is 174 | still an instance of `Guacamole.Mouse.State`. 175 | 176 | Ultimately, you could assign the same event handler to all the events of both 177 | an instance of `Guacamole.Mouse` as well as `Guacamole.Touchscreen` or 178 | `Guacamole.Touchpad`, and you would magically gain mouse and touch support. 179 | This support, being driven by the needs of remote desktop, is naturally geared 180 | around the mouse and providing a reasonable means of interacting with it. For 181 | an actual mouse, events are translated simply and literally, while touch events 182 | go through additional emulation and heuristics. From the perspective of the 183 | user and the code, this is all transparent. 184 | 185 | (guacamole-keyboard)= 186 | 187 | ### Keyboard 188 | 189 | Keyboard events in Guacamole are abstracted with the `Guacamole.Keyboard` 190 | object as only keyup and keydown events; there is no keypress like there is in 191 | JavaScript. Further, all the craziness of keycodes vs. scancodes vs. key 192 | identifiers normally present across browsers is abstracted away. All your event 193 | handlers will see is an X11 keysym, which represent every key unambiguously. 194 | Conveniently, X11 keysyms are also what the Guacamole protocol requires, so if 195 | you want to use `Guacamole.Keyboard` to drive key events sent over the 196 | Guacamole protocol, everything can be connected directly. 197 | 198 | Just like the other input abstraction objects, `Guacamole.Keyboard` requires a 199 | DOM element as an event target. Only key events directed at this element will 200 | be handled. 201 | 202 | ```javascript 203 | var keyboard = new Guacamole.Keyboard(document); 204 | 205 | keyboard.onkeydown = function(keysym) { 206 | // Do something ... 207 | }; 208 | 209 | keyboard.onkeyup = function(keysym) { 210 | // Do something ... 211 | }; 212 | ``` 213 | 214 | In this case, we are using `document` as the event target, thus receiving all 215 | key events while the browser window (or tab) has focus. 216 | 217 | On-screen keyboard 218 | ------------------ 219 | 220 | The Guacamole JavaScript API also provides an extendable on-screen keyboard, 221 | `Guacamole.OnScreenKeyboard`, which requires the URL of an XML file describing 222 | the keyboard layout. The on-screen keyboard object provides no hard-coded 223 | layout information; the keyboard layout is described entirely within the XML 224 | layout file. 225 | 226 | ### Keyboard layouts 227 | 228 | The keyboard layout XML included in the Guacamole web application would be a 229 | good place to start regarding how these layout files are written, but in 230 | general, the keyboard is simply a set of rows or columns, denoted with `` 231 | and `` tags respectively, where each can be nested within the other as 232 | desired. 233 | 234 | Each key is represented with a `` tag, but this is not what the user sees, 235 | nor what generates the key event. Each key contains any number of `` tags, 236 | which represent the visible part of the key. The cap describes which X11 237 | keysym will be sent when the key is pressed. Each cap can be associated with 238 | any combination of arbitrary modifier flags which dictate when that cap is 239 | active. 240 | 241 | For example: 242 | 243 | ```xml 244 | 245 | 246 | 247 | Shift 248 | 249 | 250 | a 251 | A 252 | 253 | 254 | 255 | ``` 256 | 257 | Here we have a very simple keyboard which defines only two keys: "shift" (a 258 | modifier) and the letter "a". When "shift" is pressed, it sets the "shift" 259 | modifier, affecting other keys in the keyboard. The "a" key has two caps: one 260 | lowercase (the default) and one uppercase (which requires the shift modifier to 261 | be active). 262 | 263 | Notice that the shift key needed the keysym explicitly specified, while the "a" 264 | key did not. This is because the on-screen keyboard will automatically derive 265 | the correct keysym from the text of the key cap if the text contains only a 266 | single character. 267 | 268 | (displaying-osk)= 269 | 270 | ### Displaying the keyboard 271 | 272 | Once you have a keyboard layout available, adding an on-screen keyboard to your 273 | application is simple: 274 | 275 | ```javascript 276 | // Add keyboard to body 277 | var keyboard = new Guacamole.OnScreenKeyboard("path/to/layout.xml"); 278 | document.body.appendChild(keyboard.getElement()); 279 | 280 | // Set size of keyboard to 100 pixels 281 | keyboard.resize(100); 282 | ``` 283 | 284 | Here, we have explicitly specified the width of the keyboard as 100 pixels. 285 | Normally, you would determine this by inspecting the width of the containing 286 | component, or by deciding on a reasonable width beforehand. Once the width is 287 | given, the height of the keyboard is determined based on the arrangement of 288 | each row. 289 | 290 | ### Styling the keyboard 291 | 292 | While the `Guacamole.OnScreenKeyboard` object will handle most of the layout, 293 | you will still need to style everything yourself with CSS to get the elements 294 | to render properly and the keys to change state when clicked or activated. It 295 | defines several CSS classes, which you will need to manually style to get 296 | things looking as desired: 297 | 298 | `guac-keyboard` 299 | : This class is assigned to the root element containing the entire keyboard, 300 | returned by `getElement()`, 301 | 302 | `guac-keyboard-row` 303 | : Assigned to the `div` elements which contain each row. 304 | 305 | `guac-keyboard-column` 306 | : Assigned to the `div` elements which contain each column. 307 | 308 | `guac-keyboard-gap` 309 | : Assigned to any `div` elements created as a result of `` tags in the 310 | keyboard layout. `` tags are intended to behave as keys with no visible 311 | styling or caps. 312 | 313 | `guac-keyboard-key-container` 314 | : Assigned to the `div` element which contains a key, and provides that key 315 | with its required dimensions. It is this element that will be scaled relative 316 | to the size specified in the layout XML and the size given to the `resize()` 317 | function. 318 | 319 | `guac-keyboard-key` 320 | : Assigned to the `div` element which represents the actual key, not the cap. 321 | This element will not directly contain text, but it will contain all caps 322 | that this key can have. With clever CSS rules, you can take advantage of this 323 | and cause inactive caps to appear on the key in a corner (for example), or 324 | hide them entirely. 325 | 326 | `guac-keyboard-cap` 327 | : Assigned to the `div` element representing a key cap. Each cap is a child of 328 | its corresponding key, and it is up to the author of the CSS rules to hide or 329 | show or reposition each cap appropriately. Each cap will contain the display 330 | text defined within the `` element in the layout XML. 331 | 332 | {samp}`guac-keyboard-requires-{MODIFIER}` 333 | : Added to the cap element when that cap requires a specific modifier. 334 | 335 | {samp}`guac-keyboard-uses-{MODIFIER}` 336 | : Added to the key element when any cap contained within it requires a specific 337 | modifier. 338 | 339 | {samp}`guac-keyboard-modifier-{MODIFIER}` 340 | : Added to and removed from the root keyboard element when a modifier key is 341 | activated or deactivated respectively. 342 | 343 | `guac-keyboard-pressed` 344 | : Added to and removed from any key element as it is pressed and released 345 | respectively. 346 | 347 | :::{important} 348 | The CSS rules required for the on-screen keyboard to work as expected can be 349 | quite complex. Looking over the CSS rules used by the on-screen keyboard in the 350 | Guacamole web application would be a good place to start to see how the 351 | appearance of each key can be driven through the simple class changes described 352 | above. 353 | 354 | Inspecting the elements of an active on-screen keyboard within the Guacamole 355 | web application with the developer tools of your favorite browser is also a 356 | good idea. 357 | ::: 358 | 359 | (osk-event-handling)= 360 | 361 | ### Handling key events 362 | 363 | Key events generated by the on-screen keyboard are identical to those of 364 | `Guacamole.Keyboard` in that they consist only of a single X11 keysym. Only 365 | keyup and keydown events exist, as before; there is no keypress event. 366 | 367 | ```javascript 368 | // Assuming we have an instance of Guacamole.OnScreenKeyboard already 369 | // called "keyboard" 370 | 371 | keyboard.onkeydown = function(keysym) { 372 | // Do something ... 373 | }; 374 | 375 | keyboard.onkeyup = function(keysym) { 376 | // Do something ... 377 | }; 378 | ``` 379 | 380 | --------------------------------------------------------------------------------