In the previous post, I showed how I created a script to generate a realistic but fake campaign within Gophish. This is a great start to a demo, but still put the burden on the user to download and install Gophish first. Then, they’d have to delete everything manually if they wanted to remove the fake campaign.
My goal for the demo was to have everything self-contained. I wanted Gophish itself to be downloaded and run automatically. I wanted the script to execute, adding a fake campaign without any work from the user. I wanted everything to be cleaned up when the user was done with the demo.
Oh, and I wanted it all to happen in a single command.
This post talks about how I managed to get everything working seamlessly in a single docker run
command that could fit in a tweet:
Want to play with Gophish? Now you can create a demo instance with a fake campaign already set up - all with just a single command 🔥:
— Jordan Wright (@jw_sec) January 1, 2019
docker run -ti -p 3333:3333 --rm gophish/demo
Enjoy! pic.twitter.com/1OMzrEcSSe
Building the Container
I opted to use Docker for the demo, largely because it lets me abstract away all the hard work of setting up the demo into a container image.
Since I wanted everything to fit in a single command, I couldn’t use something like docker-compose
. Instead, I needed to get everything working together in a single container, which posed a few challenges.
Here’s the high-level steps I needed to do to make the demo work:
- Download and launch Gophish
- Get the API key that’s generated when the database is created
- Launch the
create_demo.py
script with the retrieved API key
Here’s a flow chart showing what this process looks like:
This made things a bit more difficult, since Docker encourages you to keep containers limited to a single process or application. This is gently enforced by only exposing a single entrypoint in an image.
To make this work, I first wrapped the demo script in a separate bash script to help set things up. I then used supervisord
to launch Gophish and the demo script as services so that they could both run in the same container.
Both of these had their own challenges, so it’s worth covering each in a bit more detail.
Wrapping the Demo Script
The create_demo.py
script assumes Gophish is already running, and requires a valid API key to interact with the Gophish API. This presented a challenge, since I knew there wasn’t a guarantee that Gophish would be ready by the time create_demo.py
was launched.
To solve this, I wrapped the script in a bash script that first waited for the gophish.db
file to exist, indicating that Gophish was running:
DATABASE=/usr/src/gophish/gophish.db
# Wait for the gophish.db file to exist
until [ -f $DATABASE ]; do
>&2 echo "Waiting for database"
sleep 1
done
Once the database was created, I used the same technique to wait for the API key to be generated and added. I could the grab the API key using a simple SQL command:
# Get the API key
export API_KEY=$(sqlite3 $DATABASE 'select api_key from users limit 1');
Finally, I can run the demo!
# Launch the demo
python create_demo.py --api-key=$API_KEY
With the details of how I wanted to execute create_demo.py
out of the way, I needed to actually launch the script and Gophish. This is where Supervisord comes in.
Setting up Supervisord
Supervisord makes it easy to manage processes as if they were services. For example, it handles launching applications and restarting them if they crash.
Supervisord is set up using a configuration file called supervisord.conf
. The configuration is divided into sections, with a section for global configuration, as well as sections for each program you want to launch.
In our case, I had two programs I wanted to launch: Gophish itself, and the demo script. For Gophish, I configured Supervisord to start the process automatically, and restart it if it were to crash for whatever reason:
[program:gophish]
command=/usr/src/gophish/gophish
directory=/usr/src/gophish/
autostart=true
autorestart=true
The demo script is similar, except I only wanted to run it once so I set the autorestart
parameter to false
. I also set up logging to stdout
so that the logs could be viewed when docker run
is executed:
[program:demo]
command=/usr/src/gophish-demo/run_demo.sh
directory=/usr/src/gophish-demo/
autostart=true
autorestart=false
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
That’s it!
All that was left was to build the image using docker build -t gophish/demo:latest
and push it to Docker Hub!
While there were some hurdles to overcome, I’m really happy with how this demo turned out. I’m excited that more people will get to play around with all the powerful features Gophish has to offer- all with a single command.
Enjoy!