Skip to content

Commit 45f71a3

Browse files
authored
Merge pull request #3114 from daw1012345/refactor-docker
Refactor Docker setup, follow best-practices in containerization and make caldera easier to deploy
2 parents ccde86b + 5e095fd commit 45f71a3

File tree

7 files changed

+174
-107
lines changed

7 files changed

+174
-107
lines changed

.dockerignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,27 @@ tests/
2121
**/release.sh
2222
**/requirements-dev.txt
2323
**/tox.ini
24+
25+
# Artifacts from previous use/compilation
26+
**/__pycache__/
27+
**/node_modules/
28+
**/plugins/magma/dist/
29+
30+
# Artifacts from previous caldera use
31+
data/*_store
32+
data/abilities/*
33+
data/adversaries/*
34+
data/results/*
35+
data/payloads/*
36+
data/facts/*
37+
data/sources/*
38+
data/objectives/*
39+
data/backup/*
40+
data/planners/*
41+
!data/planners/aaa7c857-37a0-4c4a-85f7-4e9f7f30e31a.yml
42+
conf/local.yml
43+
conf/ssh_keys/*
44+
ftp_dir/*
45+
**/plugins/atomic/data/atomic-red-team
46+
**/plugins/emu/data/adversary-emulation-plans
47+
**/plugins/emu/payloads/*

.github/workflows/publish_docker_image.yml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,38 @@ jobs:
3232
username: ${{ github.actor }}
3333
password: ${{ secrets.GITHUB_TOKEN }}
3434

35-
- name: Extract metadata (tags, labels) for Docker
36-
id: meta
35+
- name: Extract metadata (tags, labels) for Docker (slim variant)
36+
id: meta-slim
37+
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175
38+
with:
39+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
40+
flavor: |
41+
latest=auto
42+
prefix=slim-,onlatest=true
43+
suffix=
44+
45+
- name: Build and push Docker image (slim)
46+
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825
47+
with:
48+
context: .
49+
push: true
50+
tags: ${{ steps.meta-slim.outputs.tags }}
51+
labels: ${{ steps.meta-slim.outputs.labels }}
52+
build-args: |
53+
VARIANT=slim
54+
55+
- name: Extract metadata (tags, labels) for Docker (full variant)
56+
id: meta-full
3757
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175
3858
with:
3959
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
4060

41-
- name: Build and push Docker image
61+
- name: Build and push Docker image (full)
4262
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825
4363
with:
4464
context: .
4565
push: true
46-
tags: ${{ steps.meta.outputs.tags }}
47-
labels: ${{ steps.meta.outputs.labels }}
66+
tags: ${{ steps.meta-full.outputs.tags }}
67+
labels: ${{ steps.meta-full.outputs.labels }}
68+
build-args: |
69+
VARIANT=full

Dockerfile

Lines changed: 70 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,91 @@
1-
FROM ubuntu:24.04
2-
SHELL ["/bin/bash", "-c"]
3-
4-
ARG TZ="UTC"
5-
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
6-
echo $TZ > /etc/timezone
1+
# This file uses a staged build, using a different stage to build the UI (magma)
2+
# Build the UI
3+
FROM node:23 AS ui-build
74

85
WORKDIR /usr/src/app
96

10-
# Make sure user cloned caldera recursively before installing anything.
117
ADD . .
12-
RUN if [ -z "$(ls plugins/stockpile)" ]; then echo "stockpile plugin not downloaded - please ensure you recursively cloned the caldera git repository and try again."; exit 1; fi
13-
14-
RUN apt-get update && \
15-
apt-get -y install python3 python3-pip python3-venv git curl golang-go
16-
17-
18-
#WIN_BUILD is used to enable windows build in sandcat plugin
19-
ARG WIN_BUILD=false
20-
RUN if [ "$WIN_BUILD" = "true" ] ; then apt-get -y install mingw-w64; fi
21-
22-
# Set up python virtualenv
23-
ENV VIRTUAL_ENV=/opt/venv/caldera
24-
RUN python3 -m venv $VIRTUAL_ENV
25-
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
26-
27-
# Install pip requirements
28-
RUN pip3 install --no-cache-dir -r requirements.txt
29-
30-
# Set up config file and disable atomic by default
31-
RUN python3 -c "import app; import app.utility.config_generator; app.utility.config_generator.ensure_local_config();"; \
32-
sed -i '/\- atomic/d' conf/local.yml;
8+
# Build VueJS front-end
9+
RUN (cd plugins/magma; npm install && npm run build)
10+
11+
# This is the runtime stage
12+
# It containes all dependencies required by caldera
13+
FROM debian:bookworm-slim AS runtime
14+
15+
# There are two variants - slim and full
16+
# The slim variant excludes some dependencies of *emu* and *atomic* that can be downloaded on-demand if needed
17+
# They are very large
18+
ARG VARIANT=full
19+
ENV VARIANT=${VARIANT}
20+
21+
# Display an error if variant is set incorrectly, otherwise just print information regarding which variant is in use
22+
RUN if [ "$VARIANT" = "full" ]; then \
23+
echo "Building \"full\" container suitable for offline use!"; \
24+
elif [ "$VARIANT" = "slim" ]; then \
25+
echo "Building slim container - some plugins (emu, atomic) may not be available without an internet connection!"; \
26+
else \
27+
echo "Invalid Docker build-arg for VARIANT! Please provide either \"full\" or \"slim\"."; \
28+
exit 1; \
29+
fi
3330

34-
# Compile default sandcat agent binaries, which will download basic golang dependencies.
31+
WORKDIR /usr/src/app
3532

36-
# Install Go dependencies
37-
WORKDIR /usr/src/app/plugins/sandcat/gocat
38-
RUN go mod tidy && go mod download
33+
# Copy in source code and compiled UI
34+
# IMPORTANT NOTE: the .dockerignore file is very important in preventing weird issues.
35+
# Especially if caldera was ever compiled outside of Docker - we don't want those files to interfere with this build process,
36+
# which should be repeatable.
37+
ADD . .
38+
COPY --from=ui-build /usr/src/app/plugins/magma/dist /usr/src/app/plugins/magma/dist
3939

40-
WORKDIR /usr/src/app/plugins/sandcat
40+
# From https://docs.docker.com/build/building/best-practices/
41+
# Install caldera dependencies
42+
RUN apt-get update && \
43+
apt-get --no-install-recommends -y install git curl unzip python3-dev python3-pip golang-go mingw-w64 zlib1g gcc && \
44+
rm -rf /var/lib/apt/lists/*
4145

4246
# Fix line ending error that can be caused by cloning the project in a Windows environment
43-
RUN if [ "$WIN_BUILD" = "true" ] ; then cp ./update-agents.sh ./update-agents-copy.sh; fi
44-
RUN if [ "$WIN_BUILD" = "true" ] ; then tr -d '\15\32' < ./update-agents-copy.sh > ./update-agents.sh; fi
45-
RUN if [ "$WIN_BUILD" = "true" ] ; then rm ./update-agents-copy.sh; fi
46-
47-
RUN ./update-agents.sh
48-
49-
# Check if we can compile the sandcat extensions, which will download golang dependencies for agent extensions
50-
RUN mkdir /tmp/gocatextensionstest
51-
52-
RUN cp -R ./gocat /tmp/gocatextensionstest/gocat
53-
RUN cp -R ./gocat-extensions/* /tmp/gocatextensionstest/gocat/
47+
RUN cd /usr/src/app/plugins/sandcat; tr -d '\15\32' < ./update-agents.sh > ./update-agents.sh
5448

55-
RUN cp ./update-agents.sh /tmp/gocatextensionstest/update-agents.sh
56-
57-
WORKDIR /tmp/gocatextensionstest
58-
59-
RUN mkdir /tmp/gocatextensionstest/payloads
49+
# Set timezone (default to UTC)
50+
ARG TZ="UTC"
51+
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
52+
echo $TZ > /etc/timezone
6053

61-
RUN ./update-agents.sh
54+
# Install pip requirements
55+
RUN pip3 install --break-system-packages --no-cache-dir -r requirements.txt
56+
57+
# For offline atomic (disable it by default in slim image)
58+
# Disable atomic if this is not downloaded
59+
RUN if [ ! -d "/usr/src/app/plugins/atomic/data/atomic-red-team" ] && [ "$VARIANT" = "full" ]; then \
60+
git clone --depth 1 https://github.com/redcanaryco/atomic-red-team.git \
61+
/usr/src/app/plugins/atomic/data/atomic-red-team; \
62+
else \
63+
sed -i '/\- atomic/d' conf/default.yml; \
64+
fi
6265

63-
# Clone atomic red team repo for the atomic plugin
64-
RUN if [ ! -d "/usr/src/app/plugins/atomic/data/atomic-red-team" ]; then \
65-
git clone --depth 1 https://github.com/redcanaryco/atomic-red-team.git \
66-
/usr/src/app/plugins/atomic/data/atomic-red-team; \
66+
# For offline emu
67+
# (Emu is disabled by default, no need to disable it if slim variant is being built)
68+
RUN if [ ! -d "/usr/src/app/plugins/emu/data/adversary-emulation-plans" ] && [ "$VARIANT" = "full" ]; then \
69+
git clone --depth 1 https://github.com/center-for-threat-informed-defense/adversary_emulation_library \
70+
/usr/src/app/plugins/emu/data/adversary-emulation-plans; \
6771
fi
6872

69-
WORKDIR /usr/src/app/plugins/emu
73+
# Download emu payloads
74+
# emu doesn't seem capable of running this itself - always download
75+
RUN cd /usr/src/app/plugins/emu; ./download_payloads.sh
7076

71-
# If emu is enabled, complete necessary installation steps
72-
RUN if [ $(grep -c "\- emu" ../../conf/local.yml) ]; then \
73-
apt-get -y install zlib1g unzip; \
74-
pip3 install -r requirements.txt; \
75-
./download_payloads.sh; \
76-
fi
77+
# The commands above (git clone) will generate *huge* .git folders - remove them
78+
RUN (find . -type d -name ".git") | xargs rm -rf
7779

78-
WORKDIR /usr/src/app
80+
# Install Go dependencies
81+
RUN cd /usr/src/app/plugins/sandcat/gocat; go mod tidy && go mod download
7982

80-
# Install Node.js, npm, and other build VueJS front-end
81-
RUN apt-get update && \
82-
apt-get install -y nodejs npm && \
83-
# Directly use npm to install dependencies and build the application
84-
(cd plugins/magma && npm install) && \
85-
(cd plugins/magma && npm run build) && \
86-
# Remove Node.js, npm, and other unnecessary packages
87-
apt-get remove -y nodejs npm && \
88-
apt-get autoremove -y && \
89-
apt-get clean && \
90-
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
83+
# Update sandcat agents
84+
RUN cd /usr/src/app/plugins/sandcat; ./update-agents.sh
9185

92-
WORKDIR /usr/src/app
86+
# Make sure emu can always be used in container (even if not enabled right now)
87+
RUN cd /usr/src/app/plugins/emu; \
88+
pip3 install --break-system-packages -r requirements.txt
9389

9490
STOPSIGNAL SIGINT
9591

README.md

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,56 @@ These requirements are for the computer running the core framework:
6565
* Recommended: GoLang 1.17+ to dynamically compile GoLang-based agents.
6666
* NodeJS (v16+ recommended for v5 VueJS UI)
6767

68+
## Docker Installation (Recommended)
69+
**Note 1: The image on DockerHub is outdated, please do not use it for the time being!**
70+
71+
**Note 2: The builder plugin will not work within Docker**
72+
73+
**Note 3: The caldera container will automatically generate keys/usernames/password on first start.**
74+
**If you wish to override the default configuration or avoid automatically generated keys/passwords, consider bind-mounting your own**
75+
**configuration file with the `-v <your_path>/conf.yml:/usr/src/app/conf/local.yml` flag**
76+
77+
**Note 4: Data stored by caldera is ephermal by default.**
78+
**If you wish to make it persistent, use docker volumes and/or bind mounts (`-v <path_to_your_data_or_volume_name>:/usr/src/app/data/`).**
79+
**Ensure that the directory structure is the same as in the `data/` directory on GitHub, as caldera will refuse to create these sub-directories if they are missing.**
80+
**Lastly, make sure that the configuration file (Note #3) is also made persistent to prevent issues with encryption keys.**
81+
82+
**Note 5: If you wish to modify data used by the `atomic` plugin, clone the `Atomic Red Team` repository outside the container, apply your modifications and bind-mount it (`-v`) to `/usr/src/app/plugins/atomic/data/atomic-red-team` within the container.**
83+
84+
**Note 6: If you wish to modify data used by `emu`,**
85+
**clone the `adversary_emulation_library` repository locally and bind-mount it (`-v`) to**
86+
**`/usr/src/app/plugins/emu/data/adversary-emulation-plans`**
87+
88+
Local build:
89+
```sh
90+
git clone https://github.com/mitre/caldera.git --recursive
91+
cd caldera
92+
docker build --build-arg VARIANT=full -t caldera .
93+
docker run -it -p 8888:8888 caldera
94+
```
95+
96+
Adjust the port forwarding (`-p`) and build args (`--build-arg`) as desired to make ports accessible or change the caldera variant.
97+
The ports that you expose depend on which contacts you plan on using (see `Dockerfile` and `docker-compose.yml` for reference).
98+
99+
Pre-Built Image (from GitHub Container Registry):
100+
```sh
101+
docker run -it -p 8888:8888 ghcr.io/mitre/caldera:latest
102+
```
103+
This container may be slightly outdated, we recommend building the container yourself.
104+
105+
To gracefully terminate your docker container, do the following:
106+
```Bash
107+
# Find the container ID for your docker container running Caldera
108+
docker ps
109+
110+
# Stop the container
111+
docker stop [container ID]
112+
```
113+
114+
There are two variants available, *full* and *slim*.
115+
The *slim* variant doesn't include files necessary for the *emu* and *atomic* plugins, which will be downloaded on-demand if the plugins are ever enabled. The *full* variant is suitable for operation in environments without an internet connection. Slim images on GHCR are prefixed with "slim".
116+
117+
68118
## Installation
69119

70120
Concise installation steps:
@@ -112,31 +162,6 @@ If you'll be developing the UI, there are a few more additional installation ste
112162

113163
Your Caldera server is available at http://localhost:8888 as usual, but there will now be a hot-reloading development server for the VueJS front-end available at http://localhost:3000. Both logs from the server and the front-end will display in the terminal you launched the server from.
114164

115-
## Docker Deployment
116-
To build a Caldera docker image, ensure you have docker installed and perform the following actions:
117-
```Bash
118-
# Recursively clone the Caldera repository if you have not done so
119-
git clone https://github.com/mitre/caldera.git --recursive
120-
121-
# Build the docker image. Change image tagging as desired.
122-
# WIN_BUILD is set to true to allow Caldera installation to compile windows-based agents.
123-
# Alternatively, you can use the docker compose YML file via "docker-compose build"
124-
cd caldera
125-
docker build . --build-arg WIN_BUILD=true -t caldera:latest
126-
127-
# Run the image. Change port forwarding configuration as desired.
128-
docker run -p 8888:8888 caldera:latest
129-
```
130-
131-
To gracefully terminate your docker container, do the following:
132-
```Bash
133-
# Find the container ID for your docker container running Caldera
134-
docker ps
135-
136-
# Stop the container
137-
docker stop [container ID]
138-
```
139-
140165
## Contributing
141166

142167
Refer to our [contributor documentation](CONTRIBUTING.md).

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ services:
66
context: .
77
dockerfile: Dockerfile
88
args:
9-
TZ: "UTC" #TZ sets timezone for ubuntu setup
10-
WIN_BUILD: "true" #WIN_BUILD is used to enable windows build in sandcat plugin
9+
TZ: "UTC" # Timezone to use in container
10+
VARIANT: "full"
1111
image: caldera:latest
1212
ports:
1313
- "8888:8888"

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ asyncssh==2.14.1
2424
aioftp~=0.20.0
2525
packaging==23.2
2626
croniter~=3.0.3
27+
setuptools==75.6.0

tox.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ description = run tests
1818
deps =
1919
-rrequirements.txt
2020
virtualenv!=20.0.22
21-
setuptools==59.5.0
2221
pre-commit
2322
pytest
2423
pytest-aiohttp

0 commit comments

Comments
 (0)