latest changes
This commit is contained in:
@@ -174,15 +174,18 @@ export async function rdpWebsocket(fastify: FastifyInstance) {
|
|||||||
dpi: '96',
|
dpi: '96',
|
||||||
'color-depth': '32',
|
'color-depth': '32',
|
||||||
'ignore-cert': 'true',
|
'ignore-cert': 'true',
|
||||||
security: 'nla', // FreeRDP 3.x dropped 'any'; NLA is what Windows 11 uses by default
|
// 'nla' = Network Level Authentication (CredSSP). Required for Windows 11 22H2+ which
|
||||||
|
// mandates CredSSP v6. FreeRDP 3.x handles CredSSP v6 properly.
|
||||||
|
security: 'nla',
|
||||||
'disable-auth': 'false',
|
'disable-auth': 'false',
|
||||||
'enable-drive': 'false',
|
'enable-drive': 'false',
|
||||||
'create-drive-path': 'false',
|
'create-drive-path': 'false',
|
||||||
'enable-printing': 'false',
|
'enable-printing': 'false',
|
||||||
|
'disable-audio': 'true',
|
||||||
'disable-glyph-caching': 'false',
|
'disable-glyph-caching': 'false',
|
||||||
// disable-gfx removed: FreeRDP 3.x handles GFX pipeline correctly; disabling it caused crashes
|
'disable-gfx': 'true',
|
||||||
'cert-tofu': 'true',
|
'cert-tofu': 'true',
|
||||||
'resize-method': 'display-update',
|
'resize-method': 'reconnect',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Acknowledge whichever VERSION_x_y_z guacd advertises.
|
// Acknowledge whichever VERSION_x_y_z guacd advertises.
|
||||||
@@ -194,24 +197,33 @@ export async function rdpWebsocket(fastify: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Connect with values guacd requested
|
// 3. Send size instruction so guacd populates client->info.optimal_width/height/resolution.
|
||||||
const argValues = argNames.map((name) => rdpParams[name] ?? '');
|
// Without this, guacd logs "0x0 at 0 DPI" and FreeRDP may use a zero-size display.
|
||||||
guacd.write(buildInstruction('connect', ...argValues));
|
guacd.write(buildInstruction('size', rdpParams.width, rdpParams.height, rdpParams.dpi));
|
||||||
|
|
||||||
// 4. Read ready instruction
|
// 4. Connect with values guacd requested
|
||||||
|
const argValues = argNames.map((name) => rdpParams[name] ?? '');
|
||||||
|
const connectInstruction = buildInstruction('connect', ...argValues);
|
||||||
|
fastify.log.info(
|
||||||
|
{ argNames, argValues, connectInstruction: connectInstruction.substring(0, 500) },
|
||||||
|
'RDP: sending connect instruction'
|
||||||
|
);
|
||||||
|
guacd.write(connectInstruction);
|
||||||
|
|
||||||
|
// 5. Read ready instruction
|
||||||
const readyInstruction = await readInstruction(guacd, tcpBuf);
|
const readyInstruction = await readInstruction(guacd, tcpBuf);
|
||||||
fastify.log.info({ readyInstruction }, 'RDP: guacd ready instruction received');
|
fastify.log.info({ readyInstruction }, 'RDP: guacd ready instruction received');
|
||||||
if (readyInstruction[0] !== 'ready') {
|
if (readyInstruction[0] !== 'ready') {
|
||||||
throw new Error(`guacd handshake failed: expected 'ready', got '${readyInstruction[0]}'`);
|
throw new Error(`guacd handshake failed: expected 'ready', got '${readyInstruction[0]}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Send the tunnel UUID as a Guacamole internal instruction.
|
// 6. Send the tunnel UUID as a Guacamole internal instruction.
|
||||||
// WebSocketTunnel (1.5.0+) expects opcode "" (empty string) with the
|
// WebSocketTunnel (1.5.0+) expects opcode "" (empty string) with the
|
||||||
// UUID as the single argument: "0.,36.<uuid>;"
|
// UUID as the single argument: "0.,36.<uuid>;"
|
||||||
const guacdUUID = readyInstruction[1] ?? randomUUID();
|
const guacdUUID = readyInstruction[1] ?? randomUUID();
|
||||||
socket.send(buildInstruction('', guacdUUID));
|
socket.send(buildInstruction('', guacdUUID));
|
||||||
|
|
||||||
// 6. Flush any buffered bytes that arrived after 'ready'
|
// 7. Flush any buffered bytes that arrived after 'ready'
|
||||||
if (tcpBuf.value.length > 0) {
|
if (tcpBuf.value.length > 0) {
|
||||||
fastify.log.info({ flushed: tcpBuf.value.substring(0, 300) }, 'RDP: flushing buffered data after ready');
|
fastify.log.info({ flushed: tcpBuf.value.substring(0, 300) }, 'RDP: flushing buffered data after ready');
|
||||||
if (socket.readyState === WebSocket.OPEN) socket.send(tcpBuf.value);
|
if (socket.readyState === WebSocket.OPEN) socket.send(tcpBuf.value);
|
||||||
@@ -230,7 +242,8 @@ export async function rdpWebsocket(fastify: FastifyInstance) {
|
|||||||
// --- Proxy mode ---
|
// --- Proxy mode ---
|
||||||
guacd.on('data', (data: Buffer) => {
|
guacd.on('data', (data: Buffer) => {
|
||||||
const text = data.toString('utf8');
|
const text = data.toString('utf8');
|
||||||
fastify.log.debug({ guacdData: text.substring(0, 300) }, 'RDP: data from guacd');
|
// Log all data during proxy phase at info level so we can see error instructions
|
||||||
|
fastify.log.info({ guacdData: text.substring(0, 500) }, 'RDP: data from guacd');
|
||||||
if (socket.readyState === WebSocket.OPEN) socket.send(text);
|
if (socket.readyState === WebSocket.OPEN) socket.send(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
GUACD_LOG_LEVEL: debug
|
GUACD_LOG_LEVEL: debug
|
||||||
|
WLOG_LEVEL: "0"
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,24 @@
|
|||||||
# Why: The official guacamole/guacd image uses FreeRDP 2.x, which crashes
|
# Why: The official guacamole/guacd image uses FreeRDP 2.x, which crashes
|
||||||
# silently when connecting to Windows 11 22H2+ hosts due to NLA/CredSSP
|
# silently when connecting to Windows 11 22H2+ hosts due to NLA/CredSSP
|
||||||
# cipher-suite changes introduced by Microsoft. FreeRDP 3.x fixes this.
|
# cipher-suite changes introduced by Microsoft. FreeRDP 3.x fixes this.
|
||||||
# guacamole-server 1.6.0 (June 2025) has explicit FreeRDP 3.x support.
|
|
||||||
# Ubuntu 24.04 ships freerdp3-dev (FreeRDP 3.5.1+) in its universe repo.
|
# Ubuntu 24.04 ships freerdp3-dev (FreeRDP 3.5.1+) in its universe repo.
|
||||||
#
|
#
|
||||||
# Note: FreeRDP 3.x support in guacamole is currently marked experimental
|
# Source: built from git main branch (post-1.6.0) to pick up FreeRDP 3.x
|
||||||
# for some features (RemoteApp), but basic RDP/NLA works correctly.
|
# crash fixes that landed after the June 2025 release tarball.
|
||||||
|
#
|
||||||
|
# Build notes:
|
||||||
|
# - CPPFLAGS=-Wno-error=deprecated-declarations suppresses build-time warnings from
|
||||||
|
# FreeRDP 3.x headers marking some fields/functions as deprecated; these are
|
||||||
|
# warnings only and do NOT affect runtime behavior.
|
||||||
|
# - CPPFLAGS=-DHAVE_FREERDP_VERIFYCERTIFICATEEX=1 fixes a macro name mismatch bug
|
||||||
|
# in guacamole-server 1.6.0: configure.ac's AC_CHECK_MEMBERS generates the macro
|
||||||
|
# HAVE_STRUCT_FREERDP_VERIFYCERTIFICATEEX (with STRUCT_ infix), but rdp.c checks
|
||||||
|
# HAVE_FREERDP_VERIFYCERTIFICATEEX (without STRUCT_ infix), so the check is always
|
||||||
|
# false. This means guacamole never registers the VerifyCertificateEx callback, which
|
||||||
|
# FreeRDP 3.x calls during TLS certificate verification. The NULL callback causes a
|
||||||
|
# silent connection drop ~430ms after keymap loading. Defining the macro manually
|
||||||
|
# forces rdp.c to register rdp_inst->VerifyCertificateEx (correct FreeRDP 3.x path)
|
||||||
|
# instead of the legacy rdp_inst->VerifyCertificate (padding in FreeRDP 3.x).
|
||||||
|
|
||||||
FROM ubuntu:24.04
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
@@ -22,6 +35,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
build-essential \
|
build-essential \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
|
gdb \
|
||||||
|
git \
|
||||||
freerdp3-dev \
|
freerdp3-dev \
|
||||||
libcairo2-dev \
|
libcairo2-dev \
|
||||||
libjpeg-turbo8-dev \
|
libjpeg-turbo8-dev \
|
||||||
@@ -39,16 +54,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
pkgconf \
|
pkgconf \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ARG GUACAMOLE_VERSION=1.6.0
|
|
||||||
|
|
||||||
RUN FREERDP_PLUGIN_DIR=$(pkg-config --variable=libdir freerdp3 2>/dev/null)/freerdp3 \
|
RUN FREERDP_PLUGIN_DIR=$(pkg-config --variable=libdir freerdp3 2>/dev/null)/freerdp3 \
|
||||||
&& echo "Building guacamole-server ${GUACAMOLE_VERSION} with FreeRDP plugin dir: ${FREERDP_PLUGIN_DIR}" \
|
&& echo "Building guacamole-server (git main) with FreeRDP plugin dir: ${FREERDP_PLUGIN_DIR}" \
|
||||||
&& curl -fsSL \
|
&& git clone --depth=1 https://github.com/apache/guacamole-server.git \
|
||||||
"https://downloads.apache.org/guacamole/${GUACAMOLE_VERSION}/source/guacamole-server-${GUACAMOLE_VERSION}.tar.gz" \
|
&& cd guacamole-server \
|
||||||
| tar -xzf - \
|
|
||||||
&& cd "guacamole-server-${GUACAMOLE_VERSION}" \
|
|
||||||
&& autoreconf -fi \
|
&& autoreconf -fi \
|
||||||
&& CPPFLAGS="-Wno-error=deprecated-declarations" \
|
&& CPPFLAGS="-Wno-error=deprecated-declarations -DHAVE_FREERDP_VERIFYCERTIFICATEEX=1" \
|
||||||
|
CFLAGS="-g -O0" \
|
||||||
./configure \
|
./configure \
|
||||||
--prefix=/usr \
|
--prefix=/usr \
|
||||||
--sysconfdir=/etc \
|
--sysconfdir=/etc \
|
||||||
@@ -56,11 +68,11 @@ RUN FREERDP_PLUGIN_DIR=$(pkg-config --variable=libdir freerdp3 2>/dev/null)/free
|
|||||||
&& make -j"$(nproc)" \
|
&& make -j"$(nproc)" \
|
||||||
&& make install \
|
&& make install \
|
||||||
&& ldconfig \
|
&& ldconfig \
|
||||||
&& cd / && rm -rf "guacamole-server-${GUACAMOLE_VERSION}"
|
&& cd / && rm -rf guacamole-server
|
||||||
|
|
||||||
# guacd log level is passed via -L flag; exposed as env var for docker-compose
|
# guacd log level is passed via -L flag; exposed as env var for docker-compose
|
||||||
ENV GUACD_LOG_LEVEL=info
|
ENV GUACD_LOG_LEVEL=info
|
||||||
|
|
||||||
EXPOSE 4822
|
EXPOSE 4822
|
||||||
|
|
||||||
CMD sh -c "exec /usr/sbin/guacd -b 0.0.0.0 -f -L \"${GUACD_LOG_LEVEL}\""
|
CMD sh -c "ulimit -c unlimited && exec /usr/sbin/guacd -b 0.0.0.0 -f -L \"${GUACD_LOG_LEVEL}\""
|
||||||
|
|||||||
Reference in New Issue
Block a user