Docker: Add a script to build monkey island docker locally

This commit is contained in:
Mike Salvatore 2021-07-26 12:04:40 -04:00
parent d4d055ed95
commit 34b47a07d4
6 changed files with 387 additions and 0 deletions

21
docker/DOCKER_README.md Normal file
View File

@ -0,0 +1,21 @@
# Infection Monkey
How to run Monkey Island from the docker file:
Note: Ports 5000 and 5001 must be available for the island to work.
## Setup
Run the following commands:
```sh
sudo docker load -i dk.monkeyisland.MONKEY_VER_PLACEHOLDER.tar
sudo docker pull mongo:4.2
sudo mkdir -p /var/monkey-mongo/data/db
sudo docker run --name monkey-mongo --network=host -v /var/monkey-mongo/data/db:/data/db -d mongo:4.2
sudo docker run --name monkey-island --network=host -d guardicore/monkey-island:MONKEY_VER_PLACEHOLDER
```
## Start Infecting
Open `https://<Server IP>:5000` using Google Chrome and follow the instructions. You can also visit [the Infection Monkey website](https://infectionmonkey.com) and read the in-depth Getting Started guides.

27
docker/Dockerfile Normal file
View File

@ -0,0 +1,27 @@
# Install python dependencies using the bitnami/python:3.7 image, which includes
# development dependencies.
FROM bitnami/python:3.7 as builder
COPY ./monkey /monkey
WORKDIR /monkey
RUN virtualenv .
RUN . bin/activate && \
cd monkey_island && \
pip install pipenv && \
pipenv sync
# Build the final application using the bitnami/python:3.7-prod image, which
# does not include development dependencies.
FROM bitnami/python:3.7-prod
RUN apt-get update && apt-get install -y iputils-ping && apt-get clean
COPY --from=builder /monkey /monkey
WORKDIR /monkey
EXPOSE 5000
EXPOSE 5001
RUN groupadd -r monkey-island && useradd --no-log-init -r -g monkey-island monkey-island
RUN chmod 444 /monkey/monkey_island/cc/server.key
RUN chmod 444 /monkey/monkey_island/cc/server.csr
RUN chmod 444 /monkey/monkey_island/cc/server.crt
RUN mkdir /monkey_island_data && chmod 700 /monkey_island_data && chown -R monkey-island:monkey-island /monkey_island_data
USER monkey-island
ENTRYPOINT ["/monkey/entrypoint.sh"]

310
docker/build_docker.sh Executable file
View File

@ -0,0 +1,310 @@
WORKSPACE=${WORKSPACE:-$HOME}
BUILD_DIR="$PWD/monkey"
GIT=$WORKSPACE/git
DEFAULT_REPO_MONKEY_HOME=$GIT/monkey
ISLAND_PATH="$BUILD_DIR/monkey_island"
ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries"
MONKEY_ORIGIN_URL="https://github.com/guardicore/monkey.git"
CONFIG_URL="https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/config"
NODE_SRC=https://deb.nodesource.com/setup_12.x
ISLAND_DIR_COPY_TIMEOUT=60 #Seconds
OUTPUT_NAME_TGZ="$PWD/infection_monkey_docker_$(date +%Y%m%d_%H%M%S).tgz"
exit_if_missing_argument() {
if [ -z "$2" ] || [ "${2:0:1}" == "-" ]; then
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
}
echo_help() {
echo "usage: build_appimage.sh [--help] [--agent-binary-dir <PATH>] [--branch <BRANCH>]"
echo " [--monkey-repo <PATH>] [--version <MONKEY_VERSION>]"
echo ""
echo "Creates an AppImage package for Infection Monkey."
echo ""
echo "--agent-binary-dir A directory containing the agent binaries that"
echo " you'd like to include with the AppImage. If this"
echo " parameter is unspecified, the latest release"
echo " binaries will be downloaded from GitHub."
echo ""
echo "--as-root Throw caution to the wind and allow this script"
echo " to be run as root."
echo ""
echo "--branch The git branch you'd like the AppImage to be"
echo " built from. (Default: develop)"
echo ""
echo "--monkey-repo A directory containing the Infection Monkey git"
echo " repository. If the directory is empty or does"
echo " not exist, a new repo will be cloned from GitHub."
echo " If the directory is already a valid GitHub repo,"
echo " it will be used as-is and the --branch parameter"
echo " will have no effect."
echo " (Default: $DEFAULT_REPO_MONKEY_HOME)"
echo ""
echo "--version A version number for the AppImage package."
echo " (Default: dev)"
exit 0
}
is_root() {
return "$(id -u)"
}
has_sudo() {
# 0 true, 1 false
sudo -nv > /dev/null 2>&1
return $?
}
log_message() {
echo -e "\n\n"
echo -e "DOCKER IMAGE BUILDER: $1"
}
handle_error() {
echo "Fix the errors above and rerun the script"
exit 1
}
install_nodejs() {
log_message "Installing nodejs"
curl -sL $NODE_SRC | sudo -E bash -
sudo apt-get install -y nodejs
}
install_build_prereqs() {
sudo apt-get update
sudo apt-get upgrade -y
# monkey island prereqs
sudo apt-get install -y curl libcurl4 openssl git build-essential moreutils
install_nodejs
}
install_docker() {
sudo apt-get install -y docker.io
}
clone_monkey_repo() {
local repo_dir=$1
local branch=$2
if [[ ! -d "$repo_dir" ]]; then
mkdir -p "$repo_dir"
fi
log_message "Cloning files from git"
git clone -c core.autocrlf=false --single-branch --recurse-submodules -b "$branch" "$MONKEY_ORIGIN_URL" "$repo_dir" 2>&1 || handle_error
}
is_valid_git_repo() {
pushd "$1" 2>/dev/null || return 1
git status >/dev/null 2>&1
success="$?"
popd || exit 1
return $success
}
setup_build_dir() {
local agent_binary_dir=$1
local monkey_repo=$2
mkdir "$BUILD_DIR"
copy_entrypoint_to_build_dir
copy_monkey_island_to_build_dir "$monkey_repo/monkey"
add_agent_binaries_to_build_dir "$agent_binary_dir"
generate_ssl_cert
build_frontend
}
copy_entrypoint_to_build_dir() {
cp ./entrypoint.sh "$BUILD_DIR"
chmod 755 "$BUILD_DIR/entrypoint.sh"
}
copy_monkey_island_to_build_dir() {
local src=$1
cp "$src"/__init__.py "$BUILD_DIR"
cp "$src"/monkey_island.py "$BUILD_DIR"
cp -r "$src"/common "$BUILD_DIR/"
if ! timeout "${ISLAND_DIR_COPY_TIMEOUT}" cp -r "$src"/monkey_island "$BUILD_DIR/"; then
log_message "Copying island files takes too long. Maybe you're copying a dev folder instead of a fresh repository?"
exit 1
fi
cp ./server_config.json "$BUILD_DIR"/monkey_island/cc/
}
add_agent_binaries_to_build_dir() {
local agent_binary_dir=$1
if [ -z "$agent_binary_dir" ]; then
download_monkey_agent_binaries
else
copy_agent_binaries_to_appdir "$agent_binary_dir"
fi
make_linux_binaries_executable
}
download_monkey_agent_binaries() {
log_message "Downloading monkey agent binaries to ${ISLAND_BINARIES_PATH}"
load_monkey_binary_config
mkdir -p "${ISLAND_BINARIES_PATH}" || handle_error
curl -L -o "${ISLAND_BINARIES_PATH}/${LINUX_32_BINARY_NAME}" "${LINUX_32_BINARY_URL}"
curl -L -o "${ISLAND_BINARIES_PATH}/${LINUX_64_BINARY_NAME}" "${LINUX_64_BINARY_URL}"
curl -L -o "${ISLAND_BINARIES_PATH}/${WINDOWS_32_BINARY_NAME}" "${WINDOWS_32_BINARY_URL}"
curl -L -o "${ISLAND_BINARIES_PATH}/${WINDOWS_64_BINARY_NAME}" "${WINDOWS_64_BINARY_URL}"
}
load_monkey_binary_config() {
tmpfile=$(mktemp)
log_message "Downloading prebuilt binary configuration"
curl -L -s -o "$tmpfile" "$CONFIG_URL"
log_message "Loading configuration"
source "$tmpfile"
}
copy_agent_binaries_to_appdir() {
cp "$1"/* "$ISLAND_BINARIES_PATH/"
}
make_linux_binaries_executable() {
chmod a+x "$ISLAND_BINARIES_PATH"/monkey-linux-*
}
generate_ssl_cert() {
log_message "Generating certificate"
chmod u+x "${ISLAND_PATH}"/linux/create_certificate.sh
"${ISLAND_PATH}"/linux/create_certificate.sh "${ISLAND_PATH}"/cc
}
build_frontend() {
pushd "$ISLAND_PATH/cc/ui" || handle_error
log_message "Generating front end"
npm ci
npm run dist
popd || handle_error
remove_node_modules
}
remove_node_modules() {
# Node has served its purpose. We don't need to deliver the node modules with
# the AppImage.
rm -rf "$ISLAND_PATH"/cc/ui/node_modules
}
build_docker_image() {
local version=$1
docker_image_name=guardicore/monkey-island:$version
tar_name=./dk.monkeyisland.$version.tar
build_docker_image_tar "$docker_image_name" "$tar_name"
build_docker_image_tgz "$tar_name" "$version"
}
build_docker_image_tar() {
sudo docker build . -t "$1"
sudo docker save "$1" > "$2"
}
build_docker_image_tgz() {
mkdir tgz
cp "$1" ./tgz
cp ./DOCKER_README.md ./tgz/README.md
tar -C ./tgz -cvf "$OUTPUT_NAME_TGZ" --gzip .
}
agent_binary_dir=""
as_root=false
branch="develop"
monkey_repo="$DEFAULT_REPO_MONKEY_HOME"
monkey_version="dev"
while (( "$#" )); do
case "$1" in
--agent-binary-dir)
exit_if_missing_argument "$1" "$2"
agent_binary_dir=$2
shift 2
;;
--as-root)
as_root=true
shift
;;
--branch)
exit_if_missing_argument "$1" "$2"
branch=$2
shift 2
;;
-h|--help)
echo_help
;;
--monkey-repo)
exit_if_missing_argument "$1" "$2"
monkey_repo=$2
shift 2
;;
--version)
exit_if_missing_argument "$1" "$2"
monkey_version=$2
shift 2
;;
*)
echo "Error: Unsupported parameter $1" >&2
exit 1
;;
esac
done
log_message "Building Monkey Island Docker image."
if ! $as_root && is_root; then
log_message "Please don't run this script as root"
exit 1
fi
if ! has_sudo; then
log_message "You need root permissions for some of this script operations. \
Run \`sudo -v\`, enter your password, and then re-run this script."
exit 1
fi
install_build_prereqs
install_docker
if ! is_valid_git_repo "$monkey_repo"; then
clone_monkey_repo "$monkey_repo" "$branch"
fi
setup_build_dir "$agent_binary_dir" "$monkey_repo"
build_docker_image "$monkey_version"
log_message "Docker build script finished."
exit 0

12
docker/clean.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# This is a utility script to clean up after a failed or successful Docker
# image build in order to speed up development and debugging
BUILD_DIR=$HOME/docker
rm -rf $HOME/git/monkey
rm -rf $BUILD_DIR/monkey
rm -rf $BUILD_DIR/tgz
rm $BUILD_DIR/dk.monkeyisland.*.tar
rm $BUILD_DIR/infection_monkey_docker*.tgz

6
docker/entrypoint.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
echo "$@"
source /monkey/bin/activate
python /monkey/monkey_island.py "$@"

11
docker/server_config.json Normal file
View File

@ -0,0 +1,11 @@
{
"data_dir": "/monkey_island_data",
"log_level": "DEBUG",
"environment": {
"server_config": "password",
"deployment": "docker"
},
"mongodb": {
"start_mongodb": false
}
}