Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
249 lines (215 loc) · 9.56 KB
/
Copy pathDockerfile
File metadata and controls
249 lines (215 loc) · 9.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# =============================================================================
# Multi-stage Dockerfile for rustguac
#
# Stages:
# 1. guacd-builder — compile guacd from guacamole-server source
# 2. rust-builder — compile rustguac binary
# 3. runtime — minimal image with both binaries + runtime deps
#
# Build:
# docker build -t rustguac .
#
# Run:
# docker run -d -p 8089:8089 rustguac
#
# Run with VDI (Docker desktop containers):
# docker run -d -p 8089:8089 \
# -v /var/run/docker.sock:/var/run/docker.sock \
# --group-add $(getent group docker | cut -d: -f3) \
# rustguac
#
# The image runs both guacd and rustguac under a simple entrypoint script.
# =============================================================================
# ---------------------------------------------------------------------------
# Stage 1: Build guacd from source
# ---------------------------------------------------------------------------
FROM debian:trixie-slim AS guacd-builder
RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf automake libtool pkg-config make gcc g++ git ca-certificates \
libcairo2-dev libjpeg-dev libpng-dev libwebp-dev \
libssh2-1-dev libssl-dev libvncserver-dev \
libpango1.0-dev libpulse-dev \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
libcunit1-dev libtelnet-dev libwebsockets-dev \
uuid-dev freerdp3-dev libspice-client-glib-2.0-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Pin to known-good commit to avoid upstream -Werror breakage
RUN git clone https://github.com/apache/guacamole-server.git \
&& cd guacamole-server && git checkout 6719b20d
# Apply patches for FreeRDP 3.x / Debian 13 compatibility
COPY patches/ /build/patches/
WORKDIR /build/guacamole-server
RUN for patch in /build/patches/*.patch; do \
[ -f "$patch" ] || continue; \
echo "Applying patch: $(basename "$patch")"; \
git apply "$patch"; \
done
RUN autoreconf -fi
WORKDIR /build/guacd-build
RUN /build/guacamole-server/configure \
--prefix=/opt/rustguac \
--with-ssh \
--with-vnc \
--with-rdp \
--with-spice \
--without-telnet \
--without-kubernetes \
--disable-guacenc \
--disable-guaclog \
--disable-guacclip \
--disable-static \
&& make -j"$(nproc)" \
&& make install \
&& mkdir -p /opt/rustguac/lib/freerdp3 \
&& cp /opt/rustguac/lib/libguac*.so* /opt/rustguac/lib/freerdp3/ \
&& cp /usr/lib/x86_64-linux-gnu/freerdp3/libguac*.so* /opt/rustguac/lib/freerdp3/ 2>/dev/null || true
# ---------------------------------------------------------------------------
# Stage 2: Build rustguac
# ---------------------------------------------------------------------------
FROM rust:1-bookworm AS rust-builder
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
COPY build.rs ./
COPY src/ src/
COPY docs/ docs/
COPY static/ static/
RUN cargo build --release
# ---------------------------------------------------------------------------
# Stage 3: Runtime image
# ---------------------------------------------------------------------------
FROM debian:trixie-slim AS runtime
# Runtime libraries for guacd
RUN apt-get update && apt-get install -y --no-install-recommends \
libcairo2 libjpeg62-turbo libpng16-16t64 libwebp7 \
libssh2-1 libssl3t64 libvncclient1 \
libpango-1.0-0 libpulse0 \
libspice-client-glib-2.0-8 \
libavcodec61 libavformat61 libavutil59 libswscale8 \
libtelnet2 libwebsockets19t64 \
libfreerdp3-3 libfreerdp-client3-3 libwinpr3-3 \
# Xvnc + Chromium for web browser sessions
tigervnc-standalone-server \
chromium chromium-sandbox \
x11-utils \
# Minimal runtime utilities
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install guacd
COPY --from=guacd-builder /opt/rustguac/sbin/ /opt/rustguac/sbin/
COPY --from=guacd-builder /opt/rustguac/lib/ /opt/rustguac/lib/
# Install rustguac binary
COPY --from=rust-builder /build/target/release/rustguac /opt/rustguac/bin/rustguac
# Install static web assets
COPY static/ /opt/rustguac/static/
# Library path for guacd
RUN echo "/opt/rustguac/lib" > /etc/ld.so.conf.d/rustguac.conf && ldconfig
# FreeRDP plugin setup: guacd loads "guac-common-svc" by name, which FreeRDP
# resolves to "guac-common-svc.so" in its plugin path. The build installs it as
# "libguac-common-svc-client.so", so we create a symlink with the expected name.
# We also ensure the system FreeRDP plugin dir exists and contains the plugins.
RUN mkdir -p /usr/lib/x86_64-linux-gnu/freerdp3 && \
if [ -d /opt/rustguac/lib/freerdp3 ]; then \
cp /opt/rustguac/lib/freerdp3/*.so* /usr/lib/x86_64-linux-gnu/freerdp3/ 2>/dev/null; \
ln -sf libguac-common-svc-client.so /opt/rustguac/lib/freerdp3/guac-common-svc.so; \
ln -sf libguac-common-svc-client.so /usr/lib/x86_64-linux-gnu/freerdp3/guac-common-svc.so; \
echo "FreeRDP plugins installed:"; \
ls /usr/lib/x86_64-linux-gnu/freerdp3/guac* /opt/rustguac/lib/freerdp3/guac-common-svc.so 2>/dev/null; \
fi
# Create writable runtime directories
RUN mkdir -p /opt/rustguac/data /opt/rustguac/recordings /opt/rustguac/tls \
/opt/rustguac/certs /opt/rustguac/drives /opt/rustguac/scripts \
/opt/rustguac/vdi-homes
# Chromium policy: web session hardening.
# DeveloperToolsAvailability=0: CDP needed for login scripts. Users can't reach DevTools
# through the UI anyway — chrome://* is in URLBlocklist.
RUN mkdir -p /etc/chromium/policies/managed && \
echo '{"AllowFileSelectionDialogs": false, "PasswordManagerEnabled": true, "ImportSavedPasswords": false, "DeveloperToolsAvailability": 0, "DownloadRestrictions": 3, "PrintingEnabled": false, "EditBookmarksEnabled": false, "BrowserSignin": 0, "SyncDisabled": true, "ExtensionInstallBlocklist": ["*"], "URLBlocklist": ["file://*", "chrome://*", "chrome-extension://*", "view-source:*", "javascript:*"], "URLAllowlist": ["chrome://policy"]}' \
> /etc/chromium/policies/managed/rustguac.json
# Create non-root user with a real home directory (Chromium crashpad needs it)
RUN groupadd -r rustguac && useradd -r -g rustguac -m -d /home/rustguac -s /bin/sh rustguac
# Generate self-signed cert for guacd TLS (internal loopback encryption)
RUN /opt/rustguac/bin/rustguac generate-cert --hostname localhost --out-dir /opt/rustguac/tls
# Default config template (copied to config.toml on first run if not mounted)
RUN cat > /opt/rustguac/config.toml.default <<'EOF'
listen_addr = "0.0.0.0:8089"
guacd_addr = "127.0.0.1:4822"
recording_path = "/opt/rustguac/recordings"
static_path = "/opt/rustguac/static"
db_path = "/opt/rustguac/data/rustguac.db"
session_pending_timeout_secs = 60
xvnc_path = "Xvnc"
chromium_path = "chromium"
display_range_start = 100
display_range_end = 199
[tls]
cert_path = "/opt/rustguac/tls/cert.pem"
key_path = "/opt/rustguac/tls/key.pem"
guacd_cert_path = "/opt/rustguac/tls/cert.pem"
# VDI Docker desktop containers (uncomment to enable)
# Requires: -v /var/run/docker.sock:/var/run/docker.sock
# [vdi]
# enabled = true
# idle_timeout_mins = 60
# home_base = "/opt/rustguac/vdi-homes"
EOF
# Set ownership so the non-root user can write to runtime dirs.
# The top-level dir is chowned (not recursive) so loaders can create config.toml;
# subdirs are chowned recursively for data, certs, etc.
RUN chown rustguac:rustguac /opt/rustguac && \
chown -R rustguac:rustguac /opt/rustguac/data /opt/rustguac/recordings \
/opt/rustguac/tls /opt/rustguac/certs /opt/rustguac/drives \
/opt/rustguac/scripts /opt/rustguac/vdi-homes /opt/rustguac/config.toml.default
# Entrypoint script: starts guacd in background, then rustguac in foreground
RUN cat > /opt/rustguac/entrypoint.sh <<'SCRIPT'
#!/bin/sh
set -e
# Copy default config on first run (if no config file is mounted/present)
CONFIG_PATH="/opt/rustguac/config.toml"
if [ ! -f "$CONFIG_PATH" ]; then
echo "No config.toml found — copying default configuration."
cp /opt/rustguac/config.toml.default "$CONFIG_PATH"
fi
# Create an admin API key on first run. rustguac decides what "first run"
# means: it opens the database named by db_path in the config and creates
# the admin only if there is none yet. Checking for a database file at a fixed
# path here broke any config that moved db_path: every restart then tried to
# create the admin again and failed on the existing one (#240).
ADMIN_OUT=$(/opt/rustguac/bin/rustguac --config "$CONFIG_PATH" add-admin --name docker-admin --if-none)
echo "$ADMIN_OUT"
case "$ADMIN_OUT" in
*"API Key:"*)
echo ""
echo "==> SAVE THE API KEY ABOVE — it is only shown once! <=="
echo ""
;;
esac
# Start guacd in background
echo "Starting guacd..."
LD_LIBRARY_PATH=/opt/rustguac/lib FREERDP_ADDIN_PATH=/opt/rustguac/lib/freerdp3 \
/opt/rustguac/sbin/guacd \
-b 127.0.0.1 -l 4822 -L "${GUACD_LOG_LEVEL:-info}" -f \
-C /opt/rustguac/tls/cert.pem -K /opt/rustguac/tls/key.pem &
GUACD_PID=$!
# Wait briefly to confirm guacd started
sleep 0.5
if ! kill -0 "$GUACD_PID" 2>/dev/null; then
echo "ERROR: guacd failed to start"
exit 1
fi
echo "guacd started (pid=$GUACD_PID)"
# Trap signals to shut down both processes
trap 'kill $GUACD_PID 2>/dev/null; wait; exit 0' TERM INT
# Run rustguac in foreground
echo "Starting rustguac..."
exec /opt/rustguac/bin/rustguac --config "$CONFIG_PATH" serve
SCRIPT
RUN chmod +x /opt/rustguac/entrypoint.sh
WORKDIR /opt/rustguac
EXPOSE 8089
VOLUME ["/opt/rustguac/data", "/opt/rustguac/recordings", "/opt/rustguac/drives", "/opt/rustguac/vdi-homes"]
ENV RUST_LOG=info
ENV GUACD_LOG_LEVEL=info
ENV HOME=/home/rustguac
USER rustguac
ENTRYPOINT ["/opt/rustguac/entrypoint.sh"]