Files
BetterDesk/Dockerfile
T
UNITRONIX 0f161181f1 feat(installers): run services under unprivileged accounts by default
Privilege separation across all installers so the long-running services no longer run with full administrative rights:

betterdesk.sh: installer keeps root but systemd units now run as a dedicated unprivileged 'betterdesk' system account by default (auto-created via ensure_service_user). Added full systemd hardening for the Go server (NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, ReadWritePaths) and light hardening for the Node.js console. chown migrates existing root-owned data to the service account on update. Opt-out via --run-as-root / BETTERDESK_RUN_AS_ROOT=1; custom account via BETTERDESK_SERVICE_USER. Minimal mode covered too.

betterdesk.ps1: NSSM services now run under their per-service low-privilege virtual accounts (NT SERVICE\<service>) instead of LocalSystem, with scoped icacls grants on the install/data dirs (Set-ServiceLeastPrivilege helper). Applied to the Go server, Node.js console and minimal-mode service. Opt-out via -RunAsRoot / BETTERDESK_RUN_AS_ROOT=1.

Docker: verified already privilege-separated (supervisord drops both programs to user=betterdesk; multi-container images drop via su-exec).

Also bundles in-progress changes to the Go server API, Node.js console services and Docker compose/Dockerfiles.

This commit was made possible thanks to Insolve.
2026-05-31 00:35:28 +02:00

128 lines
4.2 KiB
Docker

# check=skip=SecretsUsedInArgOrEnv
# BetterDesk — Single Container Image
# =====================================
# Combines Go server (signal + relay + API) and Node.js web console
# into a single container using supervisord as process manager.
#
# Build: docker build -t betterdesk:local .
# Run: docker compose up -d
#
# Ports:
# 21121 - HTTP API (Go server: RustDesk client API + REST)
# 21115 - NAT type test
# 21116 - Signal TCP/UDP
# 21117 - Relay TCP
# 21118 - WebSocket Signal
# 21119 - WebSocket Relay
# 5000 - Web Console (Node.js admin panel)
# ============= Stage 1: Build Go server =============
FROM golang:1.25-alpine AS go-builder
# Retry apk in case of transient DNS failures (common on AlmaLinux/CentOS Docker)
RUN apk add --no-cache git || { sleep 2 && apk add --no-cache git; }
WORKDIR /src
COPY betterdesk-server/go.mod betterdesk-server/go.sum ./
RUN go mod download
COPY betterdesk-server/ .
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-tags "netgo osusergo" \
-o /betterdesk-server .
# ============= Stage 2: Build Node.js console =============
FROM node:20-alpine AS node-builder
WORKDIR /app
# Build dependencies for native modules (better-sqlite3, bcrypt)
# Note: sqlite-dev is NOT needed — better-sqlite3 bundles its own SQLite
RUN apk add --no-cache python3 make g++ || { sleep 2 && apk add --no-cache python3 make g++; }
COPY web-nodejs/package.json web-nodejs/package-lock.json* ./
RUN npm install --production
# ============= Stage 3: Production runtime =============
# Note: supervisord requires root to manage child processes with user= directive.
# Both betterdesk-server and betterdesk-console run as non-root 'betterdesk' user
# via supervisord configuration (user=betterdesk).
FROM node:20-alpine
LABEL maintainer="UNITRONIX"
LABEL description="BetterDesk — All-in-One (Go Server + Node.js Console)"
LABEL version="2.4.0"
# Install runtime packages (retry for transient DNS failures)
RUN apk add --no-cache \
ca-certificates \
curl \
sqlite \
tini \
supervisor \
&& mkdir -p /var/log/supervisor \
|| { sleep 2 && apk add --no-cache \
ca-certificates \
curl \
sqlite \
tini \
supervisor \
&& mkdir -p /var/log/supervisor; }
# Create betterdesk user and directories
RUN addgroup -g 10001 -S betterdesk && \
adduser -u 10001 -S -G betterdesk betterdesk && \
mkdir -p /opt/rustdesk /app/data /var/log/betterdesk && \
chown -R betterdesk:betterdesk /opt/rustdesk /app/data /var/log/betterdesk
# ---- Go server binary ----
COPY --from=go-builder /betterdesk-server /usr/local/bin/betterdesk-server
RUN chmod +x /usr/local/bin/betterdesk-server
# ---- Node.js console ----
WORKDIR /app
# IMPORTANT: Copy app code FIRST, then overlay compiled node_modules.
# This prevents local node_modules (if any) from overwriting the
# properly compiled Alpine/musl native modules from the builder.
COPY web-nodejs/ .
COPY --from=node-builder /app/node_modules ./node_modules/
# ---- Supervisord config ----
COPY docker/supervisord.conf /etc/supervisor/conf.d/betterdesk.conf
# ---- Entrypoint ----
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Environment variables (defaults)
ENV NODE_ENV=production
ENV PORT=5000
ENV SIGNAL_PORT=21116
ENV SIGNAL_RATE_LIMIT_PER_IP=20
ENV HOST=0.0.0.0
ENV API_HOST=0.0.0.0
ENV DATA_DIR=/app/data
ENV RUSTDESK_PATH=/opt/rustdesk
ENV DB_PATH=/opt/rustdesk/db_v2.sqlite3
ENV PUB_KEY_PATH=/opt/rustdesk/id_ed25519.pub
ENV API_KEY_PATH=/opt/rustdesk/.api_key
ENV SERVER_BACKEND=betterdesk
# API consolidated onto the Go server (21121); console proxies to it and does
# not run its own client API listener.
ENV API_ENABLED=false
ENV HBBS_API_URL=http://127.0.0.1:21121/api
ENV BETTERDESK_API_URL=http://127.0.0.1:21121/api
ENV DOCKER=true
ENV ENCRYPTED_ONLY=1\nENV RELAY_SERVERS=
# Expose all ports
EXPOSE 5000 21115 21116/tcp 21116/udp 21117 21118 21119 21121
# Health check: both Go server API and Node.js console must be healthy
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -sf http://localhost:21121/api/health && curl -sf http://localhost:5000/health || exit 1
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/entrypoint.sh"]