Lab 10 - Configuration Management

Overview

For this lab, we will be installing and configuring a Minecraft server. All of this configuration can be sucessfully done without a Minecraft client. In fact, the server runs out of memory before it even starts properly (before Part h was added) so it would be impossible to play Minecraft on the server anyways.

Getting help

If you want any help with any part of this lab, join the OCF slack (https://ocf.io/slack), and mention me (@fydai) in #decal-general.

The most important trick with puppet

If you mess anything up, delete everything (in particular /home/minecraft), and just run puppet again! Puppet ensures that even starting from nothing, you can reconstruct your entire previous state. If you do this and get issues with Puppet executing things out of order than you would like them, add in a require parameter to the resource that should be defined later. For instance, if you want to create something after the /home/minecraft directory, throw in an require => File['/home/minecraft'] option. In general, capitalize the name of the resource, and put the string before the colon between the square braces.

Part 1: Installing Puppet

First, we’re going to install Puppet. Feel free to simply copy the commands below to set up Puppet. Make sure to copy the whole thing!

wget https://apt.puppetlabs.com/puppet6-release-bionic.deb && \
sudo dpkg -i puppet6-release-bionic.deb && \
sudo apt-get update && \
sudo apt-get -y install puppet

Part 2: Using Puppet

Make a minecraft.pp file anywhere, which through the course of this lab, will eventually configure and run a Minecraft Server. To run your puppet code, use sudo puppet apply minecraft.pp. Puppet, being declarative, will do nothing if the system is already configured properly, so run puppet early and often to detect bugs as soon as possible.

Useful references are this section are the Puppet documentation and there is a lot of sample code avaliable in the OCF Puppet configuration. When you are stuck, looking at existing code and see how they did things will generally be helpful. Also remember that there are code examples on the slides!

Part a: Making a Home Directory and a User

Put the following code into minecraft.pp:

file { '/home/minecraft':
  ensure => 'directory',
}

Run sudo puppet apply minecraft.pp to apply it, and ensure that the /home/minecraft directory was created.

Inside minecraft.pp, write some Puppet code to create a user named minecraft, that the Minecraft server will run under. The minecraft user should have home directory /home/minecraft. Now modify the code creating /home/minecraft to set owner the owner to minecraft user you just made. Check the example code on the slides if you are unsure about how to do this.

Checking Part a:

Run sudo puppet apply minecraft.pp.

Now run ls -l /home and verify that /home/minecraft is owned by the minecraft user.

Part b: Install java

Add a few lines to minecraft.pp to install the default-jre package.

Checking Part b:

Run java and verify that the binary exists.

Part c: Installing the Minecraft Server configuration

Copy paste the contents of https://raw.githubusercontent.com/0xcf/decal-labs/master/a10/server.properties locally into a file named server.properties

Ensure that /home/minecraft/server.properties contains the contents of the server.properties you just saved.

Hint: Use the file function! Also note that in this lab, you should be using absolute file paths.

Read and agree to the Minecraft EULA, and ensure that /home/minecraft/eula.txt contains the text eula=true by hardcoding the string eula=true into your minecraft.pp.

Make sure that all of the above files are owned by the minecraft user.

Checking Part c:

Run ls -l /home/minecraft and ensure that the above files exist, contain what they’re supposed to, and they are all owned by the minecraft user, not yours.

Part d: Installing the Minecraft Server

Ensure that /home/minecraft/server.jar contains the Minecraft Server, available at https://launcher.mojang.com/v1/objects/3dc3d84a581f14691199cf6831b71ed1296a9fdf/server.jar.

A previous version of the lab specified the incorrect link https://launcher.mojang.com/v1/objects/3dc3d84a582f14691199cf6831b71ed1296a9fdf/server.jar. The wrong link has “582” has a substring, whereas the correct link has “581”.

Note that the source parameter of the file resource accepts a URL as its argument. Also make it owned by the minecraft user.

Checking Part d:

You know what to do!

Part e: Templating a systemd unit file

Copy the following template into the same directory into your minecraft.pp file as minecraft.service.erb.

Edit the file to be a proper erb template, so that <INSERT YOUR RAM AMOUNT HERE> becomes the value of the memory_available variable when puppet runs. You want to use the templated variable @memory_available in the .erb file, and declare the variable $memory_available it in the .pp file.

Hint: In the slides, there is an example of templating a file.

Now edit your minecraft.pp file, so that it sets the $memory_available variable to be the half the total amount of RAM available to the system (use Google and StackOverflow), and that it puts the templated file into /etc/systemd/system/minecraft.service.

Hint: You need to figure out how to define variables and set variables in puppet. Note that variables should be prefixed by $. You can assign variables just like any other language. Don’t forget to look at the sample code on the slides (it doesn’t cover variable assignment however) and don’t forget to use absolute paths!

Note that the systemd unit file does not have a proper ExecStop, which maybe result in some world corruption.

[Unit]
Description=Minecraft Server

Wants=network.target
After=network.target

[Service]
User=minecraft
WorkingDirectory=/home/minecraft
# This should look like ExecStart=/usr/bin/java -Xmx504578 -jar server.jar
ExecStart=/usr/bin/java -Xmx<INSERT YOUR RAM AMOUNT HERE> -jar server.jar
ExecStop=/bin/kill -- $MAINPID TimeoutStopSec=5

[Install]
WantedBy=multi-user.target

Checking Part e

Look at /etc/systemd/system/minecraft.service to ensure it contains the contents you want before proceeding.

Part f: Running the service

Ensure that the minecraft systemd service is enabled and started.

Checking Part f

This is the critical moment! If you’ve done everything before correctly, this should work (until Minecraft OOMs)! If the systemd unit fails immediately, try to run the ExecStart command manually, by going into /home/minecraft/ and running sudo -u minecraft java -Xmx1009156 -jar server.jar.

You can verify that something is trying to start by running tail -f /home/minecraft/logs/latest.log. If it ever stops loading, the server has run out of memory, and “Part h” below should have a workaround.

Part g: Backups

We should backup our minecraft server!

Ensure there is a directory /home/minecraft/backups/, owned by the minecraft user.

Ensure there is a script, /home/minecraft/backup.sh that is executable, with the following contents however you’d like.

#!/bin/sh
cp -r /home/minecraft/world "/home/minecraft/backups/world-$(date -Is)"

The command copies the directory containing into the minecraft world into a subdirectory of backups indexed by the current date.

Use puppet to add a cron entry to execute /home/minecraft/backup.sh every minute as the minecraft user.

Checking Part g:

Look in the /home/minecraft/backups contains backups!

Part h: (Bonus, optional if you want Minecraft to not Out Of Memory)

We could be doing this by typing this a bunch of commands to add a swap file, and enabling it as swap, but we will do this puppet style!

Configure puppet to create a 4GB file /swapfile with the command dd if=/dev/zero of=/swapfile bs=2M count=2048. Look through the flags to the exec resource to see how you can do this.

Now configure Puppet to run mkswap /swapfile && swapon /swapfile unless swap is currently active, which you can check by seeing if swapon -s | grep /swapfile returns a zero code. There are two arguments that can do this, unless or onlyif. Experiment to see which one works.

Checking Part h:

Run swapon -s, and check that /swapfile is listed. Ensure that there is no extranous output when you run puppet, if your checks are correctly done, this shouldn’t happen. Check /home/minecraft/logs/latest.log to make sure that the server has start up properly. If it has, then congratulations!

Part i: (Bonus, running Minecraft)

Due to security issues, the Minecraft server running is only listening to 127.0.0.1, which means by default you can only access a Minecraft client running on the VM. You have two ways to get around this. One is changing server-ip in server.properties to 0.0.0.0, which will allow access by anybody. If you do this you probably want to set up a whitelist. The other option is SSH port forwarding. The command, if run on your machine, captures traffic at port 25565 on your local computer, and forwards them to port 25565 on your VM. The command will run forever, just leave it in the background while you try to connect.

ssh -NL 25565:localhost:25565 <your VM>

If you chose the first option, you can connect by typing your domain name or IP into minecraft, if you chose the second, you can connect to localhost.

Part 3: Cleanup

There is currently a cronjob copying the minecraft world every minute. That might run you out of disk space. To disable that, you should run sudo crontab -e and remove the line for the backups.

Trying to run a Minecraft Server constantly might also eat up some system resources. You can stop and disable the minecraft systmemd unit manually if it is causing issues.

If you added the swapfile, you might want to remove that.

Another way you can clean up is with puppet. By default, puppet doesn’t remove files it doesn’t know about. However, you can use ensure => absent to make sure files are gone, and similarly for the other resource types.

Part 4: Submission

Congratulations on finishing the lab!

To submit, copy paste the code you have for each section into the gradescope submission.