Kali Docker Setup Guide

Preamble
I’ve been using Kali docker for all my hacking needs for a while now, and have been largely happy with the setup. I wasn’t utilizing Docker at its fullest however, and it showed. Every work session started with a series of terminal tabs and commands to facilitate my admittedly Rube Goldberg-y setup (i.e. Burp on the host with a SOCKS proxy to an SSH tunnel into the container so it uses the VPN that’s running from the container so I can seamlessly transfer from analyzing traffic to, say, fuzzing). And god forbid anything ever happened to a container! I’m still heartbroken from the time I tried to automate (well, make Claude automate) the aforementioned setup, only to have it delete the container at teardown.
I was actually having a decent run with my latest container, when randomly and out of nowhere metasploit stopped working. Running msfconsole
did absolutely nothing, and after some debugging I got the gist that systemd
wasn’t running as PID=1 and msfdb
couldn’t locate it.
Never mind that this very container was running for months, during which I used metasploit more than once (although I now suspect I might have been running it directly on my host). Most of the suggested fixes (reinstalling metasploit, running a new container as --privileged
etc) didn’t help, and others required investing time into a more serious setup.
After a brief interlude trying VMWare Fusion, which ended up being clunky and required a ton of finicky adjustments from screen resolutions to key mappings, I have decided that it is indeed high time no invest in a more serious setup.
Requirements
There were 3 main annoyances in my existing work flow:
- Installing metapackages and tools on a new container, which takes time and bandwidth
- Configuring all the little QoL tweaks, which happen on the go and aren’t documented anywhere
- Moving various files between the docker and the host, which generated a lot of duplicates
The first issue is easily addressed using a two-step approach: generate a “baseline” docker image that installs all the required tools, then use it as a base for subsequent containers. By adding various configs (such as ZSH aliases and plugins) to the baseline image, and mounting some shared directories for scripts and VPN config files and such, I resolved the other two issues.
I instructed my AI of choice (still Claude, despite the heartbreak) to generate a dockerfile for the baseline image, as well as scripts to build both the baseline and the actual containers. After some back and forth, I ended up with a fairly robust setup that I plan to use from now on, and in case you want to give it a go - it’s available on my GitHub.
Setup
The main files involved in the setup are a Dockerfile and two deployment scripts. Anything else is there because Claude is an overachiever, and I support that.
The Dockerfile
The baseline container is itself based on the kalilinux/kali-rolling
image. Then we run apt-update
and begin installing packages, which is the most time-consuming part of the whole process. If you’re customizing this for yourself, this is where you add anything you’d like to be preinstalled on your container.
RUN apt-get update && \
apt-get install -y systemd systemd-sysv && \
kali-tools-web \
kali-tools-fuzzing \
...
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Next there are RUN sections for system configs, shell extensions, startup scripts and such. In case you’re not familiar with dockers, each RUN section is translated into a separate layer of the image. An image is essentially a stack of layers, so that each subsequent layer represents the diff from the last one (a bit like Git commits). The separation into layers serves several purposes, the main one being the caching of layers as they are built. If I ever need to change some configuration, such as an oh-my-zsh
theme, rebuilding the baseline image won’t involve re-downloading all the metapackages, because they have been installed in an earlier layer, cached, and can be reused. For this reason, the layers are arranged in order of least-to-most likely to be tinkered with, which also happens to be the order of importance: if you’re a minimalist who doesn’t like fancy shells or shortcuts, feel free to remove the last two sections.
# Create directories and update system packages
RUN mkdir -p /ovpn-configs /host-scripts /var/run/sshd && \
...
# Configure SSH and system settings
RUN echo 'root:kali' | chpasswd && \
...
# Clean up systemd for container use and enable services
RUN cd /lib/systemd/system/sysinit.target.wants/ && \
...
# Create startup script
RUN cat > /startup.sh << 'EOF'
...
# Create systemd service and enable it
RUN cat > /etc/systemd/system/container-init.service << 'EOF' && \
...
# Install and configure oh-my-zsh with plugins and themes
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended && \
...
# Configure powerlevel10k theme
RUN cat > /root/.p10k.zsh << 'EOF'
...
Finally, the work dir is set to be /root
and the init script is set to be systemd
, so Metasploit DB can easily find it under PID=1 and thus resolve the very issue that got me here in the first place!
The Scripts
The create-baseline.sh
script is short and sweet and self-explanatory: run it to create a baseline image from the dockerfile.
quick-deploy.sh
is a bit more elaborate. The intended usage is to pass it a name and a port (for forwarding), and it will create a container based on the latest baseline. Behind the scenes, it runs some checks (to make sure the name and the port aren’t already in use), and creates the shared host directories in case they don’t exist. The docker run
command itself is fairly significant: it mounts the shared host directories, maps the port forwarding and runs the container in --privileged
mode, enabling both the port forwarding and systemd
. Everything else is basically fluff, bit it does look pretty in the terminal.
As for kali-manager.sh
, I’d say it’s still a bit of a work in progress. Claude suggested most of the functionality there, and I went with it, but I’m sure some commands will turn out to be redundant or useless, and I will probably end up adding more commands as I keep using this setup. A few things that already come to mind are altering the container-cleanup
command to stop and delete all containers that don’t stem from the latest baseline, adding an update-packages
that syncs any manually added tools into the dockerfile for future baselines, and maybe some usage stats, because I like it when numbers.
Using Kali Docker Setup
Start by cloning the repo into a directory of your choice. Then cd
into it and give the shells execution permissions.
chmod +x *.sh
Run create-baseline.sh
and wait for it to finish. Then, run quick-deploy.sh
, passing it a witty name and a port you’ll use for tunneling. Access your new container with docker exec -it [container] zsh
.
Containers are cheap, so don’t get attached. Spin up a new one for every engagement, every lab, or each time you come up with a new witty name. Don’t forget to occasionally rebuild your baseline from scratch to grab up-to-date versions of packages and kali itself.
If you want to set up a tunnel to use Burp with a VPN that’s running on your new container, run the following command on your host (you might need to start the ssh service first):
ssh -D [PORT_A] -vCN root@localhost -p [PORT_B]
Where PORT_B
is the port you used when building your container, and root
is the user configured by the dockerfile. Then open Burpsuite, go to Settings > Network > Connections and scroll all the way down to SOCKS proxy. Set the proxy host to localhost
or 127.0.0.1
, and the port to PORT_A
from the command above.
I’d be happy to hear about your experience with the setup, so drop me an email! And if you have issues, suggestions or improvements, feel free to open an issue on GitHub.
Happy hacking, y’all!