Kali-Linux mit XFCE im Docker-Container

Virtuelle Umgebung zum Aufbau von z.B. Schulungsumgebungen oder einfach nur als Jumphost.

Kali-Linux mit XFCE im Docker-Container

Ich habe bereits in einem früheren Blogbeitrag beschrieben, wie man Xubuntu in einem Container betreiben kann. Leider war das nicht so stabil wie ich mir erhoffte.

Xubuntu im Docker-Container
Eine einfache Anleitung, um Xfce4 mit xrdp in einem Docker-Container zu starten.

Da ich in manchen Netzen einen vollständigen Linux-Desktop brauche, dort aber nur eine Docker-Umgebung vorhanden ist, muss ich das Projekt noch einmal etwas stabiler planen. Und das Ergebnis sieht sehr gut aus und lässt sich sehr einfach, wie auch schon die LXC-Variante, aus dem Internet erreichbar machen.

Kali-Desktop in einem Proxmox LXC-Container
Container sind erstaunlich schlank. Doch leider haben sie keine grafische Oberfläche. Es gibt auch kein Kali-Linux CT-Template für Proxmox. An einem dienstlichen Arbeitsplatz kann es hin und wieder erforderlich sein, Netzwerkprobleme zu analysieren. Und möglicherweise gestattet der Arbeitgeber die…

Überblick

Ich werde zuerst ein Image bauen und dieses dann für meinen Container nutzen. Dazu brauchen wir:

  • Das Dockerfile, welches das Image beschreibt
  • Daraus bauen wir dann das Image
  • Mit dem Image starten wir dann einen Container

I. Dockerfile anlegen

Um ein Docker-Image zu bauen (denn diese Anforderung gibts ja noch nicht öffentlich 😉), müssen wir beschreiben, was sich darin befinden soll. Dies geschieht im Dockerfile. Ich habe folgendes dort eingetragen:

FROM kalilinux/kali-rolling

ARG DESKTOP_ENVIRONMENT
ARG REMOTE_ACCESS
ARG KALI_PACKAGE
ARG SSH_PORT
ARG RDP_PORT
ARG VNC_PORT
ARG VNC_DISPLAY
ARG BUILD_ENV
ARG HOSTDIR
ARG CONTAINERDIR
ARG UNAME
ARG UPASS

ENV DEBIAN_FRONTEND noninteractive

# #####################################################
# valid choices for the desktop are 
# e17, gnome, i3, i3-gaps, kde, live, lxde, mate, xfce
# #####################################################

ENV DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT:-xfce}
ENV DESKTOP_PKG=kali-desktop-${DESKTOP_ENVIRONMENT}

# #####################################################
# the remote client to use
# if it is null then it will default to x2go
# valid choices are vnc, rdp, x2go
# #####################################################

ENV REMOTE_ACCESS=${REMOTE_ACCESS:-rdp}

# #####################################################
# the kali packages to install
# if it is null then it will default to "default"
# valid choices are arm, core, default, everything, 
# firmware, headless, labs, large, nethunter
# #####################################################

ENV KALI_PACKAGE=${KALI_PACKAGE:-default}
ENV KALI_PKG=kali-linux-${KALI_PACKAGE}

# #####################################################
# install packages that we always want
# #####################################################

RUN apt update -q --fix-missing  
RUN apt upgrade -y

# Add your packages here:
RUN apt -y install --no-install-recommends sudo wget curl dbus-x11 xinit openssh-server ${DESKTOP_PKG}

RUN apt -y install locales
RUN sed -i s/^#\ de_DE.UTF-8\ UTF-8/de_DE.UTF-8\ UTF-8/ /etc/locale.gen
RUN locale-gen

# #####################################################
# create the start bash shell file
# #####################################################

RUN echo "#!/bin/bash" > /startkali.sh
RUN echo "/etc/init.d/ssh start" >> /startkali.sh
RUN chmod 755 /startkali.sh

# #####################################################
# Install the Kali Packages
# #####################################################

RUN apt -y install --no-install-recommends ${KALI_PKG}

# #####################################################
# create the non-root kali user
# #####################################################

RUN useradd -m -s /bin/bash -G sudo ${UNAME}
RUN echo "${UNAME}:${UPASS}" | chpasswd

# #####################################################
# change the ssh port in /etc/ssh/sshd_config
# When you use the bridge network, then you would
# not have to do that. You could rather add a port
# mapping argument such as -p 2022:22 to the 
# Docker create command. But we might as well
# use the host network and port 22 might be taken
# on the Docker host. Hence we change it 
# here inside the container
# #####################################################

RUN echo "Port $SSH_PORT" >>/etc/ssh/sshd_config

# #################################
# disable power manager plugin xfce
# #################################

RUN rm /etc/xdg/autostart/xfce4-power-manager.desktop >/dev/null 2>&1
RUN if [ -e /etc/xdg/xfce4/panel/default.xml ] ; \
    then \
        sed -i s/power/fail/ /etc/xdg/xfce4/panel/default.xml ; \
    fi

# #############################
# install and configure x2go
# x2go uses ssh
# #############################

RUN if [ "xx2go" = "x${REMOTE_ACCESS}" ]  ; \
    then \
        apt -y install --no-install-recommends x2goserver ; \
        echo "/etc/init.d/x2goserver start" >> /startkali.sh ; \
    fi

# #############################
# install and configure xrdp
# #############################
# currently, xrdp only works
# with the xfce desktop
# #############################

RUN if [ "xrdp" = "x${REMOTE_ACCESS}" ] ; \
    then \
            apt -y install --no-install-recommends xorg xorgxrdp xrdp ; \
            echo "rm -rf /var/run/xrdp >/dev/null 2>&1" >> /startkali.sh ; \
            echo "/etc/init.d/xrdp start" >> /startkali.sh ; \
            sed -i s/^port=3389/port=${RDP_PORT}/ /etc/xrdp/xrdp.ini ; \
            adduser xrdp ssl-cert ; \
            if [ "xfce" = "${DESKTOP_ENVIRONMENT}" ] ; \
            then \
                echo xfce4-session > /home/${UNAME}/.xsession ; \
                chmod +x /home/${UNAME}/.xsession ; \
            fi ; \
    fi

# ###########################################################
# install and configure tigervnc-standalone-server
# ###########################################################
# this needs a bit more tweaking than the other protocols
# we need to set the mandatory security options,
# the password for the connection, the port to use
# and also define the ${UNAME} to be used for the 
# screen VNC_DISPLAY
# the password seems to be overwritten so I am hard
# setting it in the /startkali.sh script each time 
# After running tigervncsession-start, the session will
# terminate once the user logs out. Therefore
# we do a sudo -u ${UNAME} vncserver in an endless loop 
# afterwords. This way we always have a running vnc server
# ###########################################################

RUN if [ "xvnc" = "x${REMOTE_ACCESS}" ] ; \
    then \
        apt -y install --no-install-recommends tigervnc-standalone-server tigervnc-tools; \
        echo "/usr/libexec/tigervncsession-start :${VNC_DISPLAY} " >> /startkali.sh ; \
        echo "echo -e '${UPASS}' | vncpasswd -f >/home/${UNAME}/.vnc/passwd" >> /startkali.sh  ;\
        echo "while true; do sudo -u ${UNAME} vncserver -fg -v ; done" >> /startkali.sh ; \
        echo ":${VNC_DISPLAY}=${UNAME}" >>/etc/tigervnc/vncserver.users ;\
        echo '$localhost = "no";' >>/etc/tigervnc/vncserver-config-mandatory ;\
        echo '$SecurityTypes = "VncAuth";' >>/etc/tigervnc/vncserver-config-mandatory ;\
        mkdir -p /home/${UNAME}/.vnc ;\
        chown ${UNAME}:${UNAME} /home/${UNAME}/.vnc ;\
        touch /home/${UNAME}/.vnc/passwd ;\
        chown ${UNAME}:${UNAME} /home/${UNAME}/.vnc/passwd ;\
        chmod 600 /home/${UNAME}/.vnc/passwd ;\
    fi

# ###########################################################
# The /startkali.sh script may terminate, i.e. if we only 
# have statements inside it like /etc/init.d/xxx start
# then once the startscript has finished, the container 
# would stop. We want to keep it running though.
# therefore I just call /bin/bash at the end of the start
# script. This will not terminate and keep the container
# up and running until it is stopped.
# ###########################################################

RUN echo "/bin/bash" >> /startkali.sh

# ###########################################################
# expose the right ports and set the entrypoint
# ###########################################################

EXPOSE ${SSH_PORT} ${RDP_PORT} ${VNC_PORT}
WORKDIR "/root"
ENTRYPOINT ["/bin/bash"]
CMD ["/startkali.sh"]

Dockerfile

Schauen wir uns die einzelnen Schritte des Dockerfiles genauer an:

Basis-Image:
  • Als Basis-Image wird "kalilinux/kali-rolling" verwendet.
Argument Definition:
  • Diese Argumente übergeben wir später einfach beim Bauen des Images. Aber diese Form macht es später leichter etwas zu ändern oder auszuprobieren.
Umgebungsvariable:
  • DEBIAN_FRONTEND habe ich auf "noninteractive" gesetzt, damit Paket-Installationen still im Hintergrund ablaufen.
Desktop-Umgebung:
  • Wenn kein Wert für DESKTOP_ENVIRONMENT übergeben wird, wird standardmäßig "xfce" verwendet.
  • Entsprechend habe ich die passende Paketgruppe (kali-desktop-${DESKTOP_ENVIRONMENT}) zum Installieren definiert.
Fernzugriff:
  • Ähnlich wie bei der Desktop-Umgebung wird ein Standardwert ("rdp") für REMOTE_ACCESS gesetzt und die passende Paketgruppe (x2goserver oder xrdp) festgelegt.
Kali-Pakete:
  • Standardmäßig werden mit kali-linux-default die allgemeinen Kali-Pakete installiert. Da ich aber nur einen Desktop benötige, habe ich später beim Bauen des Images core übergeben.
Paket-Aktualisierung:
  • Aktualisiert die Paketlisten und führt ein Upgrade aller installierten Pakete durch.

Grundlegende Pakete installieren:

  • Installiert benötigte Pakete wie sudo, wget, curl und die Desktop-Umgebung.

Lokalisierung einrichten:

  • Installiert Lokalisierungspakete.
  • Aktiviert die deutsche Lokalisierung (de_DE.UTF-8).
  • Generiert die Lokalisierungsdaten.

Start-Skript erstellen:

  • Erstellt ein Skript /startkali.sh, welches beim Starten des Containers verschiedene Dienste startet.

Kali-Pakete installieren:

  • Installiert die ausgewählten Pakete aus den Kali-Repositories.

Benutzer anlegen:

  • Legt einen neuen Benutzer (UNAME) mit Passwort (UPASS) und Root-Rechten (sudo) an.

SSH-Port ändern:

  • Ändert den SSH-Port in der Konfigurationsdatei (/etc/ssh/sshd_config), da der standardmäßige Port (22) auf dem Host-System bereits belegt sein könnte.

Energiesparmodus deaktivieren (xfce):

  • Deaktiviert den Energiesparmodus, falls die xfce-Desktop-Umgebung verwendet wird.

x2go Server installieren (optional):

  • Installiert den x2go-Server, wenn RDP als Fernzugriff ausgewählt ist.
  • Fügt den Befehl zum Starten des Servers dem startkali.sh-Skript hinzu.

xrdp Server installieren (optional):

  • Installiert den xrdp-Server, wenn RDP als Fernzugriff ausgewählt ist (Achtung: Funktioniert aktuell nur mit xfce).
  • Fügt Befehle zum Starten des Servers, Setzen des Ports (RDP_PORT) und Konfiguration des xrdp-Servers dem startkali.sh-Skript hinzu.
  • Legt einen neuen Benutzer (xrdp) für den xrdp-Server an.
  • Setzt die gewünschte Desktop-Sitzung (xfce4-session) für den Benutzer.

tigervnc Server installieren (optional):

  • Installiert den tigervnc-Server, wenn VNC ausgewählt wurde.

Whoohaa. Ja. Es ist etwas aufwändiger geworden. Aber dafür auch deutlich flexibler, wie man ab jetzt sehen kann 🙂.

II. Image bauen

Da wir im Dockerfile mit Argumenten/Variablen gearbeitet haben, können wir diese nun einfach übergeben. Der Build-Befehl sieht daher für mein Image so aus:

docker image build --platform linux/amd64 \
        -t custom/kali-linux \
        --build-arg DESKTOP_ENVIRONMENT=xfce \
        --build-arg REMOTE_ACCESS=rdp \
        --build-arg KALI_PACKAGE=core \
        --build-arg RDP_PORT=3389 \
        --build-arg VNC_PORT=5908 \
        --build-arg VNC_DISPLAY=$XVNC_DISPLAY \
        --build-arg SSH_PORT=20022 \
        --build-arg BUILD_ENV=amd64 \
        --build-arg HOSTDIR \
        --build-arg CONTAINERDIR \
        --build-arg UNAME=user \
        --build-arg UPASS=theuserpasswd \
        .

Wie man sieht, werden hier auch die Nutzerdaten festgelegt. Wer also einen anderen Benutzernamen oder Passwort haben will, hat hier die Gelegenheit das anzupassen. Schließlich werden einige Meldungen durch die Console laufen. Dies sind die Ausgaben der Befehle, die im Dockerfile durchlaufen werden.

III. Docker-Container

Wenn das Image fertig gestellt ist, können wir damit einen Container erstellen und starten. Ich habe ihn mit folgendem Befehl erstellt:

docker create   --name kali-linux \
                --network bridge \
                --platform linux/amd64 \
                -p 3389:3389 \
                -p 5908:5908 \
                -p 20022:20022 \
                -t \
                -v ./kali/home/user:/opt \
                custom/kali-linux

Und dann einfach gestartet:

docker start kali-linux

Das wars dann auch schon. Jetzt kann man von Windows aus mittels mstsc.exe und den oben festgelegten Zugangsdaten user:theuserpasswd direkt auf den Desktop zugreifen. Oder von Linux aus mit folgendem Befehl:

xfreerdp /u:user /p:theuserpasswd /dynamic-resolution /v:[HOST_IP]

Das sieht dann so aus:

Login, bei Aufruf ohne Zugangsdaten

Wahrscheinlich will man nun noch seine Tastatur anpassen - Aber ab diesem Punkt hat man bereits ein bestens funktionierendes System. Es benötigt zwar mehr Speicher als die LXC-Lösung. Aber diese Anforderung stand in diesem Fall auf Platz 2.

Fazit

Die Daten persistent abzuspeichern ist noch nicht perfekt gelöst. Daher bevorzuge ich klar meine Proxmox LXC Lösung. Aber als stabiler Desktop in einer Docker-Welt ist dies ein sehr brauchbares System, welches sich auch wieder perfekt via Guacamole im Browserfenster anbieten lässt.

So lässt sich damit recht leicht eine Schulungsumgebung aufbauen, da jeder Teilnehmer schnell seinen Container zugewiesen bekommen kann. Und wenn dann jemand seine Umgebung zerschossen hat, hilft ein simpler Neustart des Containers und der saubere Ausgangszustand ist wieder hergestellt.