binwalk: new binwalk-build stage adapted from binwalk's own Dockerfile to Ubuntu 24.04, with every source pinned: binwalk master (3.1.1), Rust 1.99.1, dumpifs/lzfse/dmg2img commits, and the Python extractors (jefferson, ubi-reader, uefi_firmware, vmlinux-to-elf, lz4, zstandard) in /opt/binwalk-py. The final image adds upstream's apt extractors, sasquatch (SHA-256 pinned), and Kaleido via KALEIDO_PATH so that binwalk -E -p works (binwalk's build otherwise leaves it in /root of the build stage and -p panics). BINWALK_RM_EXTRACTION_SYMLINK=1 avoids dangling links to container paths. esptool 5.6.0 in /opt/esptool. Entry points of both venvs are linked into /usr/local/bin without shadowing the system python3. ESP32 flash loader: dynacylabs/ghidra-esp32-flash-loader, built in an esp32-loader-build stage against Ghidra 12.1.4 (no binaries are published, and the older forks don't compile against Ghidra 12), pinned to a master commit with its svd and ghidra_scripts submodules, Gradle checked against gradle.org's SHA-256. Installed into Ghidra/Extensions/esp32. Ghidra itself is now downloaded once in a ghidra-dist stage and copied into the final image and the build stage. Verified: binwalk extracted squashfs/gzip/cpio/7z from a test image and rendered an entropy PNG; the loader imported a test ESP32 app image and a flash dump with bootloader at 0x0 (partition table, bootloader and factory app, SVD peripherals, ROM symbols). Original-ESP32 dumps with the bootloader at 0x1000 are not supported; documented in CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
317 lines
17 KiB
Docker
317 lines
17 KiB
Docker
# Ghidra from the official GitHub release, checked against the SHA-256 in the
|
|
# release notes: https://github.com/NationalSecurityAgency/ghidra/releases
|
|
# 12.1.4 because that is the version the GhidraMCP release below is built
|
|
# for; Ghidra only loads extensions built for its exact version.
|
|
ARG GHIDRA_VERSION=12.1.4
|
|
ARG GHIDRA_BUILD_DATE=20260921
|
|
ARG GHIDRA_SHA256=ddac49f903da9d5bac833e5cc79395098b9c33cfd3279be5f31bd00387d2d4db
|
|
|
|
|
|
# --- Stage: Ghidra distribution ----------------------------------------------
|
|
# Downloaded once, used by the final image and to build extensions against.
|
|
FROM ubuntu:24.04 AS ghidra-dist
|
|
ARG GHIDRA_VERSION
|
|
ARG GHIDRA_BUILD_DATE
|
|
ARG GHIDRA_SHA256
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ca-certificates curl 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
|
|
|
|
|
|
# --- Build stage: ESP32 Flash Loader for Ghidra --------------------------------
|
|
# https://github.com/dynacylabs/ghidra-esp32-flash-loader: loads ESP32 flash
|
|
# dumps / app images (partition table, segments at their addresses, chip
|
|
# ROM, peripherals from Espressif's SVD files). Its tags ship no binaries,
|
|
# so it's built against this exact Ghidra. Pinned to a master commit (Ghidra
|
|
# 12 API, newer than the v12.0 tag) and its submodules' commits. Gradle is
|
|
# downloaded directly, checked against the SHA-256 gradle.org publishes,
|
|
# because Ghidra's own wrapper doesn't verify its download.
|
|
FROM ubuntu:24.04 AS esp32-loader-build
|
|
ARG DEBIAN_FRONTEND=noninteractive
|
|
ARG GHIDRA_VERSION
|
|
ARG ESP32_LOADER_COMMIT=59d88034d07010fb3a3601053554baf02e7be2e4
|
|
ARG ESP32_SVD_COMMIT=be20aa12560889d6125d144cdb48cf615ac17628
|
|
ARG GHIDRA_SVD_LOADER_COMMIT=8fb5601c3444111159cf1e056cbe9d8457985237
|
|
ARG GRADLE_VERSION=9.7.1
|
|
ARG GRADLE_SHA256=acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ca-certificates curl git unzip openjdk-21-jdk-headless \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
COPY --from=ghidra-dist /opt/ghidra_${GHIDRA_VERSION}_PUBLIC /opt/ghidra
|
|
RUN set -e \
|
|
&& fetch() { git init -q "$2"; git -C "$2" fetch -q --depth 1 "$1" "$3"; git -C "$2" checkout -q FETCH_HEAD; } \
|
|
&& fetch https://github.com/dynacylabs/ghidra-esp32-flash-loader.git /src/esp32 "${ESP32_LOADER_COMMIT}" \
|
|
&& rmdir /src/esp32/data/svd /src/esp32/ghidra_scripts \
|
|
&& fetch https://github.com/espressif/svd.git /src/esp32/data/svd "${ESP32_SVD_COMMIT}" \
|
|
&& fetch https://github.com/dynacylabs/ghidra-svd-loader.git /src/esp32/ghidra_scripts "${GHIDRA_SVD_LOADER_COMMIT}" \
|
|
&& curl -fsSLo /tmp/gradle.zip "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
|
|
&& echo "${GRADLE_SHA256} /tmp/gradle.zip" | sha256sum -c - \
|
|
&& unzip -q /tmp/gradle.zip -d /opt \
|
|
&& cd /src/esp32 \
|
|
&& GHIDRA_INSTALL_DIR=/opt/ghidra "/opt/gradle-${GRADLE_VERSION}/bin/gradle" --no-daemon -q buildExtension \
|
|
&& unzip -q dist/ghidra_${GHIDRA_VERSION}_PUBLIC_*_esp32.zip -d /out \
|
|
&& grep -qx "version=${GHIDRA_VERSION}" /out/esp32/extension.properties
|
|
|
|
|
|
# --- Build stage: binwalk and its helpers -------------------------------------
|
|
# Based on binwalk's own Dockerfile (https://github.com/ReFirmLabs/binwalk,
|
|
# Dockerfile at the pinned commit), adapted to Ubuntu 24.04 and with every
|
|
# source pinned. Only the results are copied into the final image, so
|
|
# compilers and the Rust toolchain stay out of it.
|
|
FROM ubuntu:24.04 AS binwalk-build
|
|
|
|
ARG DEBIAN_FRONTEND=noninteractive
|
|
# binwalk: last tag (v3.1.0) is from 2024; master is 3.1.1 with later fixes.
|
|
ARG BINWALK_COMMIT=26713972e3f9f52dc37d4a421c4554b0bd9e82ef
|
|
ARG RUST_VERSION=1.99.1
|
|
ARG DUMPIFS_COMMIT=b7bac90e8312eca2796f2003a52791899eb8dcd9
|
|
ARG LZFSE_COMMIT=e634ca58b4821d9f3d560cdc6df5dec02ffc93fd
|
|
ARG DMG2IMG_COMMIT=a3e413489ccdd05431401357bf21690536425012
|
|
ARG VMLINUX_TO_ELF_COMMIT=36da74ded9cd486f294c8852e0dcab9ef6a48c6b
|
|
ARG ESPTOOL_VERSION=5.6.0
|
|
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
ca-certificates curl git build-essential clang pkg-config \
|
|
zlib1g-dev liblzma-dev liblzo2-dev libucl-dev liblz4-dev libbz2-dev \
|
|
libssl-dev libsrecord-dev libfontconfig1-dev \
|
|
python3 python3-venv python3-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Shallow-fetch one exact commit of a git repository.
|
|
RUN printf '%s\n' '#!/bin/sh' 'set -e' \
|
|
'git init -q "$2"' \
|
|
'git -C "$2" fetch -q --depth 1 "$1" "$3"' \
|
|
'git -C "$2" checkout -q FETCH_HEAD' > /usr/local/bin/fetch-commit \
|
|
&& chmod +x /usr/local/bin/fetch-commit
|
|
|
|
# binwalk itself, with a pinned Rust toolchain and the locked crate versions.
|
|
RUN fetch-commit https://github.com/ReFirmLabs/binwalk.git /src/binwalk "${BINWALK_COMMIT}" \
|
|
&& curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_VERSION}" \
|
|
&& . /root/.cargo/env \
|
|
&& cargo build --release --locked --manifest-path /src/binwalk/Cargo.toml \
|
|
&& install -D -m 755 /src/binwalk/target/release/binwalk /out/bin/binwalk
|
|
|
|
# Extractors binwalk calls that Ubuntu doesn't package (upstream src.sh).
|
|
# dumpifs ships a prebuilt binary in its repo; it's removed and rebuilt.
|
|
RUN fetch-commit https://github.com/askac/dumpifs.git /src/dumpifs "${DUMPIFS_COMMIT}" \
|
|
&& rm /src/dumpifs/dumpifs \
|
|
&& make -C /src/dumpifs dumpifs \
|
|
&& fetch-commit https://github.com/lzfse/lzfse.git /src/lzfse "${LZFSE_COMMIT}" \
|
|
&& make -C /src/lzfse install \
|
|
&& fetch-commit https://github.com/Lekensteyn/dmg2img.git /src/dmg2img "${DMG2IMG_COMMIT}" \
|
|
&& make -C /src/dmg2img dmg2img vfdecrypt HAVE_LZFSE=1 \
|
|
&& install -m 755 /src/dumpifs/dumpifs /usr/local/bin/lzfse \
|
|
/src/dmg2img/dmg2img /src/dmg2img/vfdecrypt /out/bin/
|
|
|
|
# Python extractors binwalk calls (upstream requirements.txt, versions
|
|
# pinned), and esptool. Each gets its own venv at its final path; the final
|
|
# image uses the same base and Python, so the venvs can be copied as-is.
|
|
RUN python3 -m venv /opt/binwalk-py \
|
|
&& CC=clang /opt/binwalk-py/bin/pip install --no-cache-dir --disable-pip-version-check \
|
|
uefi_firmware==1.16 jefferson==0.4.8 ubi-reader==0.8.16 \
|
|
lz4==4.4.5 zstandard==0.25.0 \
|
|
"vmlinux-to-elf @ git+https://github.com/marin-m/vmlinux-to-elf@${VMLINUX_TO_ELF_COMMIT}" \
|
|
&& python3 -m venv /opt/esptool \
|
|
&& /opt/esptool/bin/pip install --no-cache-dir --disable-pip-version-check \
|
|
"esptool==${ESPTOOL_VERSION}"
|
|
|
|
|
|
# --- Final image -------------------------------------------------------------
|
|
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 (downloaded and verified in the ghidra-dist stage). Ghidra 12.1.4
|
|
# needs JDK 21 (application.java.min=21). The full JDK, not the headless one,
|
|
# because Ghidra's GUI needs the AWT/X11 libraries.
|
|
ARG GHIDRA_VERSION
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends openjdk-21-jdk unzip \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
COPY --from=ghidra-dist /opt/ghidra_${GHIDRA_VERSION}_PUBLIC /opt/ghidra_${GHIDRA_VERSION}_PUBLIC
|
|
RUN 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
|
|
|
|
# ESP32 Flash Loader (built above), installed like GhidraMCP into Ghidra's
|
|
# own Extensions directory. It's a loader, not a plugin, so no tool config is
|
|
# needed: it shows up as a format in File > Import File.
|
|
COPY --from=esp32-loader-build /out/esp32 /opt/ghidra/Ghidra/Extensions/esp32
|
|
|
|
# GhidraMCP (https://github.com/LaurieWired/GhidraMCP), official release zip
|
|
# checked against the SHA-256 GitHub lists for it. It holds:
|
|
# - the Ghidra extension, unpacked into Ghidra's own Extensions directory so
|
|
# it is installed for every user. Its GhidraMCPPlugin is a RELEASED plugin
|
|
# in the Developer package, so the CodeBrowser config above loads it. It
|
|
# serves an HTTP backend on 127.0.0.1:8080 while the CodeBrowser is open.
|
|
# - the ghidra-mcp Python MCP server (wheel), installed into its own venv.
|
|
# Claude Desktop starts it over stdio; it forwards calls to that backend.
|
|
# The wheel only gives version ranges, so every dependency is pinned to
|
|
# the version in GhidraMCP's uv.lock at the release commit.
|
|
ARG GHIDRAMCP_VERSION=2.0
|
|
ARG GHIDRAMCP_SHA256=ebf904fd6782e429e95a60a24fe0724b20c0acb2dedb304135a5281f6b5f2e72
|
|
ARG GHIDRAMCP_COMMIT=1e980252087b2479a21b9ca6d82d5829538651a7
|
|
ARG GHIDRAMCP_UVLOCK_SHA256=f80ffe8df8167535f253976a1dfb702a9a7f8836d884084017dd7c04e1444837
|
|
COPY ghidra/uvlock-to-constraints.py /tmp/uvlock-to-constraints.py
|
|
RUN set -e \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends python3 python3-venv \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& rel="GhidraMCP-release-$(echo "${GHIDRAMCP_VERSION}" | tr . -)" \
|
|
&& curl -fsSLo /tmp/ghidramcp.zip \
|
|
"https://github.com/LaurieWired/GhidraMCP/releases/download/${GHIDRAMCP_VERSION}/${rel}.zip" \
|
|
&& echo "${GHIDRAMCP_SHA256} /tmp/ghidramcp.zip" | sha256sum -c - \
|
|
&& unzip -q /tmp/ghidramcp.zip -d /tmp/ghidramcp \
|
|
&& unzip -q /tmp/ghidramcp/${rel}/ghidra_${GHIDRA_VERSION}_PUBLIC_*_GhidraMCP.zip \
|
|
-d /opt/ghidra/Ghidra/Extensions \
|
|
&& grep -qx "version=${GHIDRA_VERSION}" /opt/ghidra/Ghidra/Extensions/GhidraMCP/extension.properties \
|
|
&& curl -fsSLo /tmp/uv.lock \
|
|
"https://raw.githubusercontent.com/LaurieWired/GhidraMCP/${GHIDRAMCP_COMMIT}/uv.lock" \
|
|
&& echo "${GHIDRAMCP_UVLOCK_SHA256} /tmp/uv.lock" | sha256sum -c - \
|
|
&& python3 -I /tmp/uvlock-to-constraints.py /tmp/uv.lock > /tmp/constraints.txt \
|
|
&& python3 -m venv /opt/ghidra-mcp \
|
|
&& /opt/ghidra-mcp/bin/pip install --no-cache-dir --disable-pip-version-check \
|
|
--constraint /tmp/constraints.txt /tmp/ghidramcp/${rel}/ghidra_mcp-*-py3-none-any.whl \
|
|
&& /opt/ghidra-mcp/bin/ghidra-mcp --help >/dev/null \
|
|
&& rm -rf /tmp/ghidramcp /tmp/ghidramcp.zip /tmp/uv.lock /tmp/constraints.txt /tmp/uvlock-to-constraints.py
|
|
|
|
# binwalk (built above) with the extraction tools its Dockerfile installs.
|
|
# sasquatch (SquashFS variants) comes as a .deb from onekey-sec; the release
|
|
# publishes no checksums, so the SHA-256 was taken when it was pinned.
|
|
# The Python tools' entry points are linked into /usr/local/bin; the venvs'
|
|
# python/pip are not, so the system python3 isn't shadowed.
|
|
ARG SASQUATCH_RELEASE=sasquatch-v4.5.1-5
|
|
ARG SASQUATCH_SHA256_AMD64=23191cb530bed01e9f00b9a28a74d03db057fb111e13ce5fb383356c1fdf7e6c
|
|
# binwalk -E -p (entropy graph as PNG) renders through Kaleido. binwalk's
|
|
# build downloads it into the build stage's /root, which the image doesn't
|
|
# have, so -p would crash; it's fetched here instead (same release the crate
|
|
# uses, no upstream checksum, SHA-256 taken when pinned) and found through
|
|
# KALEIDO_PATH at runtime.
|
|
ARG KALEIDO_VERSION=0.2.1
|
|
ARG KALEIDO_SHA256_AMD64=3eaa3efd41a00db05dd71add7f54ad4e9d4259552e3f7470ac4c1a8594cea214
|
|
COPY --from=binwalk-build /out/bin/ /usr/local/bin/
|
|
COPY --from=binwalk-build /opt/binwalk-py /opt/binwalk-py
|
|
COPY --from=binwalk-build /opt/esptool /opt/esptool
|
|
RUN set -e \
|
|
&& arch="$(dpkg --print-architecture)" \
|
|
&& [ "$arch" = amd64 ] || { echo "sasquatch checksum only pinned for amd64" >&2; exit 1; } \
|
|
&& curl -fsSLo /tmp/sasquatch.deb \
|
|
"https://github.com/onekey-sec/sasquatch/releases/download/${SASQUATCH_RELEASE}/sasquatch_1.0_${arch}.deb" \
|
|
&& echo "${SASQUATCH_SHA256_AMD64} /tmp/sasquatch.deb" | sha256sum -c - \
|
|
&& curl -fsSLo /tmp/kaleido.zip \
|
|
"https://github.com/plotly/Kaleido/releases/download/v${KALEIDO_VERSION}/kaleido_linux_x64.zip" \
|
|
&& echo "${KALEIDO_SHA256_AMD64} /tmp/kaleido.zip" | sha256sum -c - \
|
|
&& unzip -q /tmp/kaleido.zip -d /opt/kaleido \
|
|
&& rm /tmp/kaleido.zip \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
/tmp/sasquatch.deb \
|
|
7zip 7zip-standalone zstd srecord tar unzip sleuthkit cabextract \
|
|
lz4 lzop unrar unyaffs cpio device-tree-compiler \
|
|
liblzo2-2 libucl1 libfontconfig1 \
|
|
&& rm -rf /var/lib/apt/lists/* /tmp/sasquatch.deb \
|
|
&& for f in /opt/binwalk-py/bin/* /opt/esptool/bin/*; do \
|
|
case "$(basename "$f")" in python*|pip*|activate*|Activate*) continue ;; esac; \
|
|
ln -s "$f" /usr/local/bin/; \
|
|
done \
|
|
&& binwalk --version && esptool version
|
|
# Binwalk leaves a symlink to the input file in its extraction directory. The
|
|
# target is a container path, which is dangling when viewed from the host.
|
|
ENV BINWALK_RM_EXTRACTION_SYMLINK=1 \
|
|
KALEIDO_PATH=/opt/kaleido
|
|
|
|
# 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" \
|
|
"/home/${USER_NAME}/.config/Claude"
|
|
|
|
# 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/
|
|
|
|
# Claude Desktop's MCP servers. Only this file is put in ~/.config/Claude;
|
|
# sign-in data is still created at runtime and discarded with the container.
|
|
COPY --chown=${USER_UID}:${USER_GID} claude-desktop/claude_desktop_config.json \
|
|
/home/${USER_NAME}/.config/Claude/claude_desktop_config.json
|
|
|
|
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"]
|