GP-3579 Added docker image generation to Ghidra distribution. Use from development source repo is not supported.

This commit is contained in:
ghidraffe 2025-05-08 15:47:15 -04:00 committed by ghidra1
parent 397a814f5f
commit fbdcd1c709
7 changed files with 354 additions and 0 deletions

62
docker/Dockerfile Normal file
View file

@ -0,0 +1,62 @@
###############################################################
# Dockerfile
# This is the Dockerfile for a release of Ghidra.
# It should be built from the root directory of
# a ghidra release using the command
# docker build -f docker/Dockerfile -t <image-name>
###############################################################
FROM alpine:3.20 as base
LABEL org.opencontainers.image.title="ghidra" \
org.opencontainers.image.description="Docker image for Ghidra" \
org.opencontainers.image.source="https://github.com/NationalSecurityAgency/ghidra" \
org.opencontainers.image.licenses="Apache 2.0"
# Configure user, entrypoint, and some env vars first, before making the image larger with dependencies
# so that we can keep the image size as small as possible.
RUN addgroup -g 1001 -S ghidra && adduser -u 1001 -S ghidra -G ghidra
ENTRYPOINT ["/bin/bash", "/ghidra/docker/entrypoint.sh"]
# Set JAVA_HOME so that we don't need to do this manually when Ghidra is first started.
ENV JAVA_HOME /usr/lib/jvm/java-21-openjdk
ENV LD_LIBRARY_PATH /usr/lib/jvm/java-21-openjdk/lib/:/usr/lib/jvm/java-21-openjdk/lib/server/
WORKDIR /ghidra
# validate build context is correct. an error will happen if this file is not present.
COPY ./ghidraRun ./ghidraRun
# update and install dependencies used to both build and run ghidra
RUN apk update \
&& apk add openjdk21 python3 \
bash gcompat \
fontconfig msttcorefonts-installer \
linux-headers libressl-dev \
&& update-ms-fonts
FROM base as build
# install additional dependencies used to build ghidra
RUN apk add gradle \
python3-dev py-pip \
alpine-sdk \
build-base \
gcc g++ make libc-dev zlib-dev musl-dev \
zip readline-dev
# copy the contents of the release into the current working dir.
COPY . .
# build postgres and install pyghidra
RUN /ghidra/Ghidra/Features/BSim/support/make-postgres.sh \
&& python3 -m venv /ghidra/venv \
&& /ghidra/venv/bin/python3 -m pip install --no-index -f /ghidra/Ghidra/Features/PyGhidra/pypkg/dist pyghidra \
&& mkdir /ghidra/repositories && mkdir /ghidra/bsim_datadir
FROM base as runtime
# install additional dependencies needed for running ghidra
RUN apk add openssl openssh-client \
xhost musl-locales musl-locales-lang
USER ghidra
WORKDIR /ghidra
COPY --chown=ghidra:ghidra --from=build /ghidra /ghidra

View file

@ -0,0 +1 @@
docker/Dockerfile*

203
docker/README.md Normal file
View file

@ -0,0 +1,203 @@
# Dockerized Ghidra
## Build
From the root directory of your Ghidra release, run the following command with the correct version for your release.
```
docker build -f docker/Dockerfile -t ghidra/ghidra[:<version>] .
```
The image tag is optional, but highly recommended.
## The MODE environment variable
The Ghidra Docker Container supports the following `MODE`'s of execution:
- gui
- headless
- ghidra-server
- bsim
- bsim-server
- pyghidra
The `MODE` environment variable designates which entrypoint of Ghidra to execute.
The `entrypoint.sh` script is executed upon container startup.
## Configuring a Container
Configuration of a container is done just as any other docker container would be configured.
Volumes can be mounted, environment variables can be set, ports can be mapped from container to the host, and so on.
Configuration steps vary a lot based on what MODE the container is started with.
The base directory for Ghidra within the container is located at `/ghidra`.
All of ghidra's default locations for files, configs, etc., are the same within that.
Ghidra is run as the user `ghidra` within the container.
The `ghidra` user only has permissions to the following directories inside the container:
- `/ghidra`
- `/home/ghidra`
When a container does not receive any arguments passed to it with the `docker run` command,
the corresponding Command Line Interface (CLI) for the `MODE` executed will display it's usage statement.
### Mapping Local Volumes to a Container
Volumes within the container may run into permission issues if the volumes are not accessible by users in the group id `1001`.
The default uid and guid for the container is `1001:1001`. Volumes that get mapped to the container should be accessible by this uid/guid.
Adding the host machine's user to the group `1001` on the host helps manage volumes that will be used in the container.
This can easily be done by executing `sudo usermod -aG 1001 <user>` on Linux.
### Example of Headless Mode
```
docker run \
--env MODE=headless \
--rm \
--volume /path/to/myproject:/home/ghidra/myproject \
--volume /path/to/mybinary:/home/ghidra/mybinary \
ghidra/ghidra:<version> \
/myproject programFolder -import /mybinary
```
Breaking this down line by line:
- `docker run` is going to start a docker container using the image `ghidra/ghidra<:<version>`
- `--env MODE=headless` configures the environment variable `MODE` within the container to be the value `headless`
- `--rm` removes the container after the command is complete
- `--volume /path/to/myproject:/myproject` mounts the local volume `/path/to/myproject` on the host to `/myproject` within the container
- `--volume /path/to/mybinary:/mybinary` mounts the local volume `/path/to/mybinary` on the host to `/mybinary` within the container
- `ghidra/ghidra:<version>` is the full reference for the docker image, where `ghidra/ghidra` is the group and name of the image, and `<version>` is the tag.
- `/myproject programFolder -import /mybinary` are arguments being passed to Ghidra's headless analyzer's command line interface
Passing no arguments will result in the usage of the headless analyzer being displayed.
### Example of Gui Mode
Running Ghidra's Graphical User Interface (GUI) in the docker container is not a recommended method for running Ghidra.
GUI's are not a typical use case for dockerized applications.
```
docker run \
--env MODE=gui \
-it \
--rm \
--net host \
--env DISPLAY \
--volume="$HOME/.Xauthority:/home/ghidra/.Xauthority" \
ghidra/ghidra:<version>
```
In this mode, the container relies on X11 forwarding to display the GUI. Configuration of X11 can vary, but in this case,
the host's Xauthority file is mounted into the container, the container is configured to use the host's network, and the DISPLAY
environment variable is passed to the container. This enables forwarding the GUI back to the host machine's display. Volumes
containing binaries would still need to be mounted to the container as well as volumes for ghidra projects.
The host's `.Xauthority` file must have appropriate permissions - assigned the group`:1001` with `rw` group permissions.
### Example of Ghidra Server Mode
```
docker run \
--env MODE=ghidra-server \
--rm \
-it \
--volume /path/to/my/repositories:/ghidra/repositories \
--volume /path/to/my/configs/server.conf:/ghidra/server/server.conf \
-p 13100:13100 \
-p 13101:13101 \
-p 13102:13102 \
ghidra/ghidra:<version>
```
Volumes would need to be mounted to the server container to save the repositories, users, and also to configure the server as well.
To utilize svrAdmin, exec into the running ghidra server container (`docker exec -it <container-id> bash`) for a bash shell in the container.
After exec'ing into the container, administration and management of the Ghidra server is the same as outside of a containerized environment.
To stop the container, execute the command `docker stop <container-id>`.
## Example of BSIM Server Mode
```
export DATADIR_PATH=/home/ghidrausr/datadir
docker run \
--env MODE=bsim-server \
--rm \
-it \
--volume /path/to/my/datadir:/ghidra/bsim_datadir \
-p 5432:5432 \
ghidra/ghidra:<version> \
/ghidra/bsim_datadir
```
`/ghidra/bsim_datadir` is the directory used to store bsim's data in the container. Other directories could be used on the container,
but make sure that the folder on the host machine has appropriate permissions, assigned the group `:1001`.
This example simply starts a bsim server. Configuring the bsim server and populating it with data
could be done post start within the container in a similar way that ghidra server administration is done.
An administrator would have to exec into the running bsim server container (`docker exec -it <container-id> bash`),
and after exec'ing into the container, administration and management of the Bsim server is the same as outside of a containerized environment.
To stop the container, execute the command `docker stop <container-id>`.
## Example of BSIM CLI Mode
```
docker run \
--env MODE=bsim \
--rm \
-it \
ghidra/ghidra:RELEASE \
generatesigs ghidra://ghidrasvr/demo /home/ghidra \
--bsim postgresql://bsimsvr/demo \
--commit --overwrite \
--user ghidra
```
In this example, the bsim CLI is used to connect to a ghidra server hosted on `ghidrasvr`,
generate signatures for the `demo` repository in that ghidra server and save them to `/home/ghidra`.
and then commit the signatures to the BSIM server hosted on `bsimsvr` in the `demo` database.
## Example of Pyghidra Gui Mode
Running Ghidra's Graphical User Interface (GUI) in the docker container is not a recommended method for running Ghidra.
GUI's are not a typical use case for dockerized applications.
```
docker run \
--env MODE=pyghidra \
-it \
--rm \
--net host \
--env DISPLAY \
--volume="$HOME/.Xauthority:/home/ghidra/.Xauthority:rw" \
ghidra/ghidra:<version> -c
```
In this mode, the container relies on X11 forwarding to display the GUI. Configuration of X11 can vary, but in this case,
the host's Xauthority file is mounted into the container, the container is configured to use the host's network, and the DISPLAY
environment variable is passed to the container. This enables forwarding the GUI back to the host machine's display. Volumes
containing binaries would still need to be mounted to the container as well as volumes for ghidra projects.
The host's `.Xauthority` file must have appropriate permissions - owned by `:1001` with `rw` group permissions.
## Example of Pyghidra Headless Mode
```
docker run \
--env MODE=pyghidra \
--rm \
--volume /path/to/myproject:/myproject \
--volume /path/to/mybinary:/mybinary \
ghidra/ghidra:<version> -H \
/myproject programFolder -import /mybinary
```
Passing no arguments to the pyghidra headless analyzer will result in the help menu being displayed, just like the headless analyzer.
This use case is very similar to the headless mode's example with the added benefit of being able to utilize python3 for Ghidra Scripts.

28
docker/build.gradle Normal file
View file

@ -0,0 +1,28 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
rootProject.assembleDistribution {
from (ROOT_PROJECT_DIR + "/docker") {
into "docker"
exclude "build.gradle"
}
}
rootProject.assembleMarkdownToHtml {
from ("${this.projectDir}/README.md") {
into "."
}
}

View file

@ -0,0 +1,4 @@
##VERSION: 2.0
Dockerfile||GHIDRA||||END|
Dockerfile.dockerignore||GHIDRA||||END|
README.md||GHIDRA||||END|

55
docker/entrypoint.sh Executable file
View file

@ -0,0 +1,55 @@
#!/bin/bash
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
MODE=${MODE:="gui"}
echo "$@"
MAXMEM=${MAXMEM:=2G}
if [[ $MODE == "gui" ]] then
/ghidra/support/launch.sh bg jdk Ghidra "${MAXMEM}" "" ghidra.GhidraRun "$@"
# need to do this since the launched process is not blocking terminal exit
while ! tail -f ~/.config/ghidra/ghidra_*/application.log; do sleep 1 ; done
elif [[ $MODE == "headless" ]] then
LAUNCH_MODE=${LAUNCH_MODE:=fg}
DEBUG_ADDRESS=${DEBUG_ADDRESS:=127.0.0.1:13002}
VMARG_LIST=${VMARG_LIST:="-XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -Djava.awt.headless=true "}
DEBUG_ADDRESS=${DEBUG_ADDRESS} /ghidra/support/launch.sh "${LAUNCH_MODE}" jdk Ghidra-Headless "${MAXMEM}" "${VMARG_LIST}" ghidra.app.util.headless.AnalyzeHeadless "$@"
elif [[ $MODE == "ghidra-server" ]] then
# Note, for svrAdmin, you will need to exec into the container running the ghidra server and use the CLI there.
/ghidra/server/ghidraSvr console
elif [[ $MODE == "bsim" ]] then
LAUNCH_MODE=${LAUNCH_MODE:=fg}
VMARG_LIST=${VMARG_LIST:="-Djava.awt.headless=true "}
/ghidra/support/launch.sh $LAUNCH_MODE jdk "BSim" "${MAXMEM}" "" ghidra.features.bsim.query.ingest.BSimLaunchable "$@"
elif [[ $MODE == "bsim-server" ]] then
LAUNCH_MODE=${LAUNCH_MODE:=fg}
VMARG_LIST=${VMARG_LIST:="-Djava.awt.headless=true -Xshare:off"}
mkdir -p $DATADIR_PATH
/ghidra/support/launch.sh "$LAUNCH_MODE" jdk BSimControl "$MAXMEM" "$VMARG_LIST" ghidra.features.bsim.query.BSimControlLaunchable start $@
# need to do this since the launched process is not blocking terminal exit
while ! tail -f $DATADIR_PATH/logfile; do sleep 1 ; done
elif [[ $MODE == "pyghidra" ]] then
# Add optional JVM args inside the quotes
VMARG_LIST=${VMARG_LIST:=""}
PYGHIDRA_LAUNCHER="/ghidra/Ghidra/Features/PyGhidra/support/pyghidra_launcher.py"
set -e
source /ghidra/venv/bin/activate
/ghidra/venv/bin/python3 "${PYGHIDRA_LAUNCHER}" "/ghidra" ${VMARG_LIST} "$@"
else
echo "Unknown MODE: $MODE. Valid MODE's are gui, headless, ghidra-server, bsim, bsim_ctl, or pyghidra."
fi

View file

@ -33,6 +33,7 @@ includeProjects('GPL')
/*******************************************************************************************
* Include miscellaneous support modules
*******************************************************************************************/
includeProject('docker', '.', true)
includeProject('Doclets', 'GhidraBuild/BuildFiles', true)
includeProject('LaunchSupport', 'GhidraBuild', true)
includeProject('MarkdownSupport', 'GhidraBuild', true)