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
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.pyscript 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
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
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.