Skip to content

Instantly share code, notes, and snippets.

@adilinden
Last active October 1, 2020 11:45
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adilinden/41c0c5c4aaca301260e5980dc4e0ef1e to your computer and use it in GitHub Desktop.
Save adilinden/41c0c5c4aaca301260e5980dc4e0ef1e to your computer and use it in GitHub Desktop.
Building apache/guacamole on Ubuntu 18.04

Guacamole

This is a HTML5 to RDP gateway

Base System

Ubuntu 18.04 installed from my template.

First Login

Passwords

Configure the passwords by changing both bingo and root to something more sensible.

Networking

Configure /etc/netplan/01-netcfg.yaml for the static IP.

network:
  version: 2
  renderer: networkd
  ethernets:
    ens160:
      dhcp4: no
      addresses:
      - 10.53.79.59/24
      gateway4: 10.53.79.1
      nameservers:
        search: [example.ca]
        addresses: [10.53.79.1]

Hostname

Edit the following files to change hostname from ubuntu-18.04-amd64-tpl to guac.

sed -i 's/ubuntu-18\|ubuntu-18.04-amd64-tpl/guac/g' /etc/hosts && \
sed -i 's/ubuntu-18\|ubuntu-18.04-amd64-tpl/guac/g' /etc/hostname && \
sed -i 's/ubuntu-18\|ubuntu-18.04-amd64-tpl/guac/g' /etc/mailname && \
sed -i 's/ubuntu-18\|ubuntu-18.04-amd64-tpl/guac/g' /etc/postfix/main.cf && \
hostname guac

ssh Host Keys

Refresh the ssh host keys by removing them, then recreating them.

rm -v /etc/ssh/ssh_host*{key,pub}
dpkg-reconfigure openssh-server

Update

Update the system using apt.

apt update
apt upgrade

Guacamole

Instructions

https://www.tecmint.com/guacamole-access-remote-linux-windows-machines-via-web-browser/ https://guacamole.apache.org/doc/0.9.14/gug/installing-guacamole.html#guacamole-server-installation https://github.com/MysticRyuujin/guac-install

File locations

For reference here are some locations we are working with. Note that there are install paths to which symlinks are created during isntallation.

Directory Purpose Variable
/var/lib/tomcat8/webapps place to link .war into
/etc/guacamole configuration files install GUACAMOLE_HOME
/usr/local/share/guacamole/extensions/ jar & war install
/usr/local/src Sources build directory

Prerequisites

Install Oracle Java 8

apt install --no-install-recommends software-properties-common
add-apt-repository ppa:webupd8team/java       # For Java 8
add-apt-repository ppa:linuxuprising/java     # For Java 10/11
apt update
apt search oracle-java                        # Show available installable Oracle Java versions
apt install default-jdk                       # Install latest OpenJDK from Ubuntu
apt install oracle-java8-installer            # Install Java 8
update-alternatives --config java             # Select the default java

Make sure the last command shows Oracle Java 8 as default, as Guacamole 0.9.14 didn't build for me using the OpenJDK versions.

Install the prerequisites for the guacamole-server, guacamole-client, the tomcat server and build tools.

apt install libcairo2-dev libjpeg-turbo8-dev libjpeg-dev libpng-dev libossp-uuid-dev \
  libavcodec-dev libavutil-dev libswscale-dev libfreerdp-dev libpango1.0-dev libssh2-1-dev \
  libtelnet-dev libvncserver-dev libpulse-dev libssl-dev libvorbis-dev libwebp-dev \
  ghostscript tomcat8 tomcat8-admin tomcat8-user \
  git build-essential autoconf maven

Building guacamole-server from source

Ubuntu 18.08 build error

This error occurs on Ubuntu 18.04:

Makefile:566: recipe for target 'libguac_terminal_la-typescript.lo' failed
make[2]: *** [libguac_terminal_la-typescript.lo] Error 1
make[2]: Leaving directory '/usr/local/src/guacamole-server-0.9.14/src/terminal'
Makefile:503: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/usr/local/src/guacamole-server-0.9.14'
Makefile:427: recipe for target 'all' failed
make: *** [all] Error 2

It has been discussed here and fixed here.

Place ubuntu-18.04-amd64-20181012.patch into /usr/local/src and apply at the appropriate stage.

From git

Create a convenient location to build, then download and build the server sources.

mkdir -p /usr/local/src
cd /usr/local/src
git clone git://github.com/apache/guacamole-server.git
cd guacamole-server
git checkout tags/0.9.14 -b tag-0.9.14
patch -p1 < ../ubuntu-18.04-amd64-20181012.patch # Apply patch for <1.0.0
autoreconf -fi
./configure --with-init-dir=/etc/init.d
make
make install

From tarball

Create a convenient location to build, then download and build the server sources.

mkdir -p /usr/local/src
cd /usr/local/src
wget http://apache.mirror.iweb.ca/guacamole/0.9.14/source/guacamole-server-0.9.14.tar.gz
tar -xzf guacamole-server-0.9.14.tar.gz
patch -p0 < ../ubuntu-18.04-amd64-20181012.patch # Apply patch for <1.0.0
cd guacamole-server-0.9.14/
./configure --with-init-dir=/etc/init.d
make
make install

Common tasks

Link the libraries.

mkdir -p /usr/lib/$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)/freerdp
ln -s /usr/local/lib/freerdp/guac*.so /usr/lib/$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)/freerdp/
ldconfig

Building guacamole-client from source

From git

Download and build the client sources.

cd /usr/local/src
git clone https://github.com/apache/guacamole-client.git
cd guacamole-client
git checkout tags/0.9.14 -b tag-0.9.14
mvn package

Note: Use mvn clean to cleanup sources before rebuilding. Important when checking out a different release.

From tarball

Download and build the client sources.

cd /usr/local/src
wget http://apache.mirror.iweb.ca/guacamole/0.9.14/source/guacamole-client-0.9.14.tar.gz
tar xzf guacamole-client-0.9.14.tar.gz
cd guacamole-client-0.9.14
mvn package

Common tasks

Installation

mkdir -p /usr/local/share/guacamole/extensions
cp guacamole/target/guacamole-0.9.14.war /usr/local/share/guacamole/
find extensions -type f -name *.jar -exec cp {} /usr/local/share/guacamole/extensions/ \;

Basic Deployment

Create directories and deploy webapp

mkdir -p /etc/guacamole/{lib,extensions}
ln -sf /usr/local/share/guacamole/guacamole-0.9.14.war /var/lib/tomcat8/webapps/guacamole.war

File /etc/guacamole/guacamole.properties

guacd-hostname: localhost
guacd-port: 4822

File /etc/guacamole/user-mapping.xml

<user-mapping>
  <authorize 
    username="tecmint" 
    password="8383339b9c90775ac14693d8e620981f" 
    encoding="md5">
    <connection name="RHEL 7">
      <protocol>ssh</protocol>
      <param name="hostname">10.53.79.18</param>
      <param name="port">22</param>
      <param name="username">gacanepa</param>
    </connection>
    <connection name="Windows 10">
      <protocol>rdp</protocol>
      <param name="hostname">10.53.79.19</param>
      <param name="port">3389</param>
      <param name="security">tls</param>
      <param name="ignore-cert">true</param>
      <param name="enable-printing">true</param>
    </connection>
    <connection name="VNC Host">
      <protocol>vnc</protocol>
      <param name="hostname">10.53.79.21</param>
      <param name="port">5900</param>
    </connection>
  </authorize>
</user-mapping>

Passwords are MD5 and can be generated with

printf '%s' "tecmint01" | md5sum

Restart services.

systemctl enable guacd
service guacd start
service tomcat8 restart

Optional SSL for tomcat

Create a keystore with a single self-signed certificate.

keytool -genkey -alias Guacamole -keyalg RSA -keystore /etc/tomcat8/guac.jks -storepass cookedBeaver \
    -keypass cookedBeaver -noprompt -dname "CN=guac.example.ca,OU=,O=,L=,S=,C="

Add the port 8443 connector to /etc/tomcat8/server.xml.

<Connector port="8443" protocol="HTTP/1.1" 
           SSLEnabled="true"
           maxThreads="150"
           scheme="https"
           secure="true"
           clientAuth="false"
           sslProtocol="TLS"
           keystoreFile="conf/guac.jks"
           keystorePass="cookedBeaver"
           URIEncoding="UTF-8" />

Comment out the <Connector port="8443" in /etc/tomcat8/server.xml if only SSL traffic is desired.

Optional SQL authentication

Install MariaDB packages

apt install --no-install-recommends mariadb-client mariadb-server mariadb-common libmysql-java

Create the database and database user

mysql -u root
  create database guacdb;
  create user 'guac_user'@'localhost' identified by 'somesecret';
  GRANT SELECT,INSERT,UPDATE,DELETE ON guacdb.* TO 'guac_user'@'localhost';
  flush privileges;

Deploy the database schema and create the default administrative user (guacadmin/guacadmin)

cat /usr/local/src/guacamole-client/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/*.sql | mysql -u root guacdb

Add to the contents of /etc/guacamole/guacamole.properties.

mysql-hostname: localhost
mysql-port: 3306
mysql-database: guacdb
mysql-username: guac_user
mysql-password: somesecret

Remove /etc/guacamole/user-mapping.xml.

rm /etc/guacamole/user-mapping.xml

Install the MySQL extension and JDBC driver

ln -sf /usr/local/share/guacamole/extensions/guacamole-auth-jdbc-mysql-0.9.14.jar /etc/guacamole/extensions/guacamole-auth-jdbc-mysql.jar
ln -sf /usr/share/java/mysql-connector-java.jar /etc/guacamole/lib/

Restart services

service guacd restart
service tomcat8 restart

Optional HTTP header authentication

Guacamole supports authentication via HTTP header. This is NOT real authentication but just passing the username to guacamole. SOme initial troubles resolved via mailing-list thread

Without any connections defined for the user, the user has access to create connections when directly connected and authenticated to the SQL database. However, the same user “authenticated” via header cannot access the connections tab nor create connections.

Once connections are defined for the user, the user sees the connections tab in settings and can access existing and create new connections no matter whether authenticated directly or whether accessing via proxy and headers.

Any connections defined in user-mappings.xml are seen when connected direct (authenticated against SQL) but ignored when accessing via proxy and headers.

Install the guacamole extension.

ln -sf /usr/local/share/guacamole/extensions/guacamole-auth-header-0.9.14.jar /etc/guacamole/extensions/guacamole-auth-header.jar

Optionally, add http-auth-header to the /etc/guacamole/guacamole.properties file for a header other than REMOTE_USER to pull username from.

http-auth-header: X-Guacamole-User

This would require the following additional statements in an nginx proxy.

proxy_set_header Authorization "";
proxy_set_header X-Guacamole-User $remote_user;

Proxying guacamole with nginx reverse proxy

Official doc: https://guacamole.apache.org/doc/gug/proxying-guacamole.html#proxying-with-nginx

This is the standard nginx config fragment.

location /guacamole/ {
    proxy_pass http://HOSTNAME:8080/guacamole/;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    access_log off;
}

This a config fragment with path changed to new-path.

location /new-path/ {
    proxy_pass http://HOSTNAME:8080/guacamole/;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_cookie_path /guacamole/ /new-path/;
    access_log off;
}
guacd-hostname: localhost
guacd-port: 4822
http-auth-header: X-Guacamole-User
mysql-hostname: localhost
mysql-port: 3306
mysql-database: guacdb
mysql-username: guac_user
mysql-password: somesecret
# proxy.example.ca
# =====================================================
#
# Creating web basic authentication users and passwords
#
# Generate password for htpasswd using:
# echo -n "Username: "; read user; printf "$user:`openssl passwd -apr1`\n"
#
# Generate base64 for header using:
# echo -n 'user:password' | openssl base64
#
# =====================================================
# For reference when defining logging directives, this is the default
# log format.
#
# log_format combined '$remote_addr - $remote_user [$time_local] '
# '"$request" $status $body_bytes_sent '
# '"$http_referer" "$http_user_agent"';
log_format mainlog '$remote_addr $host $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
log_format proxylog '$remote_addr $host $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'to: $upstream_addr urt=upstream_response_time '
'rt=$request_time';
error_page 400 402 405 406 407 408 /4xx.html;
error_page 500 501 505 506 507 508 509 /5xx.html;
error_page 401 /401.html;
error_page 403 /403.html;
error_page 404 /404.html;
error_page 502 /502.html;
error_page 503 /503.html;
error_page 504 /504.html;
index index.html;
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
root /var/www/html;
access_log /var/log/nginx/access.log mainlog;
# SSL certificates
# - pre-production
include snippets/snakeoil.conf;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name proxy.example.ca;
root /var/www/html;
access_log /var/log/nginx/access.log mainlog;
# No redirect for certbot
location /.well-known/acme-challenge/ {
try_files $uri $uri/ =404;
}
location / {
return 302 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name proxy.example.ca;
root /var/www/html;
access_log /var/log/nginx/access.log mainlog;
# SSL
ssl on;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
# - pre-production
#include snippets/snakeoil.conf;
# - production
ssl_certificate /etc/letsencrypt/live/proxy.example.ca/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy.example.ca/privkey.pem;
# Require authentication to access any services!
auth_basic "proxy.example.ca - SSO";
auth_basic_user_file /etc/htpasswd/default-read;
location /guacamole/ {
access_log /var/log/nginx/proxy.log proxylog;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_cookie_path /guacamole/ /guacamole/;
proxy_set_header Authorization "";
proxy_set_header X-Guacamole-User $remote_user;
proxy_pass https://10.53.79.59:8443/guacamole/;
}
location / {
# No authentication for static files and certbot
auth_basic off;
try_files $uri $uri/ =404;
}
}
# End
diff -Naur guacamole-server-0.9.14-orig/src/terminal/typescript.c guacamole-server-0.9.14/src/terminal/typescript.c
--- guacamole-server-0.9.14-orig/src/terminal/typescript.c 2017-09-22 15:21:12.000000000 -0500
+++ guacamole-server-0.9.14/src/terminal/typescript.c 2018-10-12 10:22:41.406958491 -0500
@@ -129,9 +129,29 @@
return NULL;
}
+ /*
+ * Patch for compile time error on Ubuntu 18.04
+ *
+ * Discussed here: http://apache-guacamole-general-user-mailing-list.2363388.n4.nabble.com/Error-quot-make-quot-guacamole-quot-please-help-me-quot-td3831.html
+ * Patch here: https://github.com/apache/guacamole-server/pull/150
+ */
+
+ /* Append suffix to basename */
+ //sprintf(typescript->timing_filename, "%s.%s", typescript->data_filename,
+ // GUAC_TERMINAL_TYPESCRIPT_TIMING_SUFFIX);
+
/* Append suffix to basename */
- sprintf(typescript->timing_filename, "%s.%s", typescript->data_filename,
- GUAC_TERMINAL_TYPESCRIPT_TIMING_SUFFIX);
+ if (snprintf(typescript->timing_filename, sizeof(typescript->timing_filename),
+ "%s.%s", typescript->data_filename, GUAC_TERMINAL_TYPESCRIPT_TIMING_SUFFIX)
+ >= sizeof(typescript->timing_filename)) {
+ close(typescript->data_fd);
+ free(typescript);
+ return NULL;
+ }
+
+ /*
+ * End patch
+ */
/* Attempt to open typescript timing file */
typescript->timing_fd = open(typescript->timing_filename,
<user-mapping>
<authorize
username="tecmint"
password="8383339b9c90775ac14693d8e620981f"
encoding="md5">
<connection name="RHEL 7">
<protocol>ssh</protocol>
<param name="hostname">10.53.79.18</param>
<param name="port">22</param>
<param name="username">gacanepa</param>
</connection>
<connection name="Windows 10">
<protocol>rdp</protocol>
<param name="hostname">10.53.79.19</param>
<param name="port">3389</param>
<param name="security">tls</param>
<param name="ignore-cert">true</param>
<param name="enable-printing">true</param>
</connection>
<connection name="VNC Host">
<protocol>vnc</protocol>
<param name="hostname">10.53.79.21</param>
<param name="port">5900</param>
</connection>
</authorize>
</user-mapping>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment