Skip to content

GUACAMOLE-2064: Add interactive database terminal protocols#694

Open
4m1nr wants to merge 7 commits into
apache:mainfrom
4m1nr:GUACAMOLE-2064-database-protocols
Open

GUACAMOLE-2064: Add interactive database terminal protocols#694
4m1nr wants to merge 7 commits into
apache:mainfrom
4m1nr:GUACAMOLE-2064-database-protocols

Conversation

@4m1nr

@4m1nr 4m1nr commented Jul 14, 2026

Copy link
Copy Markdown

What this does

Adds interactive, terminal-based access to five databases from within
Guacamole — MySQL/MariaDB, PostgreSQL, SQL Server, MongoDB, and Oracle
implementing the feature requested in
GUACAMOLE-2064.

Following the approach suggested on the issue, guacd does not spawn a
command-line client (mysql, psql, …). Instead each protocol links the
database's own C client library and drives libguac-terminal directly,
presenting a prompt at which statements are typed, edited, and executed. As a
result, every feature Guacamole already provides for terminal protocols —
typescripts, graphical session recording, terminal resize, clipboard, and
shared / read-only sessions — works here unchanged.

How it is structured

All database-agnostic logic lives in one new convenience library,
libguac-dbshell (src/dbshell), modeled on src/common-ssh:

  • Line editor — a byte-stream editor with cursor movement
    (Left/Right/Home/End), in-memory command history (Up/Down), UTF-8-aware
    insertion/deletion, the usual control shortcuts (Ctrl-A/E/K/U/W/L), and a
    parser that consumes-and-drops unknown escape sequences so arrow keys etc.
    never leak bytes into the buffer.
  • Statement splitter — accumulates input and detects complete statements
    per dialect: MySQL, PostgreSQL, Transact-SQL, Oracle, and JSON. Handles
    single/double/backtick/bracket quoting, -- / # / block comments,
    PostgreSQL dollar-quoting (and $1 positional params), T-SQL GO batches,
    Oracle PL/SQL /-terminated blocks, and JSON brace/bracket balancing.
  • Result table renderer — buffers an initial window of rows to compute
    column widths, then streams the remainder; mysql-style borders, right-aligned
    numeric columns, NULL rendering, width capping with an ellipsis. It
    strips control characters from every cell
    , so a malicious or compromised
    database server cannot inject terminal escape sequences (the DB analogue of
    CVE-2023-43826).
  • REPL, generic plugin scaffold, and common connection settings shared by
    every protocol (hostname/port/credentials/database/timeout plus the full
    terminal, typescript, recording, and Wake-on-LAN parameter sets).

Each protocol plugin (src/protocols/{mysql,postgresql,mssql,mongodb,oracle})
is a thin driver implementing a small vtable:

Protocol Library Notable driver behavior
mysql MariaDB Connector/C non-blocking API (so teardown never blocks on a dead server); KILL QUERY cancel via a second connection; LOAD DATA LOCAL INFILE disabled
postgresql libpq asynchronous API; PQcancel; server notices rendered inline; dollar-quoting-aware
mssql FreeTDS CT-Library GO batches; server messages incl. PRINT; ct_cancel attention on Ctrl-C
mongodb MongoDB C Driver Extended-JSON command shell; automatic cursor getMore; \use / \show meta-commands
oracle Oracle Call Interface EZConnect; PL/SQL blocks; OCIBreak cancel

Build / packaging

  • configure autodetects libmariadb, libpq, FreeTDS, and libmongoc and
    disables the corresponding protocol with a warning if absent (same pattern as
    the existing libwebsockets/Kubernetes check).
  • Oracle is never built by default — its client (Oracle Instant Client) is
    proprietary and non-redistributable. It is enabled explicitly with
    --with-oracle=<instant-client-dir>.
  • The guacd Dockerfile installs the four redistributable client libraries, so
    the official image gains MySQL/PostgreSQL/SQL Server/MongoDB support; Oracle
    is documented as a custom-image step.

⚠️ Open question for maintainers — OpenSSL in the Docker image. The four
database client packages on Alpine depend on openssl-dev (OpenSSL 3), which
cannot be installed alongside openssl1.1-compat-dev, the package the
image currently pins (the base image is held at Alpine 3.18 for that reason).
This PR therefore switches the image to openssl-dev; guacd and its existing
dependencies build and run against OpenSSL 3 in my testing (the full image
builds and all existing protocols — RDP/VNC/SSH/telnet/Kubernetes — are
present and load). I have kept the 3.18 base pin and only updated the
comment that explained it, rather than bumping the base image, to keep this
change minimal.

Context: the openssl1.1-compat-dev pin comes from
GUACAMOLE-1741, where
VeNCrypt VNC authentication broke against OpenSSL 3.0.8 due to a
libvncserver incompatibility (not a guacd one). The Alpine 3.18 pin was
then added in the still-open
GUACAMOLE-1891 because
Alpine 3.19 dropped the compat package; Alpine 3.18 has since reached
end-of-life (9 May 2025). That libvncserver blocker was fixed upstream in
LibVNCServer 0.9.15

(22 Dec 2024): "Fixed building with OpenSSL >= 3.0.0." Since the Dockerfile
already auto-selects the latest LibVNCServer release, the image is building
0.9.15+ today, so the original reason for the OpenSSL 1.1 pin appears to be
stale. Moving to openssl-dev here is therefore a step toward what
GUACAMOLE-1891 needs, and that ticket looks close to resolvable.

Why I did not also bump the base image off 3.18: I tried building on
alpine:latest (currently 3.24) and it is not a self-contained change — it
cascades into unrelated packaging churn: webkit2gtk-dev has been renamed
(to webkit2gtk-4.1-dev), and CMake 4 on newer Alpine hard-rejects the older
cmake_minimum_required(VERSION < 3.5) still declared by some bundled
dependency sources (the libvncserver build fails to configure), with more
likely behind it. That is a genuine base-image migration and belongs in
GUACAMOLE-1891, not in this feature PR, so I kept the 3.18 pin (Alpine 3.18
still ships OpenSSL 3.1, so openssl-dev works there) and only switched the
OpenSSL package.

The one thing I could not fully verify: that VeNCrypt VNC auth works
against libvncserver 0.9.15 + OpenSSL 3 (the exact scenario GUACAMOLE-1741 was
about). It is worth an explicit re-test before the pin is considered dead. If
you would rather the Docker packaging (and thus the OpenSSL switch and that
VeNCrypt re-test) be handled separately from this feature — e.g. under
GUACAMOLE-1891 — I'm happy to drop the Docker commit and file a follow-up; the
protocols build fine from source regardless, so this only affects the
convenience image. Please let me know which you'd prefer.

Testing

Unit tests (CUnit, make check): new suites for the line editor & input
parser, command history, the statement splitter across all five dialects, and
cell sanitization. Full suite: 160 tests across 7 suites, all passing
(was 123/6 before).

Manual / integration testing: built and ran guacd against live database
containers, opening real connections through the Guacamole protocol with a
typescript recording enabled and asserting on the recorded output:

  • MySQL (mariadb:11), PostgreSQL (postgres:16), SQL Server
    (mcr.microsoft.com/mssql/server:2022), MongoDB (mongo:7), Oracle
    (gvenzl/oracle-free): SELECT rendering (bordered table, right-aligned
    numerics, "N rows in set" footer), banners, and clean disconnect.
  • PostgreSQL: multi-line statement with continuation prompt, statement-error
    recovery (session survives), and UTF-8 round-trip (café).
  • MySQL: wrong-password path renders ERROR 1045 and aborts cleanly.
  • Oracle: multi-line BEGIN … END; / PL/SQL block followed by a SELECT … FROM dual (proves semicolons inside the block do not terminate early).
  • MongoDB: \use, \show collections, insert, and find.
  • docker build . produces a guacd image containing all four redistributable
    plugins; make check green with --with-oracle active.

-Werror -Wall clean; every function, parameter, struct member, and constant
is Doxygen-documented in the house style.

Related PRs

Part of a three-repo change under GUACAMOLE-2064:

4m1nr added 7 commits July 13, 2026 23:27
Add libguac-dbshell, a convenience library (analogous to common-ssh) that
provides all of the database-agnostic machinery for an interactive,
terminal-based database shell, so that each database protocol plugin need
only supply a thin driver.

The library contains:

  - a byte-stream line editor with cursor movement, in-memory command
    history, UTF-8-aware editing, the usual control shortcuts, and a
    parser that safely consumes and discards unknown escape sequences;
  - a statement splitter that accumulates input and recognizes complete
    statements according to the MySQL, PostgreSQL, Transact-SQL, Oracle
    (PL/SQL), and JSON dialects;
  - a result table renderer that buffers an initial window of rows to size
    columns and then streams the remainder, and which neutralizes control
    characters in cell data so that a database server cannot inject
    terminal escape sequences;
  - the REPL loop, the generic protocol-plugin scaffold, and the
    connection settings common to every database protocol.

CUnit tests are included for the line editor and input parser, command
history, the statement splitter (all five dialects), and cell
sanitization. The library builds whenever terminal support is enabled.
Add the "mysql" protocol, providing an interactive shell for MySQL and
MariaDB servers via the dbshell library and MariaDB Connector/C. The
non-blocking API of MariaDB Connector/C is used so that session teardown
can never block on an unresponsive server, a running statement can be
cancelled with Ctrl-C (via KILL QUERY on a secondary connection), and
LOAD DATA LOCAL INFILE is disabled so the server cannot read files local
to guacd. The plugin is built automatically when MariaDB Connector/C is
present, and is disabled with a warning otherwise.
Add the "postgresql" protocol, providing an interactive shell for
PostgreSQL servers via the dbshell library and libpq. The asynchronous
libpq API is used so that teardown never blocks and a running statement
can be cancelled with Ctrl-C; server notices are rendered inline, and the
statement splitter understands PostgreSQL dollar-quoting and nested block
comments. The plugin is built automatically when libpq is present, and is
disabled with a warning otherwise.
Add the "mssql" protocol, providing an interactive shell for Microsoft
SQL Server via the dbshell library and the CT-Library interface of
FreeTDS. In addition to semicolons, a line containing only GO submits the
current batch; server messages including PRINT output are displayed, and a
running statement can be cancelled with Ctrl-C via a TDS attention. The
plugin is built automatically when FreeTDS is present, and is disabled
with a warning otherwise.
Add the "mongodb" protocol, providing an interactive shell for MongoDB
servers via the dbshell library and the MongoDB C Driver. Each statement
is a MongoDB database command written as an Extended JSON document;
replies are rendered as indented JSON, cursors are followed to completion
automatically, and the shell provides the meta-commands \use, \show
dbs, and \show collections. The plugin is built automatically when the
MongoDB C Driver is present, and is disabled with a warning otherwise.
Add the "oracle" protocol, providing an interactive shell for Oracle
Database servers via the dbshell library and the Oracle Call Interface
(OCI). Plain SQL statements end with a semicolon while PL/SQL blocks end
with a line containing only a slash, and a running statement can be
cancelled with Ctrl-C via OCIBreak. Because it depends on the proprietary
Oracle Instant Client, which cannot be redistributed, Oracle support is
never built by default; it is enabled by configuring with
--with-oracle=<path>.
…image.

Install the MariaDB Connector/C, libpq, FreeTDS, and MongoDB C Driver
development packages in the guacd image builder so that the MySQL,
PostgreSQL, SQL Server, and MongoDB protocols are compiled into the
official image; their runtime libraries are picked up automatically by the
existing dependency-detection step. Oracle is not included, as its client
library cannot be redistributed.

These database client libraries depend on the OpenSSL 3 development package
(openssl-dev), which cannot be installed alongside the openssl1.1-compat-dev
package the image previously used. This commit therefore switches the image
back to openssl-dev, effectively reverting the workaround from GUACAMOLE-1741:
that workaround existed because libvncclient could not authenticate VeNCrypt
VNC connections against OpenSSL 3, an incompatibility since fixed upstream in
LibVNCServer 0.9.15 (which the image already builds, as it auto-selects the
latest release).

The base image remains pinned to Alpine 3.18. Migrating to a newer base is a
separate, larger effort (newer Alpine renames packages such as webkit2gtk and
ships a CMake that rejects the older cmake_minimum_required used by some
bundled dependency sources) and is left to be handled under GUACAMOLE-1891.
@mike-jumper

Copy link
Copy Markdown
Contributor

Haven't read through this yet, but in principle I like the idea. It's broad and would be an interesting addition to the suite of supported protocols.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants