A development environment often requires sudo permissions to install, configure, and use dependencies during runtime. GitLab now allows secure sudo access for GitLab Remote Development workspaces. This tutorial shows you how to enable GitLab workspace users to securely use sudo commands to perform common tasks.
The challenge
For the sake of this article, say your project is as simple as the below code.
package main
import (
"encoding/json"
"log/slog"
"net/http"
"os"
)
func main() {
// Set up JSON logger
logFile, err := os.OpenFile("server.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer logFile.Close()
jsonHandler := slog.NewJSONHandler(logFile, nil)
logger := slog.New(jsonHandler)
slog.SetDefault(logger)
// Define handlers
http.HandleFunc("/path1", handleRequest)
http.HandleFunc("/path2", handleRequest)
// Start server
slog.Info("Starting server on :3000")
err = http.ListenAndServe(":3000", nil)
if err != nil {
slog.Error("Server failed to start", "error", err)
}
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
data := make(map[string]interface{})
for k, v := range r.Header {
data[k] = v
}
data["method"] = r.Method
data["url"] = r.URL.String()
data["remote_addr"] = r.RemoteAddr
response, err := json.MarshalIndent(data, "", " ")
if err != nil {
slog.Error("Failed to marshal metadata", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Log the metadata
slog.Info("Request received",
"path", r.URL.Path,
"response", string(response),
)
// Write response
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
This code starts an HTTP server on port 3000, exposes two paths: path1
and path2
. Each HTTP request received is logged to a file server.log
.
Let's run this code with go run main.go
and generate some requests.
i=1
while [ "$i" -le 100 ]; do
echo "Iteration $i"
if [ $((random_number % 2)) -eq 0 ]; then
curl "localhost:3000/path1"
else
curl "localhost:3000/path2"
fi
i=$((i + 1))
done
As you work on this application, you realize the need to analyze the logs to debug an issue. You look at the log file and it is long to parse with a simple glance. You remember there is a handy tool, jq, which parses JSON data. But your workspace does not have it installed.
You want to install jq
through the package manager for this workspace only.
sudo apt update
sudo apt install jq
The output is:
sudo: The "no new privileges" flag is set, which prevents sudo from running as root.
sudo: If sudo is running in a container, you may need to adjust the container configuration to disable the flag.
This happens because GitLab workspaces explicitly disallows sudo
access to prevent privilege escalation on the Kubernetes host.
Now, there is a more secure way to run sudo
commands in a workspace.
How sudo access works
That is exactly what we have unlocked in the 17.4 release of GitLab.
You can configure secure sudo access for workspaces using any of the following options:
- Sysbox
- Kata Containers
- User namespaces
We will set up three GitLab agents for workspaces to demonstrate each option.
Sysbox
Sysbox is a container runtime that improves container isolation and enables containers to run the same workloads as virtual machines.
To configure sudo access for a workspace with Sysbox:
- In the Kubernetes cluster, install Sysbox.
- In the GitLab agent for workspaces, set the following config:
remote_development:
enabled: true
dns_zone: "sysbox-update.me.com"
default_runtime_class: "sysbox-runc"
allow_privilege_escalation: true
- Add other settings in the agent config as per your requirements. GitLab agent for workspaces settings for more information about individual settings.
- Allow the agent to be used for workspaces in a group. See the documentation for more information.
- Update GitLab Workspaces Proxy to serve traffic for the domain used in the above agent configuration. See Tutorial: Set up the GitLab workspaces proxy for more information.
Kata Containers
Kata Containers is a standard implementation of lightweight virtual machines that perform like containers but provide the workload isolation and security of virtual machines.
To configure sudo access for a workspace with Kata Containers:
- In the Kubernetes cluster, install Kata Containers.
- In the GitLab agent for workspaces, set the following config:
remote_development:
enabled: true
dns_zone: "kata-update.me.com"
default_runtime_class: "kata-qemu"
allow_privilege_escalation: true
- Add other settings in the agent config as per your requirements. GitLab agent for workspaces settings for more information about individual settings.
- Allow the agent to be used for workspaces in a group. See the documentation for more information.
- Update GitLab Workspaces Proxy to serve traffic for the domain used in the above agent configuration. See Tutorial: Set up the GitLab workspaces proxy for more information.
User namespaces
User namespaces isolate the user running inside the container from the user on the host.
To configure sudo access for a workspace with user namespaces:
- In the Kubernetes cluster, configure user namespaces.
- In the GitLab agent for workspaces, set the following config:
remote_development:
enabled: true
dns_zone: "userns-update.me.com"
use_kubernetes_user_namespaces: true
allow_privilege_escalation: true
- Add other settings in the agent config as per your requirements. GitLab agent for workspaces settings for more information about individual settings.
- Allow the agent to be used for workspaces in a group. See the documentation for more information.
- Update GitLab Workspaces Proxy to serve traffic for the domain used in the above agent configuration. See Tutorial: Set up the GitLab workspaces proxy for more information.
Setting up a Kubernetes cluster with user namespaces configured is challenging since it is behind a beta feature gate in Kubernetes Version 1.31.0. This means it is not yet possible to configure such a cluster on the major cloud providers because they don't provide a mechanism to enable feature gates in their managed Kubernetes offering. Here is an example of configuring a simple Kuberenetes cluster using kubeadm
.
Create a workspace
If you now create a workspace with these agents and try installing jq
through a package manager, it should succeed!
You can analyze the logs using jq
. Say you wanted to inspect the log entries where the path is /path1
, you can run:
jq 'select(.path == "/path1")' server.log
The output is:
{
"time": "2024-10-31T12:04:38.474806+05:30",
"level": "INFO",
"msg": "Request received",
"path": "/path1",
"response": "{\n \"Accept\": [\n \"*/*\"\n ],\n \"User-Agent\": [\n \"curl/8.7.1\"\n ],\n \"method\": \"GET\",\n \"remote_addr\": \"[::1]:61246\",\n \"url\": \"/path1\"\n}"
}
{
"time": "2024-10-31T12:06:22.397453+05:30",
"level": "INFO",
"msg": "Request received",
"path": "/path1",
"response": "{\n \"Accept\": [\n \"*/*\"\n ],\n \"User-Agent\": [\n \"curl/8.7.1\"\n ],\n \"method\": \"GET\",\n \"remote_addr\": \"[::1]:61311\",\n \"url\": \"/path1\"\n}"
}
{
"time": "2024-10-31T12:19:34.974354+05:30",
"level": "INFO",
"msg": "Request received",
"path": "/path1",
"response": "{\n \"Accept\": [\n \"*/*\"\n ],\n \"User-Agent\": [\n \"curl/8.7.1\"\n ],\n \"method\": \"GET\",\n \"remote_addr\": \"[::1]:61801\",\n \"url\": \"/path1\"\n}"
}
Get started today
Learn even more with our Configure sudo access for a workspace documentation. See GitLab agent for workspaces settings for details on individual settings.
New to GitLab Remote Development? Here is a quickstart guide to get you up to speed.