diff --git a/cmd/incusd/instance.go b/cmd/incusd/instance.go index 45340e2cd98..1ebfe1689fa 100644 --- a/cmd/incusd/instance.go +++ b/cmd/incusd/instance.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/kballard/go-shellquote" ociSpecs "github.com/opencontainers/runtime-spec/specs-go" internalInstance "github.com/lxc/incus/v6/internal/instance" @@ -245,6 +246,23 @@ func instanceCreateFromImage(ctx context.Context, s *state.State, r *http.Reques } } + // Set the entrypoint configuration options. + if len(config.Process.Args) > 0 && args.Config["oci.entrypoint"] == "" { + args.Config["oci.entrypoint"] = shellquote.Join(config.Process.Args...) + } + + if config.Process.Cwd != "" && args.Config["oci.cwd"] == "" { + args.Config["oci.cwd"] = config.Process.Cwd + } + + if args.Config["oci.uid"] == "" { + args.Config["oci.uid"] = fmt.Sprintf("%d", config.Process.User.UID) + } + + if args.Config["oci.gid"] == "" { + args.Config["oci.gid"] = fmt.Sprintf("%d", config.Process.User.GID) + } + err = inst.Update(args, false) if err != nil { return err diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 6cf83f78628..86299222f87 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2764,3 +2764,14 @@ Adds a new `usb` value for `io.bus` on `disk` devices. ## `storage_driver_linstor` This adds a LINSTOR storage driver. + +## `instance_oci_entrypoint` + +This introduces a set of new configuration options on the container to configure the OCI entry point: + +* `oci.entrypoint` +* `oci.cwd` +* `oci.uid` +* `oci.gid` + +Those are initialized at creation time using the values from the OCI image. diff --git a/doc/config_options.txt b/doc/config_options.txt index 09ade1f304e..95ed23f3540 100644 --- a/doc/config_options.txt +++ b/doc/config_options.txt @@ -665,6 +665,40 @@ The specified version expression is used to set `libnvidia-container NVIDIA_REQU ``` + +```{config:option} oci.cwd instance-oci +:condition: "OCI container" +:liveupdate: "no" +:shortdesc: "OCI container working directory" +:type: "string" +Override the working directory of an OCI container. +``` + +```{config:option} oci.entrypoint instance-oci +:condition: "OCI container" +:liveupdate: "no" +:shortdesc: "OCI container entrypoint" +:type: "string" +Override the entrypoint of an OCI container. +``` + +```{config:option} oci.gid instance-oci +:condition: "OCI container" +:liveupdate: "no" +:shortdesc: "OCI container GID" +:type: "string" +Override the GID of the process run in an OCI container. +``` + +```{config:option} oci.uid instance-oci +:condition: "OCI container" +:liveupdate: "no" +:shortdesc: "OCI container UID" +:type: "string" +Override the UID of the process run in an OCI container. +``` + + ```{config:option} raw.apparmor instance-raw :liveupdate: "yes" diff --git a/internal/instance/config.go b/internal/instance/config.go index 8c00a2e0523..150c1e12d10 100644 --- a/internal/instance/config.go +++ b/internal/instance/config.go @@ -652,6 +652,42 @@ var InstanceConfigKeysContainer = map[string]func(value string) error{ // shortdesc: Required driver version "nvidia.require.driver": validate.IsAny, + // gendoc:generate(entity=instance, group=oci, key=oci.entrypoint) + // Override the entrypoint of an OCI container. + // --- + // type: string + // liveupdate: no + // condition: OCI container + // shortdesc: OCI container entrypoint + "oci.entrypoint": validate.IsAny, + + // gendoc:generate(entity=instance, group=oci, key=oci.cwd) + // Override the working directory of an OCI container. + // --- + // type: string + // liveupdate: no + // condition: OCI container + // shortdesc: OCI container working directory + "oci.cwd": validate.Optional(validate.IsAbsFilePath), + + // gendoc:generate(entity=instance, group=oci, key=oci.gid) + // Override the GID of the process run in an OCI container. + // --- + // type: string + // liveupdate: no + // condition: OCI container + // shortdesc: OCI container GID + "oci.gid": validate.Optional(validate.IsUint32), + + // gendoc:generate(entity=instance, group=oci, key=oci.uid) + // Override the UID of the process run in an OCI container. + // --- + // type: string + // liveupdate: no + // condition: OCI container + // shortdesc: OCI container UID + "oci.uid": validate.Optional(validate.IsUint32), + // Caller is responsible for full validation of any raw.* value. // gendoc:generate(entity=instance, group=raw, key=raw.lxc) diff --git a/internal/server/instance/drivers/driver_lxc.go b/internal/server/instance/drivers/driver_lxc.go index 63b177ce985..48137ce8ad3 100644 --- a/internal/server/instance/drivers/driver_lxc.go +++ b/internal/server/instance/drivers/driver_lxc.go @@ -2341,33 +2341,65 @@ func (d *lxc) startCommon() (string, []func() error, error) { } // Configure the entry point. - if len(config.Process.Args) > 0 && slices.Contains([]string{"/init", "/sbin/init", "/s6-init"}, config.Process.Args[0]) { + entrypoint := config.Process.Args + if d.expandedConfig["oci.entrypoint"] != "" { + entrypoint, err = shellquote.Split(d.expandedConfig["oci.entrypoint"]) + if err != nil { + return "", nil, err + } + } + + if len(entrypoint) > 0 && slices.Contains([]string{"/init", "/sbin/init", "/s6-init"}, entrypoint[0]) { // For regular init systems, call them directly as PID1. - err = lxcSetConfigItem(cc, "lxc.init.cmd", shellquote.Join(config.Process.Args...)) + err = lxcSetConfigItem(cc, "lxc.init.cmd", shellquote.Join(entrypoint...)) if err != nil { return "", nil, err } } else { // For anything else, run them under our own PID1. - err = lxcSetConfigItem(cc, "lxc.execute.cmd", shellquote.Join(config.Process.Args...)) + err = lxcSetConfigItem(cc, "lxc.execute.cmd", shellquote.Join(entrypoint...)) if err != nil { return "", nil, err } } - err = lxcSetConfigItem(cc, "lxc.init.cwd", config.Process.Cwd) - if err != nil { - return "", nil, err + // Configure the cwd. + if d.expandedConfig["oci.cwd"] != "" { + err = lxcSetConfigItem(cc, "lxc.init.cwd", d.expandedConfig["oci.cwd"]) + if err != nil { + return "", nil, err + } + } else { + err = lxcSetConfigItem(cc, "lxc.init.cwd", config.Process.Cwd) + if err != nil { + return "", nil, err + } } - err = lxcSetConfigItem(cc, "lxc.init.uid", fmt.Sprintf("%d", config.Process.User.UID)) - if err != nil { - return "", nil, err + // Configure the UID + if d.expandedConfig["oci.uid"] != "" { + err = lxcSetConfigItem(cc, "lxc.init.uid", d.expandedConfig["oci.uid"]) + if err != nil { + return "", nil, err + } + } else { + err = lxcSetConfigItem(cc, "lxc.init.uid", fmt.Sprintf("%d", config.Process.User.UID)) + if err != nil { + return "", nil, err + } } - err = lxcSetConfigItem(cc, "lxc.init.gid", fmt.Sprintf("%d", config.Process.User.GID)) - if err != nil { - return "", nil, err + // Configure the GID + if d.expandedConfig["oci.gid"] != "" { + err = lxcSetConfigItem(cc, "lxc.init.gid", d.expandedConfig["oci.gid"]) + if err != nil { + return "", nil, err + } + } else { + err = lxcSetConfigItem(cc, "lxc.init.gid", fmt.Sprintf("%d", config.Process.User.GID)) + if err != nil { + return "", nil, err + } } // Get all mounts so far. diff --git a/internal/server/metadata/configuration.json b/internal/server/metadata/configuration.json index 778b1c259bb..3e13c882154 100644 --- a/internal/server/metadata/configuration.json +++ b/internal/server/metadata/configuration.json @@ -730,6 +730,46 @@ } ] }, + "oci": { + "keys": [ + { + "oci.cwd": { + "condition": "OCI container", + "liveupdate": "no", + "longdesc": "Override the working directory of an OCI container.", + "shortdesc": "OCI container working directory", + "type": "string" + } + }, + { + "oci.entrypoint": { + "condition": "OCI container", + "liveupdate": "no", + "longdesc": "Override the entrypoint of an OCI container.", + "shortdesc": "OCI container entrypoint", + "type": "string" + } + }, + { + "oci.gid": { + "condition": "OCI container", + "liveupdate": "no", + "longdesc": "Override the GID of the process run in an OCI container.", + "shortdesc": "OCI container GID", + "type": "string" + } + }, + { + "oci.uid": { + "condition": "OCI container", + "liveupdate": "no", + "longdesc": "Override the UID of the process run in an OCI container.", + "shortdesc": "OCI container UID", + "type": "string" + } + } + ] + }, "raw": { "keys": [ { diff --git a/internal/version/api.go b/internal/version/api.go index 4bebe0aea30..7eb0e8e1915 100644 --- a/internal/version/api.go +++ b/internal/version/api.go @@ -475,6 +475,7 @@ var APIExtensions = []string{ "network_io_bus", "disk_io_bus_usb", "storage_driver_linstor", + "instance_oci_entrypoint", } // APIExtensionsCount returns the number of available API extensions.