diff --git a/.editorconfig b/.editorconfig
index 8da4715e7dae6f3e4c91e9c5642ecab5d361d3f7..6e1faa73bc99322651d37b03aabb24a693f1f0e7 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,20 +1,38 @@
-# EditorConfig is awesome: https://EditorConfig.org
+; EditorConfig is awesome: https://EditorConfig.org
 
-# top-most EditorConfig file
+;top-most EditorConfig file
 root = true
 
-# Unix-style newlines with a newline ending every file
+; Ruby style as default
+; UTF-8 charset
+; Unix-style newlines with a newline ending every file
+; 2 space indent
+; Trim trailing whitespace
 [*]
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-charset = utf-8
 indent_style = space
 indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+; Markdown
+; 4 space indent
+; Trailing whitespace is potentially meaningful, leave it
+[*.md]
+indent_size = 4
+trim_trailing_whitespace = false
 
-# The JSON files contain newlines inconsistently
+; Shell scripts & Python
+; 4 space indent
+[*.{sh,py}]
+indent_size = 4
+
+[Makefile]
+indent_style = tab
+
+; The JSON files contain newlines inconsistently
 [*.json]
-indent_size = 2
 insert_final_newline = ignore
 
 [*.{ps1,psm1}]
diff --git a/.gitattributes b/.gitattributes
index 9032a014a054849db4e22f22b6536cf92c7edd27..77515d78cbad2c4d1739da5120cdce402d747d74 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,3 +3,11 @@
 *.pp eol=lf
 *.sh eol=lf
 *.epp eol=lf
+.editorconfig eol=lf
+.gitattributes eol=lf
+.gitignore eol=lf
+.gitlab-ci.yml eol=lf
+.pdkignore eol=lf
+.vscode/* eol=lf
+Gemfile eol=lf
+metadata.json eol=lf
diff --git a/.gitignore b/.gitignore
index 4c4fe74cda71a571f53cdfe1ffdb9aaad0b4bb27..a9965320a67cf67e14f5bfa41aaf270ebbd32906 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@
 .project
 .envrc
 /inventory.yaml
+/spec/fixtures/litmus_inventory.yaml
 *~
 \#*\#
 .\#*
diff --git a/.pdkignore b/.pdkignore
index 3c059934e7b2f21e361661b8c5c72b819145d191..f96299629ecb210a0cdd0fdb38a4b955515392d6 100644
--- a/.pdkignore
+++ b/.pdkignore
@@ -25,10 +25,12 @@
 .project
 .envrc
 /inventory.yaml
+/spec/fixtures/litmus_inventory.yaml
 *~
 \#*\#
 .\#*
 /appveyor.yml
+/.editorconfig
 /.fixtures.yml
 /Gemfile
 /.gitattributes
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 24650e52586e9ab1e85de79e06f5dc8790efcdd2..a5f6a2b80eb07663d44f5e705340b4e116bb0b67 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -3,6 +3,7 @@
     "puppet.puppet-vscode",
     "rebornix.Ruby",
     "ms-vscode.powershell",
-    "EditorConfig.EditorConfig"
+    "EditorConfig.EditorConfig",
+    "glenbuktenica.unicode-substitutions"
   ]
 }
diff --git a/Gemfile b/Gemfile
index 581f5d708f2ae0c66e6298611cc15c5b6cfe539a..18272fd72e8b95feb3fe64e34a51a9b75a7af794 100644
--- a/Gemfile
+++ b/Gemfile
@@ -24,13 +24,13 @@ group :development do
   gem "puppet-module-posix-dev-r#{minor_version}", '~> 1.0',     require: false, platforms: [:ruby]
   gem "puppet-module-win-default-r#{minor_version}", '~> 1.0',   require: false, platforms: [:mswin, :mingw, :x64_mingw]
   gem "puppet-module-win-dev-r#{minor_version}", '~> 1.0',       require: false, platforms: [:mswin, :mingw, :x64_mingw]
-  gem "puppet-lint-absolute_classname-check", '3.0.0',           require: false
+  gem "puppet-lint-absolute_classname-check", '3.0.1',           require: false
   gem "puppet-lint-absolute_template_path", '1.0.1',             require: false
   gem "puppet-lint-empty_trailing_lines", '0.0.1',               require: false
-  gem "puppet-lint-file_ensure-check", '0.3.1',                  require: false
-  gem "puppet-lint-strict_indent-check", '2.0.7',                require: false
+  gem "puppet-lint-file_ensure-check", '1.0.0',                  require: false
+  gem "puppet-lint-strict_indent-check", '2.0.8',                require: false
   gem "puppet-lint-trailing_comma-check", '0.4.2',               require: false
-  gem "puppet-lint-unquoted_string-check", '2.0.0',              require: false
+  gem "puppet-lint-unquoted_string-check", '2.1.0',              require: false
 end
 group :system_tests do
   gem "puppet-module-posix-system-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby]
diff --git a/files/auth/config.json b/files/auth/config.json
index 465a73855342c28f4fd10e583775f2ea9ee96953..9ec947ccfff50f78769a2f8f7091a6b4592dc137 100644
--- a/files/auth/config.json
+++ b/files/auth/config.json
@@ -59,6 +59,19 @@
 	"DB" : {
 	    "message_size" : 1024000,
 	    "groups" : [ "", "ADMC", "MS", "AUTH" ]
+	},
+
+	// The sandbox server.
+	// Note: The auth server needs to be able to talk to the sandbox server when authenticating (anonymous) students.
+	"SAND" : {
+	    "message_size" : 102400,
+	    "groups" : [ "AUTH", "ADMC", "DB", "SAcl" ]
+	},
+
+	// Command-line tool for sandbox management.
+	"SAcl" : {
+	    "message_size" : 102400,
+	    "groups" : [ "SAND" ]
 	}
     },
 
@@ -81,7 +94,8 @@
 	    "ADMC" : [ "admin" ],
 	    // Note: This might not be a good idea in the long run. We should at least
 	    // put "admin" here instead of "staff".
-	    "KMGR" : [ "staff" ]
+	    "KMGR" : [ "staff" ],
+	    "SAcl" : [ "staff" ]
 	}
     },
 
@@ -92,7 +106,7 @@
 	{
 	    // A list of groups that we allow authenticating using this method. This is mandatory
 	    // for all elements in here.
-	    "allow" : [ "TEST", "EC", "SC", "MS" ],
+	    "allow" : [ "TEST", "EC", "SC", "MS", "ADMC", "DB", "SAND" ],
 
 	    // The debug auth is the simplest. It just allows whatever the connected client
 	    // claimed. It is not good to use in production, and is always disabled unless the
@@ -100,7 +114,7 @@
 	    "type" : "debug"
 	},
 	{
-	    "allow" : [ "DB", "MS", "KDB" ],
+	    "allow" : [ "DB", "MS", "KDB", "SAND" ],
 
 	    // File system authentication. This works for clients on the same system as the
 	    // authentication server (e.g. DB, ARLA, etc.), and relies on UNIX permissions. This
@@ -118,7 +132,7 @@
 	},
 	{
 	    // Slightly different requirements for ADMC and the Key manager.
-	    "allow" : [ "ADMC", "KMGR" ],
+	    "allow" : [ "ADMC", "KMGR", "SAcl" ],
 	    "type" : "fs",
 	    "path" : "/tmp",
 	    "permissions" : "0777",
@@ -134,12 +148,12 @@
 	},
 	{
 	    // Allow authenticating SC, EC and AdmC with Kerberos.
-	    "allow" : [ "EC", "SC", "ADMC" ],
+	    "allow" : [ "EC", "SC", "ADMC", "KMGR", "SAcl" ],
 	    "type" : "kerberos"
 	},
 	{
 	    // Allow TEST, EC, SC, and KMGR with SSH.
-	    "allow" : [ "TEST", "EC", "SC", "KMGR" ],
+	    "allow" : [ "TEST", "EC", "SC", "KMGR", "SAcl" ],
 	    "type" : "ssh",
 
 	    "identity_db" : {
diff --git a/files/broker/broker.service b/files/broker/broker.service
index 751cec9243a3fac5e5e965162b3926e301589433..a4afb6ede7cadd23c9db20b717e9a7b3fb9aaab4 100644
--- a/files/broker/broker.service
+++ b/files/broker/broker.service
@@ -10,6 +10,10 @@ ExecStart=/srv/broker/bin/broker --ssl ssl/cert.pem ssl/key.pem file:ssl/passwor
 Restart=on-failure
 RestartSec=10
 
+# Increase the number of fds available to the broker. The default max is 4096, which should be enough,
+# but a large margin is good here.
+LimitNOFILE=10000
+
 # No limit. We won't overload the system anyway.
 StartLimitInterval=0
 
diff --git a/files/squid/squid.conf b/files/squid/squid.conf
index 7d7c17d558e2ec1bd8d45cc7c22f5a71121287e2..137bf1dd2409319bce09e1aa2d5af71d1b19a0a6 100644
--- a/files/squid/squid.conf
+++ b/files/squid/squid.conf
@@ -19,6 +19,11 @@
 # http://tentix.ida.liu.se:3128/squid-internal-mgr/info
 #
 # Yearly update of certificate:
+# run make_certificate.sh
+# restart squid service
+# update dotfiles
+#
+# OLD
 # 1. Generate certificate:
 #  openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -keyout myCA.pem -out myCA.pem
 #  openssl x509 -in myCA.pem -outform DER -out myCA.der
@@ -35,7 +40,7 @@
 # certutil -d sql:./.pki/nssdb -D -n "exam.ida.liu.se - Linkoping university"
 # certutil -d sql:./.pki/nssdb -L
 
-# OLD. Change in exam environment, (after added in Chromium myCA.der end up "somewhere" in ~/.pki/*
+# OLDER: Change in exam environment, (after added in Chromium myCA.der end up "somewhere" in ~/.pki/*
 #    Use this command to edit chrome exam template settings:
 #  env -i XAUTHORITY=/home/examadm/.Xauthority DISPLAY=$DISPLAY HOME=/home/examadm/Version-3.1/sea/env/courses/template_student_home_files/owned_by_uid chromium-browser --proxy-server="exam.ida.liu.se:3128" --temp-profile
 
@@ -121,7 +126,7 @@ http_access deny all
 
 # Squid normally listens to port 3128
 # http_port 3128
-http_port 3128 ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/etc/ssl/certs/squid/Oct20-Oct21/myCA.pem
+http_port 3128 ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/etc/pki/tls/certs/squid/current/exam.crt
 always_direct allow all
 ssl_bump server-first all
 
diff --git a/metadata.json b/metadata.json
index 30851da4a6d6861e83fcdbadd5b0ec14638fa582..b01ff50fc8501ea71fe6d677d6c5835fde581329 100644
--- a/metadata.json
+++ b/metadata.json
@@ -26,7 +26,7 @@
       "version_requirement": ">= 6.21.0 < 8.0.0"
     }
   ],
-  "pdk-version": "2.1.0",
+  "pdk-version": "2.2.0",
   "template-url": "https://gitlab.it.liu.se/puppet-infra/pdk-templates.git#liu",
-  "template-ref": "heads/liu-0-g8fb0f81"
+  "template-ref": "heads/liu-0-gd8925d4"
 }
diff --git a/pdk.yaml b/pdk.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4bef4bd0f902390cfeb13e968d1879c74963b50e
--- /dev/null
+++ b/pdk.yaml
@@ -0,0 +1,2 @@
+---
+ignore: []
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index c4cb9ef0f8906567ea118c5db8ba59e01707525b..ee9e00395e4a18319dfa49b95f7030797471cc8a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -51,6 +51,18 @@ RSpec.configure do |c|
   c.after(:suite) do
     RSpec::Puppet::Coverage.report!(0)
   end
+
+  # Filter backtrace noise
+  backtrace_exclusion_patterns = [
+    %r{spec_helper},
+    %r{gems},
+  ]
+
+  if c.respond_to?(:backtrace_exclusion_patterns)
+    c.backtrace_exclusion_patterns = backtrace_exclusion_patterns
+  elsif c.respond_to?(:backtrace_clean_patterns)
+    c.backtrace_clean_patterns = backtrace_exclusion_patterns
+  end
 end
 
 # Ensures that a module is defined