From d57b36c0a4385585fb6fd7963b829a6a7c186aca Mon Sep 17 00:00:00 2001 From: Anand-240 Date: Mon, 24 Aug 2026 17:17:44 +0530 Subject: [PATCH] feat(config): allow overriding the config path via URUNC_CONFIG_FILE The config path was hardcoded to /etc/urunc/config.toml, which breaks on immutable or read-only hosts like Talos where /etc is not writable. Add ResolveUruncConfigPath, which reads the URUNC_CONFIG_FILE env var and falls back to /etc/urunc/config.toml when it is unset, and use it at the three places that load the config. Setting the var in the shim is enough since the urunc process it spawns inherits the environment. Fixes: #987 Signed-off-by: Anand-240 --- cmd/urunc/create.go | 2 +- cmd/urunc/main.go | 2 +- docs/configuration.md | 11 ++++++++++- pkg/containerd-shim/guest_rootfs.go | 2 +- pkg/unikontainers/urunc_config.go | 15 +++++++++++++++ pkg/unikontainers/urunc_config_test.go | 14 ++++++++++++++ 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/cmd/urunc/create.go b/cmd/urunc/create.go index 5a9e2f94d..aeb04b77a 100644 --- a/cmd/urunc/create.go +++ b/cmd/urunc/create.go @@ -74,7 +74,7 @@ var createCommand = &cli.Command{ return err } if !cmd.Bool("reexec") { - uruncCfg, _ := unikontainers.LoadUruncConfig(unikontainers.UruncConfigPath) // ignore the error and use default config + uruncCfg, _ := unikontainers.LoadUruncConfig(unikontainers.ResolveUruncConfigPath()) // ignore the error and use default config return createUnikontainer(cmd, uruncCfg) } diff --git a/cmd/urunc/main.go b/cmd/urunc/main.go index 7a103aacf..6b673d97d 100644 --- a/cmd/urunc/main.go +++ b/cmd/urunc/main.go @@ -135,7 +135,7 @@ func main() { return nil, err } // ignore error since ParseLogMetricsConfig will print a warning and return default values - cfg, _ := unikontainers.ParseLogMetricsConfig(unikontainers.UruncConfigPath) + cfg, _ := unikontainers.ParseLogMetricsConfig(unikontainers.ResolveUruncConfigPath()) err := configLogrus(cmd, cfg.Log) if err != nil { return nil, err diff --git a/docs/configuration.md b/docs/configuration.md index a2daa4ba4..312fb3108 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,7 +2,16 @@ ## Configuration File Location -`urunc` looks for its configuration file at `/etc/urunc/config.toml`. If the file doesn't exist or contains invalid configuration, `urunc` will use sensible defaults and continue to operate normally. +`urunc` looks for its configuration file at `/etc/urunc/config.toml` by default. The location can be overridden with the `URUNC_CONFIG_FILE` environment variable, for example `URUNC_CONFIG_FILE=/var/etc/urunc/config.toml`. If the file doesn't exist or contains invalid configuration, `urunc` will use sensible defaults and continue to operate normally. + +One way to set the variable is to replace the `containerd-shim-urunc-v2` binary that containerd invokes with a small wrapper script that sets it and execs the real shim. For example, move the real shim aside (e.g. to `containerd-shim-urunc-v2.real`) and save the following as `/usr/local/bin/containerd-shim-urunc-v2`: + +```bash +#!/bin/bash +URUNC_CONFIG_FILE=/some/path/config.toml exec /usr/local/bin/containerd-shim-urunc-v2.real "$@" +``` + +where `/some/path/config.toml` is the configuration file to use and `/usr/local/bin/containerd-shim-urunc-v2.real` is the actual shim binary. ## Configuration File Format diff --git a/pkg/containerd-shim/guest_rootfs.go b/pkg/containerd-shim/guest_rootfs.go index f8982ecf1..98a4b2531 100644 --- a/pkg/containerd-shim/guest_rootfs.go +++ b/pkg/containerd-shim/guest_rootfs.go @@ -59,7 +59,7 @@ func chooseGuestRootfs(r *taskAPI.CreateTaskRequest) error { } annotations := config.Map() - uruncCfg, err := unikontainers.LoadUruncConfig(unikontainers.UruncConfigPath) + uruncCfg, err := unikontainers.LoadUruncConfig(unikontainers.ResolveUruncConfigPath()) if err != nil && uruncCfg == nil { return err } diff --git a/pkg/unikontainers/urunc_config.go b/pkg/unikontainers/urunc_config.go index 22573f43c..fb071fea3 100644 --- a/pkg/unikontainers/urunc_config.go +++ b/pkg/unikontainers/urunc_config.go @@ -15,6 +15,7 @@ package unikontainers import ( + "os" "strconv" "strings" @@ -24,6 +25,20 @@ import ( const UruncConfigPath = "/etc/urunc/config.toml" +// UruncConfigFileEnv is the environment variable that overrides the default +// location of the urunc configuration file. +const UruncConfigFileEnv = "URUNC_CONFIG_FILE" + +// ResolveUruncConfigPath returns the path to the urunc configuration file. It +// uses the URUNC_CONFIG_FILE environment variable when it is set and falls back +// to UruncConfigPath (/etc/urunc/config.toml) otherwise. +func ResolveUruncConfigPath() string { + if path := os.Getenv(UruncConfigFileEnv); path != "" { + return path + } + return UruncConfigPath +} + type UruncLog struct { Level string `toml:"level"` Syslog bool `toml:"syslog"` diff --git a/pkg/unikontainers/urunc_config_test.go b/pkg/unikontainers/urunc_config_test.go index a51a3eb96..ad50c48c9 100644 --- a/pkg/unikontainers/urunc_config_test.go +++ b/pkg/unikontainers/urunc_config_test.go @@ -618,3 +618,17 @@ path = "/usr/bin/mon" assert.Equal(t, defaultMonitorsConfig(), config.Monitors) }) } + +// Note: these tests use t.Setenv and therefore must not call t.Parallel(). +func TestResolveUruncConfigPath(t *testing.T) { + t.Run("env var unset returns default path", func(t *testing.T) { + t.Setenv(UruncConfigFileEnv, "") + assert.Equal(t, UruncConfigPath, ResolveUruncConfigPath()) + }) + + t.Run("env var set overrides the path", func(t *testing.T) { + customPath := "/var/etc/urunc/config.toml" + t.Setenv(UruncConfigFileEnv, customPath) + assert.Equal(t, customPath, ResolveUruncConfigPath()) + }) +}