Home
Unchained
Open Source Blog

Guardcraft: A Minecraft Java Server with Zero CVEs

Erika Heidi, Staff Developer Experience Engineer

When my 8-year-old daughter started playing Minecraft, it didn’t take long for her to feel lonely in her single player world. I tried to join her from my Steam Deck (running Linux and Minecraft Java Edition), but my attempts failed miserably.


“Aren't you a hacker?” she declared, her voice dripping with the skeptical disbelief only an 8-year-old can muster.


As it turns out, Minecraft has two versions that aren’t compatible with each other, and to play with her I had to compromise and set up a Windows machine with Minecraft Bedrock edition. This was a small inconvenience to make your kids happy, which is business as usual for a parent. But it wasn’t only that – I needed a server. I decided to pay for a Minecraft Realm, which works as a managed server subscription, to avoid the pain of setting up and managing a remote server by myself. But I didn’t give up on the idea of eventually going back to Java edition, since that version allows for more extension and customization.


Descending deeper into the Minecraft rabbit hole (and inevitably stumbling upon the Minecraft Reddit hive mind), I began to grasp the true potential of this game – a fantastic platform for family fun. While revisiting the idea of running my own Java server, I eventually came to this question: can I run Minecraft on containers? Better yet, can I run Minecraft on a Chainguard container image?


Running a Minecraft Server on Containers


The first question I had to find an answer for, as a Minecraft newbie, was whether or not I could run a Minecraft server on a container. My colleagues pointed out that yes, this was possible, and shared with me a popular Minecraft server container image from Docker Hub: itzg/minecraft-server . With over 100 million downloads and 2,000 stars, this image jumped out as a popular choice for running a Java Minecraft server on containers. A Bedrock version is also available, from the same author.


A quick docker inspect command tells me a few important things about this image. It is based on Ubuntu, and seems to be up-to-date with frequent builds. Unpacked, it has a size of 793MB, with over 20 layers. But what do security scanners have to say about it? Let’s investigate.


At the time of writing, and according to Grype, this image has an impressive 165 unresolved known vulnerabilities (also known as CVEs - Common Vulnerabilities and Exposures), including 3 critical and 5 high severity ones. Why does that matter? Unpatched software can lead to exploitation and severe consequences.


❯ grype itzg/minecraft-server
 ✔ Vulnerability DB                [no update available]  
 ✔ Loaded image                                                                            itzg/minecraft-server:latest
 ✔ Parsed image                                 sha256:d322e476683085de418f2760bd8a4a5ddc007abe845c662892d81e6c6bcba373
 ✔ Cataloged contents                                  5b7c64643bc7e5afb3c328a47686159cafe0edf922c5e5182eede87b6d94491d
   ├── ✔ Packages                        [494 packages]  
   ├── ✔ File digests                    [8,287 files]  
   ├── ✔ File metadata                   [8,287 locations]  
   └── ✔ Executables                     [1,656 executables]  
 ✔ Scanned for vulnerabilities     [165 vulnerability matches]  
   ├── by severity: 3 critical, 5 high, 101 medium, 41 low, 15 negligible
   └── by status:   16 fixed, 149 not-fixed, 0 ignored 

Let’s never forget about the Log4Shell vulnerability (CVE-2021-44228), a critical vulnerability in the popular Log4J Java logging library. Researchers discovered the first attacks on Minecraft servers, even though malicious actors were already exploiting the vulnerability at large for almost two weeks prior to this initial discovery.



Mojang, the company behind Minecraft, quickly intervened to help users get their servers fixed. But due to the decentralized nature of community servers and the fact that many are run and managed by hobbyists with little to no experience in cybersecurity, it was a slow process at best. The incident highlighted the critical need for robust server security measures, prompting increased security awareness and the implementation of stronger safeguards within the Minecraft community.


The question now is: can we create a better container image to run a Minecraft Java server?


Guardcraft: a Minecraft Java Server Image with low-to-0 CVEs


After digging into some tutorials, I found out that installing a Minecraft Java server wasn’t all that hard. Assuming you have an up-to-date Java runtime installed on your system, the process involves the following steps:


  1. Download the .jar server file

  2. Unpack the file to a local folder

  3. Edit the eula.txt file to assert that you agree with the terms

  4. Edit the server.properties file to customize your server options

  5. Run the .jar server


Following these steps, I was able to build an entirely customizable Minecraft Java server with low-to-no CVEs, based on our free JRE Chainguard Image. The demo files are available in the chainguard-dev/guardcraft-server GitHub repository.


Let’s have a look at what Grype has to say about this image:


❯ grype guardcraft-java
 ✔ Vulnerability DB                [updated]  
 ✔ Loaded image                                                                                  guardcraft-java:latest
 ✔ Parsed image                                 sha256:1416d0516aa11d599f2ad159ccd2cc1e65ed91df8ae59d6432f2058a3e00ea17
 ✔ Cataloged contents                                  3e7608daf5ed0e67f865db7382247b23023a5e6db008e5261638d74dd5f44a1e
   ├── ✔ Packages                        [166 packages]  
   ├── ✔ File digests                    [1,665 files]  
   ├── ✔ File metadata                   [1,665 locations]  
   └── ✔ Executables                     [223 executables]  
 ✔ Scanned for vulnerabilities     [1 vulnerability matches]  
   ├── by severity: 0 critical, 0 high, 0 medium, 0 low, 0 negligible (1 unknown)
   └── by status:   1 fixed, 0 not-fixed, 0 ignored 

From 165 down to 1 CVE of unknown severity, meaning a CVE that is currently under investigation and may not need patching. These numbers can fluctuate as patches are made available by upstream sources. That’s still a terrific improvement when compared to the popular image we analyzed in the previous section of this post.


Demo Overview


Our Dockerfile is based on cgr.dev/chainguard/jre:latest-dev, since it requires a couple of additional packages to be installed: curl and libudev. It creates a new non-root user to run the server, and then proceeds to the installation as described in the previous section. We use an entrypoint script that looks for environment variables with the MC_ prefix, using sed to replace their values within the server.properties file.


Here is the full content of this Dockerfile for your reference:


FROM cgr.dev/chainguard/jre:latest-dev

USER root
RUN apk update && apk add curl libudev
RUN adduser --system minecraft
WORKDIR /usr/share/minecraft

RUN curl -O https://piston-data.mojang.com/v1/objects/4707d00eb834b446575d89a61a11b5d548d8c001/server.jar
RUN java -jar server.jar nogui
RUN sed -i 's/false/true/' eula.txt

COPY build-config.sh /usr/share/minecraft/build-config.sh
RUN chmod +x /usr/share/minecraft/build-config.sh

RUN chown -R minecraft /usr/share/minecraft
USER minecraft

ENTRYPOINT ["/usr/share/minecraft/build-config.sh", "java", "-jar" , "/usr/share/minecraft/server.jar", "nogui"]

To facilitate setting up environment variables, port redirection, and volume shares (in case you need to persist server data), a docker-compose.yaml file is also included:


services:
  java-server:
    image: guardcraft-java
    restart: unless-stopped
    ports:
      - 25565:25565
    environment:
      # Server properties Set Up
      # MC_* variables will be replaced in the server.properties file
      # Hyphens must be replaced with underscores
      MC_gamemode: "survival"
      MC_difficulty: "easy"
      MC_motd: "Welcome to GuardCraft!"
      MC_level_name: "GuardCraft"
      MC_level_seed: "-1718501946501227358"

You can use the MC_ environment variables to set up any valid server property, using the format MC_<property_name>. Just remember to replace hyphens (-) with underscores (_), since hyphens are not supported in bash variable names. For example, to set the server-port property, use the MC_server_port environment variable.


Running the Demo


To run the demo, you’ll need Docker Engine. Check the Docker documentation for download instructions.


Start by cloning the repository to your local machine and accessing the project folder:


git clone https://github.com/chainguard-dev/guardcraft-server.git
cd guardcraft-server

To build the image, run:


docker build -t guardcraft-java .

Use the following command to run the image in your local environment:


docker compose up

You should get output with relevant information about the server setup:


[+] Running 1/1
 ✔ Container guardcraft-server-java-server-1  Recreated                                                             0.1s 
Attaching to java-server-1
java-server-1  | Setting difficulty=easy
java-server-1  | Setting gamemode=survival
java-server-1  | Setting level-name=GuardCraft
java-server-1  | Setting level-seed=-1718501946501227358
java-server-1  | Setting motd=Welcome to GuardCraft!
java-server-1  | Starting net.minecraft.server.Main
java-server-1  | [18:11:14] [ServerMain/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]
java-server-1  | [18:11:14] [ServerMain/INFO]: No existing world data, creating new world
java-server-1  | [18:11:15] [ServerMain/INFO]: Loaded 1370 recipes
java-server-1  | [18:11:15] [ServerMain/INFO]: Loaded 1481 advancements
java-server-1  | [18:11:15] [Server thread/INFO]: Starting minecraft server version 1.21.4
java-server-1  | [18:11:15] [Server thread/INFO]: Loading properties
java-server-1  | [18:11:15] [Server thread/INFO]: Default game type: SURVIVAL
java-server-1  | [18:11:15] [Server thread/INFO]: Generating keypair
java-server-1  | [18:11:15] [Server thread/INFO]: Starting Minecraft server on *:25565

Once the server is up and running, you can connect using a Minecraft Java client on the same local network.


Connecting to the Server


To connect to the server, you'll need to access the “multiplayer” menu and add a new server using the host machine's local IP address and port 25565 (the default port). You can also use the localhost address if you're running the server on the same machine as the client.


To find your local IP address on Linux systems, you can use the following command:


ip -o route get to 8.8.8.8 | sed -n 's/.*src \([0-9.]\+\).*/\1/p'

When connecting, you should see details about your user on the server logs:


java-server-1  | [18:12:21] [Server thread/INFO]: Server empty for 60 seconds, pausing
java-server-1  | [18:14:29] [User Authenticator #1/INFO]: UUID of player boredcatmom is xxxxx-xxxx-xxxx-xxxx-xxxxxxxx
java-server-1  | [18:14:30] [Server thread/INFO]: boredcatmom[/192.168.178.167:37192] logged in with entity id 78 at (20.5, 66.0, 4.5)
java-server-1  | [18:14:30] [Server thread/INFO]: boredcatmom joined the game

By default, the image spins up a server in Survival mode, with Easy difficulty, and a Welcome to GuardCraft! message of the day. The server uses the specified seed to generate the world. You should spawn in an area with a village nearby.



Guardcraft Texture Pack (Optional)


For those of you who are excited about the idea of slaying CVEs and smashing Zero-Days, we created the Guardcraft Texture Pack, a Minecraft mod that will give you a taste of what we do daily at Chainguard to keep your container images safe from those nasty vulnerabilities, starring Linky and a few other nice surprises! To install it, you can uncomment the last 4 lines in the included docker-compose.yaml file:


# GuardCraft Custom Resource pack: Optional. Uncomment from here to enable
      MC_resource_pack: "https://github.com/chainguard-dev/guardcraft-server/releases/download/0.1.2/GuardCraft.zip"
      MC_resource_pack_sha1: "ec784f4156bf0057967620020c9c1010eed2276f"
      MC_resource_pack_id: "be26d8a0-6f82-4dcd-b286-6c5fc3a1e51f"
      MC_require_resource_pack: "false"

This will set up the server to prompt users to automatically download the texture pack when they join the server. You can also download the texture pack directly from the releases page on GitHub and manually install it on any Minecraft Java world.



If you prefer, you can also manually download and install the resource pack in any Java Minecraft world. Download it directly from the GuardCraft Resource Pack Releases page and place the zip file in your resource packs folder, in your local Minecraft installation (no need to unpack it). On Linux systems, the location should be the ~/.minecraft/resourcepacks directory. On macOS, you can find the resource packs folder in ~/Library/Application Support/minecraft/resourcepacks. On Windows, the folder is located at %appdata%\.minecraft\resourcepacks. You'll then be able to modify your world's settings to enable the resource pack by accessing the "Options" menu, then "Resource Packs".


Have fun with low to zero CVEs, and reach out if you would like to learn more! We'll also be hosting a Learning Lab on March 18 that will show you how to build the server yourself – sign up today.

Share

Ready to Lock Down Your Supply Chain?

Talk to our customer obsessed, community-driven team.

Get a demo