A bare <PACKAGE NAME="Developer" /> only adds the package's RELEASED plugins, so half of them (the STABLE ones) were missing and Configure showed Developer unticked. The project window wasn't configured at all. - CodeBrowser: insert ghidra/codebrowser-developer.xml, which lists the STABLE plugins explicitly, into the default CodeBrowser.tool in Public_Release.jar. - Project window: it has no default config to patch, so ship ghidra/FrontEndTool.xml in the user settings with its three Developer plugins. - Pre-creating the settings dir makes Ghidra show its user agreement; ghidra/preferences sets USER_AGREEMENT=ACCEPT, the value Ghidra writes by itself on a fresh start. Verified by closing Ghidra cleanly: it rewrites FrontEndTool.xml from the loaded plugins and kept all three Developer plugins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
106 lines
5.4 KiB
Docker
106 lines
5.4 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
|
|
|
|
# Enable the "Developer" plugin package (File > Configure > Developer) in the
|
|
# CodeBrowser. Ghidra copies its default tools into the user's settings on
|
|
# first start, and settings are reset on every container start, so the
|
|
# default CodeBrowser.tool inside the jar is patched instead of user config.
|
|
# A bare <PACKAGE NAME="Developer" /> only adds the package's RELEASED
|
|
# plugins; the snippet also lists the STABLE ones. The build fails if the
|
|
# snippet could not be inserted.
|
|
COPY ghidra/codebrowser-developer.xml /tmp/codebrowser-developer.xml
|
|
RUN set -e; cd /tmp \
|
|
&& jar_file=/opt/ghidra/Ghidra/Configurations/Public_Release/lib/Public_Release.jar \
|
|
&& unzip -q "$jar_file" defaultTools/CodeBrowser.tool \
|
|
&& awk 'FNR==NR { snip = snip $0 "\n"; next } !done && /<ROOT_NODE/ { printf "%s", snip; done = 1 } { print }' \
|
|
/tmp/codebrowser-developer.xml defaultTools/CodeBrowser.tool > /tmp/CodeBrowser.tool \
|
|
&& mv /tmp/CodeBrowser.tool defaultTools/CodeBrowser.tool \
|
|
&& grep -q 'FidDebugPlugin' defaultTools/CodeBrowser.tool \
|
|
&& jar uf "$jar_file" defaultTools/CodeBrowser.tool \
|
|
&& rm -r /tmp/defaultTools /tmp/codebrowser-developer.xml
|
|
|
|
# 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" \
|
|
&& install -d -o "${USER_UID}" -g "${USER_GID}" \
|
|
"/home/${USER_NAME}/.config" \
|
|
"/home/${USER_NAME}/.config/ghidra" \
|
|
"/home/${USER_NAME}/.config/ghidra/ghidra_${GHIDRA_VERSION}_PUBLIC"
|
|
|
|
# Ghidra's project window has no default config to patch; it builds one in
|
|
# code and saves it on exit. So a starting FrontEndTool.xml with the Developer
|
|
# plugins is put in the user settings. Pre-creating the settings dir makes
|
|
# Ghidra ask for its user agreement, which it otherwise accepts by itself on
|
|
# a fresh start, so preferences carries the same USER_AGREEMENT=ACCEPT.
|
|
COPY --chown=${USER_UID}:${USER_GID} ghidra/FrontEndTool.xml ghidra/preferences \
|
|
/home/${USER_NAME}/.config/ghidra/ghidra_${GHIDRA_VERSION}_PUBLIC/
|
|
|
|
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"]
|