feat: environment variables via the kernel arguments

Unify getting environment variables, support passing environment
variables via kernel args.

Fixes #6984
See #6999

For META this will be used to pass environment variables to the
installer for ISO images (or PXE booting).

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2023-03-27 23:19:39 +04:00
parent 94c24ca64e
commit ea0e9bdbe4
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
14 changed files with 185 additions and 57 deletions

View File

@ -138,6 +138,18 @@ Talos API was extended to support retrieving a list of network connections (sock
Talos now supports loading network configuration on VMWare platform from the `metadata` key.
See [CAPV IPAM Support](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/blob/main/docs/proposal/20220929-ipam-support.md) and
[Talos issue 6708](https://github.com/siderolabs/talos/issues/6708) for details.
"""
[notes.env]
title = "Kernel Argument `talos.environment`"
description="""\
Talos now supports passing environment variables via `talos.environment` kernel argument.
Example:
```
talos.environment=http_proxy=http://proxy.example.com:8080 talos.environment=https_proxy=http://proxy.example.com:8080
```
"""
[make_deps]

View File

@ -55,6 +55,7 @@ import (
"github.com/siderolabs/talos/internal/app/maintenance"
"github.com/siderolabs/talos/internal/pkg/console"
"github.com/siderolabs/talos/internal/pkg/cri"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/etcd"
"github.com/siderolabs/talos/internal/pkg/install"
"github.com/siderolabs/talos/internal/pkg/meta"
@ -739,7 +740,9 @@ func DiskSizeCheck(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
// SetUserEnvVars represents the SetUserEnvVars task.
func SetUserEnvVars(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
for key, val := range r.Config().Machine().Env() {
for _, env := range environment.Get(r.Config()) {
key, val, _ := strings.Cut(env, "=")
if err = os.Setenv(key, val); err != nil {
return fmt.Errorf("failed to set enivronment variable: %w", err)
}

View File

@ -29,6 +29,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
@ -160,7 +161,9 @@ func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) {
env := []string{}
for key, val := range r.Config().Machine().Env() {
for _, value := range environment.Get(r.Config()) {
key, _, _ := strings.Cut(value, "=")
switch strings.ToLower(key) {
// explicitly exclude proxy variables from apid since this will
// negatively impact grpc connections.
@ -170,7 +173,7 @@ func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) {
case "http_proxy":
case "https_proxy":
default:
env = append(env, fmt.Sprintf("%s=%s", key, val))
env = append(env, value)
}
}

View File

@ -19,6 +19,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
@ -93,14 +94,6 @@ func (c *Containerd) Runner(r runtime.Runtime) (runner.Runner, error) {
},
}
env := []string{}
if r.Config() != nil {
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
}
debug := false
if r.Config() != nil {
@ -111,7 +104,7 @@ func (c *Containerd) Runner(r runtime.Runtime) (runner.Runner, error) {
debug,
args,
runner.WithLoggingManager(r.Logging()),
runner.WithEnv(env),
runner.WithEnv(environment.Get(r.Config())),
runner.WithOOMScoreAdj(-999),
runner.WithCgroupPath(constants.CgroupSystemRuntime),
runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities),

View File

@ -20,6 +20,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
@ -93,16 +94,11 @@ func (c *CRI) Runner(r runtime.Runtime) (runner.Runner, error) {
},
}
env := []string{}
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
return restart.New(process.NewRunner(
r.Config().Debug(),
args,
runner.WithLoggingManager(r.Logging()),
runner.WithEnv(env),
runner.WithEnv(environment.Get(r.Config())),
runner.WithOOMScoreAdj(-500),
runner.WithCgroupPath(constants.CgroupPodRuntime),
runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities),

View File

@ -38,6 +38,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/containers/image"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/etcd"
"github.com/siderolabs/talos/internal/pkg/meta"
"github.com/siderolabs/talos/pkg/argsbuilder"
@ -185,10 +186,7 @@ func (e *Etcd) Runner(r runtime.Runtime) (runner.Runner, error) {
{Type: "bind", Destination: constants.EtcdDataPath, Source: constants.EtcdDataPath, Options: []string{"rbind", "rw"}},
}
env := []string{}
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
env := environment.Get(r.Config())
if goruntime.GOARCH == "arm64" {
env = append(env, "ETCD_UNSUPPORTED_ARCH=arm64")

View File

@ -7,7 +7,6 @@ package services
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
@ -20,6 +19,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/capability"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/mount"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
@ -150,11 +150,6 @@ func (svc *Extension) Runner(r runtime.Runtime) (runner.Runner, error) {
}
}
env := []string{}
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
var restartType restart.Type
switch svc.Spec.Restart {
@ -172,7 +167,7 @@ func (svc *Extension) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithLoggingManager(r.Logging()),
runner.WithNamespace(constants.SystemContainerdNamespace),
runner.WithContainerdAddress(constants.SystemContainerdAddress),
runner.WithEnv(env),
runner.WithEnv(environment.Get(r.Config())),
runner.WithOCISpecOpts(svc.getOCIOptions()...),
runner.WithOOMScoreAdj(-600),
),

View File

@ -27,6 +27,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/capability"
"github.com/siderolabs/talos/internal/pkg/containers/image"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
@ -141,18 +142,13 @@ func (k *Kubelet) Runner(r runtime.Runtime) (runner.Runner, error) {
mounts = append(mounts, mount)
}
env := []string{}
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
return restart.New(containerd.NewRunner(
r.Config().Debug() && r.Config().Machine().Type() == machine.TypeWorker, // enable debug logs only for the worker nodes
&args,
runner.WithLoggingManager(r.Logging()),
runner.WithNamespace(constants.SystemContainerdNamespace),
runner.WithContainerImage(spec.Image),
runner.WithEnv(env),
runner.WithEnv(environment.Get(r.Config())),
runner.WithOCISpecOpts(
containerd.WithRootfsPropagation("shared"),
oci.WithCgroup(constants.CgroupKubelet),

View File

@ -28,6 +28,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
@ -138,10 +139,7 @@ func (t *Trustd) Runner(r runtime.Runtime) (runner.Runner, error) {
{Type: "bind", Destination: filepath.Dir(constants.TrustdRuntimeSocketPath), Source: filepath.Dir(constants.TrustdRuntimeSocketPath), Options: []string{"rbind", "ro"}},
}
env := []string{}
for key, val := range r.Config().Machine().Env() {
env = append(env, fmt.Sprintf("%s=%s", key, val))
}
env := environment.Get(r.Config())
if debug.RaceEnabled {
env = append(env, "GORACE=halt_on_error=1")

View File

@ -0,0 +1,44 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package environment provides a set of functions to get environment variables.
package environment
import (
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/talos/pkg/machinery/config"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
// Get the desired set of the environment variables based on kernel cmdline and machine config.
//
// The returned value is a list of strings in the form of "key=value".
func Get(cfg config.Provider) []string {
return GetCmdline(procfs.ProcCmdline(), cfg)
}
// GetCmdline the desired set of the environment variables based on kernel cmdline.
func GetCmdline(cmdline *procfs.Cmdline, cfg config.Provider) []string {
var result []string
param := cmdline.Get(constants.KernelParamEnvironment)
for idx := 0; ; idx++ {
val := param.Get(idx)
if val == nil {
break
}
result = append(result, *val)
}
if cfg != nil {
for k, v := range cfg.Machine().Env() {
result = append(result, k+"="+v)
}
}
return result
}

View File

@ -0,0 +1,82 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package environment_test
import (
"testing"
"github.com/siderolabs/go-procfs/procfs"
"github.com/stretchr/testify/assert"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/pkg/machinery/config"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
)
func TestGet(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
cmdline string
cfg map[string]string
expected []string
}{
{
name: "empty",
},
{
name: "machine config only",
cfg: map[string]string{
"http_proxy": "http://proxy.example.com:8080",
},
expected: []string{
"http_proxy=http://proxy.example.com:8080",
},
},
{
name: "cmdline only",
cmdline: "talos.environment=foo=bar talos.environment=bar=baz",
expected: []string{
"foo=bar",
"bar=baz",
},
},
{
name: "cmdline and machine config",
cmdline: "talos.environment=foo=bar",
cfg: map[string]string{
"http_proxy": "http://proxy.example.com:8080",
},
expected: []string{
"foo=bar",
"http_proxy=http://proxy.example.com:8080",
},
},
} {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
cmdline := procfs.NewCmdline(test.cmdline)
var cfg config.Provider
if test.cfg != nil {
cfg = &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineEnv: test.cfg,
},
}
}
result := environment.GetCmdline(cmdline, cfg)
assert.Equal(t, test.expected, result)
})
}
}

View File

@ -27,6 +27,7 @@ import (
containerdrunner "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/pkg/capability"
"github.com/siderolabs/talos/internal/pkg/containers/image"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/extensions"
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/config"
@ -165,24 +166,16 @@ func RunInstallerContainer(disk, platform, ref string, cfg config.Provider, opts
args = append(args, "--extra-kernel-arg", arg)
}
if c := procfs.ProcCmdline().Get(constants.KernelParamSideroLink).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamSideroLink, *c))
}
if c := procfs.ProcCmdline().Get(constants.KernelParamEventsSink).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamEventsSink, *c))
}
if c := procfs.ProcCmdline().Get(constants.KernelParamLoggingKernel).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamLoggingKernel, *c))
}
if c := procfs.ProcCmdline().Get(constants.KernelParamEquinixMetalEvents).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamEquinixMetalEvents, *c))
}
if c := procfs.ProcCmdline().Get(constants.KernelParamDashboardDisabled).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", constants.KernelParamDashboardDisabled, *c))
for _, preservedArg := range []string{
constants.KernelParamSideroLink,
constants.KernelParamEventsSink,
constants.KernelParamLoggingKernel,
constants.KernelParamEquinixMetalEvents,
constants.KernelParamDashboardDisabled,
} {
if c := procfs.ProcCmdline().Get(preservedArg).First(); c != nil {
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", preservedArg, *c))
}
}
specOpts := []oci.SpecOpts{
@ -203,6 +196,7 @@ func RunInstallerContainer(disk, platform, ref string, cfg config.Provider, opts
oci.WithApparmorProfile(""),
oci.WithSeccompUnconfined,
oci.WithAllDevicesAllowed,
oci.WithEnv(environment.Get(cfg)),
}
containerOpts := []containerd.NewContainerOpts{

View File

@ -52,6 +52,9 @@ const (
// KernelParamDashboardDisabled is the kernel parameter name for disabling the dashboard.
KernelParamDashboardDisabled = "talos.dashboard.disabled"
// KernelParamEnvironment is the kernel parameter name for passing process environment.
KernelParamEnvironment = "talos.environment"
// BoardNone indicates that the install is not for a specific board.
BoardNone = "none"

View File

@ -225,3 +225,14 @@ If you set `talos.dashboard.disabled=1`, this behavior will be disabled.
Kernel logs will be sent to the currently active console and the dashboard will not be started.
It is set to be `1` by default on SBCs.
#### `talos.environment`
Each value of the argument sets a default environment variable.
The expected format is `key=value`.
Example:
```text
talos.environment=http_proxy=http://proxy.example.com:8080 talos.environment=https_proxy=http://proxy.example.com:8080
```