Install Ghidra 12.2 from the official GitHub release into /opt/ghidra, verified against the SHA-256 in the release notes, with Ubuntu's openjdk-25-jdk (Ghidra 12.2 requires Java 25). Version, build date and hash are build args. The entrypoint, renamed from launch-claude.sh to launch-apps.sh, now starts both apps and stops both, ending the container, when either is closed: Ghidra when its process exits (closing the project window), Claude Desktop when none of its windows is open or minimized. Claude is stopped via its main process so it can shut down its own helpers. CLAUDE.md now records that Claude Desktop and Ghidra share one container, at the user's request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
3.5 KiB
Docker
76 lines
3.5 KiB
Docker
FROM ubuntu:24.04
|
|
|
|
ARG DEBIAN_FRONTEND=noninteractive
|
|
# Match the host user so files written to mounted volumes keep the right owner.
|
|
ARG USER_NAME=re
|
|
ARG USER_UID=1000
|
|
ARG USER_GID=1000
|
|
|
|
# Fingerprint of Anthropic's apt signing key, from the official install docs:
|
|
# https://code.claude.com/docs/en/desktop-linux
|
|
ARG ANTHROPIC_APT_FPR=31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE
|
|
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ca-certificates curl gnupg \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Add Anthropic's apt repository and refuse to continue if the key is wrong.
|
|
RUN curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc \
|
|
https://downloads.claude.ai/claude-desktop/key.asc \
|
|
&& gpg --show-keys --with-colons /usr/share/keyrings/claude-desktop-archive-keyring.asc \
|
|
| grep -q "^fpr:::::::::${ANTHROPIC_APT_FPR}:" \
|
|
&& echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" \
|
|
> /etc/apt/sources.list.d/claude-desktop.list
|
|
|
|
# Recommends are skipped: they pull in QEMU/OVMF for Cowork, which needs KVM
|
|
# and isn't useful in this container. No keyring is installed: sign-in is not
|
|
# kept between runs. x11-utils provides the display check tools.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
claude-desktop \
|
|
x11-utils \
|
|
xauth \
|
|
fonts-dejavu-core \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Ghidra from the official GitHub release, checked against the SHA-256 in the
|
|
# release notes: https://github.com/NationalSecurityAgency/ghidra/releases
|
|
# Ghidra 12.2 needs JDK 25 (application.java.min=25). The full JDK, not the
|
|
# headless one, because Ghidra's GUI needs the AWT/X11 libraries.
|
|
ARG GHIDRA_VERSION=12.2
|
|
ARG GHIDRA_BUILD_DATE=20261016
|
|
ARG GHIDRA_SHA256=2c2cd776db3c1f6a097ba418922378ac3649cadd5e844a6f5f088ae628a7be67
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends openjdk-25-jdk unzip \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& curl -fsSLo /tmp/ghidra.zip \
|
|
"https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_${GHIDRA_VERSION}_build/ghidra_${GHIDRA_VERSION}_PUBLIC_${GHIDRA_BUILD_DATE}.zip" \
|
|
&& echo "${GHIDRA_SHA256} /tmp/ghidra.zip" | sha256sum -c - \
|
|
&& unzip -q /tmp/ghidra.zip -d /opt \
|
|
&& rm /tmp/ghidra.zip \
|
|
&& ln -s "/opt/ghidra_${GHIDRA_VERSION}_PUBLIC" /opt/ghidra \
|
|
&& ln -s /opt/ghidra/ghidraRun /usr/local/bin/ghidra
|
|
ENV GHIDRA_INSTALL_DIR=/opt/ghidra
|
|
|
|
# Ubuntu 24.04 ships a default "ubuntu" user on UID 1000; replace it.
|
|
RUN if id ubuntu >/dev/null 2>&1; then userdel -r ubuntu; fi \
|
|
&& groupadd -g "${USER_GID}" "${USER_NAME}" \
|
|
&& useradd -m -u "${USER_UID}" -g "${USER_GID}" -s /bin/bash "${USER_NAME}" \
|
|
&& install -d -o "${USER_UID}" -g "${USER_GID}" /workspace \
|
|
&& ln -s /workspace "/home/${USER_NAME}/workspace"
|
|
|
|
COPY --chmod=755 launch-apps.sh /usr/local/bin/launch-apps
|
|
|
|
USER ${USER_NAME}
|
|
WORKDIR /home/${USER_NAME}
|
|
|
|
# launch-apps starts Claude Desktop and Ghidra and stops both (and so the
|
|
# container) when either one is closed.
|
|
# No keyring: tell Electron not to look for one. Session data lives only in
|
|
# the container's filesystem, which is discarded on exit (run with --rm), so
|
|
# the user signs in on every start.
|
|
# --no-sandbox: Docker's default seccomp profile blocks the namespaces
|
|
# Chromium's sandbox needs, so the zygote dies on start. The container's own
|
|
# seccomp/namespaces stay in place as the boundary to the host instead.
|
|
CMD ["launch-apps", "--password-store=basic", "--no-sandbox"]
|